Added PEAR:DB support plus database replication support
17 files added
1 files deleted
4 files modified
| | |
| | | |
| | | $rcmail_config = array(); |
| | | |
| | | // database engine (currently supported: mysql) |
| | | $rcmail_config['db_type'] = 'mysql'; |
| | | // PEAR database DSN for read/write operations |
| | | //format is db_provider://user:password@host/databse |
| | | |
| | | // database host |
| | | $rcmail_config['db_host'] = 'localhost'; |
| | | $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail'; |
| | | |
| | | // database user |
| | | $rcmail_config['db_user'] = 'roundcube'; |
| | | // PEAR database DSN for read only operations (if empty write database will be used) |
| | | // userful for database replication |
| | | |
| | | // pwd |
| | | $rcmail_config['db_pass'] = 'pass'; |
| | | |
| | | // database name |
| | | $rcmail_config['db_name'] = 'roundcubemail'; |
| | | |
| | | $rcmail_config['db_dsnr'] = ''; |
| | | |
| | | // you can define specific table names used to store webmail data |
| | | $rcmail_config['db_table_users'] = 'users'; |
| | |
| | | // include base files |
| | | require_once('include/rcube_shared.inc'); |
| | | require_once('include/rcube_imap.inc'); |
| | | require_once('include/rcube_mysql.inc'); |
| | | require_once('include/rcube_db.inc'); |
| | | require_once('include/bugs.inc'); |
| | | require_once('include/main.inc'); |
| | | require_once('include/cache.inc'); |
| | |
| | | in /www/roundcube/webmail/program/steps/mail/sendmail.inc on line 198 |
| | | [30-Sep-2005 23:31:56 +0200] SMTP Error: Connection failed: SMTP: Failed to send data |
| | | in /www/roundcube/webmail/program/steps/mail/sendmail.inc on line 198 |
| | | [02-Oct-2005 12:12:32] PHP Fatal error: Cannot instantiate non-existent class: rcube_db in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 57 |
| | | [02-Oct-2005 12:12:53] PHP Fatal error: Call to undefined function: db_connect() in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_db.inc on line 95 |
| | | [02-Oct-2005 12:13:23] PHP Fatal error: Call to undefined function: dsn_connect() in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_db.inc on line 77 |
| | | [02-Oct-2005 12:13:39] PHP Fatal error: Call to a member function on a non-object in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_db.inc on line 97 |
| | | [02-Oct-2005 12:14:07] PHP Fatal error: Call to a member function on a non-object in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_db.inc on line 97 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: client_id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 69 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: user_prefs in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 84 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: task in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 89 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: mbox in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 145 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 86 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 86 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 218 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 218 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: head in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 64 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined variable: _auth in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 135 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: user_id in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 146 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: user_id in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 158 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: user_id in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 173 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 710 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: newline in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 713 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 710 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: newline in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 713 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: _user in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 953 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined property: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 645 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined property: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 645 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined property: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 645 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: host in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 973 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined variable: form_host in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 999 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: foot in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 107 |
| | | [02-Oct-2005 12:15:14] PHP Notice: Undefined index: foot in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 111 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: user_prefs in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 84 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: mbox in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 145 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 86 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 86 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 218 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 218 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: head in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 64 |
| | | [02-Oct-2005 12:15:22] PHP Notice: Undefined index: _host in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 107 |
| | | [02-Oct-2005 12:15:23] PHP Notice: Undefined variable: auth in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 382 |
| | | [02-Oct-2005 12:15:23] PHP Notice: Undefined offset: 0 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:23] PHP Notice: Undefined offset: 1 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:23] PHP Notice: Undefined offset: 0 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:23] PHP Notice: Undefined offset: 0 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:23] PHP Notice: Undefined offset: 1 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: user_prefs in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 84 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: mbox in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 145 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 86 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/index.php on line 86 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 218 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: _framed in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 218 |
| | | [02-Oct-2005 12:15:26] PHP Notice: Undefined index: head in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 64 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: auth in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 382 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined offset: 0 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined offset: 1 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined offset: 0 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined offset: 0 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined offset: 1 in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 240 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: _mbox in /usr/local/http-docs/admin/html/roundcubemail/program/steps/mail/func.inc on line 29 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: _page in /usr/local/http-docs/admin/html/roundcubemail/program/steps/mail/func.inc on line 35 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: _uid in /usr/local/http-docs/admin/html/roundcubemail/program/steps/mail/func.inc on line 43 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/steps/mail/func.inc on line 322 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 664 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:27] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/steps/mail/func.inc on line 62 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: result in /usr/local/http-docs/admin/html/roundcubemail/program/lib/imap.inc on line 1394 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imagepas in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imagepas in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: class in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: select-all in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 671 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: title in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 686 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imagepas in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imagepas in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: class in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: select-all in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 671 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: title in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 686 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imagepas in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 649 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: image in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: arg in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imagepas in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: class in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 659 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: select-none in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 671 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: title in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 686 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: type in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 646 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 655 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: name in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 661 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: id in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 682 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: nr in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1041 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: command in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1044 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1114 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: a_replace_vars in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1120 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: ucfirst in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1131 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: uppercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1133 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: lowercase in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 1135 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 688 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: alt in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 691 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classact in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 706 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined variable: attirb in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: classsel in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 707 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 708 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: href in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 711 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: prop in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 715 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: imageover in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 717 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: label in /usr/local/http-docs/admin/html/roundcubemail/program/include/main.inc on line 732 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: foot in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 107 |
| | | [02-Oct-2005 12:15:28] PHP Notice: Undefined index: foot in /usr/local/http-docs/admin/html/roundcubemail/program/include/rcube_shared.inc on line 111 |
| | |
| | | |
| | | |
| | | // prepare DB connection |
| | | if (strtolower($CONFIG['db_type'])=='mysql') |
| | | $DB = new rcube_mysql($CONFIG['db_name'], $CONFIG['db_user'], $CONFIG['db_pass'], $CONFIG['db_host']); |
| | | |
| | | // database not supported |
| | | else |
| | | { |
| | | raise_error(array('code' => 500, |
| | | 'type' => 'php', |
| | | 'line' => __LINE__, |
| | | 'file' => __FILE__, |
| | | 'message' => "Database not supported"), TRUE, TRUE); |
| | | return; |
| | | } |
| | | |
| | | $DB = new rcube_db($CONFIG['db_dsnw'], $CONFIG['db_dsnr']); |
| | | |
| | | // we can use the database for storing session data |
| | | if (is_object($DB) && $DB->connect()) |
| | | if (is_object($DB)) |
| | | include_once('include/session.inc'); |
| | | |
| | | |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_db.inc | |
| | | | | |
| | | | This file is part of the RoundCube Webmail client | |
| | | | Copyright (C) 2005, RoundCube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | | PURPOSE: | |
| | | | PEAR:DB wrapper class that implements PEAR DB functions | |
| | | | See http://pear.php.net/package/DB | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | require_once('DB.php'); |
| | | |
| | | class rcube_db |
| | | { |
| | | var $db_dsnw; // DSN for write operations |
| | | var $db_dsnr; // DSN for read operations |
| | | var $db_connected=false; // Already connected ? |
| | | var $db_mode=''; // Connection mode |
| | | var $db_handle=0; // Connection handle |
| | | |
| | | var $a_query_results = array('dummy'); |
| | | var $last_res_id = 0; |
| | | |
| | | // PHP 5 constructor |
| | | function __construct($db_dsnw,$db_dsnr='') |
| | | { |
| | | if ($db_dsnr=='') $db_dsnr=$db_dsnw; |
| | | |
| | | $this->db_dsnw = $db_dsnw; |
| | | $this->db_dsnr = $db_dsnr; |
| | | } |
| | | |
| | | // PHP 4 compatibility |
| | | function rcube_db($db_dsnw,$db_dsnr='') |
| | | { |
| | | $this->__construct($db_dsnw,$db_dsnr); |
| | | } |
| | | |
| | | // Connect to specific database |
| | | function dsn_connect($dsn) |
| | | { |
| | | $dbh = DB::connect($dsn); |
| | | |
| | | if (DB::isError($dbh)) |
| | | raise_error(array('code' => 500, |
| | | 'type' => 'db', |
| | | 'line' => __LINE__, |
| | | 'file' => __FILE__, |
| | | 'message' => $dbh->getMessage()), TRUE, FALSE); |
| | | return $dbh; |
| | | } |
| | | |
| | | // Connect to appropiate databse |
| | | function db_connect ($mode) |
| | | { |
| | | if ($this->db_connected && $this->db_mode=='w') return; |
| | | |
| | | if ($this->db_connected && $this->db_mode==$mode) return; |
| | | |
| | | if ($mode=='r') |
| | | $dsn=$this->db_dsnr; |
| | | else |
| | | $dsn=$this->db_dsnw; |
| | | |
| | | $this->db_handle = $this->dsn_connect($dsn); |
| | | $this->db_connected = true; |
| | | $this->db_mode = $mode; |
| | | } |
| | | |
| | | // Query database (read operations) |
| | | |
| | | function query($query) |
| | | { |
| | | // Read or write ? |
| | | |
| | | if (strtolower(trim(substr($query,0,6)))=='select') |
| | | $mode='r'; |
| | | else |
| | | { |
| | | $mode='w'; |
| | | } |
| | | |
| | | $this->db_connect($mode); |
| | | |
| | | $result = $this->db_handle->query($query); |
| | | |
| | | if (DB::isError($result)) |
| | | raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, |
| | | 'file' => __FILE__, |
| | | 'message' => $result->getMessage()), TRUE, FALSE); |
| | | |
| | | return $this->_add_result($result, $query); |
| | | } |
| | | |
| | | function db_execute ($query) |
| | | { |
| | | db_connect('w'); |
| | | |
| | | $result = $this->db_handle->query($query); |
| | | |
| | | } |
| | | |
| | | function num_rows($res_id=NULL) |
| | | { |
| | | if (!$this->db_handle) |
| | | return FALSE; |
| | | |
| | | $result = $this->_get_result($res_id); |
| | | |
| | | if ($result) |
| | | return $result->numRows(); |
| | | else |
| | | return FALSE; |
| | | } |
| | | |
| | | function affected_rows($res_id=NULL) |
| | | { |
| | | if (!$this->db_handle) |
| | | return FALSE; |
| | | |
| | | return $this->last_res_id->affectedRows(); |
| | | } |
| | | |
| | | function insert_id($sequence = '') |
| | | { |
| | | if (!$this->db_link || $this->db_mode=='r') |
| | | return FALSE; |
| | | |
| | | switch($this->db_provider) |
| | | { |
| | | case 'pgsql': |
| | | // PostgreSQL uses sequences |
| | | $result =& $this->db_handle->getOne("SELECT CURRVAL('$sequence')"); |
| | | if (DB::isError($result)) |
| | | raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => $result->getMessage()), TRUE, TRUE); |
| | | return $result; |
| | | |
| | | case 'mysql': // This is unfortuneate |
| | | return mysql_insert_id($this->db_handle); |
| | | |
| | | default: |
| | | die("portability issue with this database, please have the developer fix"); |
| | | } |
| | | } |
| | | |
| | | |
| | | function fetch_assoc($res_id=NULL) |
| | | { |
| | | $result = $this->_get_result($res_id); |
| | | |
| | | if (DB::isError($result)) |
| | | raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => $this->db_link->getMessage()), TRUE, TRUE); |
| | | |
| | | return $result->fetchRow(DB_FETCHMODE_ASSOC); |
| | | } |
| | | |
| | | function _add_result($res, $query) |
| | | { |
| | | // sql error occured |
| | | if (DB::isError($res)) |
| | | { |
| | | raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $res->getMessage() . " Query: " . preg_replace('/[\r\n]+\s*/', ' ', $query)), TRUE, FALSE); |
| | | return FALSE; |
| | | } |
| | | else |
| | | { |
| | | $res_id = sizeof($this->a_query_results); |
| | | $this->a_query_results[$res_id] = $res; |
| | | $this->last_res_id = $res_id; |
| | | return $res_id; |
| | | } |
| | | } |
| | | |
| | | |
| | | function _get_result($res_id) |
| | | { |
| | | if ($res_id==NULL) |
| | | $res_id = $this->last_res_id; |
| | | |
| | | if ($res_id && isset($this->a_query_results[$res_id])) |
| | | return $this->a_query_results[$res_id]; |
| | | else |
| | | return FALSE; |
| | | } |
| | | |
| | | } |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Database independent query interface |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Tomas V.V.Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the PEAR class so it can be extended from |
| | | */ |
| | | require_once 'PEAR.php'; |
| | | |
| | | |
| | | // {{{ constants |
| | | // {{{ error codes |
| | | |
| | | /**#@+ |
| | | * One of PEAR DB's portable error codes. |
| | | * @see DB_common::errorCode(), DB::errorMessage() |
| | | * |
| | | * {@internal If you add an error code here, make sure you also add a textual |
| | | * version of it in DB::errorMessage().}} |
| | | */ |
| | | |
| | | /** |
| | | * The code returned by many methods upon success |
| | | */ |
| | | define('DB_OK', 1); |
| | | |
| | | /** |
| | | * Unkown error |
| | | */ |
| | | define('DB_ERROR', -1); |
| | | |
| | | /** |
| | | * Syntax error |
| | | */ |
| | | define('DB_ERROR_SYNTAX', -2); |
| | | |
| | | /** |
| | | * Tried to insert a duplicate value into a primary or unique index |
| | | */ |
| | | define('DB_ERROR_CONSTRAINT', -3); |
| | | |
| | | /** |
| | | * An identifier in the query refers to a non-existant object |
| | | */ |
| | | define('DB_ERROR_NOT_FOUND', -4); |
| | | |
| | | /** |
| | | * Tried to create a duplicate object |
| | | */ |
| | | define('DB_ERROR_ALREADY_EXISTS', -5); |
| | | |
| | | /** |
| | | * The current driver does not support the action you attempted |
| | | */ |
| | | define('DB_ERROR_UNSUPPORTED', -6); |
| | | |
| | | /** |
| | | * The number of parameters does not match the number of placeholders |
| | | */ |
| | | define('DB_ERROR_MISMATCH', -7); |
| | | |
| | | /** |
| | | * A literal submitted did not match the data type expected |
| | | */ |
| | | define('DB_ERROR_INVALID', -8); |
| | | |
| | | /** |
| | | * The current DBMS does not support the action you attempted |
| | | */ |
| | | define('DB_ERROR_NOT_CAPABLE', -9); |
| | | |
| | | /** |
| | | * A literal submitted was too long so the end of it was removed |
| | | */ |
| | | define('DB_ERROR_TRUNCATED', -10); |
| | | |
| | | /** |
| | | * A literal number submitted did not match the data type expected |
| | | */ |
| | | define('DB_ERROR_INVALID_NUMBER', -11); |
| | | |
| | | /** |
| | | * A literal date submitted did not match the data type expected |
| | | */ |
| | | define('DB_ERROR_INVALID_DATE', -12); |
| | | |
| | | /** |
| | | * Attempt to divide something by zero |
| | | */ |
| | | define('DB_ERROR_DIVZERO', -13); |
| | | |
| | | /** |
| | | * A database needs to be selected |
| | | */ |
| | | define('DB_ERROR_NODBSELECTED', -14); |
| | | |
| | | /** |
| | | * Could not create the object requested |
| | | */ |
| | | define('DB_ERROR_CANNOT_CREATE', -15); |
| | | |
| | | /** |
| | | * Could not drop the database requested because it does not exist |
| | | */ |
| | | define('DB_ERROR_CANNOT_DROP', -17); |
| | | |
| | | /** |
| | | * An identifier in the query refers to a non-existant table |
| | | */ |
| | | define('DB_ERROR_NOSUCHTABLE', -18); |
| | | |
| | | /** |
| | | * An identifier in the query refers to a non-existant column |
| | | */ |
| | | define('DB_ERROR_NOSUCHFIELD', -19); |
| | | |
| | | /** |
| | | * The data submitted to the method was inappropriate |
| | | */ |
| | | define('DB_ERROR_NEED_MORE_DATA', -20); |
| | | |
| | | /** |
| | | * The attempt to lock the table failed |
| | | */ |
| | | define('DB_ERROR_NOT_LOCKED', -21); |
| | | |
| | | /** |
| | | * The number of columns doesn't match the number of values |
| | | */ |
| | | define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); |
| | | |
| | | /** |
| | | * The DSN submitted has problems |
| | | */ |
| | | define('DB_ERROR_INVALID_DSN', -23); |
| | | |
| | | /** |
| | | * Could not connect to the database |
| | | */ |
| | | define('DB_ERROR_CONNECT_FAILED', -24); |
| | | |
| | | /** |
| | | * The PHP extension needed for this DBMS could not be found |
| | | */ |
| | | define('DB_ERROR_EXTENSION_NOT_FOUND',-25); |
| | | |
| | | /** |
| | | * The present user has inadequate permissions to perform the task requestd |
| | | */ |
| | | define('DB_ERROR_ACCESS_VIOLATION', -26); |
| | | |
| | | /** |
| | | * The database requested does not exist |
| | | */ |
| | | define('DB_ERROR_NOSUCHDB', -27); |
| | | |
| | | /** |
| | | * Tried to insert a null value into a column that doesn't allow nulls |
| | | */ |
| | | define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); |
| | | /**#@-*/ |
| | | |
| | | |
| | | // }}} |
| | | // {{{ prepared statement-related |
| | | |
| | | |
| | | /**#@+ |
| | | * Identifiers for the placeholders used in prepared statements. |
| | | * @see DB_common::prepare() |
| | | */ |
| | | |
| | | /** |
| | | * Indicates a scalar (<kbd>?</kbd>) placeholder was used |
| | | * |
| | | * Quote and escape the value as necessary. |
| | | */ |
| | | define('DB_PARAM_SCALAR', 1); |
| | | |
| | | /** |
| | | * Indicates an opaque (<kbd>&</kbd>) placeholder was used |
| | | * |
| | | * The value presented is a file name. Extract the contents of that file |
| | | * and place them in this column. |
| | | */ |
| | | define('DB_PARAM_OPAQUE', 2); |
| | | |
| | | /** |
| | | * Indicates a misc (<kbd>!</kbd>) placeholder was used |
| | | * |
| | | * The value should not be quoted or escaped. |
| | | */ |
| | | define('DB_PARAM_MISC', 3); |
| | | /**#@-*/ |
| | | |
| | | |
| | | // }}} |
| | | // {{{ binary data-related |
| | | |
| | | |
| | | /**#@+ |
| | | * The different ways of returning binary data from queries. |
| | | */ |
| | | |
| | | /** |
| | | * Sends the fetched data straight through to output |
| | | */ |
| | | define('DB_BINMODE_PASSTHRU', 1); |
| | | |
| | | /** |
| | | * Lets you return data as usual |
| | | */ |
| | | define('DB_BINMODE_RETURN', 2); |
| | | |
| | | /** |
| | | * Converts the data to hex format before returning it |
| | | * |
| | | * For example the string "123" would become "313233". |
| | | */ |
| | | define('DB_BINMODE_CONVERT', 3); |
| | | /**#@-*/ |
| | | |
| | | |
| | | // }}} |
| | | // {{{ fetch modes |
| | | |
| | | |
| | | /**#@+ |
| | | * Fetch Modes. |
| | | * @see DB_common::setFetchMode() |
| | | */ |
| | | |
| | | /** |
| | | * Indicates the current default fetch mode should be used |
| | | * @see DB_common::$fetchmode |
| | | */ |
| | | define('DB_FETCHMODE_DEFAULT', 0); |
| | | |
| | | /** |
| | | * Column data indexed by numbers, ordered from 0 and up |
| | | */ |
| | | define('DB_FETCHMODE_ORDERED', 1); |
| | | |
| | | /** |
| | | * Column data indexed by column names |
| | | */ |
| | | define('DB_FETCHMODE_ASSOC', 2); |
| | | |
| | | /** |
| | | * Column data as object properties |
| | | */ |
| | | define('DB_FETCHMODE_OBJECT', 3); |
| | | |
| | | /** |
| | | * For multi-dimensional results, make the column name the first level |
| | | * of the array and put the row number in the second level of the array |
| | | * |
| | | * This is flipped from the normal behavior, which puts the row numbers |
| | | * in the first level of the array and the column names in the second level. |
| | | */ |
| | | define('DB_FETCHMODE_FLIPPED', 4); |
| | | /**#@-*/ |
| | | |
| | | /**#@+ |
| | | * Old fetch modes. Left here for compatibility. |
| | | */ |
| | | define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); |
| | | define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); |
| | | define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); |
| | | /**#@-*/ |
| | | |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() && autoPrepare()-related |
| | | |
| | | |
| | | /**#@+ |
| | | * The type of information to return from the tableInfo() method. |
| | | * |
| | | * Bitwised constants, so they can be combined using <kbd>|</kbd> |
| | | * and removed using <kbd>^</kbd>. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | * |
| | | * {@internal Since the TABLEINFO constants are bitwised, if more of them are |
| | | * added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}} |
| | | */ |
| | | define('DB_TABLEINFO_ORDER', 1); |
| | | define('DB_TABLEINFO_ORDERTABLE', 2); |
| | | define('DB_TABLEINFO_FULL', 3); |
| | | /**#@-*/ |
| | | |
| | | |
| | | /**#@+ |
| | | * The type of query to create with the automatic query building methods. |
| | | * @see DB_common::autoPrepare(), DB_common::autoExecute() |
| | | */ |
| | | define('DB_AUTOQUERY_INSERT', 1); |
| | | define('DB_AUTOQUERY_UPDATE', 2); |
| | | /**#@-*/ |
| | | |
| | | |
| | | // }}} |
| | | // {{{ portability modes |
| | | |
| | | |
| | | /**#@+ |
| | | * Portability Modes. |
| | | * |
| | | * Bitwised constants, so they can be combined using <kbd>|</kbd> |
| | | * and removed using <kbd>^</kbd>. |
| | | * |
| | | * @see DB_common::setOption() |
| | | * |
| | | * {@internal Since the PORTABILITY constants are bitwised, if more of them are |
| | | * added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}} |
| | | */ |
| | | |
| | | /** |
| | | * Turn off all portability features |
| | | */ |
| | | define('DB_PORTABILITY_NONE', 0); |
| | | |
| | | /** |
| | | * Convert names of tables and fields to lower case |
| | | * when using the get*(), fetch*() and tableInfo() methods |
| | | */ |
| | | define('DB_PORTABILITY_LOWERCASE', 1); |
| | | |
| | | /** |
| | | * Right trim the data output by get*() and fetch*() |
| | | */ |
| | | define('DB_PORTABILITY_RTRIM', 2); |
| | | |
| | | /** |
| | | * Force reporting the number of rows deleted |
| | | */ |
| | | define('DB_PORTABILITY_DELETE_COUNT', 4); |
| | | |
| | | /** |
| | | * Enable hack that makes numRows() work in Oracle |
| | | */ |
| | | define('DB_PORTABILITY_NUMROWS', 8); |
| | | |
| | | /** |
| | | * Makes certain error messages in certain drivers compatible |
| | | * with those from other DBMS's |
| | | * |
| | | * + mysql, mysqli: change unique/primary key constraints |
| | | * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT |
| | | * |
| | | * + odbc(access): MS's ODBC driver reports 'no such field' as code |
| | | * 07001, which means 'too few parameters.' When this option is on |
| | | * that code gets mapped to DB_ERROR_NOSUCHFIELD. |
| | | */ |
| | | define('DB_PORTABILITY_ERRORS', 16); |
| | | |
| | | /** |
| | | * Convert null values to empty strings in data output by |
| | | * get*() and fetch*() |
| | | */ |
| | | define('DB_PORTABILITY_NULL_TO_EMPTY', 32); |
| | | |
| | | /** |
| | | * Turn on all portability features |
| | | */ |
| | | define('DB_PORTABILITY_ALL', 63); |
| | | /**#@-*/ |
| | | |
| | | // }}} |
| | | |
| | | |
| | | // }}} |
| | | // {{{ class DB |
| | | |
| | | /** |
| | | * Database independent query interface |
| | | * |
| | | * The main "DB" class is simply a container class with some static |
| | | * methods for creating DB objects as well as some utility functions |
| | | * common to all parts of DB. |
| | | * |
| | | * The object model of DB is as follows (indentation means inheritance): |
| | | * <pre> |
| | | * DB The main DB class. This is simply a utility class |
| | | * with some "static" methods for creating DB objects as |
| | | * well as common utility functions for other DB classes. |
| | | * |
| | | * DB_common The base for each DB implementation. Provides default |
| | | * | implementations (in OO lingo virtual methods) for |
| | | * | the actual DB implementations as well as a bunch of |
| | | * | query utility functions. |
| | | * | |
| | | * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. |
| | | * When calling DB::factory or DB::connect for MySQL |
| | | * connections, the object returned is an instance of this |
| | | * class. |
| | | * </pre> |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Tomas V.V.Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB |
| | | { |
| | | // {{{ &factory() |
| | | |
| | | /** |
| | | * Create a new DB object for the specified database type but don't |
| | | * connect to the database |
| | | * |
| | | * @param string $type the database type (eg "mysql") |
| | | * @param array $options an associative array of option names and values |
| | | * |
| | | * @return object a new DB object. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::setOption() |
| | | */ |
| | | function &factory($type, $options = false) |
| | | { |
| | | if (!is_array($options)) { |
| | | $options = array('persistent' => $options); |
| | | } |
| | | |
| | | if (isset($options['debug']) && $options['debug'] >= 2) { |
| | | // expose php errors with sufficient debug level |
| | | include_once "DB/{$type}.php"; |
| | | } else { |
| | | @include_once "DB/{$type}.php"; |
| | | } |
| | | |
| | | $classname = "DB_${type}"; |
| | | |
| | | if (!class_exists($classname)) { |
| | | $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
| | | "Unable to include the DB/{$type}.php" |
| | | . " file for '$dsn'", |
| | | 'DB_Error', true); |
| | | return $tmp; |
| | | } |
| | | |
| | | @$obj =& new $classname; |
| | | |
| | | foreach ($options as $option => $value) { |
| | | $test = $obj->setOption($option, $value); |
| | | if (DB::isError($test)) { |
| | | return $test; |
| | | } |
| | | } |
| | | |
| | | return $obj; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ &connect() |
| | | |
| | | /** |
| | | * Create a new DB object including a connection to the specified database |
| | | * |
| | | * Example 1. |
| | | * <code> |
| | | * require_once 'DB.php'; |
| | | * |
| | | * $dsn = 'pgsql://user:password@host/database'; |
| | | * $options = array( |
| | | * 'debug' => 2, |
| | | * 'portability' => DB_PORTABILITY_ALL, |
| | | * ); |
| | | * |
| | | * $db =& DB::connect($dsn, $options); |
| | | * if (PEAR::isError($db)) { |
| | | * die($db->getMessage()); |
| | | * } |
| | | * </code> |
| | | * |
| | | * @param mixed $dsn the string "data source name" or array in the |
| | | * format returned by DB::parseDSN() |
| | | * @param array $options an associative array of option names and values |
| | | * |
| | | * @return object a new DB object. A DB_Error object on failure. |
| | | * |
| | | * @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(), |
| | | * DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(), |
| | | * DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(), |
| | | * DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(), |
| | | * DB_sybase::connect() |
| | | * |
| | | * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError() |
| | | */ |
| | | function &connect($dsn, $options = array()) |
| | | { |
| | | $dsninfo = DB::parseDSN($dsn); |
| | | $type = $dsninfo['phptype']; |
| | | |
| | | if (!is_array($options)) { |
| | | /* |
| | | * For backwards compatibility. $options used to be boolean, |
| | | * indicating whether the connection should be persistent. |
| | | */ |
| | | $options = array('persistent' => $options); |
| | | } |
| | | |
| | | if (isset($options['debug']) && $options['debug'] >= 2) { |
| | | // expose php errors with sufficient debug level |
| | | include_once "DB/${type}.php"; |
| | | } else { |
| | | @include_once "DB/${type}.php"; |
| | | } |
| | | |
| | | $classname = "DB_${type}"; |
| | | if (!class_exists($classname)) { |
| | | $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
| | | "Unable to include the DB/{$type}.php" |
| | | . " file for '$dsn'", |
| | | 'DB_Error', true); |
| | | return $tmp; |
| | | } |
| | | |
| | | @$obj =& new $classname; |
| | | |
| | | foreach ($options as $option => $value) { |
| | | $test = $obj->setOption($option, $value); |
| | | if (DB::isError($test)) { |
| | | return $test; |
| | | } |
| | | } |
| | | |
| | | $err = $obj->connect($dsninfo, $obj->getOption('persistent')); |
| | | if (DB::isError($err)) { |
| | | $err->addUserInfo($dsn); |
| | | return $err; |
| | | } |
| | | |
| | | return $obj; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ apiVersion() |
| | | |
| | | /** |
| | | * Return the DB API version |
| | | * |
| | | * @return string the DB API version number |
| | | */ |
| | | function apiVersion() |
| | | { |
| | | return '@package_version@'; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isError() |
| | | |
| | | /** |
| | | * Determines if a variable is a DB_Error object |
| | | * |
| | | * @param mixed $value the variable to check |
| | | * |
| | | * @return bool whether $value is DB_Error object |
| | | */ |
| | | function isError($value) |
| | | { |
| | | return is_a($value, 'DB_Error'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isConnection() |
| | | |
| | | /** |
| | | * Determines if a value is a DB_<driver> object |
| | | * |
| | | * @param mixed $value the value to test |
| | | * |
| | | * @return bool whether $value is a DB_<driver> object |
| | | */ |
| | | function isConnection($value) |
| | | { |
| | | return (is_object($value) && |
| | | is_subclass_of($value, 'db_common') && |
| | | method_exists($value, 'simpleQuery')); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ isManip() |
| | | |
| | | /** |
| | | * Tell whether a query is a data manipulation or data definition query |
| | | * |
| | | * Examples of data manipulation queries are INSERT, UPDATE and DELETE. |
| | | * Examples of data definition queries are CREATE, DROP, ALTER, GRANT, |
| | | * REVOKE. |
| | | * |
| | | * @param string $query the query |
| | | * |
| | | * @return boolean whether $query is a data manipulation query |
| | | */ |
| | | function isManip($query) |
| | | { |
| | | $manips = 'INSERT|UPDATE|DELETE|REPLACE|' |
| | | . 'CREATE|DROP|' |
| | | . 'LOAD DATA|SELECT .* INTO|COPY|' |
| | | . 'ALTER|GRANT|REVOKE|' |
| | | . 'LOCK|UNLOCK'; |
| | | if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) { |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorMessage() |
| | | |
| | | /** |
| | | * Return a textual error message for a DB error code |
| | | * |
| | | * @param integer $value the DB error code |
| | | * |
| | | * @return string the error message or false if the error code was |
| | | * not recognized |
| | | */ |
| | | function errorMessage($value) |
| | | { |
| | | static $errorMessages; |
| | | if (!isset($errorMessages)) { |
| | | $errorMessages = array( |
| | | DB_ERROR => 'unknown error', |
| | | DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', |
| | | DB_ERROR_ALREADY_EXISTS => 'already exists', |
| | | DB_ERROR_CANNOT_CREATE => 'can not create', |
| | | DB_ERROR_CANNOT_DROP => 'can not drop', |
| | | DB_ERROR_CONNECT_FAILED => 'connect failed', |
| | | DB_ERROR_CONSTRAINT => 'constraint violation', |
| | | DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', |
| | | DB_ERROR_DIVZERO => 'division by zero', |
| | | DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', |
| | | DB_ERROR_INVALID => 'invalid', |
| | | DB_ERROR_INVALID_DATE => 'invalid date or time', |
| | | DB_ERROR_INVALID_DSN => 'invalid DSN', |
| | | DB_ERROR_INVALID_NUMBER => 'invalid number', |
| | | DB_ERROR_MISMATCH => 'mismatch', |
| | | DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', |
| | | DB_ERROR_NODBSELECTED => 'no database selected', |
| | | DB_ERROR_NOSUCHDB => 'no such database', |
| | | DB_ERROR_NOSUCHFIELD => 'no such field', |
| | | DB_ERROR_NOSUCHTABLE => 'no such table', |
| | | DB_ERROR_NOT_CAPABLE => 'DB backend not capable', |
| | | DB_ERROR_NOT_FOUND => 'not found', |
| | | DB_ERROR_NOT_LOCKED => 'not locked', |
| | | DB_ERROR_SYNTAX => 'syntax error', |
| | | DB_ERROR_UNSUPPORTED => 'not supported', |
| | | DB_ERROR_TRUNCATED => 'truncated', |
| | | DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', |
| | | DB_OK => 'no error', |
| | | ); |
| | | } |
| | | |
| | | if (DB::isError($value)) { |
| | | $value = $value->getCode(); |
| | | } |
| | | |
| | | return isset($errorMessages[$value]) ? $errorMessages[$value] |
| | | : $errorMessages[DB_ERROR]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ parseDSN() |
| | | |
| | | /** |
| | | * Parse a data source name |
| | | * |
| | | * Additional keys can be added by appending a URI query string to the |
| | | * end of the DSN. |
| | | * |
| | | * The format of the supplied DSN is in its fullest form: |
| | | * <code> |
| | | * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true |
| | | * </code> |
| | | * |
| | | * Most variations are allowed: |
| | | * <code> |
| | | * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 |
| | | * phptype://username:password@hostspec/database_name |
| | | * phptype://username:password@hostspec |
| | | * phptype://username@hostspec |
| | | * phptype://hostspec/database |
| | | * phptype://hostspec |
| | | * phptype(dbsyntax) |
| | | * phptype |
| | | * </code> |
| | | * |
| | | * @param string $dsn Data Source Name to be parsed |
| | | * |
| | | * @return array an associative array with the following keys: |
| | | * + phptype: Database backend used in PHP (mysql, odbc etc.) |
| | | * + dbsyntax: Database used with regards to SQL syntax etc. |
| | | * + protocol: Communication protocol to use (tcp, unix etc.) |
| | | * + hostspec: Host specification (hostname[:port]) |
| | | * + database: Database to use on the DBMS server |
| | | * + username: User name for login |
| | | * + password: Password for login |
| | | */ |
| | | function parseDSN($dsn) |
| | | { |
| | | $parsed = array( |
| | | 'phptype' => false, |
| | | 'dbsyntax' => false, |
| | | 'username' => false, |
| | | 'password' => false, |
| | | 'protocol' => false, |
| | | 'hostspec' => false, |
| | | 'port' => false, |
| | | 'socket' => false, |
| | | 'database' => false, |
| | | ); |
| | | |
| | | if (is_array($dsn)) { |
| | | $dsn = array_merge($parsed, $dsn); |
| | | if (!$dsn['dbsyntax']) { |
| | | $dsn['dbsyntax'] = $dsn['phptype']; |
| | | } |
| | | return $dsn; |
| | | } |
| | | |
| | | // Find phptype and dbsyntax |
| | | if (($pos = strpos($dsn, '://')) !== false) { |
| | | $str = substr($dsn, 0, $pos); |
| | | $dsn = substr($dsn, $pos + 3); |
| | | } else { |
| | | $str = $dsn; |
| | | $dsn = null; |
| | | } |
| | | |
| | | // Get phptype and dbsyntax |
| | | // $str => phptype(dbsyntax) |
| | | if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { |
| | | $parsed['phptype'] = $arr[1]; |
| | | $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; |
| | | } else { |
| | | $parsed['phptype'] = $str; |
| | | $parsed['dbsyntax'] = $str; |
| | | } |
| | | |
| | | if (!count($dsn)) { |
| | | return $parsed; |
| | | } |
| | | |
| | | // Get (if found): username and password |
| | | // $dsn => username:password@protocol+hostspec/database |
| | | if (($at = strrpos($dsn,'@')) !== false) { |
| | | $str = substr($dsn, 0, $at); |
| | | $dsn = substr($dsn, $at + 1); |
| | | if (($pos = strpos($str, ':')) !== false) { |
| | | $parsed['username'] = rawurldecode(substr($str, 0, $pos)); |
| | | $parsed['password'] = rawurldecode(substr($str, $pos + 1)); |
| | | } else { |
| | | $parsed['username'] = rawurldecode($str); |
| | | } |
| | | } |
| | | |
| | | // Find protocol and hostspec |
| | | |
| | | if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { |
| | | // $dsn => proto(proto_opts)/database |
| | | $proto = $match[1]; |
| | | $proto_opts = $match[2] ? $match[2] : false; |
| | | $dsn = $match[3]; |
| | | |
| | | } else { |
| | | // $dsn => protocol+hostspec/database (old format) |
| | | if (strpos($dsn, '+') !== false) { |
| | | list($proto, $dsn) = explode('+', $dsn, 2); |
| | | } |
| | | if (strpos($dsn, '/') !== false) { |
| | | list($proto_opts, $dsn) = explode('/', $dsn, 2); |
| | | } else { |
| | | $proto_opts = $dsn; |
| | | $dsn = null; |
| | | } |
| | | } |
| | | |
| | | // process the different protocol options |
| | | $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; |
| | | $proto_opts = rawurldecode($proto_opts); |
| | | if ($parsed['protocol'] == 'tcp') { |
| | | if (strpos($proto_opts, ':') !== false) { |
| | | list($parsed['hostspec'], |
| | | $parsed['port']) = explode(':', $proto_opts); |
| | | } else { |
| | | $parsed['hostspec'] = $proto_opts; |
| | | } |
| | | } elseif ($parsed['protocol'] == 'unix') { |
| | | $parsed['socket'] = $proto_opts; |
| | | } |
| | | |
| | | // Get dabase if any |
| | | // $dsn => database |
| | | if ($dsn) { |
| | | if (($pos = strpos($dsn, '?')) === false) { |
| | | // /database |
| | | $parsed['database'] = rawurldecode($dsn); |
| | | } else { |
| | | // /database?param1=value1¶m2=value2 |
| | | $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); |
| | | $dsn = substr($dsn, $pos + 1); |
| | | if (strpos($dsn, '&') !== false) { |
| | | $opts = explode('&', $dsn); |
| | | } else { // database?param1=value1 |
| | | $opts = array($dsn); |
| | | } |
| | | foreach ($opts as $opt) { |
| | | list($key, $value) = explode('=', $opt); |
| | | if (!isset($parsed[$key])) { |
| | | // don't allow params overwrite |
| | | $parsed[$key] = rawurldecode($value); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $parsed; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class DB_Error |
| | | |
| | | /** |
| | | * DB_Error implements a class for reporting portable database error |
| | | * messages |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_Error extends PEAR_Error |
| | | { |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * DB_Error constructor |
| | | * |
| | | * @param mixed $code DB error code, or string with error message |
| | | * @param int $mode what "error mode" to operate in |
| | | * @param int $level what error level to use for $mode & |
| | | * PEAR_ERROR_TRIGGER |
| | | * @param mixed $debuginfo additional debug info, such as the last query |
| | | * |
| | | * @see PEAR_Error |
| | | */ |
| | | function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, |
| | | $level = E_USER_NOTICE, $debuginfo = null) |
| | | { |
| | | if (is_int($code)) { |
| | | $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, |
| | | $mode, $level, $debuginfo); |
| | | } else { |
| | | $this->PEAR_Error("DB Error: $code", DB_ERROR, |
| | | $mode, $level, $debuginfo); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class DB_result |
| | | |
| | | /** |
| | | * This class implements a wrapper for a DB result set |
| | | * |
| | | * A new instance of this class will be returned by the DB implementation |
| | | * after processing a query that returns data. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_result |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * Should results be freed automatically when there are no more rows? |
| | | * @var boolean |
| | | * @see DB_common::$options |
| | | */ |
| | | var $autofree; |
| | | |
| | | /** |
| | | * A reference to the DB_<driver> object |
| | | * @var object |
| | | */ |
| | | var $dbh; |
| | | |
| | | /** |
| | | * The current default fetch mode |
| | | * @var integer |
| | | * @see DB_common::$fetchmode |
| | | */ |
| | | var $fetchmode; |
| | | |
| | | /** |
| | | * The name of the class into which results should be fetched when |
| | | * DB_FETCHMODE_OBJECT is in effect |
| | | * |
| | | * @var string |
| | | * @see DB_common::$fetchmode_object_class |
| | | */ |
| | | var $fetchmode_object_class; |
| | | |
| | | /** |
| | | * The number of rows to fetch from a limit query |
| | | * @var integer |
| | | */ |
| | | var $limit_count = null; |
| | | |
| | | /** |
| | | * The row to start fetching from in limit queries |
| | | * @var integer |
| | | */ |
| | | var $limit_from = null; |
| | | |
| | | /** |
| | | * The execute parameters that created this result |
| | | * @var array |
| | | * @since Property available since Release 1.7.0 |
| | | */ |
| | | var $parameters; |
| | | |
| | | /** |
| | | * The query string that created this result |
| | | * |
| | | * Copied here incase it changes in $dbh, which is referenced |
| | | * |
| | | * @var string |
| | | * @since Property available since Release 1.7.0 |
| | | */ |
| | | var $query; |
| | | |
| | | /** |
| | | * The query result resource id created by PHP |
| | | * @var resource |
| | | */ |
| | | var $result; |
| | | |
| | | /** |
| | | * The present row being dealt with |
| | | * @var integer |
| | | */ |
| | | var $row_counter = null; |
| | | |
| | | /** |
| | | * The prepared statement resource id created by PHP in $dbh |
| | | * |
| | | * This resource is only available when the result set was created using |
| | | * a driver's native execute() method, not PEAR DB's emulated one. |
| | | * |
| | | * Copied here incase it changes in $dbh, which is referenced |
| | | * |
| | | * {@internal Mainly here because the InterBase/Firebird API is only |
| | | * able to retrieve data from result sets if the statemnt handle is |
| | | * still in scope.}} |
| | | * |
| | | * @var resource |
| | | * @since Property available since Release 1.7.0 |
| | | */ |
| | | var $statement; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor sets the object's properties |
| | | * |
| | | * @param object &$dbh the DB object reference |
| | | * @param resource $result the result resource id |
| | | * @param array $options an associative array with result options |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_result(&$dbh, $result, $options = array()) |
| | | { |
| | | $this->autofree = $dbh->options['autofree']; |
| | | $this->dbh = &$dbh; |
| | | $this->fetchmode = $dbh->fetchmode; |
| | | $this->fetchmode_object_class = $dbh->fetchmode_object_class; |
| | | $this->parameters = $dbh->last_parameters; |
| | | $this->query = $dbh->last_query; |
| | | $this->result = $result; |
| | | $this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt; |
| | | foreach ($options as $key => $value) { |
| | | $this->setOption($key, $value); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Set options for the DB_result object |
| | | * |
| | | * @param string $key the option to set |
| | | * @param mixed $value the value to set the option to |
| | | * |
| | | * @return void |
| | | */ |
| | | function setOption($key, $value = null) |
| | | { |
| | | switch ($key) { |
| | | case 'limit_from': |
| | | $this->limit_from = $value; |
| | | break; |
| | | case 'limit_count': |
| | | $this->limit_count = $value; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchRow() |
| | | |
| | | /** |
| | | * Fetch a row of data and return it by reference into an array |
| | | * |
| | | * The type of array returned can be controlled either by setting this |
| | | * method's <var>$fetchmode</var> parameter or by changing the default |
| | | * fetch mode setFetchMode() before calling this method. |
| | | * |
| | | * There are two options for standardizing the information returned |
| | | * from databases, ensuring their values are consistent when changing |
| | | * DBMS's. These portability options can be turned on when creating a |
| | | * new DB object or by using setOption(). |
| | | * |
| | | * + <var>DB_PORTABILITY_LOWERCASE</var> |
| | | * convert names of fields to lower case |
| | | * |
| | | * + <var>DB_PORTABILITY_RTRIM</var> |
| | | * right trim the data |
| | | * |
| | | * @param int $fetchmode the constant indicating how to format the data |
| | | * @param int $rownum the row number to fetch (index starts at 0) |
| | | * |
| | | * @return mixed an array or object containing the row's data, |
| | | * NULL when the end of the result set is reached |
| | | * or a DB_Error object on failure. |
| | | * |
| | | * @see DB_common::setOption(), DB_common::setFetchMode() |
| | | */ |
| | | function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) |
| | | { |
| | | if ($fetchmode === DB_FETCHMODE_DEFAULT) { |
| | | $fetchmode = $this->fetchmode; |
| | | } |
| | | if ($fetchmode === DB_FETCHMODE_OBJECT) { |
| | | $fetchmode = DB_FETCHMODE_ASSOC; |
| | | $object_class = $this->fetchmode_object_class; |
| | | } |
| | | if ($this->limit_from !== null) { |
| | | if ($this->row_counter === null) { |
| | | $this->row_counter = $this->limit_from; |
| | | // Skip rows |
| | | if ($this->dbh->features['limit'] === false) { |
| | | $i = 0; |
| | | while ($i++ < $this->limit_from) { |
| | | $this->dbh->fetchInto($this->result, $arr, $fetchmode); |
| | | } |
| | | } |
| | | } |
| | | if ($this->row_counter >= ($this->limit_from + $this->limit_count)) |
| | | { |
| | | if ($this->autofree) { |
| | | $this->free(); |
| | | } |
| | | $tmp = null; |
| | | return $tmp; |
| | | } |
| | | if ($this->dbh->features['limit'] === 'emulate') { |
| | | $rownum = $this->row_counter; |
| | | } |
| | | $this->row_counter++; |
| | | } |
| | | $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); |
| | | if ($res === DB_OK) { |
| | | if (isset($object_class)) { |
| | | // The default mode is specified in the |
| | | // DB_common::fetchmode_object_class property |
| | | if ($object_class == 'stdClass') { |
| | | $arr = (object) $arr; |
| | | } else { |
| | | $arr = &new $object_class($arr); |
| | | } |
| | | } |
| | | return $arr; |
| | | } |
| | | if ($res == null && $this->autofree) { |
| | | $this->free(); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Fetch a row of data into an array which is passed by reference |
| | | * |
| | | * The type of array returned can be controlled either by setting this |
| | | * method's <var>$fetchmode</var> parameter or by changing the default |
| | | * fetch mode setFetchMode() before calling this method. |
| | | * |
| | | * There are two options for standardizing the information returned |
| | | * from databases, ensuring their values are consistent when changing |
| | | * DBMS's. These portability options can be turned on when creating a |
| | | * new DB object or by using setOption(). |
| | | * |
| | | * + <var>DB_PORTABILITY_LOWERCASE</var> |
| | | * convert names of fields to lower case |
| | | * |
| | | * + <var>DB_PORTABILITY_RTRIM</var> |
| | | * right trim the data |
| | | * |
| | | * @param array &$arr the variable where the data should be placed |
| | | * @param int $fetchmode the constant indicating how to format the data |
| | | * @param int $rownum the row number to fetch (index starts at 0) |
| | | * |
| | | * @return mixed DB_OK if a row is processed, NULL when the end of the |
| | | * result set is reached or a DB_Error object on failure |
| | | * |
| | | * @see DB_common::setOption(), DB_common::setFetchMode() |
| | | */ |
| | | function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) |
| | | { |
| | | if ($fetchmode === DB_FETCHMODE_DEFAULT) { |
| | | $fetchmode = $this->fetchmode; |
| | | } |
| | | if ($fetchmode === DB_FETCHMODE_OBJECT) { |
| | | $fetchmode = DB_FETCHMODE_ASSOC; |
| | | $object_class = $this->fetchmode_object_class; |
| | | } |
| | | if ($this->limit_from !== null) { |
| | | if ($this->row_counter === null) { |
| | | $this->row_counter = $this->limit_from; |
| | | // Skip rows |
| | | if ($this->dbh->features['limit'] === false) { |
| | | $i = 0; |
| | | while ($i++ < $this->limit_from) { |
| | | $this->dbh->fetchInto($this->result, $arr, $fetchmode); |
| | | } |
| | | } |
| | | } |
| | | if ($this->row_counter >= ( |
| | | $this->limit_from + $this->limit_count)) |
| | | { |
| | | if ($this->autofree) { |
| | | $this->free(); |
| | | } |
| | | return null; |
| | | } |
| | | if ($this->dbh->features['limit'] === 'emulate') { |
| | | $rownum = $this->row_counter; |
| | | } |
| | | |
| | | $this->row_counter++; |
| | | } |
| | | $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); |
| | | if ($res === DB_OK) { |
| | | if (isset($object_class)) { |
| | | // default mode specified in the |
| | | // DB_common::fetchmode_object_class property |
| | | if ($object_class == 'stdClass') { |
| | | $arr = (object) $arr; |
| | | } else { |
| | | $arr = new $object_class($arr); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | if ($res == null && $this->autofree) { |
| | | $this->free(); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Get the the number of columns in a result set |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | */ |
| | | function numCols() |
| | | { |
| | | return $this->dbh->numCols($this->result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Get the number of rows in a result set |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function numRows() |
| | | { |
| | | if ($this->dbh->features['numrows'] === 'emulate' |
| | | && $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS) |
| | | { |
| | | if ($this->dbh->features['prepare']) { |
| | | $res = $this->dbh->query($this->query, $this->parameters); |
| | | } else { |
| | | $res = $this->dbh->query($this->query); |
| | | } |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | $i = 0; |
| | | while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) { |
| | | $i++; |
| | | } |
| | | return $i; |
| | | } else { |
| | | return $this->dbh->numRows($this->result); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Get the next result if a batch of queries was executed |
| | | * |
| | | * @return bool true if a new result is available or false if not |
| | | */ |
| | | function nextResult() |
| | | { |
| | | return $this->dbh->nextResult($this->result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ free() |
| | | |
| | | /** |
| | | * Frees the resources allocated for this result set |
| | | * |
| | | * @return bool true on success. A DB_Error object on failure. |
| | | */ |
| | | function free() |
| | | { |
| | | $err = $this->dbh->freeResult($this->result); |
| | | if (DB::isError($err)) { |
| | | return $err; |
| | | } |
| | | $this->result = false; |
| | | $this->statement = false; |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * @see DB_common::tableInfo() |
| | | * @deprecated Method deprecated some time before Release 1.2 |
| | | */ |
| | | function tableInfo($mode = null) |
| | | { |
| | | if (is_string($mode)) { |
| | | return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | return $this->dbh->tableInfo($this, $mode); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getQuery() |
| | | |
| | | /** |
| | | * Determine the query string that created this result |
| | | * |
| | | * @return string the query string |
| | | * |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function getQuery() |
| | | { |
| | | return $this->query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getRowCounter() |
| | | |
| | | /** |
| | | * Tells which row number is currently being processed |
| | | * |
| | | * @return integer the current row being looked at. Starts at 1. |
| | | */ |
| | | function getRowCounter() |
| | | { |
| | | return $this->row_counter; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ class DB_row |
| | | |
| | | /** |
| | | * PEAR DB Row Object |
| | | * |
| | | * The object contains a row of data from a result set. Each column's data |
| | | * is placed in a property named for the column. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | * @see DB_common::setFetchMode() |
| | | */ |
| | | class DB_row |
| | | { |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * The constructor places a row's data into properties of this object |
| | | * |
| | | * @param array the array containing the row's data |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_row(&$arr) |
| | | { |
| | | foreach ($arr as $key => $value) { |
| | | $this->$key = &$arr[$key]; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Contains the DB_common base class |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Tomas V.V. Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the PEAR class so it can be extended from |
| | | */ |
| | | require_once 'PEAR.php'; |
| | | |
| | | /** |
| | | * DB_common is the base class from which each database driver class extends |
| | | * |
| | | * All common methods are declared here. If a given DBMS driver contains |
| | | * a particular method, that method will overload the one here. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Tomas V.V. Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_common extends PEAR |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The current default fetch mode |
| | | * @var integer |
| | | */ |
| | | var $fetchmode = DB_FETCHMODE_ORDERED; |
| | | |
| | | /** |
| | | * The name of the class into which results should be fetched when |
| | | * DB_FETCHMODE_OBJECT is in effect |
| | | * |
| | | * @var string |
| | | */ |
| | | var $fetchmode_object_class = 'stdClass'; |
| | | |
| | | /** |
| | | * Was a connection present when the object was serialized()? |
| | | * @var bool |
| | | * @see DB_common::__sleep(), DB_common::__wake() |
| | | */ |
| | | var $was_connected = null; |
| | | |
| | | /** |
| | | * The most recently executed query |
| | | * @var string |
| | | */ |
| | | var $last_query = ''; |
| | | |
| | | /** |
| | | * Run-time configuration options |
| | | * |
| | | * The 'optimize' option has been deprecated. Use the 'portability' |
| | | * option instead. |
| | | * |
| | | * @var array |
| | | * @see DB_common::setOption() |
| | | */ |
| | | var $options = array( |
| | | 'result_buffering' => 500, |
| | | 'persistent' => false, |
| | | 'ssl' => false, |
| | | 'debug' => 0, |
| | | 'seqname_format' => '%s_seq', |
| | | 'autofree' => false, |
| | | 'portability' => DB_PORTABILITY_NONE, |
| | | 'optimize' => 'performance', // Deprecated. Use 'portability'. |
| | | ); |
| | | |
| | | /** |
| | | * The parameters from the most recently executed query |
| | | * @var array |
| | | * @since Property available since Release 1.7.0 |
| | | */ |
| | | var $last_parameters = array(); |
| | | |
| | | /** |
| | | * The elements from each prepared statement |
| | | * @var array |
| | | */ |
| | | var $prepare_tokens = array(); |
| | | |
| | | /** |
| | | * The data types of the various elements in each prepared statement |
| | | * @var array |
| | | */ |
| | | var $prepare_types = array(); |
| | | |
| | | /** |
| | | * The prepared queries |
| | | * @var array |
| | | */ |
| | | var $prepared_queries = array(); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ DB_common |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->PEAR('DB_Error')</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_common() |
| | | { |
| | | $this->PEAR('DB_Error'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ __sleep() |
| | | |
| | | /** |
| | | * Automatically indicates which properties should be saved |
| | | * when PHP's serialize() function is called |
| | | * |
| | | * @return array the array of properties names that should be saved |
| | | */ |
| | | function __sleep() |
| | | { |
| | | if ($this->connection) { |
| | | // Don't disconnect(), people use serialize() for many reasons |
| | | $this->was_connected = true; |
| | | } else { |
| | | $this->was_connected = false; |
| | | } |
| | | if (isset($this->autocommit)) { |
| | | return array('autocommit', |
| | | 'dbsyntax', |
| | | 'dsn', |
| | | 'features', |
| | | 'fetchmode', |
| | | 'fetchmode_object_class', |
| | | 'options', |
| | | 'was_connected', |
| | | ); |
| | | } else { |
| | | return array('dbsyntax', |
| | | 'dsn', |
| | | 'features', |
| | | 'fetchmode', |
| | | 'fetchmode_object_class', |
| | | 'options', |
| | | 'was_connected', |
| | | ); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ __wakeup() |
| | | |
| | | /** |
| | | * Automatically reconnects to the database when PHP's unserialize() |
| | | * function is called |
| | | * |
| | | * The reconnection attempt is only performed if the object was connected |
| | | * at the time PHP's serialize() function was run. |
| | | * |
| | | * @return void |
| | | */ |
| | | function __wakeup() |
| | | { |
| | | if ($this->was_connected) { |
| | | $this->connect($this->dsn, $this->options); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ __toString() |
| | | |
| | | /** |
| | | * Automatic string conversion for PHP 5 |
| | | * |
| | | * @return string a string describing the current PEAR DB object |
| | | * |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function __toString() |
| | | { |
| | | $info = strtolower(get_class($this)); |
| | | $info .= ': (phptype=' . $this->phptype . |
| | | ', dbsyntax=' . $this->dbsyntax . |
| | | ')'; |
| | | if ($this->connection) { |
| | | $info .= ' [connected]'; |
| | | } |
| | | return $info; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ toString() |
| | | |
| | | /** |
| | | * DEPRECATED: String conversion method |
| | | * |
| | | * @return string a string describing the current PEAR DB object |
| | | * |
| | | * @deprecated Method deprecated in Release 1.7.0 |
| | | */ |
| | | function toString() |
| | | { |
| | | return $this->__toString(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteString() |
| | | |
| | | /** |
| | | * DEPRECATED: Quotes a string so it can be safely used within string |
| | | * delimiters in a query |
| | | * |
| | | * @param string $string the string to be quoted |
| | | * |
| | | * @return string the quoted string |
| | | * |
| | | * @see DB_common::quoteSmart(), DB_common::escapeSimple() |
| | | * @deprecated Method deprecated some time before Release 1.2 |
| | | */ |
| | | function quoteString($string) |
| | | { |
| | | $string = $this->quote($string); |
| | | if ($string{0} == "'") { |
| | | return substr($string, 1, -1); |
| | | } |
| | | return $string; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quote() |
| | | |
| | | /** |
| | | * DEPRECATED: Quotes a string so it can be safely used in a query |
| | | * |
| | | * @param string $string the string to quote |
| | | * |
| | | * @return string the quoted string or the string <samp>NULL</samp> |
| | | * if the value submitted is <kbd>null</kbd>. |
| | | * |
| | | * @see DB_common::quoteSmart(), DB_common::escapeSimple() |
| | | * @deprecated Deprecated in release 1.6.0 |
| | | */ |
| | | function quote($string = null) |
| | | { |
| | | return ($string === null) ? 'NULL' |
| | | : "'" . str_replace("'", "''", $string) . "'"; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteIdentifier() |
| | | |
| | | /** |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * Delimiting style depends on which database driver is being used. |
| | | * |
| | | * NOTE: just because you CAN use delimited identifiers doesn't mean |
| | | * you SHOULD use them. In general, they end up causing way more |
| | | * problems than they solve. |
| | | * |
| | | * Portability is broken by using the following characters inside |
| | | * delimited identifiers: |
| | | * + backtick (<kbd>`</kbd>) -- due to MySQL |
| | | * + double quote (<kbd>"</kbd>) -- due to Oracle |
| | | * + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access |
| | | * |
| | | * Delimited identifiers are known to generally work correctly under |
| | | * the following drivers: |
| | | * + mssql |
| | | * + mysql |
| | | * + mysqli |
| | | * + oci8 |
| | | * + odbc(access) |
| | | * + odbc(db2) |
| | | * + pgsql |
| | | * + sqlite |
| | | * + sybase (must execute <kbd>set quoted_identifier on</kbd> sometime |
| | | * prior to use) |
| | | * |
| | | * InterBase doesn't seem to be able to use delimited identifiers |
| | | * via PHP 4. They work fine under PHP 5. |
| | | * |
| | | * @param string $str the identifier name to be quoted |
| | | * |
| | | * @return string the quoted identifier |
| | | * |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteIdentifier($str) |
| | | { |
| | | return '"' . str_replace('"', '""', $str) . '"'; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteSmart() |
| | | |
| | | /** |
| | | * Formats input so it can be safely used in a query |
| | | * |
| | | * The output depends on the PHP data type of input and the database |
| | | * type being used. |
| | | * |
| | | * @param mixed $in the data to be formatted |
| | | * |
| | | * @return mixed the formatted data. The format depends on the input's |
| | | * PHP type: |
| | | * <ul> |
| | | * <li> |
| | | * <kbd>input</kbd> -> <samp>returns</samp> |
| | | * </li> |
| | | * <li> |
| | | * <kbd>null</kbd> -> the string <samp>NULL</samp> |
| | | * </li> |
| | | * <li> |
| | | * <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number |
| | | * </li> |
| | | * <li> |
| | | * <kbd>bool</kbd> -> output depends on the driver in use |
| | | * Most drivers return integers: <samp>1</samp> if |
| | | * <kbd>true</kbd> or <samp>0</samp> if |
| | | * <kbd>false</kbd>. |
| | | * Some return strings: <samp>TRUE</samp> if |
| | | * <kbd>true</kbd> or <samp>FALSE</samp> if |
| | | * <kbd>false</kbd>. |
| | | * Finally one returns strings: <samp>T</samp> if |
| | | * <kbd>true</kbd> or <samp>F</samp> if |
| | | * <kbd>false</kbd>. Here is a list of each DBMS, |
| | | * the values returned and the suggested column type: |
| | | * <ul> |
| | | * <li> |
| | | * <kbd>dbase</kbd> -> <samp>T/F</samp> |
| | | * (<kbd>Logical</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp> |
| | | * (<kbd>BOOLEAN</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>ibase</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>SMALLINT</kbd>) [1] |
| | | * </li> |
| | | * <li> |
| | | * <kbd>ifx</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>SMALLINT</kbd>) [1] |
| | | * </li> |
| | | * <li> |
| | | * <kbd>msql</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>INTEGER</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>mssql</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>BIT</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>mysql</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>TINYINT(1)</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>mysqli</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>TINYINT(1)</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>oci8</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>NUMBER(1)</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>odbc</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>SMALLINT</kbd>) [1] |
| | | * </li> |
| | | * <li> |
| | | * <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp> |
| | | * (<kbd>BOOLEAN</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>sqlite</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>INTEGER</kbd>) |
| | | * </li> |
| | | * <li> |
| | | * <kbd>sybase</kbd> -> <samp>1/0</samp> |
| | | * (<kbd>TINYINT(1)</kbd>) |
| | | * </li> |
| | | * </ul> |
| | | * [1] Accommodate the lowest common denominator because not all |
| | | * versions of have <kbd>BOOLEAN</kbd>. |
| | | * </li> |
| | | * <li> |
| | | * other (including strings and numeric strings) -> |
| | | * the data with single quotes escaped by preceeding |
| | | * single quotes, backslashes are escaped by preceeding |
| | | * backslashes, then the whole string is encapsulated |
| | | * between single quotes |
| | | * </li> |
| | | * </ul> |
| | | * |
| | | * @see DB_common::escapeSimple() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteSmart($in) |
| | | { |
| | | if (is_int($in) || is_double($in)) { |
| | | return $in; |
| | | } elseif (is_bool($in)) { |
| | | return $in ? 1 : 0; |
| | | } elseif (is_null($in)) { |
| | | return 'NULL'; |
| | | } else { |
| | | return "'" . $this->escapeSimple($in) . "'"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ escapeSimple() |
| | | |
| | | /** |
| | | * Escapes a string according to the current DBMS's standards |
| | | * |
| | | * In SQLite, this makes things safe for inserts/updates, but may |
| | | * cause problems when performing text comparisons against columns |
| | | * containing binary data. See the |
| | | * {@link http://php.net/sqlite_escape_string PHP manual} for more info. |
| | | * |
| | | * @param string $str the string to be escaped |
| | | * |
| | | * @return string the escaped string |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function escapeSimple($str) |
| | | { |
| | | return str_replace("'", "''", $str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ provides() |
| | | |
| | | /** |
| | | * Tells whether the present driver supports a given feature |
| | | * |
| | | * @param string $feature the feature you're curious about |
| | | * |
| | | * @return bool whether this driver supports $feature |
| | | */ |
| | | function provides($feature) |
| | | { |
| | | return $this->features[$feature]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setFetchMode() |
| | | |
| | | /** |
| | | * Sets the fetch mode that should be used by default for query results |
| | | * |
| | | * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC |
| | | * or DB_FETCHMODE_OBJECT |
| | | * @param string $object_class the class name of the object to be returned |
| | | * by the fetch methods when the |
| | | * DB_FETCHMODE_OBJECT mode is selected. |
| | | * If no class is specified by default a cast |
| | | * to object from the assoc array row will be |
| | | * done. There is also the posibility to use |
| | | * and extend the 'DB_row' class. |
| | | * |
| | | * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT |
| | | */ |
| | | function setFetchMode($fetchmode, $object_class = 'stdClass') |
| | | { |
| | | switch ($fetchmode) { |
| | | case DB_FETCHMODE_OBJECT: |
| | | $this->fetchmode_object_class = $object_class; |
| | | case DB_FETCHMODE_ORDERED: |
| | | case DB_FETCHMODE_ASSOC: |
| | | $this->fetchmode = $fetchmode; |
| | | break; |
| | | default: |
| | | return $this->raiseError('invalid fetchmode mode'); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setOption() |
| | | |
| | | /** |
| | | * Sets run-time configuration options for PEAR DB |
| | | * |
| | | * Options, their data types, default values and description: |
| | | * <ul> |
| | | * <li> |
| | | * <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp> |
| | | * <br />should results be freed automatically when there are no |
| | | * more rows? |
| | | * </li><li> |
| | | * <var>result_buffering</var> <kbd>integer</kbd> = <samp>500</samp> |
| | | * <br />how many rows of the result set should be buffered? |
| | | * <br />In mysql: mysql_unbuffered_query() is used instead of |
| | | * mysql_query() if this value is 0. (Release 1.7.0) |
| | | * <br />In oci8: this value is passed to ocisetprefetch(). |
| | | * (Release 1.7.0) |
| | | * </li><li> |
| | | * <var>debug</var> <kbd>integer</kbd> = <samp>0</samp> |
| | | * <br />debug level |
| | | * </li><li> |
| | | * <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp> |
| | | * <br />should the connection be persistent? |
| | | * </li><li> |
| | | * <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp> |
| | | * <br />portability mode constant (see below) |
| | | * </li><li> |
| | | * <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp> |
| | | * <br />the sprintf() format string used on sequence names. This |
| | | * format is applied to sequence names passed to |
| | | * createSequence(), nextID() and dropSequence(). |
| | | * </li><li> |
| | | * <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp> |
| | | * <br />use ssl to connect? |
| | | * </li> |
| | | * </ul> |
| | | * |
| | | * ----------------------------------------- |
| | | * |
| | | * PORTABILITY MODES |
| | | * |
| | | * These modes are bitwised, so they can be combined using <kbd>|</kbd> |
| | | * and removed using <kbd>^</kbd>. See the examples section below on how |
| | | * to do this. |
| | | * |
| | | * <samp>DB_PORTABILITY_NONE</samp> |
| | | * turn off all portability features |
| | | * |
| | | * This mode gets automatically turned on if the deprecated |
| | | * <var>optimize</var> option gets set to <samp>performance</samp>. |
| | | * |
| | | * |
| | | * <samp>DB_PORTABILITY_LOWERCASE</samp> |
| | | * convert names of tables and fields to lower case when using |
| | | * <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd> |
| | | * |
| | | * This mode gets automatically turned on in the following databases |
| | | * if the deprecated option <var>optimize</var> gets set to |
| | | * <samp>portability</samp>: |
| | | * + oci8 |
| | | * |
| | | * |
| | | * <samp>DB_PORTABILITY_RTRIM</samp> |
| | | * right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd> |
| | | * |
| | | * |
| | | * <samp>DB_PORTABILITY_DELETE_COUNT</samp> |
| | | * force reporting the number of rows deleted |
| | | * |
| | | * Some DBMS's don't count the number of rows deleted when performing |
| | | * simple <kbd>DELETE FROM tablename</kbd> queries. This portability |
| | | * mode tricks such DBMS's into telling the count by adding |
| | | * <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries. |
| | | * |
| | | * This mode gets automatically turned on in the following databases |
| | | * if the deprecated option <var>optimize</var> gets set to |
| | | * <samp>portability</samp>: |
| | | * + fbsql |
| | | * + mysql |
| | | * + mysqli |
| | | * + sqlite |
| | | * |
| | | * |
| | | * <samp>DB_PORTABILITY_NUMROWS</samp> |
| | | * enable hack that makes <kbd>numRows()</kbd> work in Oracle |
| | | * |
| | | * This mode gets automatically turned on in the following databases |
| | | * if the deprecated option <var>optimize</var> gets set to |
| | | * <samp>portability</samp>: |
| | | * + oci8 |
| | | * |
| | | * |
| | | * <samp>DB_PORTABILITY_ERRORS</samp> |
| | | * makes certain error messages in certain drivers compatible |
| | | * with those from other DBMS's |
| | | * |
| | | * + mysql, mysqli: change unique/primary key constraints |
| | | * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT |
| | | * |
| | | * + odbc(access): MS's ODBC driver reports 'no such field' as code |
| | | * 07001, which means 'too few parameters.' When this option is on |
| | | * that code gets mapped to DB_ERROR_NOSUCHFIELD. |
| | | * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD |
| | | * |
| | | * <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp> |
| | | * convert null values to empty strings in data output by get*() and |
| | | * fetch*(). Needed because Oracle considers empty strings to be null, |
| | | * while most other DBMS's know the difference between empty and null. |
| | | * |
| | | * |
| | | * <samp>DB_PORTABILITY_ALL</samp> |
| | | * turn on all portability features |
| | | * |
| | | * ----------------------------------------- |
| | | * |
| | | * Example 1. Simple setOption() example |
| | | * <code> |
| | | * $db->setOption('autofree', true); |
| | | * </code> |
| | | * |
| | | * Example 2. Portability for lowercasing and trimming |
| | | * <code> |
| | | * $db->setOption('portability', |
| | | * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); |
| | | * </code> |
| | | * |
| | | * Example 3. All portability options except trimming |
| | | * <code> |
| | | * $db->setOption('portability', |
| | | * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); |
| | | * </code> |
| | | * |
| | | * @param string $option option name |
| | | * @param mixed $value value for the option |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::$options |
| | | */ |
| | | function setOption($option, $value) |
| | | { |
| | | if (isset($this->options[$option])) { |
| | | $this->options[$option] = $value; |
| | | |
| | | /* |
| | | * Backwards compatibility check for the deprecated 'optimize' |
| | | * option. Done here in case settings change after connecting. |
| | | */ |
| | | if ($option == 'optimize') { |
| | | if ($value == 'portability') { |
| | | switch ($this->phptype) { |
| | | case 'oci8': |
| | | $this->options['portability'] = |
| | | DB_PORTABILITY_LOWERCASE | |
| | | DB_PORTABILITY_NUMROWS; |
| | | break; |
| | | case 'fbsql': |
| | | case 'mysql': |
| | | case 'mysqli': |
| | | case 'sqlite': |
| | | $this->options['portability'] = |
| | | DB_PORTABILITY_DELETE_COUNT; |
| | | break; |
| | | } |
| | | } else { |
| | | $this->options['portability'] = DB_PORTABILITY_NONE; |
| | | } |
| | | } |
| | | |
| | | return DB_OK; |
| | | } |
| | | return $this->raiseError("unknown option $option"); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getOption() |
| | | |
| | | /** |
| | | * Returns the value of an option |
| | | * |
| | | * @param string $option the option name you're curious about |
| | | * |
| | | * @return mixed the option's value |
| | | */ |
| | | function getOption($option) |
| | | { |
| | | if (isset($this->options[$option])) { |
| | | return $this->options[$option]; |
| | | } |
| | | return $this->raiseError("unknown option $option"); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ prepare() |
| | | |
| | | /** |
| | | * Prepares a query for multiple execution with execute() |
| | | * |
| | | * Creates a query that can be run multiple times. Each time it is run, |
| | | * the placeholders, if any, will be replaced by the contents of |
| | | * execute()'s $data argument. |
| | | * |
| | | * Three types of placeholders can be used: |
| | | * + <kbd>?</kbd> scalar value (i.e. strings, integers). The system |
| | | * will automatically quote and escape the data. |
| | | * + <kbd>!</kbd> value is inserted 'as is' |
| | | * + <kbd>&</kbd> requires a file name. The file's contents get |
| | | * inserted into the query (i.e. saving binary |
| | | * data in a db) |
| | | * |
| | | * Example 1. |
| | | * <code> |
| | | * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); |
| | | * $data = array( |
| | | * "John's text", |
| | | * "'it''s good'", |
| | | * 'filename.txt' |
| | | * ); |
| | | * $res = $db->execute($sth, $data); |
| | | * </code> |
| | | * |
| | | * Use backslashes to escape placeholder characters if you don't want |
| | | * them to be interpreted as placeholders: |
| | | * <pre> |
| | | * "UPDATE foo SET col=? WHERE col='over \& under'" |
| | | * </pre> |
| | | * |
| | | * With some database backends, this is emulated. |
| | | * |
| | | * {@internal ibase and oci8 have their own prepare() methods.}} |
| | | * |
| | | * @param string $query the query to be prepared |
| | | * |
| | | * @return mixed DB statement resource on success. A DB_Error object |
| | | * on failure. |
| | | * |
| | | * @see DB_common::execute() |
| | | */ |
| | | function prepare($query) |
| | | { |
| | | $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
| | | PREG_SPLIT_DELIM_CAPTURE); |
| | | $token = 0; |
| | | $types = array(); |
| | | $newtokens = array(); |
| | | |
| | | foreach ($tokens as $val) { |
| | | switch ($val) { |
| | | case '?': |
| | | $types[$token++] = DB_PARAM_SCALAR; |
| | | break; |
| | | case '&': |
| | | $types[$token++] = DB_PARAM_OPAQUE; |
| | | break; |
| | | case '!': |
| | | $types[$token++] = DB_PARAM_MISC; |
| | | break; |
| | | default: |
| | | $newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val); |
| | | } |
| | | } |
| | | |
| | | $this->prepare_tokens[] = &$newtokens; |
| | | end($this->prepare_tokens); |
| | | |
| | | $k = key($this->prepare_tokens); |
| | | $this->prepare_types[$k] = $types; |
| | | $this->prepared_queries[$k] = implode(' ', $newtokens); |
| | | |
| | | return $k; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoPrepare() |
| | | |
| | | /** |
| | | * Automaticaly generates an insert or update query and pass it to prepare() |
| | | * |
| | | * @param string $table the table name |
| | | * @param array $table_fields the array of field names |
| | | * @param int $mode a type of query to make: |
| | | * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE |
| | | * @param string $where for update queries: the WHERE clause to |
| | | * append to the SQL statement. Don't |
| | | * include the "WHERE" keyword. |
| | | * |
| | | * @return resource the query handle |
| | | * |
| | | * @uses DB_common::prepare(), DB_common::buildManipSQL() |
| | | */ |
| | | function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, |
| | | $where = false) |
| | | { |
| | | $query = $this->buildManipSQL($table, $table_fields, $mode, $where); |
| | | if (DB::isError($query)) { |
| | | return $query; |
| | | } |
| | | return $this->prepare($query); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoExecute() |
| | | |
| | | /** |
| | | * Automaticaly generates an insert or update query and call prepare() |
| | | * and execute() with it |
| | | * |
| | | * @param string $table the table name |
| | | * @param array $fields_values the associative array where $key is a |
| | | * field name and $value its value |
| | | * @param int $mode a type of query to make: |
| | | * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE |
| | | * @param string $where for update queries: the WHERE clause to |
| | | * append to the SQL statement. Don't |
| | | * include the "WHERE" keyword. |
| | | * |
| | | * @return mixed a new DB_result object for successful SELECT queries |
| | | * or DB_OK for successul data manipulation queries. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @uses DB_common::autoPrepare(), DB_common::execute() |
| | | */ |
| | | function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, |
| | | $where = false) |
| | | { |
| | | $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, |
| | | $where); |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | $ret =& $this->execute($sth, array_values($fields_values)); |
| | | $this->freePrepared($sth); |
| | | return $ret; |
| | | |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ buildManipSQL() |
| | | |
| | | /** |
| | | * Produces an SQL query string for autoPrepare() |
| | | * |
| | | * Example: |
| | | * <pre> |
| | | * buildManipSQL('table_sql', array('field1', 'field2', 'field3'), |
| | | * DB_AUTOQUERY_INSERT); |
| | | * </pre> |
| | | * |
| | | * That returns |
| | | * <samp> |
| | | * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) |
| | | * </samp> |
| | | * |
| | | * NOTES: |
| | | * - This belongs more to a SQL Builder class, but this is a simple |
| | | * facility. |
| | | * - Be carefull! If you don't give a $where param with an UPDATE |
| | | * query, all the records of the table will be updated! |
| | | * |
| | | * @param string $table the table name |
| | | * @param array $table_fields the array of field names |
| | | * @param int $mode a type of query to make: |
| | | * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE |
| | | * @param string $where for update queries: the WHERE clause to |
| | | * append to the SQL statement. Don't |
| | | * include the "WHERE" keyword. |
| | | * |
| | | * @return string the sql query for autoPrepare() |
| | | */ |
| | | function buildManipSQL($table, $table_fields, $mode, $where = false) |
| | | { |
| | | if (count($table_fields) == 0) { |
| | | return $this->raiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | $first = true; |
| | | switch ($mode) { |
| | | case DB_AUTOQUERY_INSERT: |
| | | $values = ''; |
| | | $names = ''; |
| | | foreach ($table_fields as $value) { |
| | | if ($first) { |
| | | $first = false; |
| | | } else { |
| | | $names .= ','; |
| | | $values .= ','; |
| | | } |
| | | $names .= $value; |
| | | $values .= '?'; |
| | | } |
| | | return "INSERT INTO $table ($names) VALUES ($values)"; |
| | | case DB_AUTOQUERY_UPDATE: |
| | | $set = ''; |
| | | foreach ($table_fields as $value) { |
| | | if ($first) { |
| | | $first = false; |
| | | } else { |
| | | $set .= ','; |
| | | } |
| | | $set .= "$value = ?"; |
| | | } |
| | | $sql = "UPDATE $table SET $set"; |
| | | if ($where) { |
| | | $sql .= " WHERE $where"; |
| | | } |
| | | return $sql; |
| | | default: |
| | | return $this->raiseError(DB_ERROR_SYNTAX); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ execute() |
| | | |
| | | /** |
| | | * Executes a DB statement prepared with prepare() |
| | | * |
| | | * Example 1. |
| | | * <code> |
| | | * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); |
| | | * $data = array( |
| | | * "John's text", |
| | | * "'it''s good'", |
| | | * 'filename.txt' |
| | | * ); |
| | | * $res =& $db->execute($sth, $data); |
| | | * </code> |
| | | * |
| | | * @param resource $stmt a DB statement resource returned from prepare() |
| | | * @param mixed $data array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return mixed a new DB_result object for successful SELECT queries |
| | | * or DB_OK for successul data manipulation queries. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * {@internal ibase and oci8 have their own execute() methods.}} |
| | | * |
| | | * @see DB_common::prepare() |
| | | */ |
| | | function &execute($stmt, $data = array()) |
| | | { |
| | | $realquery = $this->executeEmulateQuery($stmt, $data); |
| | | if (DB::isError($realquery)) { |
| | | return $realquery; |
| | | } |
| | | $result = $this->simpleQuery($realquery); |
| | | |
| | | if ($result === DB_OK || DB::isError($result)) { |
| | | return $result; |
| | | } else { |
| | | $tmp =& new DB_result($this, $result); |
| | | return $tmp; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ executeEmulateQuery() |
| | | |
| | | /** |
| | | * Emulates executing prepared statements if the DBMS not support them |
| | | * |
| | | * @param resource $stmt a DB statement resource returned from execute() |
| | | * @param mixed $data array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return mixed a string containing the real query run when emulating |
| | | * prepare/execute. A DB_Error object on failure. |
| | | * |
| | | * @access protected |
| | | * @see DB_common::execute() |
| | | */ |
| | | function executeEmulateQuery($stmt, $data = array()) |
| | | { |
| | | $stmt = (int)$stmt; |
| | | $data = (array)$data; |
| | | $this->last_parameters = $data; |
| | | |
| | | if (count($this->prepare_types[$stmt]) != count($data)) { |
| | | $this->last_query = $this->prepared_queries[$stmt]; |
| | | return $this->raiseError(DB_ERROR_MISMATCH); |
| | | } |
| | | |
| | | $realquery = $this->prepare_tokens[$stmt][0]; |
| | | |
| | | $i = 0; |
| | | foreach ($data as $value) { |
| | | if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) { |
| | | $realquery .= $this->quoteSmart($value); |
| | | } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) { |
| | | $fp = @fopen($value, 'rb'); |
| | | if (!$fp) { |
| | | return $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
| | | } |
| | | $realquery .= $this->quoteSmart(fread($fp, filesize($value))); |
| | | fclose($fp); |
| | | } else { |
| | | $realquery .= $value; |
| | | } |
| | | |
| | | $realquery .= $this->prepare_tokens[$stmt][++$i]; |
| | | } |
| | | |
| | | return $realquery; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ executeMultiple() |
| | | |
| | | /** |
| | | * Performs several execute() calls on the same statement handle |
| | | * |
| | | * $data must be an array indexed numerically |
| | | * from 0, one execute call is done for every "row" in the array. |
| | | * |
| | | * If an error occurs during execute(), executeMultiple() does not |
| | | * execute the unfinished rows, but rather returns that error. |
| | | * |
| | | * @param resource $stmt query handle from prepare() |
| | | * @param array $data numeric array containing the |
| | | * data to insert into the query |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::prepare(), DB_common::execute() |
| | | */ |
| | | function executeMultiple($stmt, $data) |
| | | { |
| | | foreach ($data as $value) { |
| | | $res =& $this->execute($stmt, $value); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freePrepared() |
| | | |
| | | /** |
| | | * Frees the internal resources associated with a prepared query |
| | | * |
| | | * @param resource $stmt the prepared statement's PHP resource |
| | | * @param bool $free_resource should the PHP resource be freed too? |
| | | * Use false if you need to get data |
| | | * from the result set later. |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_common::prepare() |
| | | */ |
| | | function freePrepared($stmt, $free_resource = true) |
| | | { |
| | | $stmt = (int)$stmt; |
| | | if (isset($this->prepare_tokens[$stmt])) { |
| | | unset($this->prepare_tokens[$stmt]); |
| | | unset($this->prepare_types[$stmt]); |
| | | unset($this->prepared_queries[$stmt]); |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyQuery() |
| | | |
| | | /** |
| | | * Changes a query string for various DBMS specific reasons |
| | | * |
| | | * It is defined here to ensure all drivers have this method available. |
| | | * |
| | | * @param string $query the query string to modify |
| | | * |
| | | * @return string the modified query string |
| | | * |
| | | * @access protected |
| | | * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(), |
| | | * DB_sqlite::modifyQuery() |
| | | */ |
| | | function modifyQuery($query) |
| | | { |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * It is defined here to assure that all implementations |
| | | * have this method defined. |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ query() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * The query string can be either a normal statement to be sent directly |
| | | * to the server OR if <var>$params</var> are passed the query can have |
| | | * placeholders and it will be passed through prepare() and execute(). |
| | | * |
| | | * @param string $query the SQL query or the statement to prepare |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return mixed a new DB_result object for successful SELECT queries |
| | | * or DB_OK for successul data manipulation queries. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_result, DB_common::prepare(), DB_common::execute() |
| | | */ |
| | | function &query($query, $params = array()) |
| | | { |
| | | if (sizeof($params) > 0) { |
| | | $sth = $this->prepare($query); |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | $ret =& $this->execute($sth, $params); |
| | | $this->freePrepared($sth, false); |
| | | return $ret; |
| | | } else { |
| | | $this->last_parameters = array(); |
| | | $result = $this->simpleQuery($query); |
| | | if ($result === DB_OK || DB::isError($result)) { |
| | | return $result; |
| | | } else { |
| | | $tmp =& new DB_result($this, $result); |
| | | return $tmp; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ limitQuery() |
| | | |
| | | /** |
| | | * Generates and executes a LIMIT query |
| | | * |
| | | * @param string $query the query |
| | | * @param intr $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return mixed a new DB_result object for successful SELECT queries |
| | | * or DB_OK for successul data manipulation queries. |
| | | * A DB_Error object on failure. |
| | | */ |
| | | function &limitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | $query = $this->modifyLimitQuery($query, $from, $count, $params); |
| | | if (DB::isError($query)){ |
| | | return $query; |
| | | } |
| | | $result =& $this->query($query, $params); |
| | | if (is_a($result, 'DB_result')) { |
| | | $result->setOption('limit_from', $from); |
| | | $result->setOption('limit_count', $count); |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getOne() |
| | | |
| | | /** |
| | | * Fetches the first column of the first row from a query result |
| | | * |
| | | * Takes care of doing the query and freeing the results when finished. |
| | | * |
| | | * @param string $query the SQL query |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return mixed the returned value of the query. |
| | | * A DB_Error object on failure. |
| | | */ |
| | | function &getOne($query, $params = array()) |
| | | { |
| | | $params = (array)$params; |
| | | // modifyLimitQuery() would be nice here, but it causes BC issues |
| | | if (sizeof($params) > 0) { |
| | | $sth = $this->prepare($query); |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | $res =& $this->execute($sth, $params); |
| | | $this->freePrepared($sth); |
| | | } else { |
| | | $res =& $this->query($query); |
| | | } |
| | | |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | |
| | | $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED); |
| | | $res->free(); |
| | | |
| | | if ($err !== DB_OK) { |
| | | return $err; |
| | | } |
| | | |
| | | return $row[0]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getRow() |
| | | |
| | | /** |
| | | * Fetches the first row of data returned from a query result |
| | | * |
| | | * Takes care of doing the query and freeing the results when finished. |
| | | * |
| | | * @param string $query the SQL query |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * @param int $fetchmode the fetch mode to use |
| | | * |
| | | * @return array the first row of results as an array. |
| | | * A DB_Error object on failure. |
| | | */ |
| | | function &getRow($query, $params = array(), |
| | | $fetchmode = DB_FETCHMODE_DEFAULT) |
| | | { |
| | | // compat check, the params and fetchmode parameters used to |
| | | // have the opposite order |
| | | if (!is_array($params)) { |
| | | if (is_array($fetchmode)) { |
| | | if ($params === null) { |
| | | $tmp = DB_FETCHMODE_DEFAULT; |
| | | } else { |
| | | $tmp = $params; |
| | | } |
| | | $params = $fetchmode; |
| | | $fetchmode = $tmp; |
| | | } elseif ($params !== null) { |
| | | $fetchmode = $params; |
| | | $params = array(); |
| | | } |
| | | } |
| | | // modifyLimitQuery() would be nice here, but it causes BC issues |
| | | if (sizeof($params) > 0) { |
| | | $sth = $this->prepare($query); |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | $res =& $this->execute($sth, $params); |
| | | $this->freePrepared($sth); |
| | | } else { |
| | | $res =& $this->query($query); |
| | | } |
| | | |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | |
| | | $err = $res->fetchInto($row, $fetchmode); |
| | | |
| | | $res->free(); |
| | | |
| | | if ($err !== DB_OK) { |
| | | return $err; |
| | | } |
| | | |
| | | return $row; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getCol() |
| | | |
| | | /** |
| | | * Fetches a single column from a query result and returns it as an |
| | | * indexed array |
| | | * |
| | | * @param string $query the SQL query |
| | | * @param mixed $col which column to return (integer [column number, |
| | | * starting at 0] or string [column name]) |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return array the results as an array. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::query() |
| | | */ |
| | | function &getCol($query, $col = 0, $params = array()) |
| | | { |
| | | $params = (array)$params; |
| | | if (sizeof($params) > 0) { |
| | | $sth = $this->prepare($query); |
| | | |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | |
| | | $res =& $this->execute($sth, $params); |
| | | $this->freePrepared($sth); |
| | | } else { |
| | | $res =& $this->query($query); |
| | | } |
| | | |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | |
| | | $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC; |
| | | |
| | | if (!is_array($row = $res->fetchRow($fetchmode))) { |
| | | $ret = array(); |
| | | } else { |
| | | if (!array_key_exists($col, $row)) { |
| | | $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD); |
| | | } else { |
| | | $ret = array($row[$col]); |
| | | while (is_array($row = $res->fetchRow($fetchmode))) { |
| | | $ret[] = $row[$col]; |
| | | } |
| | | } |
| | | } |
| | | |
| | | $res->free(); |
| | | |
| | | if (DB::isError($row)) { |
| | | $ret = $row; |
| | | } |
| | | |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getAssoc() |
| | | |
| | | /** |
| | | * Fetches an entire query result and returns it as an |
| | | * associative array using the first column as the key |
| | | * |
| | | * If the result set contains more than two columns, the value |
| | | * will be an array of the values from column 2-n. If the result |
| | | * set contains only two columns, the returned value will be a |
| | | * scalar with the value of the second column (unless forced to an |
| | | * array with the $force_array parameter). A DB error code is |
| | | * returned on errors. If the result set contains fewer than two |
| | | * columns, a DB_ERROR_TRUNCATED error is returned. |
| | | * |
| | | * For example, if the table "mytable" contains: |
| | | * |
| | | * <pre> |
| | | * ID TEXT DATE |
| | | * -------------------------------- |
| | | * 1 'one' 944679408 |
| | | * 2 'two' 944679408 |
| | | * 3 'three' 944679408 |
| | | * </pre> |
| | | * |
| | | * Then the call getAssoc('SELECT id,text FROM mytable') returns: |
| | | * <pre> |
| | | * array( |
| | | * '1' => 'one', |
| | | * '2' => 'two', |
| | | * '3' => 'three', |
| | | * ) |
| | | * </pre> |
| | | * |
| | | * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns: |
| | | * <pre> |
| | | * array( |
| | | * '1' => array('one', '944679408'), |
| | | * '2' => array('two', '944679408'), |
| | | * '3' => array('three', '944679408') |
| | | * ) |
| | | * </pre> |
| | | * |
| | | * If the more than one row occurs with the same value in the |
| | | * first column, the last row overwrites all previous ones by |
| | | * default. Use the $group parameter if you don't want to |
| | | * overwrite like this. Example: |
| | | * |
| | | * <pre> |
| | | * getAssoc('SELECT category,id,name FROM mytable', false, null, |
| | | * DB_FETCHMODE_ASSOC, true) returns: |
| | | * |
| | | * array( |
| | | * '1' => array(array('id' => '4', 'name' => 'number four'), |
| | | * array('id' => '6', 'name' => 'number six') |
| | | * ), |
| | | * '9' => array(array('id' => '4', 'name' => 'number four'), |
| | | * array('id' => '6', 'name' => 'number six') |
| | | * ) |
| | | * ) |
| | | * </pre> |
| | | * |
| | | * Keep in mind that database functions in PHP usually return string |
| | | * values for results regardless of the database's internal type. |
| | | * |
| | | * @param string $query the SQL query |
| | | * @param bool $force_array used only when the query returns |
| | | * exactly two columns. If true, the values |
| | | * of the returned array will be one-element |
| | | * arrays instead of scalars. |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of |
| | | * items passed must match quantity of |
| | | * placeholders in query: meaning 1 |
| | | * placeholder for non-array parameters or |
| | | * 1 placeholder per array element. |
| | | * @param int $fetchmode the fetch mode to use |
| | | * @param bool $group if true, the values of the returned array |
| | | * is wrapped in another array. If the same |
| | | * key value (in the first column) repeats |
| | | * itself, the values will be appended to |
| | | * this array instead of overwriting the |
| | | * existing values. |
| | | * |
| | | * @return array the associative array containing the query results. |
| | | * A DB_Error object on failure. |
| | | */ |
| | | function &getAssoc($query, $force_array = false, $params = array(), |
| | | $fetchmode = DB_FETCHMODE_DEFAULT, $group = false) |
| | | { |
| | | $params = (array)$params; |
| | | if (sizeof($params) > 0) { |
| | | $sth = $this->prepare($query); |
| | | |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | |
| | | $res =& $this->execute($sth, $params); |
| | | $this->freePrepared($sth); |
| | | } else { |
| | | $res =& $this->query($query); |
| | | } |
| | | |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | if ($fetchmode == DB_FETCHMODE_DEFAULT) { |
| | | $fetchmode = $this->fetchmode; |
| | | } |
| | | $cols = $res->numCols(); |
| | | |
| | | if ($cols < 2) { |
| | | $tmp =& $this->raiseError(DB_ERROR_TRUNCATED); |
| | | return $tmp; |
| | | } |
| | | |
| | | $results = array(); |
| | | |
| | | if ($cols > 2 || $force_array) { |
| | | // return array values |
| | | // XXX this part can be optimized |
| | | if ($fetchmode == DB_FETCHMODE_ASSOC) { |
| | | while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) { |
| | | reset($row); |
| | | $key = current($row); |
| | | unset($row[key($row)]); |
| | | if ($group) { |
| | | $results[$key][] = $row; |
| | | } else { |
| | | $results[$key] = $row; |
| | | } |
| | | } |
| | | } elseif ($fetchmode == DB_FETCHMODE_OBJECT) { |
| | | while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) { |
| | | $arr = get_object_vars($row); |
| | | $key = current($arr); |
| | | if ($group) { |
| | | $results[$key][] = $row; |
| | | } else { |
| | | $results[$key] = $row; |
| | | } |
| | | } |
| | | } else { |
| | | while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { |
| | | // we shift away the first element to get |
| | | // indices running from 0 again |
| | | $key = array_shift($row); |
| | | if ($group) { |
| | | $results[$key][] = $row; |
| | | } else { |
| | | $results[$key] = $row; |
| | | } |
| | | } |
| | | } |
| | | if (DB::isError($row)) { |
| | | $results = $row; |
| | | } |
| | | } else { |
| | | // return scalar values |
| | | while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { |
| | | if ($group) { |
| | | $results[$row[0]][] = $row[1]; |
| | | } else { |
| | | $results[$row[0]] = $row[1]; |
| | | } |
| | | } |
| | | if (DB::isError($row)) { |
| | | $results = $row; |
| | | } |
| | | } |
| | | |
| | | $res->free(); |
| | | |
| | | return $results; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getAll() |
| | | |
| | | /** |
| | | * Fetches all of the rows from a query result |
| | | * |
| | | * @param string $query the SQL query |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of |
| | | * items passed must match quantity of |
| | | * placeholders in query: meaning 1 |
| | | * placeholder for non-array parameters or |
| | | * 1 placeholder per array element. |
| | | * @param int $fetchmode the fetch mode to use: |
| | | * + DB_FETCHMODE_ORDERED |
| | | * + DB_FETCHMODE_ASSOC |
| | | * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED |
| | | * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED |
| | | * |
| | | * @return array the nested array. A DB_Error object on failure. |
| | | */ |
| | | function &getAll($query, $params = array(), |
| | | $fetchmode = DB_FETCHMODE_DEFAULT) |
| | | { |
| | | // compat check, the params and fetchmode parameters used to |
| | | // have the opposite order |
| | | if (!is_array($params)) { |
| | | if (is_array($fetchmode)) { |
| | | if ($params === null) { |
| | | $tmp = DB_FETCHMODE_DEFAULT; |
| | | } else { |
| | | $tmp = $params; |
| | | } |
| | | $params = $fetchmode; |
| | | $fetchmode = $tmp; |
| | | } elseif ($params !== null) { |
| | | $fetchmode = $params; |
| | | $params = array(); |
| | | } |
| | | } |
| | | |
| | | if (sizeof($params) > 0) { |
| | | $sth = $this->prepare($query); |
| | | |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | |
| | | $res =& $this->execute($sth, $params); |
| | | $this->freePrepared($sth); |
| | | } else { |
| | | $res =& $this->query($query); |
| | | } |
| | | |
| | | if ($res === DB_OK || DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | |
| | | $results = array(); |
| | | while (DB_OK === $res->fetchInto($row, $fetchmode)) { |
| | | if ($fetchmode & DB_FETCHMODE_FLIPPED) { |
| | | foreach ($row as $key => $val) { |
| | | $results[$key][] = $val; |
| | | } |
| | | } else { |
| | | $results[] = $row; |
| | | } |
| | | } |
| | | |
| | | $res->free(); |
| | | |
| | | if (DB::isError($row)) { |
| | | $tmp =& $this->raiseError($row); |
| | | return $tmp; |
| | | } |
| | | return $results; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Determines the number of rows in a query result |
| | | * |
| | | * @param resource $result the query result idenifier produced by PHP |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSequenceName() |
| | | |
| | | /** |
| | | * Generates the name used inside the database for a sequence |
| | | * |
| | | * The createSequence() docblock contains notes about storing sequence |
| | | * names. |
| | | * |
| | | * @param string $sqn the sequence's public name |
| | | * |
| | | * @return string the sequence's name in the backend |
| | | * |
| | | * @access protected |
| | | * @see DB_common::createSequence(), DB_common::dropSequence(), |
| | | * DB_common::nextID(), DB_common::setOption() |
| | | */ |
| | | function getSequenceName($sqn) |
| | | { |
| | | return sprintf($this->getOption('seqname_format'), |
| | | preg_replace('/[^a-z0-9_.]/i', '_', $sqn)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::dropSequence(), |
| | | * DB_common::getSequenceName() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ createSequence() |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * The name of a given sequence is determined by passing the string |
| | | * provided in the <var>$seq_name</var> argument through PHP's sprintf() |
| | | * function using the value from the <var>seqname_format</var> option as |
| | | * the sprintf()'s format argument. |
| | | * |
| | | * <var>seqname_format</var> is set via setOption(). |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_common::nextID() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_common::nextID() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ raiseError() |
| | | |
| | | /** |
| | | * Communicates an error and invoke error callbacks, etc |
| | | * |
| | | * Basically a wrapper for PEAR::raiseError without the message string. |
| | | * |
| | | * @param mixed integer error code, or a PEAR error object (all |
| | | * other parameters are ignored if this parameter is |
| | | * an object |
| | | * @param int error mode, see PEAR_Error docs |
| | | * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the |
| | | * error level (E_USER_NOTICE etc). If error mode is |
| | | * PEAR_ERROR_CALLBACK, this is the callback function, |
| | | * either as a function name, or as an array of an |
| | | * object and method name. For other error modes this |
| | | * parameter is ignored. |
| | | * @param string extra debug information. Defaults to the last |
| | | * query and native error code. |
| | | * @param mixed native error code, integer or string depending the |
| | | * backend |
| | | * |
| | | * @return object the PEAR_Error object |
| | | * |
| | | * @see PEAR_Error |
| | | */ |
| | | function &raiseError($code = DB_ERROR, $mode = null, $options = null, |
| | | $userinfo = null, $nativecode = null) |
| | | { |
| | | // The error is yet a DB error object |
| | | if (is_object($code)) { |
| | | // because we the static PEAR::raiseError, our global |
| | | // handler should be used if it is set |
| | | if ($mode === null && !empty($this->_default_error_mode)) { |
| | | $mode = $this->_default_error_mode; |
| | | $options = $this->_default_error_options; |
| | | } |
| | | $tmp = PEAR::raiseError($code, null, $mode, $options, |
| | | null, null, true); |
| | | return $tmp; |
| | | } |
| | | |
| | | if ($userinfo === null) { |
| | | $userinfo = $this->last_query; |
| | | } |
| | | |
| | | if ($nativecode) { |
| | | $userinfo .= ' [nativecode=' . trim($nativecode) . ']'; |
| | | } else { |
| | | $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']'; |
| | | } |
| | | |
| | | $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, |
| | | 'DB_Error', true); |
| | | return $tmp; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return mixed the DBMS' error code. A DB_Error object on failure. |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Maps native error codes to DB's portable ones |
| | | * |
| | | * Uses the <var>$errorcode_map</var> property defined in each driver. |
| | | * |
| | | * @param string|int $nativecode the error code returned by the DBMS |
| | | * |
| | | * @return int the portable DB error code. Return DB_ERROR if the |
| | | * current driver doesn't have a mapping for the |
| | | * $nativecode submitted. |
| | | */ |
| | | function errorCode($nativecode) |
| | | { |
| | | if (isset($this->errorcode_map[$nativecode])) { |
| | | return $this->errorcode_map[$nativecode]; |
| | | } |
| | | // Fall back to DB_ERROR if there was no mapping. |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorMessage() |
| | | |
| | | /** |
| | | * Maps a DB error code to a textual message |
| | | * |
| | | * @param integer $dbcode the DB error code |
| | | * |
| | | * @return string the error message corresponding to the error code |
| | | * submitted. FALSE if the error code is unknown. |
| | | * |
| | | * @see DB::errorMessage() |
| | | */ |
| | | function errorMessage($dbcode) |
| | | { |
| | | return DB::errorMessage($this->errorcode_map[$dbcode]); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * The format of the resulting array depends on which <var>$mode</var> |
| | | * you select. The sample output below is based on this query: |
| | | * <pre> |
| | | * SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId |
| | | * FROM tblFoo |
| | | * JOIN tblBar ON tblFoo.fldId = tblBar.fldId |
| | | * </pre> |
| | | * |
| | | * <ul> |
| | | * <li> |
| | | * |
| | | * <kbd>null</kbd> (default) |
| | | * <pre> |
| | | * [0] => Array ( |
| | | * [table] => tblFoo |
| | | * [name] => fldId |
| | | * [type] => int |
| | | * [len] => 11 |
| | | * [flags] => primary_key not_null |
| | | * ) |
| | | * [1] => Array ( |
| | | * [table] => tblFoo |
| | | * [name] => fldPhone |
| | | * [type] => string |
| | | * [len] => 20 |
| | | * [flags] => |
| | | * ) |
| | | * [2] => Array ( |
| | | * [table] => tblBar |
| | | * [name] => fldId |
| | | * [type] => int |
| | | * [len] => 11 |
| | | * [flags] => primary_key not_null |
| | | * ) |
| | | * </pre> |
| | | * |
| | | * </li><li> |
| | | * |
| | | * <kbd>DB_TABLEINFO_ORDER</kbd> |
| | | * |
| | | * <p>In addition to the information found in the default output, |
| | | * a notation of the number of columns is provided by the |
| | | * <samp>num_fields</samp> element while the <samp>order</samp> |
| | | * element provides an array with the column names as the keys and |
| | | * their location index number (corresponding to the keys in the |
| | | * the default output) as the values.</p> |
| | | * |
| | | * <p>If a result set has identical field names, the last one is |
| | | * used.</p> |
| | | * |
| | | * <pre> |
| | | * [num_fields] => 3 |
| | | * [order] => Array ( |
| | | * [fldId] => 2 |
| | | * [fldTrans] => 1 |
| | | * ) |
| | | * </pre> |
| | | * |
| | | * </li><li> |
| | | * |
| | | * <kbd>DB_TABLEINFO_ORDERTABLE</kbd> |
| | | * |
| | | * <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more |
| | | * dimensions to the array in which the table names are keys and |
| | | * the field names are sub-keys. This is helpful for queries that |
| | | * join tables which have identical field names.</p> |
| | | * |
| | | * <pre> |
| | | * [num_fields] => 3 |
| | | * [ordertable] => Array ( |
| | | * [tblFoo] => Array ( |
| | | * [fldId] => 0 |
| | | * [fldPhone] => 1 |
| | | * ) |
| | | * [tblBar] => Array ( |
| | | * [fldId] => 2 |
| | | * ) |
| | | * ) |
| | | * </pre> |
| | | * |
| | | * </li> |
| | | * </ul> |
| | | * |
| | | * The <samp>flags</samp> element contains a space separated list |
| | | * of extra information about the field. This data is inconsistent |
| | | * between DBMS's due to the way each DBMS works. |
| | | * + <samp>primary_key</samp> |
| | | * + <samp>unique_key</samp> |
| | | * + <samp>multiple_key</samp> |
| | | * + <samp>not_null</samp> |
| | | * |
| | | * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp> |
| | | * elements if <var>$result</var> is a table name. The following DBMS's |
| | | * provide full information from queries: |
| | | * + fbsql |
| | | * + mysql |
| | | * |
| | | * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp> |
| | | * turned on, the names of tables and fields will be lowercased. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode either unused or one of the tableInfo modes: |
| | | * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>, |
| | | * <kbd>DB_TABLEINFO_ORDER</kbd> or |
| | | * <kbd>DB_TABLEINFO_FULL</kbd> (which does both). |
| | | * These are bitwise, so the first two can be |
| | | * combined using <kbd>|</kbd>. |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::setOption() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | /* |
| | | * If the DB_<driver> class has a tableInfo() method, that one |
| | | * overrides this one. But, if the driver doesn't have one, |
| | | * this method runs and tells users about that fact. |
| | | */ |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getTables() |
| | | |
| | | /** |
| | | * Lists the tables in the current database |
| | | * |
| | | * @return array the list of tables. A DB_Error object on failure. |
| | | * |
| | | * @deprecated Method deprecated some time before Release 1.2 |
| | | */ |
| | | function getTables() |
| | | { |
| | | return $this->getListOf('tables'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getListOf() |
| | | |
| | | /** |
| | | * Lists internal database information |
| | | * |
| | | * @param string $type type of information being sought. |
| | | * Common items being sought are: |
| | | * tables, databases, users, views, functions |
| | | * Each DBMS's has its own capabilities. |
| | | * |
| | | * @return array an array listing the items sought. |
| | | * A DB DB_Error object on failure. |
| | | */ |
| | | function getListOf($type) |
| | | { |
| | | $sql = $this->getSpecialQuery($type); |
| | | if ($sql === null) { |
| | | $this->last_query = ''; |
| | | return $this->raiseError(DB_ERROR_UNSUPPORTED); |
| | | } elseif (is_int($sql) || DB::isError($sql)) { |
| | | // Previous error |
| | | return $this->raiseError($sql); |
| | | } elseif (is_array($sql)) { |
| | | // Already the result |
| | | return $sql; |
| | | } |
| | | // Launch this query |
| | | return $this->getCol($sql); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | return $this->raiseError(DB_ERROR_UNSUPPORTED); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _rtrimArrayValues() |
| | | |
| | | /** |
| | | * Right-trims all strings in an array |
| | | * |
| | | * @param array $array the array to be trimmed (passed by reference) |
| | | * |
| | | * @return void |
| | | * |
| | | * @access protected |
| | | */ |
| | | function _rtrimArrayValues(&$array) |
| | | { |
| | | foreach ($array as $key => $value) { |
| | | if (is_string($value)) { |
| | | $array[$key] = rtrim($value); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _convertNullArrayValuesToEmpty() |
| | | |
| | | /** |
| | | * Converts all null values in an array to empty strings |
| | | * |
| | | * @param array $array the array to be de-nullified (passed by reference) |
| | | * |
| | | * @return void |
| | | * |
| | | * @access protected |
| | | */ |
| | | function _convertNullArrayValuesToEmpty(&$array) |
| | | { |
| | | foreach ($array as $key => $value) { |
| | | if (is_null($value)) { |
| | | $array[$key] = ''; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's dbase extension |
| | | * for interacting with dBase databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Tomas V.V. Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's dbase extension |
| | | * for interacting with dBase databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Tomas V.V. Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_dbase extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'dbase'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'dbase'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => false, |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => false, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => false, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * A means of emulating result resources |
| | | * @var array |
| | | */ |
| | | var $res_row = array(); |
| | | |
| | | /** |
| | | * The quantity of results so far |
| | | * |
| | | * For emulating result resources. |
| | | * |
| | | * @var integer |
| | | */ |
| | | var $result = 0; |
| | | |
| | | /** |
| | | * Maps dbase data type id's to human readable strings |
| | | * |
| | | * The human readable values are based on the output of PHP's |
| | | * dbase_get_header_info() function. |
| | | * |
| | | * @var array |
| | | * @since Property available since Release 1.7.0 |
| | | */ |
| | | var $types = array( |
| | | 'C' => 'character', |
| | | 'D' => 'date', |
| | | 'L' => 'boolean', |
| | | 'M' => 'memo', |
| | | 'N' => 'number', |
| | | ); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_dbase() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database and create it if it doesn't exist |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's dbase driver supports the following extra DSN options: |
| | | * + mode An integer specifying the read/write mode to use |
| | | * (0 = read only, 1 = write only, 2 = read/write). |
| | | * Available since PEAR DB 1.7.0. |
| | | * + fields An array of arrays that PHP's dbase_create() function needs |
| | | * to create a new database. This information is used if the |
| | | * dBase file specified in the "database" segment of the DSN |
| | | * does not exist. For more info, see the PHP manual's |
| | | * {@link http://php.net/dbase_create dbase_create()} page. |
| | | * Available since PEAR DB 1.7.0. |
| | | * |
| | | * Example of how to connect and establish a new dBase file if necessary: |
| | | * <code> |
| | | * require_once 'DB.php'; |
| | | * |
| | | * $dsn = array( |
| | | * 'phptype' => 'dbase', |
| | | * 'database' => '/path/and/name/of/dbase/file', |
| | | * 'mode' => 2, |
| | | * 'fields' => array( |
| | | * array('a', 'N', 5, 0), |
| | | * array('b', 'C', 40), |
| | | * array('c', 'C', 255), |
| | | * array('d', 'C', 20), |
| | | * ), |
| | | * ); |
| | | * $options = array( |
| | | * 'debug' => 2, |
| | | * 'portability' => DB_PORTABILITY_ALL, |
| | | * ); |
| | | * |
| | | * $db =& DB::connect($dsn, $options); |
| | | * if (PEAR::isError($db)) { |
| | | * die($db->getMessage()); |
| | | * } |
| | | * </code> |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('dbase')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | /* |
| | | * Turn track_errors on for entire script since $php_errormsg |
| | | * is the only way to find errors from the dbase extension. |
| | | */ |
| | | ini_set('track_errors', 1); |
| | | $php_errormsg = ''; |
| | | |
| | | if (!file_exists($dsn['database'])) { |
| | | $this->dsn['mode'] = 2; |
| | | if (empty($dsn['fields']) || !is_array($dsn['fields'])) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | 'the dbase file does not exist and ' |
| | | . 'it could not be created because ' |
| | | . 'the "fields" element of the DSN ' |
| | | . 'is not properly set'); |
| | | } |
| | | $this->connection = @dbase_create($dsn['database'], |
| | | $dsn['fields']); |
| | | if (!$this->connection) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | 'the dbase file does not exist and ' |
| | | . 'the attempt to create it failed: ' |
| | | . $php_errormsg); |
| | | } |
| | | } else { |
| | | if (!isset($this->dsn['mode'])) { |
| | | $this->dsn['mode'] = 0; |
| | | } |
| | | $this->connection = @dbase_open($dsn['database'], |
| | | $this->dsn['mode']); |
| | | if (!$this->connection) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @dbase_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ &query() |
| | | |
| | | function &query($query = null) |
| | | { |
| | | // emulate result resources |
| | | $this->res_row[(int)$this->result] = 0; |
| | | $tmp =& new DB_result($this, $this->result++); |
| | | return $tmp; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum === null) { |
| | | $rownum = $this->res_row[(int)$result]++; |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @dbase_get_record_with_names($this->connection, $rownum); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @dbase_get_record($this->connection, $rownum); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($foo) |
| | | { |
| | | return @dbase_numfields($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($foo) |
| | | { |
| | | return @dbase_numrecords($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteSmart() |
| | | |
| | | /** |
| | | * Formats input so it can be safely used in a query |
| | | * |
| | | * @param mixed $in the data to be formatted |
| | | * |
| | | * @return mixed the formatted data. The format depends on the input's |
| | | * PHP type: |
| | | * + null = the string <samp>NULL</samp> |
| | | * + boolean = <samp>T</samp> if true or |
| | | * <samp>F</samp> if false. Use the <kbd>Logical</kbd> |
| | | * data type. |
| | | * + integer or double = the unquoted number |
| | | * + other (including strings and numeric strings) = |
| | | * the data with single quotes escaped by preceeding |
| | | * single quotes then the whole string is encapsulated |
| | | * between single quotes |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteSmart($in) |
| | | { |
| | | if (is_int($in) || is_double($in)) { |
| | | return $in; |
| | | } elseif (is_bool($in)) { |
| | | return $in ? 'T' : 'F'; |
| | | } elseif (is_null($in)) { |
| | | return 'NULL'; |
| | | } else { |
| | | return "'" . $this->escapeSimple($in) . "'"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about the current database |
| | | * |
| | | * @param mixed $result THIS IS UNUSED IN DBASE. The current database |
| | | * is examined regardless of what is provided here. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function tableInfo($result = null, $mode = null) |
| | | { |
| | | if (function_exists('dbase_get_header_info')) { |
| | | $id = @dbase_get_header_info($this->connection); |
| | | if (!$id && $php_errormsg) { |
| | | return $this->raiseError(DB_ERROR, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | } else { |
| | | /* |
| | | * This segment for PHP 4 is loosely based on code by |
| | | * Hadi Rusiah <deegos@yahoo.com> in the comments on |
| | | * the dBase reference page in the PHP manual. |
| | | */ |
| | | $db = @fopen($this->dsn['database'], 'r'); |
| | | if (!$db) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | |
| | | $id = array(); |
| | | $i = 0; |
| | | |
| | | $line = fread($db, 32); |
| | | while (!feof($db)) { |
| | | $line = fread($db, 32); |
| | | if (substr($line, 0, 1) == chr(13)) { |
| | | break; |
| | | } else { |
| | | $pos = strpos(substr($line, 0, 10), chr(0)); |
| | | $pos = ($pos == 0 ? 10 : $pos); |
| | | $id[$i] = array( |
| | | 'name' => substr($line, 0, $pos), |
| | | 'type' => $this->types[substr($line, 11, 1)], |
| | | 'length' => ord(substr($line, 16, 1)), |
| | | 'precision' => ord(substr($line, 17, 1)), |
| | | ); |
| | | } |
| | | $i++; |
| | | } |
| | | |
| | | fclose($db); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $res = array(); |
| | | $count = count($id); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $res[$i] = array( |
| | | 'table' => $this->dsn['database'], |
| | | 'name' => $case_func($id[$i]['name']), |
| | | 'type' => $id[$i]['type'], |
| | | 'len' => $id[$i]['length'], |
| | | 'flags' => '' |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's fbsql extension |
| | | * for interacting with FrontBase databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Frank M. Kromann <frank@frontbase.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's fbsql extension |
| | | * for interacting with FrontBase databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Frank M. Kromann <frank@frontbase.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | * @since Class functional since Release 1.7.0 |
| | | */ |
| | | class DB_fbsql extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'fbsql'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'fbsql'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'alter', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | 22 => DB_ERROR_SYNTAX, |
| | | 85 => DB_ERROR_ALREADY_EXISTS, |
| | | 108 => DB_ERROR_SYNTAX, |
| | | 116 => DB_ERROR_NOSUCHTABLE, |
| | | 124 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | 215 => DB_ERROR_NOSUCHFIELD, |
| | | 217 => DB_ERROR_INVALID_NUMBER, |
| | | 226 => DB_ERROR_NOSUCHFIELD, |
| | | 231 => DB_ERROR_INVALID, |
| | | 239 => DB_ERROR_TRUNCATED, |
| | | 251 => DB_ERROR_SYNTAX, |
| | | 266 => DB_ERROR_NOT_FOUND, |
| | | 357 => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | 358 => DB_ERROR_CONSTRAINT, |
| | | 360 => DB_ERROR_CONSTRAINT, |
| | | 361 => DB_ERROR_CONSTRAINT, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_fbsql() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('fbsql')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $params = array( |
| | | $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', |
| | | $dsn['username'] ? $dsn['username'] : null, |
| | | $dsn['password'] ? $dsn['password'] : null, |
| | | ); |
| | | |
| | | $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect'; |
| | | |
| | | $ini = ini_get('track_errors'); |
| | | $php_errormsg = ''; |
| | | if ($ini) { |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | } else { |
| | | ini_set('track_errors', 1); |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | ini_set('track_errors', $ini); |
| | | } |
| | | |
| | | if (!$this->connection) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | |
| | | if ($dsn['database']) { |
| | | if (!@fbsql_select_db($dsn['database'], $this->connection)) { |
| | | return $this->fbsqlRaiseError(); |
| | | } |
| | | } |
| | | |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @fbsql_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | $result = @fbsql_query("$query;", $this->connection); |
| | | if (!$result) { |
| | | return $this->fbsqlRaiseError(); |
| | | } |
| | | // Determine which queries that should return data, and which |
| | | // should return an error code only. |
| | | if (DB::isManip($query)) { |
| | | return DB_OK; |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal fbsql result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return @fbsql_next_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@fbsql_data_seek($result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @fbsql_fetch_array($result, FBSQL_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @fbsql_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @fbsql_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff=false) |
| | | { |
| | | if ($onoff) { |
| | | $this->query("SET COMMIT TRUE"); |
| | | } else { |
| | | $this->query("SET COMMIT FALSE"); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | @fbsql_commit(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | @fbsql_rollback(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @fbsql_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->fbsqlRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @fbsql_num_rows($result); |
| | | if ($rows === null) { |
| | | return $this->fbsqlRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (DB::isManip($this->last_query)) { |
| | | $result = @fbsql_affected_rows($this->connection); |
| | | } else { |
| | | $result = 0; |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_fbsql::createSequence(), DB_fbsql::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | do { |
| | | $repeat = 0; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query('SELECT UNIQUE FROM ' . $seqname); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) { |
| | | $repeat = 1; |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | } else { |
| | | $repeat = 0; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->fbsqlRaiseError(); |
| | | } |
| | | $result->fetchInto($tmp, DB_FETCHMODE_ORDERED); |
| | | return $tmp[0]; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_fbsql::nextID(), DB_fbsql::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $res = $this->query('CREATE TABLE ' . $seqname |
| | | . ' (id INTEGER NOT NULL,' |
| | | . ' PRIMARY KEY(id))'); |
| | | if ($res) { |
| | | $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_fbsql::nextID(), DB_fbsql::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name) |
| | | . ' RESTRICT'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | if (DB::isManip($query)) { |
| | | return preg_replace('/^([\s(])*SELECT/i', |
| | | "\\1SELECT TOP($count)", $query); |
| | | } else { |
| | | return preg_replace('/([\s(])*SELECT/i', |
| | | "\\1SELECT TOP($from, $count)", $query); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteSmart() |
| | | |
| | | /** |
| | | * Formats input so it can be safely used in a query |
| | | * |
| | | * @param mixed $in the data to be formatted |
| | | * |
| | | * @return mixed the formatted data. The format depends on the input's |
| | | * PHP type: |
| | | * + null = the string <samp>NULL</samp> |
| | | * + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp> |
| | | * + integer or double = the unquoted number |
| | | * + other (including strings and numeric strings) = |
| | | * the data escaped according to FrontBase's settings |
| | | * then encapsulated between single quotes |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteSmart($in) |
| | | { |
| | | if (is_int($in) || is_double($in)) { |
| | | return $in; |
| | | } elseif (is_bool($in)) { |
| | | return $in ? 'TRUE' : 'FALSE'; |
| | | } elseif (is_null($in)) { |
| | | return 'NULL'; |
| | | } else { |
| | | return "'" . $this->escapeSimple($in) . "'"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fbsqlRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_fbsql::errorNative(), DB_common::errorCode() |
| | | */ |
| | | function fbsqlRaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode(fbsql_errno($this->connection)); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, |
| | | @fbsql_error($this->connection)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return int the DBMS' error code |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @fbsql_errno($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @fbsql_list_fields($this->dsn['database'], |
| | | $result, $this->connection); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @fbsql_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $res[$i] = array( |
| | | 'table' => $case_func(@fbsql_field_table($id, $i)), |
| | | 'name' => $case_func(@fbsql_field_name($id, $i)), |
| | | 'type' => @fbsql_field_type($id, $i), |
| | | 'len' => @fbsql_field_len($id, $i), |
| | | 'flags' => @fbsql_field_flags($id, $i), |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @fbsql_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SELECT "table_name" FROM information_schema.tables' |
| | | . ' t0, information_schema.schemata t1' |
| | | . ' WHERE t0.schema_pk=t1.schema_pk AND' |
| | | . ' "table_type" = \'BASE TABLE\'' |
| | | . ' AND "schema_name" = current_schema'; |
| | | case 'views': |
| | | return 'SELECT "table_name" FROM information_schema.tables' |
| | | . ' t0, information_schema.schemata t1' |
| | | . ' WHERE t0.schema_pk=t1.schema_pk AND' |
| | | . ' "table_type" = \'VIEW\'' |
| | | . ' AND "schema_name" = current_schema'; |
| | | case 'users': |
| | | return 'SELECT "user_name" from information_schema.users'; |
| | | case 'functions': |
| | | return 'SELECT "routine_name" FROM' |
| | | . ' information_schema.psm_routines' |
| | | . ' t0, information_schema.schemata t1' |
| | | . ' WHERE t0.schema_pk=t1.schema_pk' |
| | | . ' AND "routine_kind"=\'FUNCTION\'' |
| | | . ' AND "schema_name" = current_schema'; |
| | | case 'procedures': |
| | | return 'SELECT "routine_name" FROM' |
| | | . ' information_schema.psm_routines' |
| | | . ' t0, information_schema.schemata t1' |
| | | . ' WHERE t0.schema_pk=t1.schema_pk' |
| | | . ' AND "routine_kind"=\'PROCEDURE\'' |
| | | . ' AND "schema_name" = current_schema'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's interbase extension |
| | | * for interacting with Interbase and Firebird databases |
| | | * |
| | | * While this class works with PHP 4, PHP's InterBase extension is |
| | | * unstable in PHP 4. Use PHP 5. |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Sterling Hughes <sterling@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's interbase extension |
| | | * for interacting with Interbase and Firebird databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * While this class works with PHP 4, PHP's InterBase extension is |
| | | * unstable in PHP 4. Use PHP 5. |
| | | * |
| | | * NOTICE: limitQuery() only works for Firebird. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Sterling Hughes <sterling@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | * @since Class became stable in Release 1.7.0 |
| | | */ |
| | | class DB_ibase extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'ibase'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'ibase'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * NOTE: only firebird supports limit. |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => false, |
| | | 'new_link' => false, |
| | | 'numrows' => 'emulate', |
| | | 'pconnect' => true, |
| | | 'prepare' => true, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | -104 => DB_ERROR_SYNTAX, |
| | | -150 => DB_ERROR_ACCESS_VIOLATION, |
| | | -151 => DB_ERROR_ACCESS_VIOLATION, |
| | | -155 => DB_ERROR_NOSUCHTABLE, |
| | | -157 => DB_ERROR_NOSUCHFIELD, |
| | | -158 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | -170 => DB_ERROR_MISMATCH, |
| | | -171 => DB_ERROR_MISMATCH, |
| | | -172 => DB_ERROR_INVALID, |
| | | // -204 => // Covers too many errors, need to use regex on msg |
| | | -205 => DB_ERROR_NOSUCHFIELD, |
| | | -206 => DB_ERROR_NOSUCHFIELD, |
| | | -208 => DB_ERROR_INVALID, |
| | | -219 => DB_ERROR_NOSUCHTABLE, |
| | | -297 => DB_ERROR_CONSTRAINT, |
| | | -303 => DB_ERROR_INVALID, |
| | | -413 => DB_ERROR_INVALID_NUMBER, |
| | | -530 => DB_ERROR_CONSTRAINT, |
| | | -551 => DB_ERROR_ACCESS_VIOLATION, |
| | | -552 => DB_ERROR_ACCESS_VIOLATION, |
| | | // -607 => // Covers too many errors, need to use regex on msg |
| | | -625 => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | -803 => DB_ERROR_CONSTRAINT, |
| | | -804 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | -904 => DB_ERROR_CONNECT_FAILED, |
| | | -922 => DB_ERROR_NOSUCHDB, |
| | | -923 => DB_ERROR_CONNECT_FAILED, |
| | | -924 => DB_ERROR_CONNECT_FAILED |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * The number of rows affected by a data manipulation query |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $affected = 0; |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The prepared statement handle from the most recently executed statement |
| | | * |
| | | * {@internal Mainly here because the InterBase/Firebird API is only |
| | | * able to retrieve data from result sets if the statemnt handle is |
| | | * still in scope.}} |
| | | * |
| | | * @var resource |
| | | */ |
| | | var $last_stmt; |
| | | |
| | | /** |
| | | * Is the given prepared statement a data manipulation query? |
| | | * @var array |
| | | * @access private |
| | | */ |
| | | var $manip_query = array(); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_ibase() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's ibase driver supports the following extra DSN options: |
| | | * + buffers The number of database buffers to allocate for the |
| | | * server-side cache. |
| | | * + charset The default character set for a database. |
| | | * + dialect The default SQL dialect for any statement |
| | | * executed within a connection. Defaults to the |
| | | * highest one supported by client libraries. |
| | | * Functional only with InterBase 6 and up. |
| | | * + role Functional only with InterBase 5 and up. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('interbase')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | if ($this->dbsyntax == 'firebird') { |
| | | $this->features['limit'] = 'alter'; |
| | | } |
| | | |
| | | $params = array( |
| | | $dsn['hostspec'] |
| | | ? ($dsn['hostspec'] . ':' . $dsn['database']) |
| | | : $dsn['database'], |
| | | $dsn['username'] ? $dsn['username'] : null, |
| | | $dsn['password'] ? $dsn['password'] : null, |
| | | isset($dsn['charset']) ? $dsn['charset'] : null, |
| | | isset($dsn['buffers']) ? $dsn['buffers'] : null, |
| | | isset($dsn['dialect']) ? $dsn['dialect'] : null, |
| | | isset($dsn['role']) ? $dsn['role'] : null, |
| | | ); |
| | | |
| | | $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; |
| | | |
| | | $this->connection = @call_user_func_array($connect_function, $params); |
| | | if (!$this->connection) { |
| | | return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @ibase_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | $result = @ibase_query($this->connection, $query); |
| | | |
| | | if (!$result) { |
| | | return $this->ibaseRaiseError(); |
| | | } |
| | | if ($this->autocommit && $ismanip) { |
| | | @ibase_commit($this->connection); |
| | | } |
| | | if ($ismanip) { |
| | | $this->affected = $result; |
| | | return DB_OK; |
| | | } else { |
| | | $this->affected = 0; |
| | | return $result; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * Only works with Firebird. |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | if ($this->dsn['dbsyntax'] == 'firebird') { |
| | | $query = preg_replace('/^([\s(])*SELECT/i', |
| | | "SELECT FIRST $count SKIP $from", $query); |
| | | } |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal ibase result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | if (function_exists('ibase_fetch_assoc')) { |
| | | $arr = @ibase_fetch_assoc($result); |
| | | } else { |
| | | $arr = get_object_vars(ibase_fetch_object($result)); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @ibase_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @ibase_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeQuery() |
| | | |
| | | function freeQuery($query) |
| | | { |
| | | @ibase_free_query($query); |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (is_integer($this->affected)) { |
| | | return $this->affected; |
| | | } |
| | | return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @ibase_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->ibaseRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ prepare() |
| | | |
| | | /** |
| | | * Prepares a query for multiple execution with execute(). |
| | | * |
| | | * prepare() requires a generic query as string like <code> |
| | | * INSERT INTO numbers VALUES (?, ?, ?) |
| | | * </code>. The <kbd>?</kbd> characters are placeholders. |
| | | * |
| | | * Three types of placeholders can be used: |
| | | * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers |
| | | * + <kbd>!</kbd> value is inserted 'as is' |
| | | * + <kbd>&</kbd> requires a file name. The file's contents get |
| | | * inserted into the query (i.e. saving binary |
| | | * data in a db) |
| | | * |
| | | * Use backslashes to escape placeholder characters if you don't want |
| | | * them to be interpreted as placeholders. Example: <code> |
| | | * "UPDATE foo SET col=? WHERE col='over \& under'" |
| | | * </code> |
| | | * |
| | | * @param string $query query to be prepared |
| | | * @return mixed DB statement resource on success. DB_Error on failure. |
| | | */ |
| | | function prepare($query) |
| | | { |
| | | $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
| | | PREG_SPLIT_DELIM_CAPTURE); |
| | | $token = 0; |
| | | $types = array(); |
| | | $newquery = ''; |
| | | |
| | | foreach ($tokens as $key => $val) { |
| | | switch ($val) { |
| | | case '?': |
| | | $types[$token++] = DB_PARAM_SCALAR; |
| | | break; |
| | | case '&': |
| | | $types[$token++] = DB_PARAM_OPAQUE; |
| | | break; |
| | | case '!': |
| | | $types[$token++] = DB_PARAM_MISC; |
| | | break; |
| | | default: |
| | | $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); |
| | | $newquery .= $tokens[$key] . '?'; |
| | | } |
| | | } |
| | | |
| | | $newquery = substr($newquery, 0, -1); |
| | | $this->last_query = $query; |
| | | $newquery = $this->modifyQuery($newquery); |
| | | $stmt = @ibase_prepare($this->connection, $newquery); |
| | | $this->prepare_types[(int)$stmt] = $types; |
| | | $this->manip_query[(int)$stmt] = DB::isManip($query); |
| | | return $stmt; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ execute() |
| | | |
| | | /** |
| | | * Executes a DB statement prepared with prepare(). |
| | | * |
| | | * @param resource $stmt a DB statement resource returned from prepare() |
| | | * @param mixed $data array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 for non-array items or the |
| | | * quantity of elements in the array. |
| | | * @return object a new DB_Result or a DB_Error when fail |
| | | * @see DB_ibase::prepare() |
| | | * @access public |
| | | */ |
| | | function &execute($stmt, $data = array()) |
| | | { |
| | | $data = (array)$data; |
| | | $this->last_parameters = $data; |
| | | |
| | | $types =& $this->prepare_types[(int)$stmt]; |
| | | if (count($types) != count($data)) { |
| | | $tmp =& $this->raiseError(DB_ERROR_MISMATCH); |
| | | return $tmp; |
| | | } |
| | | |
| | | $i = 0; |
| | | foreach ($data as $key => $value) { |
| | | if ($types[$i] == DB_PARAM_MISC) { |
| | | /* |
| | | * ibase doesn't seem to have the ability to pass a |
| | | * parameter along unchanged, so strip off quotes from start |
| | | * and end, plus turn two single quotes to one single quote, |
| | | * in order to avoid the quotes getting escaped by |
| | | * ibase and ending up in the database. |
| | | */ |
| | | $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); |
| | | $data[$key] = str_replace("''", "'", $data[$key]); |
| | | } elseif ($types[$i] == DB_PARAM_OPAQUE) { |
| | | $fp = @fopen($data[$key], 'rb'); |
| | | if (!$fp) { |
| | | $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
| | | return $tmp; |
| | | } |
| | | $data[$key] = fread($fp, filesize($data[$key])); |
| | | fclose($fp); |
| | | } |
| | | $i++; |
| | | } |
| | | |
| | | array_unshift($data, $stmt); |
| | | |
| | | $res = call_user_func_array('ibase_execute', $data); |
| | | if (!$res) { |
| | | $tmp =& $this->ibaseRaiseError(); |
| | | return $tmp; |
| | | } |
| | | /* XXX need this? |
| | | if ($this->autocommit && $this->manip_query[(int)$stmt]) { |
| | | @ibase_commit($this->connection); |
| | | }*/ |
| | | $this->last_stmt = $stmt; |
| | | if ($this->manip_query[(int)$stmt]) { |
| | | $tmp = DB_OK; |
| | | } else { |
| | | $tmp =& new DB_result($this, $res); |
| | | } |
| | | return $tmp; |
| | | } |
| | | |
| | | /** |
| | | * Frees the internal resources associated with a prepared query |
| | | * |
| | | * @param resource $stmt the prepared statement's PHP resource |
| | | * @param bool $free_resource should the PHP resource be freed too? |
| | | * Use false if you need to get data |
| | | * from the result set later. |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_ibase::prepare() |
| | | */ |
| | | function freePrepared($stmt, $free_resource = true) |
| | | { |
| | | if (!is_resource($stmt)) { |
| | | return false; |
| | | } |
| | | if ($free_resource) { |
| | | @ibase_free_query($stmt); |
| | | } |
| | | unset($this->prepare_tokens[(int)$stmt]); |
| | | unset($this->prepare_types[(int)$stmt]); |
| | | unset($this->manip_query[(int)$stmt]); |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | $this->autocommit = $onoff ? 1 : 0; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | return @ibase_commit($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | return @ibase_rollback($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ transactionInit() |
| | | |
| | | function transactionInit($trans_args = 0) |
| | | { |
| | | return $trans_args |
| | | ? @ibase_trans($trans_args, $this->connection) |
| | | : @ibase_trans(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_ibase::createSequence(), DB_ibase::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $sqn = strtoupper($this->getSequenceName($seq_name)); |
| | | $repeat = 0; |
| | | do { |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result =& $this->query("SELECT GEN_ID(${sqn}, 1) " |
| | | . 'FROM RDB$GENERATORS ' |
| | | . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result)) { |
| | | $repeat = 1; |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | } else { |
| | | $repeat = 0; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | $result->free(); |
| | | return $arr[0]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ createSequence() |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_ibase::nextID(), DB_ibase::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $sqn = strtoupper($this->getSequenceName($seq_name)); |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query("CREATE GENERATOR ${sqn}"); |
| | | $this->popErrorHandling(); |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_ibase::nextID(), DB_ibase::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DELETE FROM RDB$GENERATORS ' |
| | | . "WHERE RDB\$GENERATOR_NAME='" |
| | | . strtoupper($this->getSequenceName($seq_name)) |
| | | . "'"); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _ibaseFieldFlags() |
| | | |
| | | /** |
| | | * Get the column's flags |
| | | * |
| | | * Supports "primary_key", "unique_key", "not_null", "default", |
| | | * "computed" and "blob". |
| | | * |
| | | * @param string $field_name the name of the field |
| | | * @param string $table_name the name of the table |
| | | * |
| | | * @return string the flags |
| | | * |
| | | * @access private |
| | | */ |
| | | function _ibaseFieldFlags($field_name, $table_name) |
| | | { |
| | | $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' |
| | | .' FROM RDB$INDEX_SEGMENTS I' |
| | | .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' |
| | | .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\'' |
| | | .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''; |
| | | |
| | | $result = @ibase_query($this->connection, $sql); |
| | | if (!$result) { |
| | | return $this->ibaseRaiseError(); |
| | | } |
| | | |
| | | $flags = ''; |
| | | if ($obj = @ibase_fetch_object($result)) { |
| | | @ibase_free_result($result); |
| | | if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { |
| | | $flags .= 'primary_key '; |
| | | } |
| | | if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { |
| | | $flags .= 'unique_key '; |
| | | } |
| | | } |
| | | |
| | | $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' |
| | | .' R.RDB$DEFAULT_SOURCE AS DSOURCE,' |
| | | .' F.RDB$FIELD_TYPE AS FTYPE,' |
| | | .' F.RDB$COMPUTED_SOURCE AS CSOURCE' |
| | | .' FROM RDB$RELATION_FIELDS R ' |
| | | .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' |
| | | .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'' |
| | | .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\''; |
| | | |
| | | $result = @ibase_query($this->connection, $sql); |
| | | if (!$result) { |
| | | return $this->ibaseRaiseError(); |
| | | } |
| | | if ($obj = @ibase_fetch_object($result)) { |
| | | @ibase_free_result($result); |
| | | if (isset($obj->NFLAG)) { |
| | | $flags .= 'not_null '; |
| | | } |
| | | if (isset($obj->DSOURCE)) { |
| | | $flags .= 'default '; |
| | | } |
| | | if (isset($obj->CSOURCE)) { |
| | | $flags .= 'computed '; |
| | | } |
| | | if (isset($obj->FTYPE) && $obj->FTYPE == 261) { |
| | | $flags .= 'blob '; |
| | | } |
| | | } |
| | | |
| | | return trim($flags); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ ibaseRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_ibase::errorNative(), DB_ibase::errorCode() |
| | | */ |
| | | function &ibaseRaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode($this->errorNative()); |
| | | } |
| | | $tmp =& $this->raiseError($errno, null, null, null, @ibase_errmsg()); |
| | | return $tmp; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return int the DBMS' error code. NULL if there is no error code. |
| | | * |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function errorNative() |
| | | { |
| | | if (function_exists('ibase_errcode')) { |
| | | return @ibase_errcode(); |
| | | } |
| | | if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i', |
| | | @ibase_errmsg(), $m)) { |
| | | return (int)$m[1]; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Maps native error codes to DB's portable ones |
| | | * |
| | | * @param int $nativecode the error code returned by the DBMS |
| | | * |
| | | * @return int the portable DB error code. Return DB_ERROR if the |
| | | * current driver doesn't have a mapping for the |
| | | * $nativecode submitted. |
| | | * |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function errorCode($nativecode = null) |
| | | { |
| | | if (isset($this->errorcode_map[$nativecode])) { |
| | | return $this->errorcode_map[$nativecode]; |
| | | } |
| | | |
| | | static $error_regexps; |
| | | if (!isset($error_regexps)) { |
| | | $error_regexps = array( |
| | | '/generator .* is not defined/' |
| | | => DB_ERROR_SYNTAX, // for compat. w ibase_errcode() |
| | | '/table.*(not exist|not found|unknown)/i' |
| | | => DB_ERROR_NOSUCHTABLE, |
| | | '/table .* already exists/i' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/unsuccessful metadata update .* failed attempt to store duplicate value/i' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/unsuccessful metadata update .* not found/i' |
| | | => DB_ERROR_NOT_FOUND, |
| | | '/validation error for column .* value "\*\*\* null/i' |
| | | => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '/violation of [\w ]+ constraint/i' |
| | | => DB_ERROR_CONSTRAINT, |
| | | '/conversion error from string/i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/no permission for/i' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/arithmetic exception, numeric overflow, or string truncation/i' |
| | | => DB_ERROR_INVALID, |
| | | ); |
| | | } |
| | | |
| | | $errormsg = @ibase_errmsg(); |
| | | foreach ($error_regexps as $regexp => $code) { |
| | | if (preg_match($regexp, $errormsg)) { |
| | | return $code; |
| | | } |
| | | } |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * NOTE: only supports 'table' and 'flags' if <var>$result</var> |
| | | * is a table name. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @ibase_query($this->connection, |
| | | "SELECT * FROM $result WHERE 1=0"); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @ibase_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $info = @ibase_field_info($id, $i); |
| | | $res[$i] = array( |
| | | 'table' => $got_string ? $case_func($result) : '', |
| | | 'name' => $case_func($info['name']), |
| | | 'type' => $info['type'], |
| | | 'len' => $info['length'], |
| | | 'flags' => ($got_string) |
| | | ? $this->_ibaseFieldFlags($info['name'], $result) |
| | | : '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @ibase_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM ' |
| | | . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0'; |
| | | case 'views': |
| | | return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS'; |
| | | case 'users': |
| | | return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's ifx extension |
| | | * for interacting with Informix databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Tomas V.V.Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's ifx extension |
| | | * for interacting with Informix databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * More info on Informix errors can be found at: |
| | | * http://www.informix.com/answers/english/ierrors.htm |
| | | * |
| | | * TODO: |
| | | * - set needed env Informix vars on connect |
| | | * - implement native prepare/execute |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Tomas V.V.Cox <cox@idecnet.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_ifx extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'ifx'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'ifx'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'emulate', |
| | | 'new_link' => false, |
| | | 'numrows' => 'emulate', |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | '-201' => DB_ERROR_SYNTAX, |
| | | '-206' => DB_ERROR_NOSUCHTABLE, |
| | | '-217' => DB_ERROR_NOSUCHFIELD, |
| | | '-236' => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | '-239' => DB_ERROR_CONSTRAINT, |
| | | '-253' => DB_ERROR_SYNTAX, |
| | | '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '-310' => DB_ERROR_ALREADY_EXISTS, |
| | | '-316' => DB_ERROR_ALREADY_EXISTS, |
| | | '-319' => DB_ERROR_NOT_FOUND, |
| | | '-329' => DB_ERROR_NODBSELECTED, |
| | | '-346' => DB_ERROR_CONSTRAINT, |
| | | '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '-554' => DB_ERROR_SYNTAX, |
| | | '-691' => DB_ERROR_CONSTRAINT, |
| | | '-692' => DB_ERROR_CONSTRAINT, |
| | | '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '-1204' => DB_ERROR_INVALID_DATE, |
| | | '-1205' => DB_ERROR_INVALID_DATE, |
| | | '-1206' => DB_ERROR_INVALID_DATE, |
| | | '-1209' => DB_ERROR_INVALID_DATE, |
| | | '-1210' => DB_ERROR_INVALID_DATE, |
| | | '-1212' => DB_ERROR_INVALID_DATE, |
| | | '-1213' => DB_ERROR_INVALID_NUMBER, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The quantity of transactions begun |
| | | * |
| | | * {@internal While this is private, it can't actually be designated |
| | | * private in PHP 5 because it is directly accessed in the test suite.}} |
| | | * |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $transaction_opcount = 0; |
| | | |
| | | /** |
| | | * The number of rows affected by a data manipulation query |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $affected = 0; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_ifx() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('informix') && |
| | | !PEAR::loadExtension('Informix')) |
| | | { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : ''; |
| | | $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : ''; |
| | | $user = $dsn['username'] ? $dsn['username'] : ''; |
| | | $pw = $dsn['password'] ? $dsn['password'] : ''; |
| | | |
| | | $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; |
| | | |
| | | $this->connection = @$connect_function($dbname, $user, $pw); |
| | | if (!is_resource($this->connection)) { |
| | | return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @ifx_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | $this->affected = null; |
| | | if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()? |
| | | // the scroll is needed for fetching absolute row numbers |
| | | // in a select query result |
| | | $result = @ifx_query($query, $this->connection, IFX_SCROLL); |
| | | } else { |
| | | if (!$this->autocommit && $ismanip) { |
| | | if ($this->transaction_opcount == 0) { |
| | | $result = @ifx_query('BEGIN WORK', $this->connection); |
| | | if (!$result) { |
| | | return $this->ifxRaiseError(); |
| | | } |
| | | } |
| | | $this->transaction_opcount++; |
| | | } |
| | | $result = @ifx_query($query, $this->connection); |
| | | } |
| | | if (!$result) { |
| | | return $this->ifxRaiseError(); |
| | | } |
| | | $this->affected = @ifx_affected_rows($result); |
| | | // Determine which queries should return data, and which |
| | | // should return an error code only. |
| | | if (preg_match('/(SELECT)/i', $query)) { |
| | | return $result; |
| | | } |
| | | // XXX Testme: free results inside a transaction |
| | | // may cause to stop it and commit the work? |
| | | |
| | | // Result has to be freed even with a insert or update |
| | | @ifx_free_result($result); |
| | | |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal ifx result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (DB::isManip($this->last_query)) { |
| | | return $this->affected; |
| | | } else { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if (($rownum !== null) && ($rownum < 0)) { |
| | | return null; |
| | | } |
| | | if ($rownum === null) { |
| | | /* |
| | | * Even though fetch_row() should return the next row if |
| | | * $rownum is null, it doesn't in all cases. Bug 598. |
| | | */ |
| | | $rownum = 'NEXT'; |
| | | } else { |
| | | // Index starts at row 1, unlike most DBMS's starting at 0. |
| | | $rownum++; |
| | | } |
| | | if (!$arr = @ifx_fetch_row($result, $rownum)) { |
| | | return null; |
| | | } |
| | | if ($fetchmode !== DB_FETCHMODE_ASSOC) { |
| | | $i=0; |
| | | $order = array(); |
| | | foreach ($arr as $val) { |
| | | $order[$i++] = $val; |
| | | } |
| | | $arr = $order; |
| | | } elseif ($fetchmode == DB_FETCHMODE_ASSOC && |
| | | $this->options['portability'] & DB_PORTABILITY_LOWERCASE) |
| | | { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | if (!$cols = @ifx_num_fields($result)) { |
| | | return $this->ifxRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @ifx_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = true) |
| | | { |
| | | // XXX if $this->transaction_opcount > 0, we should probably |
| | | // issue a warning here. |
| | | $this->autocommit = $onoff ? true : false; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | $result = @ifx_query('COMMIT WORK', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->ifxRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | $result = @ifx_query('ROLLBACK WORK', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->ifxRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ ifxRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_ifx::errorNative(), DB_ifx::errorCode() |
| | | */ |
| | | function ifxRaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode(ifx_error()); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, |
| | | $this->errorNative()); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code and message produced by the last query |
| | | * |
| | | * @return string the DBMS' error code and message |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @ifx_error() . ' ' . @ifx_errormsg(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Maps native error codes to DB's portable ones. |
| | | * |
| | | * Requires that the DB implementation's constructor fills |
| | | * in the <var>$errorcode_map</var> property. |
| | | * |
| | | * @param string $nativecode error code returned by the database |
| | | * @return int a portable DB error code, or DB_ERROR if this DB |
| | | * implementation has no mapping for the given error code. |
| | | */ |
| | | function errorCode($nativecode) |
| | | { |
| | | if (ereg('SQLCODE=(.*)]', $nativecode, $match)) { |
| | | $code = $match[1]; |
| | | if (isset($this->errorcode_map[$code])) { |
| | | return $this->errorcode_map[$code]; |
| | | } |
| | | } |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * NOTE: only supports 'table' if <var>$result</var> is a table name. |
| | | * |
| | | * If analyzing a query result and the result has duplicate field names, |
| | | * an error will be raised saying |
| | | * <samp>can't distinguish duplicate field names</samp>. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @ifx_query("SELECT * FROM $result WHERE 1=0", |
| | | $this->connection); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | $flds = @ifx_fieldproperties($id); |
| | | $count = @ifx_num_fields($id); |
| | | |
| | | if (count($flds) != $count) { |
| | | return $this->raiseError("can't distinguish duplicate field names"); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $i = 0; |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | foreach ($flds as $key => $value) { |
| | | $props = explode(';', $value); |
| | | $res[$i] = array( |
| | | 'table' => $got_string ? $case_func($result) : '', |
| | | 'name' => $case_func($key), |
| | | 'type' => $props[0], |
| | | 'len' => $props[1], |
| | | 'flags' => $props[4] == 'N' ? 'not_null' : '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | $i++; |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @ifx_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SELECT tabname FROM systables WHERE tabid >= 100'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's msql extension |
| | | * for interacting with Mini SQL databases |
| | | * |
| | | * PHP's mSQL extension did weird things with NULL values prior to PHP |
| | | * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds |
| | | * those versions. |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's msql extension |
| | | * for interacting with Mini SQL databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * PHP's mSQL extension did weird things with NULL values prior to PHP |
| | | * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds |
| | | * those versions. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | * @since Class not functional until Release 1.7.0 |
| | | */ |
| | | class DB_msql extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'msql'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'msql'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'emulate', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => false, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * The query result resource created by PHP |
| | | * |
| | | * Used to make affectedRows() work. Only contains the result for |
| | | * data manipulation queries. Contains false for other queries. |
| | | * |
| | | * @var resource |
| | | * @access private |
| | | */ |
| | | var $_result; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_msql() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * Example of how to connect: |
| | | * <code> |
| | | * require_once 'DB.php'; |
| | | * |
| | | * // $dsn = 'msql://hostname/dbname'; // use a TCP connection |
| | | * $dsn = 'msql:///dbname'; // use a socket |
| | | * $options = array( |
| | | * 'portability' => DB_PORTABILITY_ALL, |
| | | * ); |
| | | * |
| | | * $db =& DB::connect($dsn, $options); |
| | | * if (PEAR::isError($db)) { |
| | | * die($db->getMessage()); |
| | | * } |
| | | * </code> |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('msql')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $params = array(); |
| | | if ($dsn['hostspec']) { |
| | | $params[] = $dsn['port'] |
| | | ? $dsn['hostspec'] . ',' . $dsn['port'] |
| | | : $dsn['hostspec']; |
| | | } |
| | | |
| | | $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect'; |
| | | |
| | | $ini = ini_get('track_errors'); |
| | | $php_errormsg = ''; |
| | | if ($ini) { |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | } else { |
| | | ini_set('track_errors', 1); |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | ini_set('track_errors', $ini); |
| | | } |
| | | |
| | | if (!$this->connection) { |
| | | if (($err = @msql_error()) != '') { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $err); |
| | | } else { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | } |
| | | |
| | | if (!@msql_select_db($dsn['database'], $this->connection)) { |
| | | return $this->msqlRaiseError(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @msql_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | $result = @msql_query($query, $this->connection); |
| | | if (!$result) { |
| | | return $this->msqlRaiseError(); |
| | | } |
| | | // Determine which queries that should return data, and which |
| | | // should return an error code only. |
| | | if (DB::isManip($query)) { |
| | | $this->_result = $result; |
| | | return DB_OK; |
| | | } else { |
| | | $this->_result = false; |
| | | return $result; |
| | | } |
| | | } |
| | | |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal msql result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * PHP's mSQL extension did weird things with NULL values prior to PHP |
| | | * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds |
| | | * those versions. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@msql_data_seek($result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @msql_fetch_array($result, MSQL_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @msql_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @msql_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @msql_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->msqlRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @msql_num_rows($result); |
| | | if ($rows === false) { |
| | | return $this->msqlRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affected() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (!$this->_result) { |
| | | return 0; |
| | | } |
| | | return msql_affected_rows($this->_result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_msql::createSequence(), DB_msql::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $repeat = false; |
| | | do { |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result =& $this->query("SELECT _seq FROM ${seqname}"); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) { |
| | | $repeat = true; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->createSequence($seq_name); |
| | | $this->popErrorHandling(); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | } else { |
| | | $repeat = false; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | $result->free(); |
| | | return $arr[0]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ createSequence() |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * Also creates a new table to associate the sequence with. Uses |
| | | * a separate table to ensure portability with other drivers. |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_msql::nextID(), DB_msql::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $res = $this->query('CREATE TABLE ' . $seqname |
| | | . ' (id INTEGER NOT NULL)'); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | $res = $this->query("CREATE SEQUENCE ON ${seqname}"); |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_msql::nextID(), DB_msql::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteIdentifier() |
| | | |
| | | /** |
| | | * mSQL does not support delimited identifiers |
| | | * |
| | | * @param string $str the identifier name to be quoted |
| | | * |
| | | * @return object a DB_Error object |
| | | * |
| | | * @see DB_common::quoteIdentifier() |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function quoteIdentifier($str) |
| | | { |
| | | return $this->raiseError(DB_ERROR_UNSUPPORTED); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ escapeSimple() |
| | | |
| | | /** |
| | | * Escapes a string according to the current DBMS's standards |
| | | * |
| | | * @param string $str the string to be escaped |
| | | * |
| | | * @return string the escaped string |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function escapeSimple($str) |
| | | { |
| | | return addslashes($str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ msqlRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_msql::errorNative(), DB_msql::errorCode() |
| | | */ |
| | | function msqlRaiseError($errno = null) |
| | | { |
| | | $native = $this->errorNative(); |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode($native); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, $native); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error message produced by the last query |
| | | * |
| | | * @return string the DBMS' error message |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @msql_error(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Determines PEAR::DB error code from the database's text error message |
| | | * |
| | | * @param string $errormsg the error message returned from the database |
| | | * |
| | | * @return integer the error number from a DB_ERROR* constant |
| | | */ |
| | | function errorCode($errormsg) |
| | | { |
| | | static $error_regexps; |
| | | if (!isset($error_regexps)) { |
| | | $error_regexps = array( |
| | | '/^Access to database denied/i' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/^Bad index name/i' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/^Bad order field/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Bad type for comparison/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Can\'t perform LIKE on/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Can\'t use TEXT fields in LIKE comparison/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Couldn\'t create temporary table/i' |
| | | => DB_ERROR_CANNOT_CREATE, |
| | | '/^Error creating table file/i' |
| | | => DB_ERROR_CANNOT_CREATE, |
| | | '/^Field .* cannot be null$/i' |
| | | => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '/^Index (field|condition) .* cannot be null$/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Invalid date format/i' |
| | | => DB_ERROR_INVALID_DATE, |
| | | '/^Invalid time format/i' |
| | | => DB_ERROR_INVALID, |
| | | '/^Literal value for .* is wrong type$/i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/^No Database Selected/i' |
| | | => DB_ERROR_NODBSELECTED, |
| | | '/^No value specified for field/i' |
| | | => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | '/^Non unique value for unique index/i' |
| | | => DB_ERROR_CONSTRAINT, |
| | | '/^Out of memory for temporary table/i' |
| | | => DB_ERROR_CANNOT_CREATE, |
| | | '/^Permission denied/i' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/^Reference to un-selected table/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^syntax error/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Table .* exists$/i' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/^Unknown database/i' |
| | | => DB_ERROR_NOSUCHDB, |
| | | '/^Unknown field/i' |
| | | => DB_ERROR_NOSUCHFIELD, |
| | | '/^Unknown (index|system variable)/i' |
| | | => DB_ERROR_NOT_FOUND, |
| | | '/^Unknown table/i' |
| | | => DB_ERROR_NOSUCHTABLE, |
| | | '/^Unqualified field/i' |
| | | => DB_ERROR_SYNTAX, |
| | | ); |
| | | } |
| | | |
| | | foreach ($error_regexps as $regexp => $code) { |
| | | if (preg_match($regexp, $errormsg)) { |
| | | return $code; |
| | | } |
| | | } |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::setOption() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @msql_query("SELECT * FROM $result", |
| | | $this->connection); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->raiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @msql_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $tmp = @msql_fetch_field($id); |
| | | |
| | | $flags = ''; |
| | | if ($tmp->not_null) { |
| | | $flags .= 'not_null '; |
| | | } |
| | | if ($tmp->unique) { |
| | | $flags .= 'unique_key '; |
| | | } |
| | | $flags = trim($flags); |
| | | |
| | | $res[$i] = array( |
| | | 'table' => $case_func($tmp->table), |
| | | 'name' => $case_func($tmp->name), |
| | | 'type' => $tmp->type, |
| | | 'len' => msql_field_len($id, $i), |
| | | 'flags' => $flags, |
| | | ); |
| | | |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @msql_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtain a list of a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return array the array containing the list of objects requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'databases': |
| | | $id = @msql_list_dbs($this->connection); |
| | | break; |
| | | case 'tables': |
| | | $id = @msql_list_tables($this->dsn['database'], |
| | | $this->connection); |
| | | break; |
| | | default: |
| | | return null; |
| | | } |
| | | if (!$id) { |
| | | return $this->msqlRaiseError(); |
| | | } |
| | | $out = array(); |
| | | while ($row = @msql_fetch_row($id)) { |
| | | $out[] = $row[0]; |
| | | } |
| | | return $out; |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's mssql extension |
| | | * for interacting with Microsoft SQL Server databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Sterling Hughes <sterling@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's mssql extension |
| | | * for interacting with Microsoft SQL Server databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Sterling Hughes <sterling@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_mssql extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'mssql'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'mssql'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'emulate', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX |
| | | var $errorcode_map = array( |
| | | 110 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | 155 => DB_ERROR_NOSUCHFIELD, |
| | | 170 => DB_ERROR_SYNTAX, |
| | | 207 => DB_ERROR_NOSUCHFIELD, |
| | | 208 => DB_ERROR_NOSUCHTABLE, |
| | | 245 => DB_ERROR_INVALID_NUMBER, |
| | | 515 => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | 547 => DB_ERROR_CONSTRAINT, |
| | | 1913 => DB_ERROR_ALREADY_EXISTS, |
| | | 2627 => DB_ERROR_CONSTRAINT, |
| | | 2714 => DB_ERROR_ALREADY_EXISTS, |
| | | 3701 => DB_ERROR_NOSUCHTABLE, |
| | | 8134 => DB_ERROR_DIVZERO, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The quantity of transactions begun |
| | | * |
| | | * {@internal While this is private, it can't actually be designated |
| | | * private in PHP 5 because it is directly accessed in the test suite.}} |
| | | * |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $transaction_opcount = 0; |
| | | |
| | | /** |
| | | * The database specified in the DSN |
| | | * |
| | | * It's a fix to allow calls to different databases in the same script. |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_db = null; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_mssql() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase') |
| | | && !PEAR::loadExtension('sybase_ct')) |
| | | { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $params = array( |
| | | $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', |
| | | $dsn['username'] ? $dsn['username'] : null, |
| | | $dsn['password'] ? $dsn['password'] : null, |
| | | ); |
| | | if ($dsn['port']) { |
| | | $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':') |
| | | . $dsn['port']; |
| | | } |
| | | |
| | | $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; |
| | | |
| | | $this->connection = @call_user_func_array($connect_function, $params); |
| | | |
| | | if (!$this->connection) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | @mssql_get_last_message()); |
| | | } |
| | | if ($dsn['database']) { |
| | | if (!@mssql_select_db($dsn['database'], $this->connection)) { |
| | | return $this->raiseError(DB_ERROR_NODBSELECTED, |
| | | null, null, null, |
| | | @mssql_get_last_message()); |
| | | } |
| | | $this->_db = $dsn['database']; |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @mssql_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | if (!@mssql_select_db($this->_db, $this->connection)) { |
| | | return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $query = $this->modifyQuery($query); |
| | | if (!$this->autocommit && $ismanip) { |
| | | if ($this->transaction_opcount == 0) { |
| | | $result = @mssql_query('BEGIN TRAN', $this->connection); |
| | | if (!$result) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | } |
| | | $this->transaction_opcount++; |
| | | } |
| | | $result = @mssql_query($query, $this->connection); |
| | | if (!$result) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | // Determine which queries that should return data, and which |
| | | // should return an error code only. |
| | | return $ismanip ? DB_OK : $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal mssql result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return @mssql_next_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@mssql_data_seek($result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @mssql_fetch_array($result, MSSQL_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @mssql_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @mssql_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @mssql_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @mssql_num_rows($result); |
| | | if ($rows === false) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | // XXX if $this->transaction_opcount > 0, we should probably |
| | | // issue a warning here. |
| | | $this->autocommit = $onoff ? true : false; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if (!@mssql_select_db($this->_db, $this->connection)) { |
| | | return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $result = @mssql_query('COMMIT TRAN', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if (!@mssql_select_db($this->_db, $this->connection)) { |
| | | return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $result = @mssql_query('ROLLBACK TRAN', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (DB::isManip($this->last_query)) { |
| | | $res = @mssql_query('select @@rowcount', $this->connection); |
| | | if (!$res) { |
| | | return $this->mssqlRaiseError(); |
| | | } |
| | | $ar = @mssql_fetch_row($res); |
| | | if (!$ar) { |
| | | $result = 0; |
| | | } else { |
| | | @mssql_free_result($res); |
| | | $result = $ar[0]; |
| | | } |
| | | } else { |
| | | $result = 0; |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_mssql::createSequence(), DB_mssql::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | if (!@mssql_select_db($this->_db, $this->connection)) { |
| | | return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $repeat = 0; |
| | | do { |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result) && |
| | | ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) |
| | | { |
| | | $repeat = 1; |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | } elseif (!DB::isError($result)) { |
| | | $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); |
| | | $repeat = 0; |
| | | } else { |
| | | $repeat = false; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $result = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | return $result[0]; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_mssql::nextID(), DB_mssql::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | return $this->query('CREATE TABLE ' |
| | | . $this->getSequenceName($seq_name) |
| | | . ' ([id] [int] IDENTITY (1, 1) NOT NULL,' |
| | | . ' [vapor] [int] NULL)'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_mssql::nextID(), DB_mssql::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteIdentifier() |
| | | |
| | | /** |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * @param string $str identifier name to be quoted |
| | | * |
| | | * @return string quoted identifier string |
| | | * |
| | | * @see DB_common::quoteIdentifier() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteIdentifier($str) |
| | | { |
| | | return '[' . str_replace(']', ']]', $str) . ']'; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ mssqlRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_mssql::errorNative(), DB_mssql::errorCode() |
| | | */ |
| | | function mssqlRaiseError($code = null) |
| | | { |
| | | $message = @mssql_get_last_message(); |
| | | if (!$code) { |
| | | $code = $this->errorNative(); |
| | | } |
| | | return $this->raiseError($this->errorCode($code, $message), |
| | | null, null, null, "$code - $message"); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return int the DBMS' error code |
| | | */ |
| | | function errorNative() |
| | | { |
| | | $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection); |
| | | if (!$res) { |
| | | return DB_ERROR; |
| | | } |
| | | $row = @mssql_fetch_row($res); |
| | | return $row[0]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Determines PEAR::DB error code from mssql's native codes. |
| | | * |
| | | * If <var>$nativecode</var> isn't known yet, it will be looked up. |
| | | * |
| | | * @param mixed $nativecode mssql error code, if known |
| | | * @return integer an error number from a DB error constant |
| | | * @see errorNative() |
| | | */ |
| | | function errorCode($nativecode = null, $msg = '') |
| | | { |
| | | if (!$nativecode) { |
| | | $nativecode = $this->errorNative(); |
| | | } |
| | | if (isset($this->errorcode_map[$nativecode])) { |
| | | if ($nativecode == 3701 |
| | | && preg_match('/Cannot drop the index/i', $msg)) |
| | | { |
| | | return DB_ERROR_NOT_FOUND; |
| | | } |
| | | return $this->errorcode_map[$nativecode]; |
| | | } else { |
| | | return DB_ERROR; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * NOTE: only supports 'table' and 'flags' if <var>$result</var> |
| | | * is a table name. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | if (!@mssql_select_db($this->_db, $this->connection)) { |
| | | return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $id = @mssql_query("SELECT * FROM $result WHERE 1=0", |
| | | $this->connection); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @mssql_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $res[$i] = array( |
| | | 'table' => $got_string ? $case_func($result) : '', |
| | | 'name' => $case_func(@mssql_field_name($id, $i)), |
| | | 'type' => @mssql_field_type($id, $i), |
| | | 'len' => @mssql_field_length($id, $i), |
| | | // We only support flags for table |
| | | 'flags' => $got_string |
| | | ? $this->_mssql_field_flags($result, |
| | | @mssql_field_name($id, $i)) |
| | | : '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @mssql_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _mssql_field_flags() |
| | | |
| | | /** |
| | | * Get a column's flags |
| | | * |
| | | * Supports "not_null", "primary_key", |
| | | * "auto_increment" (mssql identity), "timestamp" (mssql timestamp), |
| | | * "unique_key" (mssql unique index, unique check or primary_key) and |
| | | * "multiple_key" (multikey index) |
| | | * |
| | | * mssql timestamp is NOT similar to the mysql timestamp so this is maybe |
| | | * not useful at all - is the behaviour of mysql_field_flags that primary |
| | | * keys are alway unique? is the interpretation of multiple_key correct? |
| | | * |
| | | * @param string $table the table name |
| | | * @param string $column the field name |
| | | * |
| | | * @return string the flags |
| | | * |
| | | * @access private |
| | | * @author Joern Barthel <j_barthel@web.de> |
| | | */ |
| | | function _mssql_field_flags($table, $column) |
| | | { |
| | | static $tableName = null; |
| | | static $flags = array(); |
| | | |
| | | if ($table != $tableName) { |
| | | |
| | | $flags = array(); |
| | | $tableName = $table; |
| | | |
| | | // get unique and primary keys |
| | | $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC); |
| | | |
| | | foreach ($res as $val) { |
| | | $keys = explode(', ', $val['index_keys']); |
| | | |
| | | if (sizeof($keys) > 1) { |
| | | foreach ($keys as $key) { |
| | | $this->_add_flag($flags[$key], 'multiple_key'); |
| | | } |
| | | } |
| | | |
| | | if (strpos($val['index_description'], 'primary key')) { |
| | | foreach ($keys as $key) { |
| | | $this->_add_flag($flags[$key], 'primary_key'); |
| | | } |
| | | } elseif (strpos($val['index_description'], 'unique')) { |
| | | foreach ($keys as $key) { |
| | | $this->_add_flag($flags[$key], 'unique_key'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // get auto_increment, not_null and timestamp |
| | | $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC); |
| | | |
| | | foreach ($res as $val) { |
| | | $val = array_change_key_case($val, CASE_LOWER); |
| | | if ($val['nullable'] == '0') { |
| | | $this->_add_flag($flags[$val['column_name']], 'not_null'); |
| | | } |
| | | if (strpos($val['type_name'], 'identity')) { |
| | | $this->_add_flag($flags[$val['column_name']], 'auto_increment'); |
| | | } |
| | | if (strpos($val['type_name'], 'timestamp')) { |
| | | $this->_add_flag($flags[$val['column_name']], 'timestamp'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (array_key_exists($column, $flags)) { |
| | | return(implode(' ', $flags[$column])); |
| | | } |
| | | return ''; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _add_flag() |
| | | |
| | | /** |
| | | * Adds a string to the flags array if the flag is not yet in there |
| | | * - if there is no flag present the array is created |
| | | * |
| | | * @param array &$array the reference to the flag-array |
| | | * @param string $value the flag value |
| | | * |
| | | * @return void |
| | | * |
| | | * @access private |
| | | * @author Joern Barthel <j_barthel@web.de> |
| | | */ |
| | | function _add_flag(&$array, $value) |
| | | { |
| | | if (!is_array($array)) { |
| | | $array = array($value); |
| | | } elseif (!in_array($value, $array)) { |
| | | array_push($array, $value); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return "SELECT name FROM sysobjects WHERE type = 'U'" |
| | | . ' ORDER BY name'; |
| | | case 'views': |
| | | return "SELECT name FROM sysobjects WHERE type = 'V'"; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's mysql extension |
| | | * for interacting with MySQL databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's mysql extension |
| | | * for interacting with MySQL databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_mysql extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'mysql'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'mysql'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'alter', |
| | | 'new_link' => '4.2.0', |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | 1004 => DB_ERROR_CANNOT_CREATE, |
| | | 1005 => DB_ERROR_CANNOT_CREATE, |
| | | 1006 => DB_ERROR_CANNOT_CREATE, |
| | | 1007 => DB_ERROR_ALREADY_EXISTS, |
| | | 1008 => DB_ERROR_CANNOT_DROP, |
| | | 1022 => DB_ERROR_ALREADY_EXISTS, |
| | | 1044 => DB_ERROR_ACCESS_VIOLATION, |
| | | 1046 => DB_ERROR_NODBSELECTED, |
| | | 1048 => DB_ERROR_CONSTRAINT, |
| | | 1049 => DB_ERROR_NOSUCHDB, |
| | | 1050 => DB_ERROR_ALREADY_EXISTS, |
| | | 1051 => DB_ERROR_NOSUCHTABLE, |
| | | 1054 => DB_ERROR_NOSUCHFIELD, |
| | | 1061 => DB_ERROR_ALREADY_EXISTS, |
| | | 1062 => DB_ERROR_ALREADY_EXISTS, |
| | | 1064 => DB_ERROR_SYNTAX, |
| | | 1091 => DB_ERROR_NOT_FOUND, |
| | | 1100 => DB_ERROR_NOT_LOCKED, |
| | | 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | 1142 => DB_ERROR_ACCESS_VIOLATION, |
| | | 1146 => DB_ERROR_NOSUCHTABLE, |
| | | 1216 => DB_ERROR_CONSTRAINT, |
| | | 1217 => DB_ERROR_CONSTRAINT, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The quantity of transactions begun |
| | | * |
| | | * {@internal While this is private, it can't actually be designated |
| | | * private in PHP 5 because it is directly accessed in the test suite.}} |
| | | * |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $transaction_opcount = 0; |
| | | |
| | | /** |
| | | * The database specified in the DSN |
| | | * |
| | | * It's a fix to allow calls to different databases in the same script. |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_db = ''; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_mysql() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's mysql driver supports the following extra DSN options: |
| | | * + new_link If set to true, causes subsequent calls to connect() |
| | | * to return a new connection link instead of the |
| | | * existing one. WARNING: this is not portable to |
| | | * other DBMS's. Available since PEAR DB 1.7.0. |
| | | * + client_flags Any combination of MYSQL_CLIENT_* constants. |
| | | * Only used if PHP is at version 4.3.0 or greater. |
| | | * Available since PEAR DB 1.7.0. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('mysql')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $params = array(); |
| | | if ($dsn['protocol'] && $dsn['protocol'] == 'unix') { |
| | | $params[0] = ':' . $dsn['socket']; |
| | | } else { |
| | | $params[0] = $dsn['hostspec'] ? $dsn['hostspec'] |
| | | : 'localhost'; |
| | | if ($dsn['port']) { |
| | | $params[0] .= ':' . $dsn['port']; |
| | | } |
| | | } |
| | | $params[] = $dsn['username'] ? $dsn['username'] : null; |
| | | $params[] = $dsn['password'] ? $dsn['password'] : null; |
| | | |
| | | if (!$persistent) { |
| | | if (isset($dsn['new_link']) |
| | | && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) |
| | | { |
| | | $params[] = true; |
| | | } else { |
| | | $params[] = false; |
| | | } |
| | | } |
| | | if (version_compare(phpversion(), '4.3.0', '>=')) { |
| | | $params[] = isset($dsn['client_flags']) |
| | | ? $dsn['client_flags'] : null; |
| | | } |
| | | |
| | | $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; |
| | | |
| | | $ini = ini_get('track_errors'); |
| | | $php_errormsg = ''; |
| | | if ($ini) { |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | } else { |
| | | ini_set('track_errors', 1); |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | ini_set('track_errors', $ini); |
| | | } |
| | | |
| | | if (!$this->connection) { |
| | | if (($err = @mysql_error()) != '') { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $err); |
| | | } else { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | } |
| | | |
| | | if ($dsn['database']) { |
| | | if (!@mysql_select_db($dsn['database'], $this->connection)) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | $this->_db = $dsn['database']; |
| | | } |
| | | |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @mysql_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * Generally uses mysql_query(). If you want to use |
| | | * mysql_unbuffered_query() set the "result_buffering" option to 0 using |
| | | * setOptions(). This option was added in Release 1.7.0. |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | if ($this->_db) { |
| | | if (!@mysql_select_db($this->_db, $this->connection)) { |
| | | return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | } |
| | | if (!$this->autocommit && $ismanip) { |
| | | if ($this->transaction_opcount == 0) { |
| | | $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection); |
| | | $result = @mysql_query('BEGIN', $this->connection); |
| | | if (!$result) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | } |
| | | $this->transaction_opcount++; |
| | | } |
| | | if (!$this->options['result_buffering']) { |
| | | $result = @mysql_unbuffered_query($query, $this->connection); |
| | | } else { |
| | | $result = @mysql_query($query, $this->connection); |
| | | } |
| | | if (!$result) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | if (is_resource($result)) { |
| | | return $result; |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal mysql result pointer to the next available result |
| | | * |
| | | * This method has not been implemented yet. |
| | | * |
| | | * @param a valid sql result resource |
| | | * |
| | | * @return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@mysql_data_seek($result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @mysql_fetch_array($result, MYSQL_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @mysql_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | /* |
| | | * Even though this DBMS already trims output, we do this because |
| | | * a field might have intentional whitespace at the end that |
| | | * gets removed by DB_PORTABILITY_RTRIM under another driver. |
| | | */ |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @mysql_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @mysql_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @mysql_num_rows($result); |
| | | if ($rows === null) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | // XXX if $this->transaction_opcount > 0, we should probably |
| | | // issue a warning here. |
| | | $this->autocommit = $onoff ? true : false; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if ($this->_db) { |
| | | if (!@mysql_select_db($this->_db, $this->connection)) { |
| | | return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | } |
| | | $result = @mysql_query('COMMIT', $this->connection); |
| | | $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if ($this->_db) { |
| | | if (!@mysql_select_db($this->_db, $this->connection)) { |
| | | return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | } |
| | | $result = @mysql_query('ROLLBACK', $this->connection); |
| | | $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->mysqlRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (DB::isManip($this->last_query)) { |
| | | return @mysql_affected_rows($this->connection); |
| | | } else { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_mysql::createSequence(), DB_mysql::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | do { |
| | | $repeat = 0; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query("UPDATE ${seqname} ". |
| | | 'SET id=LAST_INSERT_ID(id+1)'); |
| | | $this->popErrorHandling(); |
| | | if ($result === DB_OK) { |
| | | // COMMON CASE |
| | | $id = @mysql_insert_id($this->connection); |
| | | if ($id != 0) { |
| | | return $id; |
| | | } |
| | | // EMPTY SEQ TABLE |
| | | // Sequence table must be empty for some reason, so fill |
| | | // it and return 1 and obtain a user-level lock |
| | | $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | if ($result == 0) { |
| | | // Failed to get the lock |
| | | return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); |
| | | } |
| | | |
| | | // add the default value |
| | | $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | |
| | | // Release the lock |
| | | $result = $this->getOne('SELECT RELEASE_LOCK(' |
| | | . "'${seqname}_lock')"); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | // We know what the result will be, so no need to try again |
| | | return 1; |
| | | |
| | | } elseif ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) |
| | | { |
| | | // ONDEMAND TABLE CREATION |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } else { |
| | | $repeat = 1; |
| | | } |
| | | |
| | | } elseif (DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_ALREADY_EXISTS) |
| | | { |
| | | // BACKWARDS COMPAT |
| | | // see _BCsequence() comment |
| | | $result = $this->_BCsequence($seqname); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $repeat = 1; |
| | | } |
| | | } while ($repeat); |
| | | |
| | | return $this->raiseError($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ createSequence() |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_mysql::nextID(), DB_mysql::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $res = $this->query('CREATE TABLE ' . $seqname |
| | | . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' |
| | | . ' PRIMARY KEY(id))'); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | // insert yields value 1, nextId call will generate ID 2 |
| | | $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | // so reset to zero |
| | | return $this->query("UPDATE ${seqname} SET id = 0"); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_mysql::nextID(), DB_mysql::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _BCsequence() |
| | | |
| | | /** |
| | | * Backwards compatibility with old sequence emulation implementation |
| | | * (clean up the dupes) |
| | | * |
| | | * @param string $seqname the sequence name to clean up |
| | | * |
| | | * @return bool true on success. A DB_Error object on failure. |
| | | * |
| | | * @access private |
| | | */ |
| | | function _BCsequence($seqname) |
| | | { |
| | | // Obtain a user-level lock... this will release any previous |
| | | // application locks, but unlike LOCK TABLES, it does not abort |
| | | // the current transaction and is much less frequently used. |
| | | $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | if ($result == 0) { |
| | | // Failed to get the lock, can't do the conversion, bail |
| | | // with a DB_ERROR_NOT_LOCKED error |
| | | return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); |
| | | } |
| | | |
| | | $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); |
| | | if (DB::isError($highest_id)) { |
| | | return $highest_id; |
| | | } |
| | | // This should kill all rows except the highest |
| | | // We should probably do something if $highest_id isn't |
| | | // numeric, but I'm at a loss as how to handle that... |
| | | $result = $this->query('DELETE FROM ' . $seqname |
| | | . " WHERE id <> $highest_id"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | |
| | | // If another thread has been waiting for this lock, |
| | | // it will go thru the above procedure, but will have no |
| | | // real effect |
| | | $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteIdentifier() |
| | | |
| | | /** |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * MySQL can't handle the backtick character (<kbd>`</kbd>) in |
| | | * table or column names. |
| | | * |
| | | * @param string $str identifier name to be quoted |
| | | * |
| | | * @return string quoted identifier string |
| | | * |
| | | * @see DB_common::quoteIdentifier() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteIdentifier($str) |
| | | { |
| | | return '`' . $str . '`'; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quote() |
| | | |
| | | /** |
| | | * @deprecated Deprecated in release 1.6.0 |
| | | */ |
| | | function quote($str) |
| | | { |
| | | return $this->quoteSmart($str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ escapeSimple() |
| | | |
| | | /** |
| | | * Escapes a string according to the current DBMS's standards |
| | | * |
| | | * @param string $str the string to be escaped |
| | | * |
| | | * @return string the escaped string |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function escapeSimple($str) |
| | | { |
| | | if (function_exists('mysql_real_escape_string')) { |
| | | return @mysql_real_escape_string($str, $this->connection); |
| | | } else { |
| | | return @mysql_escape_string($str); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyQuery() |
| | | |
| | | /** |
| | | * Changes a query string for various DBMS specific reasons |
| | | * |
| | | * This little hack lets you know how many rows were deleted |
| | | * when running a "DELETE FROM table" query. Only implemented |
| | | * if the DB_PORTABILITY_DELETE_COUNT portability option is on. |
| | | * |
| | | * @param string $query the query string to modify |
| | | * |
| | | * @return string the modified query string |
| | | * |
| | | * @access protected |
| | | * @see DB_common::setOption() |
| | | */ |
| | | function modifyQuery($query) |
| | | { |
| | | if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { |
| | | // "DELETE FROM table" gives 0 affected rows in MySQL. |
| | | // This little hack lets you know how many rows were deleted. |
| | | if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { |
| | | $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', |
| | | 'DELETE FROM \1 WHERE 1=1', $query); |
| | | } |
| | | } |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | if (DB::isManip($query)) { |
| | | return $query . " LIMIT $count"; |
| | | } else { |
| | | return $query . " LIMIT $from, $count"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ mysqlRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_mysql::errorNative(), DB_common::errorCode() |
| | | */ |
| | | function mysqlRaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { |
| | | $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; |
| | | $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; |
| | | $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; |
| | | } else { |
| | | // Doing this in case mode changes during runtime. |
| | | $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; |
| | | $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; |
| | | $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; |
| | | } |
| | | $errno = $this->errorCode(mysql_errno($this->connection)); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, |
| | | @mysql_errno($this->connection) . ' ** ' . |
| | | @mysql_error($this->connection)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return int the DBMS' error code |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @mysql_errno($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @mysql_list_fields($this->dsn['database'], |
| | | $result, $this->connection); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @mysql_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $res[$i] = array( |
| | | 'table' => $case_func(@mysql_field_table($id, $i)), |
| | | 'name' => $case_func(@mysql_field_name($id, $i)), |
| | | 'type' => @mysql_field_type($id, $i), |
| | | 'len' => @mysql_field_len($id, $i), |
| | | 'flags' => @mysql_field_flags($id, $i), |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @mysql_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SHOW TABLES'; |
| | | case 'users': |
| | | return 'SELECT DISTINCT User FROM mysql.user'; |
| | | case 'databases': |
| | | return 'SHOW DATABASES'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's mysqli extension |
| | | * for interacting with MySQL databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's mysqli extension |
| | | * for interacting with MySQL databases |
| | | * |
| | | * This is for MySQL versions 4.1 and above. Requires PHP 5. |
| | | * |
| | | * Note that persistent connections no longer exist. |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | * @since Class functional since Release 1.6.3 |
| | | */ |
| | | class DB_mysqli extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'mysqli'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'mysqli'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'alter', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => false, |
| | | 'prepare' => false, |
| | | 'ssl' => true, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | 1004 => DB_ERROR_CANNOT_CREATE, |
| | | 1005 => DB_ERROR_CANNOT_CREATE, |
| | | 1006 => DB_ERROR_CANNOT_CREATE, |
| | | 1007 => DB_ERROR_ALREADY_EXISTS, |
| | | 1008 => DB_ERROR_CANNOT_DROP, |
| | | 1022 => DB_ERROR_ALREADY_EXISTS, |
| | | 1044 => DB_ERROR_ACCESS_VIOLATION, |
| | | 1046 => DB_ERROR_NODBSELECTED, |
| | | 1048 => DB_ERROR_CONSTRAINT, |
| | | 1049 => DB_ERROR_NOSUCHDB, |
| | | 1050 => DB_ERROR_ALREADY_EXISTS, |
| | | 1051 => DB_ERROR_NOSUCHTABLE, |
| | | 1054 => DB_ERROR_NOSUCHFIELD, |
| | | 1061 => DB_ERROR_ALREADY_EXISTS, |
| | | 1062 => DB_ERROR_ALREADY_EXISTS, |
| | | 1064 => DB_ERROR_SYNTAX, |
| | | 1091 => DB_ERROR_NOT_FOUND, |
| | | 1100 => DB_ERROR_NOT_LOCKED, |
| | | 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | 1142 => DB_ERROR_ACCESS_VIOLATION, |
| | | 1146 => DB_ERROR_NOSUCHTABLE, |
| | | 1216 => DB_ERROR_CONSTRAINT, |
| | | 1217 => DB_ERROR_CONSTRAINT, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The quantity of transactions begun |
| | | * |
| | | * {@internal While this is private, it can't actually be designated |
| | | * private in PHP 5 because it is directly accessed in the test suite.}} |
| | | * |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $transaction_opcount = 0; |
| | | |
| | | /** |
| | | * The database specified in the DSN |
| | | * |
| | | * It's a fix to allow calls to different databases in the same script. |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_db = ''; |
| | | |
| | | /** |
| | | * Array for converting MYSQLI_*_FLAG constants to text values |
| | | * @var array |
| | | * @access public |
| | | * @since Property available since Release 1.6.5 |
| | | */ |
| | | var $mysqli_flags = array( |
| | | MYSQLI_NOT_NULL_FLAG => 'not_null', |
| | | MYSQLI_PRI_KEY_FLAG => 'primary_key', |
| | | MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', |
| | | MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', |
| | | MYSQLI_BLOB_FLAG => 'blob', |
| | | MYSQLI_UNSIGNED_FLAG => 'unsigned', |
| | | MYSQLI_ZEROFILL_FLAG => 'zerofill', |
| | | MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', |
| | | MYSQLI_TIMESTAMP_FLAG => 'timestamp', |
| | | MYSQLI_SET_FLAG => 'set', |
| | | // MYSQLI_NUM_FLAG => 'numeric', // unnecessary |
| | | // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie |
| | | MYSQLI_GROUP_FLAG => 'group_by' |
| | | ); |
| | | |
| | | /** |
| | | * Array for converting MYSQLI_TYPE_* constants to text values |
| | | * @var array |
| | | * @access public |
| | | * @since Property available since Release 1.6.5 |
| | | */ |
| | | var $mysqli_types = array( |
| | | MYSQLI_TYPE_DECIMAL => 'decimal', |
| | | MYSQLI_TYPE_TINY => 'tinyint', |
| | | MYSQLI_TYPE_SHORT => 'int', |
| | | MYSQLI_TYPE_LONG => 'int', |
| | | MYSQLI_TYPE_FLOAT => 'float', |
| | | MYSQLI_TYPE_DOUBLE => 'double', |
| | | // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it |
| | | MYSQLI_TYPE_TIMESTAMP => 'timestamp', |
| | | MYSQLI_TYPE_LONGLONG => 'bigint', |
| | | MYSQLI_TYPE_INT24 => 'mediumint', |
| | | MYSQLI_TYPE_DATE => 'date', |
| | | MYSQLI_TYPE_TIME => 'time', |
| | | MYSQLI_TYPE_DATETIME => 'datetime', |
| | | MYSQLI_TYPE_YEAR => 'year', |
| | | MYSQLI_TYPE_NEWDATE => 'date', |
| | | MYSQLI_TYPE_ENUM => 'enum', |
| | | MYSQLI_TYPE_SET => 'set', |
| | | MYSQLI_TYPE_TINY_BLOB => 'tinyblob', |
| | | MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob', |
| | | MYSQLI_TYPE_LONG_BLOB => 'longblob', |
| | | MYSQLI_TYPE_BLOB => 'blob', |
| | | MYSQLI_TYPE_VAR_STRING => 'varchar', |
| | | MYSQLI_TYPE_STRING => 'char', |
| | | MYSQLI_TYPE_GEOMETRY => 'geometry', |
| | | ); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_mysqli() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's mysqli driver supports the following extra DSN options: |
| | | * + When the 'ssl' $option passed to DB::connect() is true: |
| | | * + key The path to the key file. |
| | | * + cert The path to the certificate file. |
| | | * + ca The path to the certificate authority file. |
| | | * + capath The path to a directory that contains trusted SSL |
| | | * CA certificates in pem format. |
| | | * + cipher The list of allowable ciphers for SSL encryption. |
| | | * |
| | | * Example of how to connect using SSL: |
| | | * <code> |
| | | * require_once 'DB.php'; |
| | | * |
| | | * $dsn = array( |
| | | * 'phptype' => 'mysqli', |
| | | * 'username' => 'someuser', |
| | | * 'password' => 'apasswd', |
| | | * 'hostspec' => 'localhost', |
| | | * 'database' => 'thedb', |
| | | * 'key' => 'client-key.pem', |
| | | * 'cert' => 'client-cert.pem', |
| | | * 'ca' => 'cacert.pem', |
| | | * 'capath' => '/path/to/ca/dir', |
| | | * 'cipher' => 'AES', |
| | | * ); |
| | | * |
| | | * $options = array( |
| | | * 'ssl' => true, |
| | | * ); |
| | | * |
| | | * $db =& DB::connect($dsn, $options); |
| | | * if (PEAR::isError($db)) { |
| | | * die($db->getMessage()); |
| | | * } |
| | | * </code> |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('mysqli')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $ini = ini_get('track_errors'); |
| | | ini_set('track_errors', 1); |
| | | $php_errormsg = ''; |
| | | |
| | | if ($this->getOption('ssl') === true) { |
| | | $init = mysqli_init(); |
| | | mysqli_ssl_set( |
| | | $init, |
| | | empty($dsn['key']) ? null : $dsn['key'], |
| | | empty($dsn['cert']) ? null : $dsn['cert'], |
| | | empty($dsn['ca']) ? null : $dsn['ca'], |
| | | empty($dsn['capath']) ? null : $dsn['capath'], |
| | | empty($dsn['cipher']) ? null : $dsn['cipher'] |
| | | ); |
| | | if ($this->connection = @mysqli_real_connect( |
| | | $init, |
| | | $dsn['hostspec'], |
| | | $dsn['username'], |
| | | $dsn['password'], |
| | | $dsn['database'], |
| | | $dsn['port'], |
| | | $dsn['socket'])) |
| | | { |
| | | $this->connection = $init; |
| | | } |
| | | } else { |
| | | $this->connection = @mysqli_connect( |
| | | $dsn['hostspec'], |
| | | $dsn['username'], |
| | | $dsn['password'], |
| | | $dsn['database'], |
| | | $dsn['port'], |
| | | $dsn['socket'] |
| | | ); |
| | | } |
| | | |
| | | ini_set('track_errors', $ini); |
| | | |
| | | if (!$this->connection) { |
| | | if (($err = @mysqli_connect_error()) != '') { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $err); |
| | | } else { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | } |
| | | |
| | | if ($dsn['database']) { |
| | | $this->_db = $dsn['database']; |
| | | } |
| | | |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @mysqli_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | if ($this->_db) { |
| | | if (!@mysqli_select_db($this->connection, $this->_db)) { |
| | | return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | } |
| | | if (!$this->autocommit && $ismanip) { |
| | | if ($this->transaction_opcount == 0) { |
| | | $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0'); |
| | | $result = @mysqli_query($this->connection, 'BEGIN'); |
| | | if (!$result) { |
| | | return $this->mysqliRaiseError(); |
| | | } |
| | | } |
| | | $this->transaction_opcount++; |
| | | } |
| | | $result = @mysqli_query($this->connection, $query); |
| | | if (!$result) { |
| | | return $this->mysqliRaiseError(); |
| | | } |
| | | if (is_object($result)) { |
| | | return $result; |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal mysql result pointer to the next available result. |
| | | * |
| | | * This method has not been implemented yet. |
| | | * |
| | | * @param resource $result a valid sql result resource |
| | | * @return false |
| | | * @access public |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@mysqli_data_seek($result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @mysqli_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | /* |
| | | * Even though this DBMS already trims output, we do this because |
| | | * a field might have intentional whitespace at the end that |
| | | * gets removed by DB_PORTABILITY_RTRIM under another driver. |
| | | */ |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @mysqli_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @mysqli_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->mysqliRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @mysqli_num_rows($result); |
| | | if ($rows === null) { |
| | | return $this->mysqliRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | // XXX if $this->transaction_opcount > 0, we should probably |
| | | // issue a warning here. |
| | | $this->autocommit = $onoff ? true : false; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if ($this->_db) { |
| | | if (!@mysqli_select_db($this->connection, $this->_db)) { |
| | | return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | } |
| | | $result = @mysqli_query($this->connection, 'COMMIT'); |
| | | $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->mysqliRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if ($this->_db) { |
| | | if (!@mysqli_select_db($this->connection, $this->_db)) { |
| | | return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | } |
| | | $result = @mysqli_query($this->connection, 'ROLLBACK'); |
| | | $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->mysqliRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (DB::isManip($this->last_query)) { |
| | | return @mysqli_affected_rows($this->connection); |
| | | } else { |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_mysqli::createSequence(), DB_mysqli::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | do { |
| | | $repeat = 0; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query('UPDATE ' . $seqname |
| | | . ' SET id = LAST_INSERT_ID(id + 1)'); |
| | | $this->popErrorHandling(); |
| | | if ($result === DB_OK) { |
| | | // COMMON CASE |
| | | $id = @mysqli_insert_id($this->connection); |
| | | if ($id != 0) { |
| | | return $id; |
| | | } |
| | | |
| | | // EMPTY SEQ TABLE |
| | | // Sequence table must be empty for some reason, |
| | | // so fill it and return 1 |
| | | // Obtain a user-level lock |
| | | $result = $this->getOne('SELECT GET_LOCK(' |
| | | . "'${seqname}_lock', 10)"); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | if ($result == 0) { |
| | | return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); |
| | | } |
| | | |
| | | // add the default value |
| | | $result = $this->query('REPLACE INTO ' . $seqname |
| | | . ' (id) VALUES (0)'); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | |
| | | // Release the lock |
| | | $result = $this->getOne('SELECT RELEASE_LOCK(' |
| | | . "'${seqname}_lock')"); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | // We know what the result will be, so no need to try again |
| | | return 1; |
| | | |
| | | } elseif ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) |
| | | { |
| | | // ONDEMAND TABLE CREATION |
| | | $result = $this->createSequence($seq_name); |
| | | |
| | | // Since createSequence initializes the ID to be 1, |
| | | // we do not need to retrieve the ID again (or we will get 2) |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } else { |
| | | // First ID of a newly created sequence is 1 |
| | | return 1; |
| | | } |
| | | |
| | | } elseif (DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_ALREADY_EXISTS) |
| | | { |
| | | // BACKWARDS COMPAT |
| | | // see _BCsequence() comment |
| | | $result = $this->_BCsequence($seqname); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $repeat = 1; |
| | | } |
| | | } while ($repeat); |
| | | |
| | | return $this->raiseError($result); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_mysqli::nextID(), DB_mysqli::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $res = $this->query('CREATE TABLE ' . $seqname |
| | | . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' |
| | | . ' PRIMARY KEY(id))'); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | // insert yields value 1, nextId call will generate ID 2 |
| | | return $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_mysql::nextID(), DB_mysql::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _BCsequence() |
| | | |
| | | /** |
| | | * Backwards compatibility with old sequence emulation implementation |
| | | * (clean up the dupes) |
| | | * |
| | | * @param string $seqname the sequence name to clean up |
| | | * |
| | | * @return bool true on success. A DB_Error object on failure. |
| | | * |
| | | * @access private |
| | | */ |
| | | function _BCsequence($seqname) |
| | | { |
| | | // Obtain a user-level lock... this will release any previous |
| | | // application locks, but unlike LOCK TABLES, it does not abort |
| | | // the current transaction and is much less frequently used. |
| | | $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | if ($result == 0) { |
| | | // Failed to get the lock, can't do the conversion, bail |
| | | // with a DB_ERROR_NOT_LOCKED error |
| | | return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); |
| | | } |
| | | |
| | | $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); |
| | | if (DB::isError($highest_id)) { |
| | | return $highest_id; |
| | | } |
| | | |
| | | // This should kill all rows except the highest |
| | | // We should probably do something if $highest_id isn't |
| | | // numeric, but I'm at a loss as how to handle that... |
| | | $result = $this->query('DELETE FROM ' . $seqname |
| | | . " WHERE id <> $highest_id"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | |
| | | // If another thread has been waiting for this lock, |
| | | // it will go thru the above procedure, but will have no |
| | | // real effect |
| | | $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteIdentifier() |
| | | |
| | | /** |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * MySQL can't handle the backtick character (<kbd>`</kbd>) in |
| | | * table or column names. |
| | | * |
| | | * @param string $str identifier name to be quoted |
| | | * |
| | | * @return string quoted identifier string |
| | | * |
| | | * @see DB_common::quoteIdentifier() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteIdentifier($str) |
| | | { |
| | | return '`' . $str . '`'; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ escapeSimple() |
| | | |
| | | /** |
| | | * Escapes a string according to the current DBMS's standards |
| | | * |
| | | * @param string $str the string to be escaped |
| | | * |
| | | * @return string the escaped string |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function escapeSimple($str) |
| | | { |
| | | return @mysqli_real_escape_string($this->connection, $str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | if (DB::isManip($query)) { |
| | | return $query . " LIMIT $count"; |
| | | } else { |
| | | return $query . " LIMIT $from, $count"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ mysqliRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_mysqli::errorNative(), DB_common::errorCode() |
| | | */ |
| | | function mysqliRaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { |
| | | $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; |
| | | $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; |
| | | $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; |
| | | } else { |
| | | // Doing this in case mode changes during runtime. |
| | | $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; |
| | | $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; |
| | | $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; |
| | | } |
| | | $errno = $this->errorCode(mysqli_errno($this->connection)); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, |
| | | @mysqli_errno($this->connection) . ' ** ' . |
| | | @mysqli_error($this->connection)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return int the DBMS' error code |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @mysqli_errno($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::setOption() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @mysqli_query($this->connection, |
| | | "SELECT * FROM $result LIMIT 0"); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_a($id, 'mysqli_result')) { |
| | | return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @mysqli_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $tmp = @mysqli_fetch_field($id); |
| | | |
| | | $flags = ''; |
| | | foreach ($this->mysqli_flags as $const => $means) { |
| | | if ($tmp->flags & $const) { |
| | | $flags .= $means . ' '; |
| | | } |
| | | } |
| | | if ($tmp->def) { |
| | | $flags .= 'default_' . rawurlencode($tmp->def); |
| | | } |
| | | $flags = trim($flags); |
| | | |
| | | $res[$i] = array( |
| | | 'table' => $case_func($tmp->table), |
| | | 'name' => $case_func($tmp->name), |
| | | 'type' => isset($this->mysqli_types[$tmp->type]) |
| | | ? $this->mysqli_types[$tmp->type] |
| | | : 'unknown', |
| | | 'len' => $tmp->max_length, |
| | | 'flags' => $flags, |
| | | ); |
| | | |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @mysqli_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SHOW TABLES'; |
| | | case 'users': |
| | | return 'SELECT DISTINCT User FROM mysql.user'; |
| | | case 'databases': |
| | | return 'SHOW DATABASES'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's oci8 extension |
| | | * for interacting with Oracle databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author James L. Pine <jlp@valinux.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's oci8 extension |
| | | * for interacting with Oracle databases |
| | | * |
| | | * Definitely works with versions 8 and 9 of Oracle. |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * Be aware... OCIError() only appears to return anything when given a |
| | | * statement, so functions return the generic DB_ERROR instead of more |
| | | * useful errors that have to do with feedback from the database. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author James L. Pine <jlp@valinux.com> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_oci8 extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'oci8'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'oci8'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'alter', |
| | | 'new_link' => '5.0.0', |
| | | 'numrows' => 'subquery', |
| | | 'pconnect' => true, |
| | | 'prepare' => true, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | 1 => DB_ERROR_CONSTRAINT, |
| | | 900 => DB_ERROR_SYNTAX, |
| | | 904 => DB_ERROR_NOSUCHFIELD, |
| | | 913 => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | 921 => DB_ERROR_SYNTAX, |
| | | 923 => DB_ERROR_SYNTAX, |
| | | 942 => DB_ERROR_NOSUCHTABLE, |
| | | 955 => DB_ERROR_ALREADY_EXISTS, |
| | | 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | 1401 => DB_ERROR_INVALID, |
| | | 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | 1418 => DB_ERROR_NOT_FOUND, |
| | | 1476 => DB_ERROR_DIVZERO, |
| | | 1722 => DB_ERROR_INVALID_NUMBER, |
| | | 2289 => DB_ERROR_NOSUCHTABLE, |
| | | 2291 => DB_ERROR_CONSTRAINT, |
| | | 2292 => DB_ERROR_CONSTRAINT, |
| | | 2449 => DB_ERROR_CONSTRAINT, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * Stores the $data passed to execute() in the oci8 driver |
| | | * |
| | | * Gets reset to array() when simpleQuery() is run. |
| | | * |
| | | * Needed in case user wants to call numRows() after prepare/execute |
| | | * was used. |
| | | * |
| | | * @var array |
| | | * @access private |
| | | */ |
| | | var $_data = array(); |
| | | |
| | | /** |
| | | * The result or statement handle from the most recently executed query |
| | | * @var resource |
| | | */ |
| | | var $last_stmt; |
| | | |
| | | /** |
| | | * Is the given prepared statement a data manipulation query? |
| | | * @var array |
| | | * @access private |
| | | */ |
| | | var $manip_query = array(); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_oci8() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * If PHP is at version 5.0.0 or greater: |
| | | * + Generally, oci_connect() or oci_pconnect() are used. |
| | | * + But if the new_link DSN option is set to true, oci_new_connect() |
| | | * is used. |
| | | * |
| | | * When using PHP version 4.x, OCILogon() or OCIPLogon() are used. |
| | | * |
| | | * PEAR DB's oci8 driver supports the following extra DSN options: |
| | | * + charset The character set to be used on the connection. |
| | | * Only used if PHP is at version 5.0.0 or greater |
| | | * and the Oracle server is at 9.2 or greater. |
| | | * Available since PEAR DB 1.7.0. |
| | | * + new_link If set to true, causes subsequent calls to |
| | | * connect() to return a new connection link |
| | | * instead of the existing one. WARNING: this is |
| | | * not portable to other DBMS's. |
| | | * Available since PEAR DB 1.7.0. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('oci8')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | if (function_exists('oci_connect')) { |
| | | if (isset($dsn['new_link']) |
| | | && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) |
| | | { |
| | | $connect_function = 'oci_new_connect'; |
| | | } else { |
| | | $connect_function = $persistent ? 'oci_pconnect' |
| | | : 'oci_connect'; |
| | | } |
| | | |
| | | // Backwards compatibility with DB < 1.7.0 |
| | | if (empty($dsn['database']) && !empty($dsn['hostspec'])) { |
| | | $db = $dsn['hostspec']; |
| | | } else { |
| | | $db = $dsn['database']; |
| | | } |
| | | |
| | | $char = empty($dsn['charset']) ? null : $dsn['charset']; |
| | | $this->connection = @$connect_function($dsn['username'], |
| | | $dsn['password'], |
| | | $db, |
| | | $char); |
| | | $error = OCIError(); |
| | | if (!empty($error) && $error['code'] == 12541) { |
| | | // Couldn't find TNS listener. Try direct connection. |
| | | $this->connection = @$connect_function($dsn['username'], |
| | | $dsn['password'], |
| | | null, |
| | | $char); |
| | | } |
| | | } else { |
| | | $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; |
| | | if ($dsn['hostspec']) { |
| | | $this->connection = @$connect_function($dsn['username'], |
| | | $dsn['password'], |
| | | $dsn['hostspec']); |
| | | } elseif ($dsn['username'] || $dsn['password']) { |
| | | $this->connection = @$connect_function($dsn['username'], |
| | | $dsn['password']); |
| | | } |
| | | } |
| | | |
| | | if (!$this->connection) { |
| | | $error = OCIError(); |
| | | $error = (is_array($error)) ? $error['message'] : null; |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $error); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | if (function_exists('oci_close')) { |
| | | $ret = @oci_close($this->connection); |
| | | } else { |
| | | $ret = @OCILogOff($this->connection); |
| | | } |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * To determine how many rows of a result set get buffered using |
| | | * ocisetprefetch(), see the "result_buffering" option in setOptions(). |
| | | * This option was added in Release 1.7.0. |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $this->_data = array(); |
| | | $this->last_parameters = array(); |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | $result = @OCIParse($this->connection, $query); |
| | | if (!$result) { |
| | | return $this->oci8RaiseError(); |
| | | } |
| | | if ($this->autocommit) { |
| | | $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); |
| | | } else { |
| | | $success = @OCIExecute($result,OCI_DEFAULT); |
| | | } |
| | | if (!$success) { |
| | | return $this->oci8RaiseError($result); |
| | | } |
| | | $this->last_stmt = $result; |
| | | if (DB::isManip($query)) { |
| | | return DB_OK; |
| | | } else { |
| | | @ocisetprefetch($result, $this->options['result_buffering']); |
| | | return $result; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal oracle result pointer to the next available result |
| | | * |
| | | * @param a valid oci8 result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && |
| | | $moredata) |
| | | { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); |
| | | } |
| | | if (!$moredata) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @OCIFreeStatement($result); |
| | | } |
| | | |
| | | /** |
| | | * Frees the internal resources associated with a prepared query |
| | | * |
| | | * @param resource $stmt the prepared statement's resource |
| | | * @param bool $free_resource should the PHP resource be freed too? |
| | | * Use false if you need to get data |
| | | * from the result set later. |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_oci8::prepare() |
| | | */ |
| | | function freePrepared($stmt, $free_resource = true) |
| | | { |
| | | if (!is_resource($stmt)) { |
| | | return false; |
| | | } |
| | | if ($free_resource) { |
| | | @ocifreestatement($stmt); |
| | | } |
| | | if (isset($this->prepare_types[(int)$stmt])) { |
| | | unset($this->prepare_types[(int)$stmt]); |
| | | unset($this->manip_query[(int)$stmt]); |
| | | } else { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * Only works if the DB_PORTABILITY_NUMROWS portability option |
| | | * is turned on. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows(), DB_common::setOption() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | // emulate numRows for Oracle. yuck. |
| | | if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && |
| | | $result === $this->last_stmt) |
| | | { |
| | | $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; |
| | | $save_query = $this->last_query; |
| | | $save_stmt = $this->last_stmt; |
| | | |
| | | if (count($this->_data)) { |
| | | $smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')'); |
| | | $count = $this->execute($smt, $this->_data); |
| | | } else { |
| | | $count =& $this->query($countquery); |
| | | } |
| | | |
| | | if (DB::isError($count) || |
| | | DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) |
| | | { |
| | | $this->last_query = $save_query; |
| | | $this->last_stmt = $save_stmt; |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | return $row[0]; |
| | | } |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @OCINumCols($result); |
| | | if (!$cols) { |
| | | return $this->oci8RaiseError($result); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ prepare() |
| | | |
| | | /** |
| | | * Prepares a query for multiple execution with execute(). |
| | | * |
| | | * With oci8, this is emulated. |
| | | * |
| | | * prepare() requires a generic query as string like <code> |
| | | * INSERT INTO numbers VALUES (?, ?, ?) |
| | | * </code>. The <kbd>?</kbd> characters are placeholders. |
| | | * |
| | | * Three types of placeholders can be used: |
| | | * + <kbd>?</kbd> a quoted scalar value, i.e. strings, integers |
| | | * + <kbd>!</kbd> value is inserted 'as is' |
| | | * + <kbd>&</kbd> requires a file name. The file's contents get |
| | | * inserted into the query (i.e. saving binary |
| | | * data in a db) |
| | | * |
| | | * Use backslashes to escape placeholder characters if you don't want |
| | | * them to be interpreted as placeholders. Example: <code> |
| | | * "UPDATE foo SET col=? WHERE col='over \& under'" |
| | | * </code> |
| | | * |
| | | * @param string $query the query to be prepared |
| | | * |
| | | * @return mixed DB statement resource on success. DB_Error on failure. |
| | | * |
| | | * @see DB_oci8::execute() |
| | | */ |
| | | function prepare($query) |
| | | { |
| | | $tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1, |
| | | PREG_SPLIT_DELIM_CAPTURE); |
| | | $binds = count($tokens) - 1; |
| | | $token = 0; |
| | | $types = array(); |
| | | $newquery = ''; |
| | | |
| | | foreach ($tokens as $key => $val) { |
| | | switch ($val) { |
| | | case '?': |
| | | $types[$token++] = DB_PARAM_SCALAR; |
| | | unset($tokens[$key]); |
| | | break; |
| | | case '&': |
| | | $types[$token++] = DB_PARAM_OPAQUE; |
| | | unset($tokens[$key]); |
| | | break; |
| | | case '!': |
| | | $types[$token++] = DB_PARAM_MISC; |
| | | unset($tokens[$key]); |
| | | break; |
| | | default: |
| | | $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); |
| | | if ($key != $binds) { |
| | | $newquery .= $tokens[$key] . ':bind' . $token; |
| | | } else { |
| | | $newquery .= $tokens[$key]; |
| | | } |
| | | } |
| | | } |
| | | |
| | | $this->last_query = $query; |
| | | $newquery = $this->modifyQuery($newquery); |
| | | if (!$stmt = @OCIParse($this->connection, $newquery)) { |
| | | return $this->oci8RaiseError(); |
| | | } |
| | | $this->prepare_types[(int)$stmt] = $types; |
| | | $this->manip_query[(int)$stmt] = DB::isManip($query); |
| | | return $stmt; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ execute() |
| | | |
| | | /** |
| | | * Executes a DB statement prepared with prepare(). |
| | | * |
| | | * To determine how many rows of a result set get buffered using |
| | | * ocisetprefetch(), see the "result_buffering" option in setOptions(). |
| | | * This option was added in Release 1.7.0. |
| | | * |
| | | * @param resource $stmt a DB statement resource returned from prepare() |
| | | * @param mixed $data array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 for non-array items or the |
| | | * quantity of elements in the array. |
| | | * |
| | | * @return mixed returns an oic8 result resource for successful SELECT |
| | | * queries, DB_OK for other successful queries. |
| | | * A DB error object is returned on failure. |
| | | * |
| | | * @see DB_oci8::prepare() |
| | | */ |
| | | function &execute($stmt, $data = array()) |
| | | { |
| | | $data = (array)$data; |
| | | $this->last_parameters = $data; |
| | | $this->_data = $data; |
| | | |
| | | $types =& $this->prepare_types[(int)$stmt]; |
| | | if (count($types) != count($data)) { |
| | | $tmp =& $this->raiseError(DB_ERROR_MISMATCH); |
| | | return $tmp; |
| | | } |
| | | |
| | | $i = 0; |
| | | foreach ($data as $key => $value) { |
| | | if ($types[$i] == DB_PARAM_MISC) { |
| | | /* |
| | | * Oracle doesn't seem to have the ability to pass a |
| | | * parameter along unchanged, so strip off quotes from start |
| | | * and end, plus turn two single quotes to one single quote, |
| | | * in order to avoid the quotes getting escaped by |
| | | * Oracle and ending up in the database. |
| | | */ |
| | | $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); |
| | | $data[$key] = str_replace("''", "'", $data[$key]); |
| | | } elseif ($types[$i] == DB_PARAM_OPAQUE) { |
| | | $fp = @fopen($data[$key], 'rb'); |
| | | if (!$fp) { |
| | | $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); |
| | | return $tmp; |
| | | } |
| | | $data[$key] = fread($fp, filesize($data[$key])); |
| | | fclose($fp); |
| | | } |
| | | if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { |
| | | $tmp = $this->oci8RaiseError($stmt); |
| | | return $tmp; |
| | | } |
| | | $i++; |
| | | } |
| | | if ($this->autocommit) { |
| | | $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); |
| | | } else { |
| | | $success = @OCIExecute($stmt, OCI_DEFAULT); |
| | | } |
| | | if (!$success) { |
| | | $tmp = $this->oci8RaiseError($stmt); |
| | | return $tmp; |
| | | } |
| | | $this->last_stmt = $stmt; |
| | | if ($this->manip_query[(int)$stmt]) { |
| | | $tmp = DB_OK; |
| | | } else { |
| | | @ocisetprefetch($stmt, $this->options['result_buffering']); |
| | | $tmp =& new DB_result($this, $stmt); |
| | | } |
| | | return $tmp; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | $this->autocommit = (bool)$onoff;; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | $result = @OCICommit($this->connection); |
| | | if (!$result) { |
| | | return $this->oci8RaiseError(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | $result = @OCIRollback($this->connection); |
| | | if (!$result) { |
| | | return $this->oci8RaiseError(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if ($this->last_stmt === false) { |
| | | return $this->oci8RaiseError(); |
| | | } |
| | | $result = @OCIRowCount($this->last_stmt); |
| | | if ($result === false) { |
| | | return $this->oci8RaiseError($this->last_stmt); |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyQuery() |
| | | |
| | | /** |
| | | * Changes a query string for various DBMS specific reasons |
| | | * |
| | | * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. |
| | | * |
| | | * @param string $query the query string to modify |
| | | * |
| | | * @return string the modified query string |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyQuery($query) |
| | | { |
| | | if (preg_match('/^\s*SELECT/i', $query) && |
| | | !preg_match('/\sFROM\s/i', $query)) { |
| | | $query .= ' FROM dual'; |
| | | } |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | // Let Oracle return the name of the columns instead of |
| | | // coding a "home" SQL parser |
| | | |
| | | if (count($params)) { |
| | | $result = $this->prepare("SELECT * FROM ($query) " |
| | | . 'WHERE NULL = NULL'); |
| | | $tmp =& $this->execute($result, $params); |
| | | } else { |
| | | $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; |
| | | |
| | | if (!$result = @OCIParse($this->connection, $q_fields)) { |
| | | $this->last_query = $q_fields; |
| | | return $this->oci8RaiseError(); |
| | | } |
| | | if (!@OCIExecute($result, OCI_DEFAULT)) { |
| | | $this->last_query = $q_fields; |
| | | return $this->oci8RaiseError($result); |
| | | } |
| | | } |
| | | |
| | | $ncols = OCINumCols($result); |
| | | $cols = array(); |
| | | for ( $i = 1; $i <= $ncols; $i++ ) { |
| | | $cols[] = '"' . OCIColumnName($result, $i) . '"'; |
| | | } |
| | | $fields = implode(', ', $cols); |
| | | // XXX Test that (tip by John Lim) |
| | | //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { |
| | | // // Introduce the FIRST_ROWS Oracle query optimizer |
| | | // $query = substr($query, strlen($match[0]), strlen($query)); |
| | | // $query = "SELECT /* +FIRST_ROWS */ " . $query; |
| | | //} |
| | | |
| | | // Construct the query |
| | | // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 |
| | | // Perhaps this could be optimized with the use of Unions |
| | | $query = "SELECT $fields FROM". |
| | | " (SELECT rownum as linenum, $fields FROM". |
| | | " ($query)". |
| | | ' WHERE rownum <= '. ($from + $count) . |
| | | ') WHERE linenum >= ' . ++$from; |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_oci8::createSequence(), DB_oci8::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $repeat = 0; |
| | | do { |
| | | $this->expectError(DB_ERROR_NOSUCHTABLE); |
| | | $result =& $this->query("SELECT ${seqname}.nextval FROM dual"); |
| | | $this->popExpect(); |
| | | if ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) { |
| | | $repeat = 1; |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | } else { |
| | | $repeat = 0; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | return $arr[0]; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_oci8::nextID(), DB_oci8::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | return $this->query('CREATE SEQUENCE ' |
| | | . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_oci8::nextID(), DB_oci8::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP SEQUENCE ' |
| | | . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ oci8RaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_oci8::errorNative(), DB_oci8::errorCode() |
| | | */ |
| | | function oci8RaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | $error = @OCIError($this->connection); |
| | | return $this->raiseError($this->errorCode($error['code']), |
| | | null, null, null, $error['message']); |
| | | } elseif (is_resource($errno)) { |
| | | $error = @OCIError($errno); |
| | | return $this->raiseError($this->errorCode($error['code']), |
| | | null, null, null, $error['message']); |
| | | } |
| | | return $this->raiseError($this->errorCode($errno)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code produced by the last query |
| | | * |
| | | * @return int the DBMS' error code. FALSE if the code could not be |
| | | * determined |
| | | */ |
| | | function errorNative() |
| | | { |
| | | if (is_resource($this->last_stmt)) { |
| | | $error = @OCIError($this->last_stmt); |
| | | } else { |
| | | $error = @OCIError($this->connection); |
| | | } |
| | | if (is_array($error)) { |
| | | return $error['code']; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * NOTE: only supports 'table' and 'flags' if <var>$result</var> |
| | | * is a table name. |
| | | * |
| | | * NOTE: flags won't contain index information. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $res = array(); |
| | | |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $result = strtoupper($result); |
| | | $q_fields = 'SELECT column_name, data_type, data_length, ' |
| | | . 'nullable ' |
| | | . 'FROM user_tab_columns ' |
| | | . "WHERE table_name='$result' ORDER BY column_id"; |
| | | |
| | | $this->last_query = $q_fields; |
| | | |
| | | if (!$stmt = @OCIParse($this->connection, $q_fields)) { |
| | | return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | if (!@OCIExecute($stmt, OCI_DEFAULT)) { |
| | | return $this->oci8RaiseError($stmt); |
| | | } |
| | | |
| | | $i = 0; |
| | | while (@OCIFetch($stmt)) { |
| | | $res[$i] = array( |
| | | 'table' => $case_func($result), |
| | | 'name' => $case_func(@OCIResult($stmt, 1)), |
| | | 'type' => @OCIResult($stmt, 2), |
| | | 'len' => @OCIResult($stmt, 3), |
| | | 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | $i++; |
| | | } |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $i; |
| | | } |
| | | @OCIFreeStatement($stmt); |
| | | |
| | | } else { |
| | | if (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $result = $result->result; |
| | | } |
| | | |
| | | $res = array(); |
| | | |
| | | if ($result === $this->last_stmt) { |
| | | $count = @OCINumCols($result); |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $res[$i] = array( |
| | | 'table' => '', |
| | | 'name' => $case_func(@OCIColumnName($result, $i+1)), |
| | | 'type' => @OCIColumnType($result, $i+1), |
| | | 'len' => @OCIColumnSize($result, $i+1), |
| | | 'flags' => '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | } else { |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE); |
| | | } |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SELECT table_name FROM user_tables'; |
| | | case 'synonyms': |
| | | return 'SELECT synonym_name FROM user_synonyms'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's odbc extension |
| | | * for interacting with databases via ODBC connections |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's odbc extension |
| | | * for interacting with databases via ODBC connections |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * More info on ODBC errors could be found here: |
| | | * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_odbc extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'odbc'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'sql92'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * NOTE: The feature set of the following drivers are different than |
| | | * the default: |
| | | * + solid: 'transactions' = true |
| | | * + navision: 'limit' = false |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'emulate', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => false, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | '01004' => DB_ERROR_TRUNCATED, |
| | | '07001' => DB_ERROR_MISMATCH, |
| | | '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | '21S02' => DB_ERROR_MISMATCH, |
| | | '22001' => DB_ERROR_INVALID, |
| | | '22003' => DB_ERROR_INVALID_NUMBER, |
| | | '22005' => DB_ERROR_INVALID_NUMBER, |
| | | '22008' => DB_ERROR_INVALID_DATE, |
| | | '22012' => DB_ERROR_DIVZERO, |
| | | '23000' => DB_ERROR_CONSTRAINT, |
| | | '23502' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '23503' => DB_ERROR_CONSTRAINT, |
| | | '23504' => DB_ERROR_CONSTRAINT, |
| | | '23505' => DB_ERROR_CONSTRAINT, |
| | | '24000' => DB_ERROR_INVALID, |
| | | '34000' => DB_ERROR_INVALID, |
| | | '37000' => DB_ERROR_SYNTAX, |
| | | '42000' => DB_ERROR_SYNTAX, |
| | | '42601' => DB_ERROR_SYNTAX, |
| | | 'IM001' => DB_ERROR_UNSUPPORTED, |
| | | 'S0000' => DB_ERROR_NOSUCHTABLE, |
| | | 'S0001' => DB_ERROR_ALREADY_EXISTS, |
| | | 'S0002' => DB_ERROR_NOSUCHTABLE, |
| | | 'S0011' => DB_ERROR_ALREADY_EXISTS, |
| | | 'S0012' => DB_ERROR_NOT_FOUND, |
| | | 'S0021' => DB_ERROR_ALREADY_EXISTS, |
| | | 'S0022' => DB_ERROR_NOSUCHFIELD, |
| | | 'S1009' => DB_ERROR_INVALID, |
| | | 'S1090' => DB_ERROR_INVALID, |
| | | 'S1C00' => DB_ERROR_NOT_CAPABLE, |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * The number of rows affected by a data manipulation query |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $affected = 0; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_odbc() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's odbc driver supports the following extra DSN options: |
| | | * + cursor The type of cursor to be used for this connection. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('odbc')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | switch ($this->dbsyntax) { |
| | | case 'access': |
| | | case 'db2': |
| | | case 'solid': |
| | | $this->features['transactions'] = true; |
| | | break; |
| | | case 'navision': |
| | | $this->features['limit'] = false; |
| | | } |
| | | |
| | | /* |
| | | * This is hear for backwards compatibility. Should have been using |
| | | * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used. |
| | | */ |
| | | if ($dsn['database']) { |
| | | $odbcdsn = $dsn['database']; |
| | | } elseif ($dsn['hostspec']) { |
| | | $odbcdsn = $dsn['hostspec']; |
| | | } else { |
| | | $odbcdsn = 'localhost'; |
| | | } |
| | | |
| | | $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; |
| | | |
| | | if (empty($dsn['cursor'])) { |
| | | $this->connection = @$connect_function($odbcdsn, $dsn['username'], |
| | | $dsn['password']); |
| | | } else { |
| | | $this->connection = @$connect_function($odbcdsn, $dsn['username'], |
| | | $dsn['password'], |
| | | $dsn['cursor']); |
| | | } |
| | | |
| | | if (!is_resource($this->connection)) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $this->errorNative()); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $err = @odbc_close($this->connection); |
| | | $this->connection = null; |
| | | return $err; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | $result = @odbc_exec($this->connection, $query); |
| | | if (!$result) { |
| | | return $this->odbcRaiseError(); // XXX ERRORMSG |
| | | } |
| | | // Determine which queries that should return data, and which |
| | | // should return an error code only. |
| | | if (DB::isManip($query)) { |
| | | $this->affected = $result; // For affectedRows() |
| | | return DB_OK; |
| | | } |
| | | $this->affected = 0; |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal odbc result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return @odbc_next_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | $arr = array(); |
| | | if ($rownum !== null) { |
| | | $rownum++; // ODBC first row is 1 |
| | | if (version_compare(phpversion(), '4.2.0', 'ge')) { |
| | | $cols = @odbc_fetch_into($result, $arr, $rownum); |
| | | } else { |
| | | $cols = @odbc_fetch_into($result, $rownum, $arr); |
| | | } |
| | | } else { |
| | | $cols = @odbc_fetch_into($result, $arr); |
| | | } |
| | | if (!$cols) { |
| | | return null; |
| | | } |
| | | if ($fetchmode !== DB_FETCHMODE_ORDERED) { |
| | | for ($i = 0; $i < count($arr); $i++) { |
| | | $colName = @odbc_field_name($result, $i+1); |
| | | $a[$colName] = $arr[$i]; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $a = array_change_key_case($a, CASE_LOWER); |
| | | } |
| | | $arr = $a; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @odbc_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @odbc_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (empty($this->affected)) { // In case of SELECT stms |
| | | return 0; |
| | | } |
| | | $nrows = @odbc_num_rows($this->affected); |
| | | if ($nrows == -1) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | return $nrows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * Not all ODBC drivers support this functionality. If they don't |
| | | * a DB_Error object for DB_ERROR_UNSUPPORTED is returned. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $nrows = @odbc_num_rows($result); |
| | | if ($nrows == -1) { |
| | | return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED); |
| | | } |
| | | if ($nrows === false) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | return $nrows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteIdentifier() |
| | | |
| | | /** |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked |
| | | * "Use ANSI quoted identifiers" when setting up the ODBC data source. |
| | | * |
| | | * @param string $str identifier name to be quoted |
| | | * |
| | | * @return string quoted identifier string |
| | | * |
| | | * @see DB_common::quoteIdentifier() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteIdentifier($str) |
| | | { |
| | | switch ($this->dsn['dbsyntax']) { |
| | | case 'access': |
| | | return '[' . $str . ']'; |
| | | case 'mssql': |
| | | case 'sybase': |
| | | return '[' . str_replace(']', ']]', $str) . ']'; |
| | | case 'mysql': |
| | | case 'mysqli': |
| | | return '`' . $str . '`'; |
| | | default: |
| | | return '"' . str_replace('"', '""', $str) . '"'; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quote() |
| | | |
| | | /** |
| | | * @deprecated Deprecated in release 1.6.0 |
| | | * @internal |
| | | */ |
| | | function quote($str) |
| | | { |
| | | return $this->quoteSmart($str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_odbc::createSequence(), DB_odbc::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $repeat = 0; |
| | | do { |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query("update ${seqname} set id = id + 1"); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) { |
| | | $repeat = 1; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->createSequence($seq_name); |
| | | $this->popErrorHandling(); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $result = $this->query("insert into ${seqname} (id) values(0)"); |
| | | } else { |
| | | $repeat = 0; |
| | | } |
| | | } while ($repeat); |
| | | |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | |
| | | $result = $this->query("select id from ${seqname}"); |
| | | if (DB::isError($result)) { |
| | | return $result; |
| | | } |
| | | |
| | | $row = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | if (DB::isError($row || !$row)) { |
| | | return $row; |
| | | } |
| | | |
| | | return $row[0]; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_odbc::nextID(), DB_odbc::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | return $this->query('CREATE TABLE ' |
| | | . $this->getSequenceName($seq_name) |
| | | . ' (id integer NOT NULL,' |
| | | . ' PRIMARY KEY(id))'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_odbc::nextID(), DB_odbc::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | if (!@odbc_autocommit($this->connection, $onoff)) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if (!@odbc_commit($this->connection)) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if (!@odbc_rollback($this->connection)) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ odbcRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_odbc::errorNative(), DB_common::errorCode() |
| | | */ |
| | | function odbcRaiseError($errno = null) |
| | | { |
| | | if ($errno === null) { |
| | | switch ($this->dbsyntax) { |
| | | case 'access': |
| | | if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { |
| | | $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD; |
| | | } else { |
| | | // Doing this in case mode changes during runtime. |
| | | $this->errorcode_map['07001'] = DB_ERROR_MISMATCH; |
| | | } |
| | | |
| | | $native_code = odbc_error($this->connection); |
| | | |
| | | // S1000 is for "General Error." Let's be more specific. |
| | | if ($native_code == 'S1000') { |
| | | $errormsg = odbc_errormsg($this->connection); |
| | | static $error_regexps; |
| | | if (!isset($error_regexps)) { |
| | | $error_regexps = array( |
| | | '/includes related records.$/i' => DB_ERROR_CONSTRAINT, |
| | | '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | ); |
| | | } |
| | | foreach ($error_regexps as $regexp => $code) { |
| | | if (preg_match($regexp, $errormsg)) { |
| | | return $this->raiseError($code, |
| | | null, null, null, |
| | | $native_code . ' ' . $errormsg); |
| | | } |
| | | } |
| | | $errno = DB_ERROR; |
| | | } else { |
| | | $errno = $this->errorCode($native_code); |
| | | } |
| | | break; |
| | | default: |
| | | $errno = $this->errorCode(odbc_error($this->connection)); |
| | | } |
| | | } |
| | | return $this->raiseError($errno, null, null, null, |
| | | $this->errorNative()); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error code and message produced by the last query |
| | | * |
| | | * @return string the DBMS' error code and message |
| | | */ |
| | | function errorNative() |
| | | { |
| | | if (!is_resource($this->connection)) { |
| | | return @odbc_error() . ' ' . @odbc_errormsg(); |
| | | } |
| | | return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @odbc_exec($this->connection, "SELECT * FROM $result"); |
| | | if (!$id) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @odbc_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $col = $i + 1; |
| | | $res[$i] = array( |
| | | 'table' => $got_string ? $case_func($result) : '', |
| | | 'name' => $case_func(@odbc_field_name($id, $col)), |
| | | 'type' => @odbc_field_type($id, $col), |
| | | 'len' => @odbc_field_len($id, $col), |
| | | 'flags' => '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @odbc_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com. |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the list of objects requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'databases': |
| | | if (!function_exists('odbc_data_source')) { |
| | | return null; |
| | | } |
| | | $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST); |
| | | if (is_array($res)) { |
| | | $out = array($res['server']); |
| | | while($res = @odbc_data_source($this->connection, |
| | | SQL_FETCH_NEXT)) |
| | | { |
| | | $out[] = $res['server']; |
| | | } |
| | | return $out; |
| | | } else { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | break; |
| | | case 'tables': |
| | | case 'schema.tables': |
| | | $keep = 'TABLE'; |
| | | break; |
| | | case 'views': |
| | | $keep = 'VIEW'; |
| | | break; |
| | | default: |
| | | return null; |
| | | } |
| | | |
| | | /* |
| | | * Removing non-conforming items in the while loop rather than |
| | | * in the odbc_tables() call because some backends choke on this: |
| | | * odbc_tables($this->connection, '', '', '', 'TABLE') |
| | | */ |
| | | $res = @odbc_tables($this->connection); |
| | | if (!$res) { |
| | | return $this->odbcRaiseError(); |
| | | } |
| | | $out = array(); |
| | | while ($row = odbc_fetch_array($res)) { |
| | | if ($row['TABLE_TYPE'] != $keep) { |
| | | continue; |
| | | } |
| | | if ($type == 'schema.tables') { |
| | | $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME']; |
| | | } else { |
| | | $out[] = $row['TABLE_NAME']; |
| | | } |
| | | } |
| | | return $out; |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's pgsql extension |
| | | * for interacting with PostgreSQL databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Rui Hirokawa <hirokawa@php.net> |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's pgsql extension |
| | | * for interacting with PostgreSQL databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Rui Hirokawa <hirokawa@php.net> |
| | | * @author Stig Bakken <ssb@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_pgsql extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'pgsql'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'pgsql'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'alter', |
| | | 'new_link' => '4.3.0', |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => true, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The quantity of transactions begun |
| | | * |
| | | * {@internal While this is private, it can't actually be designated |
| | | * private in PHP 5 because it is directly accessed in the test suite.}} |
| | | * |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $transaction_opcount = 0; |
| | | |
| | | /** |
| | | * The number of rows affected by a data manipulation query |
| | | * @var integer |
| | | */ |
| | | var $affected = 0; |
| | | |
| | | /** |
| | | * The current row being looked at in fetchInto() |
| | | * @var array |
| | | * @access private |
| | | */ |
| | | var $row = array(); |
| | | |
| | | /** |
| | | * The number of rows in a given result set |
| | | * @var array |
| | | * @access private |
| | | */ |
| | | var $_num_rows = array(); |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_pgsql() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's pgsql driver supports the following extra DSN options: |
| | | * + connect_timeout How many seconds to wait for a connection to |
| | | * be established. Available since PEAR DB 1.7.0. |
| | | * + new_link If set to true, causes subsequent calls to |
| | | * connect() to return a new connection link |
| | | * instead of the existing one. WARNING: this is |
| | | * not portable to other DBMS's. Available only |
| | | * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0. |
| | | * + options Command line options to be sent to the server. |
| | | * Available since PEAR DB 1.6.4. |
| | | * + service Specifies a service name in pg_service.conf that |
| | | * holds additional connection parameters. |
| | | * Available since PEAR DB 1.7.0. |
| | | * + sslmode How should SSL be used when connecting? Values: |
| | | * disable, allow, prefer or require. |
| | | * Available since PEAR DB 1.7.0. |
| | | * + tty This was used to specify where to send server |
| | | * debug output. Available since PEAR DB 1.6.4. |
| | | * |
| | | * Example of connecting to a new link via a socket: |
| | | * <code> |
| | | * require_once 'DB.php'; |
| | | * |
| | | * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true'; |
| | | * $options = array( |
| | | * 'portability' => DB_PORTABILITY_ALL, |
| | | * ); |
| | | * |
| | | * $db =& DB::connect($dsn, $options); |
| | | * if (PEAR::isError($db)) { |
| | | * die($db->getMessage()); |
| | | * } |
| | | * </code> |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('pgsql')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp'; |
| | | |
| | | $params = array(''); |
| | | if ($protocol == 'tcp') { |
| | | if ($dsn['hostspec']) { |
| | | $params[0] .= 'host=' . $dsn['hostspec']; |
| | | } |
| | | if ($dsn['port']) { |
| | | $params[0] .= ' port=' . $dsn['port']; |
| | | } |
| | | } elseif ($protocol == 'unix') { |
| | | // Allow for pg socket in non-standard locations. |
| | | if ($dsn['socket']) { |
| | | $params[0] .= 'host=' . $dsn['socket']; |
| | | } |
| | | if ($dsn['port']) { |
| | | $params[0] .= ' port=' . $dsn['port']; |
| | | } |
| | | } |
| | | if ($dsn['database']) { |
| | | $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\''; |
| | | } |
| | | if ($dsn['username']) { |
| | | $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\''; |
| | | } |
| | | if ($dsn['password']) { |
| | | $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\''; |
| | | } |
| | | if (!empty($dsn['options'])) { |
| | | $params[0] .= ' options=' . $dsn['options']; |
| | | } |
| | | if (!empty($dsn['tty'])) { |
| | | $params[0] .= ' tty=' . $dsn['tty']; |
| | | } |
| | | if (!empty($dsn['connect_timeout'])) { |
| | | $params[0] .= ' connect_timeout=' . $dsn['connect_timeout']; |
| | | } |
| | | if (!empty($dsn['sslmode'])) { |
| | | $params[0] .= ' sslmode=' . $dsn['sslmode']; |
| | | } |
| | | if (!empty($dsn['service'])) { |
| | | $params[0] .= ' service=' . $dsn['service']; |
| | | } |
| | | |
| | | if (isset($dsn['new_link']) |
| | | && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) |
| | | { |
| | | if (version_compare(phpversion(), '4.3.0', '>=')) { |
| | | $params[] = PGSQL_CONNECT_FORCE_NEW; |
| | | } |
| | | } |
| | | |
| | | $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; |
| | | |
| | | $ini = ini_get('track_errors'); |
| | | $php_errormsg = ''; |
| | | if ($ini) { |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | } else { |
| | | ini_set('track_errors', 1); |
| | | $this->connection = @call_user_func_array($connect_function, |
| | | $params); |
| | | ini_set('track_errors', $ini); |
| | | } |
| | | |
| | | if (!$this->connection) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @pg_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | if (!$this->autocommit && $ismanip) { |
| | | if ($this->transaction_opcount == 0) { |
| | | $result = @pg_exec($this->connection, 'begin;'); |
| | | if (!$result) { |
| | | return $this->pgsqlRaiseError(); |
| | | } |
| | | } |
| | | $this->transaction_opcount++; |
| | | } |
| | | $result = @pg_exec($this->connection, $query); |
| | | if (!$result) { |
| | | return $this->pgsqlRaiseError(); |
| | | } |
| | | // Determine which queries that should return data, and which |
| | | // should return an error code only. |
| | | if ($ismanip) { |
| | | $this->affected = @pg_affected_rows($result); |
| | | return DB_OK; |
| | | } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|SHOW)\s/si', $query)) { |
| | | /* PostgreSQL commands: |
| | | ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, |
| | | CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, |
| | | GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, |
| | | REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, |
| | | UNLISTEN, UPDATE, VACUUM |
| | | */ |
| | | $this->row[(int)$result] = 0; // reset the row counter. |
| | | $numrows = $this->numRows($result); |
| | | if (is_object($numrows)) { |
| | | return $numrows; |
| | | } |
| | | $this->_num_rows[(int)$result] = $numrows; |
| | | $this->affected = 0; |
| | | return $result; |
| | | } else { |
| | | $this->affected = 0; |
| | | return DB_OK; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal pgsql result pointer to the next available result |
| | | * |
| | | * @param a valid fbsql result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | $result_int = (int)$result; |
| | | $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int]; |
| | | if ($rownum >= $this->_num_rows[$result_int]) { |
| | | return null; |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @pg_fetch_row($result, $rownum); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | $this->row[$result_int] = ++$rownum; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | if (is_resource($result)) { |
| | | unset($this->row[(int)$result]); |
| | | unset($this->_num_rows[(int)$result]); |
| | | $this->affected = 0; |
| | | return @pg_freeresult($result); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quote() |
| | | |
| | | /** |
| | | * @deprecated Deprecated in release 1.6.0 |
| | | * @internal |
| | | */ |
| | | function quote($str) |
| | | { |
| | | return $this->quoteSmart($str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ quoteSmart() |
| | | |
| | | /** |
| | | * Formats input so it can be safely used in a query |
| | | * |
| | | * @param mixed $in the data to be formatted |
| | | * |
| | | * @return mixed the formatted data. The format depends on the input's |
| | | * PHP type: |
| | | * + null = the string <samp>NULL</samp> |
| | | * + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp> |
| | | * + integer or double = the unquoted number |
| | | * + other (including strings and numeric strings) = |
| | | * the data escaped according to MySQL's settings |
| | | * then encapsulated between single quotes |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function quoteSmart($in) |
| | | { |
| | | if (is_int($in) || is_double($in)) { |
| | | return $in; |
| | | } elseif (is_bool($in)) { |
| | | return $in ? 'TRUE' : 'FALSE'; |
| | | } elseif (is_null($in)) { |
| | | return 'NULL'; |
| | | } else { |
| | | return "'" . $this->escapeSimple($in) . "'"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ escapeSimple() |
| | | |
| | | /** |
| | | * Escapes a string according to the current DBMS's standards |
| | | * |
| | | * {@internal PostgreSQL treats a backslash as an escape character, |
| | | * so they are escaped as well. |
| | | * |
| | | * Not using pg_escape_string() yet because it requires PostgreSQL |
| | | * to be at version 7.2 or greater.}} |
| | | * |
| | | * @param string $str the string to be escaped |
| | | * |
| | | * @return string the escaped string |
| | | * |
| | | * @see DB_common::quoteSmart() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function escapeSimple($str) |
| | | { |
| | | return str_replace("'", "''", str_replace('\\', '\\\\', $str)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @pg_numfields($result); |
| | | if (!$cols) { |
| | | return $this->pgsqlRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @pg_numrows($result); |
| | | if ($rows === null) { |
| | | return $this->pgsqlRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | // XXX if $this->transaction_opcount > 0, we should probably |
| | | // issue a warning here. |
| | | $this->autocommit = $onoff ? true : false; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | // (disabled) hack to shut up error messages from libpq.a |
| | | //@fclose(@fopen("php://stderr", "w")); |
| | | $result = @pg_exec($this->connection, 'end;'); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->pgsqlRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | $result = @pg_exec($this->connection, 'abort;'); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->pgsqlRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | return $this->affected; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_pgsql::createSequence(), DB_pgsql::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $repeat = false; |
| | | do { |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result =& $this->query("SELECT NEXTVAL('${seqname}')"); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) { |
| | | $repeat = true; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->createSequence($seq_name); |
| | | $this->popErrorHandling(); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | } else { |
| | | $repeat = false; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | $result->free(); |
| | | return $arr[0]; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ createSequence() |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_pgsql::nextID(), DB_pgsql::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $result = $this->query("CREATE SEQUENCE ${seqname}"); |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_pgsql::nextID(), DB_pgsql::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP SEQUENCE ' |
| | | . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | return "$query LIMIT $count OFFSET $from"; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ pgsqlRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_pgsql::errorNative(), DB_pgsql::errorCode() |
| | | */ |
| | | function pgsqlRaiseError($errno = null) |
| | | { |
| | | $native = $this->errorNative(); |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode($native); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, $native); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error message produced by the last query |
| | | * |
| | | * {@internal Error messages are used instead of error codes |
| | | * in order to support older versions of PostgreSQL.}} |
| | | * |
| | | * @return string the DBMS' error message |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @pg_errormessage($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Determines PEAR::DB error code from the database's text error message. |
| | | * |
| | | * @param string $errormsg error message returned from the database |
| | | * @return integer an error number from a DB error constant |
| | | */ |
| | | function errorCode($errormsg) |
| | | { |
| | | static $error_regexps; |
| | | if (!isset($error_regexps)) { |
| | | $error_regexps = array( |
| | | '/(relation|sequence|table).*does not exist|class .* not found/i' |
| | | => DB_ERROR_NOSUCHTABLE, |
| | | '/index .* does not exist/' |
| | | => DB_ERROR_NOT_FOUND, |
| | | '/column .* does not exist/i' |
| | | => DB_ERROR_NOSUCHFIELD, |
| | | '/relation .* already exists/i' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/(divide|division) by zero$/i' |
| | | => DB_ERROR_DIVZERO, |
| | | '/pg_atoi: error in .*: can\'t parse /i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/invalid input syntax for( type)? (integer|numeric)/i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/value .* is out of range for type \w*int/i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/integer out of range/i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/value too long for type character/i' |
| | | => DB_ERROR_INVALID, |
| | | '/attribute .* not found|relation .* does not have attribute/i' |
| | | => DB_ERROR_NOSUCHFIELD, |
| | | '/column .* specified in USING clause does not exist in (left|right) table/i' |
| | | => DB_ERROR_NOSUCHFIELD, |
| | | '/parser: parse error at or near/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/syntax error at/' |
| | | => DB_ERROR_SYNTAX, |
| | | '/column reference .* is ambiguous/i' |
| | | => DB_ERROR_SYNTAX, |
| | | '/permission denied/' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/violates not-null constraint/' |
| | | => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '/violates [\w ]+ constraint/' |
| | | => DB_ERROR_CONSTRAINT, |
| | | '/referential integrity violation/' |
| | | => DB_ERROR_CONSTRAINT, |
| | | '/more expressions than target columns/i' |
| | | => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | ); |
| | | } |
| | | foreach ($error_regexps as $regexp => $code) { |
| | | if (preg_match($regexp, $errormsg)) { |
| | | return $code; |
| | | } |
| | | } |
| | | // Fall back to DB_ERROR if there was no mapping. |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * NOTE: only supports 'table' and 'flags' if <var>$result</var> |
| | | * is a table name. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0"); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @pg_numfields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $res[$i] = array( |
| | | 'table' => $got_string ? $case_func($result) : '', |
| | | 'name' => $case_func(@pg_fieldname($id, $i)), |
| | | 'type' => @pg_fieldtype($id, $i), |
| | | 'len' => @pg_fieldsize($id, $i), |
| | | 'flags' => $got_string |
| | | ? $this->_pgFieldFlags($id, $i, $result) |
| | | : '', |
| | | ); |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @pg_freeresult($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _pgFieldFlags() |
| | | |
| | | /** |
| | | * Get a column's flags |
| | | * |
| | | * Supports "not_null", "default_value", "primary_key", "unique_key" |
| | | * and "multiple_key". The default value is passed through |
| | | * rawurlencode() in case there are spaces in it. |
| | | * |
| | | * @param int $resource the PostgreSQL result identifier |
| | | * @param int $num_field the field number |
| | | * |
| | | * @return string the flags |
| | | * |
| | | * @access private |
| | | */ |
| | | function _pgFieldFlags($resource, $num_field, $table_name) |
| | | { |
| | | $field_name = @pg_fieldname($resource, $num_field); |
| | | |
| | | $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef |
| | | FROM pg_attribute f, pg_class tab, pg_type typ |
| | | WHERE tab.relname = typ.typname |
| | | AND typ.typrelid = f.attrelid |
| | | AND f.attname = '$field_name' |
| | | AND tab.relname = '$table_name'"); |
| | | if (@pg_numrows($result) > 0) { |
| | | $row = @pg_fetch_row($result, 0); |
| | | $flags = ($row[0] == 't') ? 'not_null ' : ''; |
| | | |
| | | if ($row[1] == 't') { |
| | | $result = @pg_exec($this->connection, "SELECT a.adsrc |
| | | FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a |
| | | WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid |
| | | AND f.attrelid = a.adrelid AND f.attname = '$field_name' |
| | | AND tab.relname = '$table_name' AND f.attnum = a.adnum"); |
| | | $row = @pg_fetch_row($result, 0); |
| | | $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]); |
| | | $flags .= 'default_' . rawurlencode($num) . ' '; |
| | | } |
| | | } else { |
| | | $flags = ''; |
| | | } |
| | | $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey |
| | | FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i |
| | | WHERE tab.relname = typ.typname |
| | | AND typ.typrelid = f.attrelid |
| | | AND f.attrelid = i.indrelid |
| | | AND f.attname = '$field_name' |
| | | AND tab.relname = '$table_name'"); |
| | | $count = @pg_numrows($result); |
| | | |
| | | for ($i = 0; $i < $count ; $i++) { |
| | | $row = @pg_fetch_row($result, $i); |
| | | $keys = explode(' ', $row[2]); |
| | | |
| | | if (in_array($num_field + 1, $keys)) { |
| | | $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : ''; |
| | | $flags .= ($row[1] == 't') ? 'primary_key ' : ''; |
| | | if (count($keys) > 1) |
| | | $flags .= 'multiple_key '; |
| | | } |
| | | } |
| | | |
| | | return trim($flags); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return 'SELECT c.relname AS "Name"' |
| | | . ' FROM pg_class c, pg_user u' |
| | | . ' WHERE c.relowner = u.usesysid' |
| | | . " AND c.relkind = 'r'" |
| | | . ' AND NOT EXISTS' |
| | | . ' (SELECT 1 FROM pg_views' |
| | | . ' WHERE viewname = c.relname)' |
| | | . " AND c.relname !~ '^(pg_|sql_)'" |
| | | . ' UNION' |
| | | . ' SELECT c.relname AS "Name"' |
| | | . ' FROM pg_class c' |
| | | . " WHERE c.relkind = 'r'" |
| | | . ' AND NOT EXISTS' |
| | | . ' (SELECT 1 FROM pg_views' |
| | | . ' WHERE viewname = c.relname)' |
| | | . ' AND NOT EXISTS' |
| | | . ' (SELECT 1 FROM pg_user' |
| | | . ' WHERE usesysid = c.relowner)' |
| | | . " AND c.relname !~ '^pg_'"; |
| | | case 'schema.tables': |
| | | return "SELECT schemaname || '.' || tablename" |
| | | . ' AS "Name"' |
| | | . ' FROM pg_catalog.pg_tables' |
| | | . ' WHERE schemaname NOT IN' |
| | | . " ('pg_catalog', 'information_schema', 'pg_toast')"; |
| | | case 'views': |
| | | // Table cols: viewname | viewowner | definition |
| | | return 'SELECT viewname from pg_views WHERE schemaname' |
| | | . " NOT IN ('information_schema', 'pg_catalog')"; |
| | | case 'users': |
| | | // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil |
| | | return 'SELECT usename FROM pg_user'; |
| | | case 'databases': |
| | | return 'SELECT datname FROM pg_database'; |
| | | case 'functions': |
| | | case 'procedures': |
| | | return 'SELECT proname FROM pg_proc WHERE proowner <> 1'; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's sqlite extension |
| | | * for interacting with SQLite databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Urs Gehrig <urs@circle.ch> |
| | | * @author Mika Tuupola <tuupola@appelsiini.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's sqlite extension |
| | | * for interacting with SQLite databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * NOTICE: This driver needs PHP's track_errors ini setting to be on. |
| | | * It is automatically turned on when connecting to the database. |
| | | * Make sure your scripts don't turn it off. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Urs Gehrig <urs@circle.ch> |
| | | * @author Mika Tuupola <tuupola@appelsiini.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_sqlite extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'sqlite'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'sqlite'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'alter', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => false, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * |
| | | * {@internal Error codes according to sqlite_exec. See the online |
| | | * manual at http://sqlite.org/c_interface.html for info. |
| | | * This error handling based on sqlite_exec is not yet implemented.}} |
| | | * |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * SQLite data types |
| | | * |
| | | * @link http://www.sqlite.org/datatypes.html |
| | | * |
| | | * @var array |
| | | */ |
| | | var $keywords = array ( |
| | | 'BLOB' => '', |
| | | 'BOOLEAN' => '', |
| | | 'CHARACTER' => '', |
| | | 'CLOB' => '', |
| | | 'FLOAT' => '', |
| | | 'INTEGER' => '', |
| | | 'KEY' => '', |
| | | 'NATIONAL' => '', |
| | | 'NUMERIC' => '', |
| | | 'NVARCHAR' => '', |
| | | 'PRIMARY' => '', |
| | | 'TEXT' => '', |
| | | 'TIMESTAMP' => '', |
| | | 'UNIQUE' => '', |
| | | 'VARCHAR' => '', |
| | | 'VARYING' => '', |
| | | ); |
| | | |
| | | /** |
| | | * The most recent error message from $php_errormsg |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_lasterror = ''; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_sqlite() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's sqlite driver supports the following extra DSN options: |
| | | * + mode The permissions for the database file, in four digit |
| | | * chmod octal format (eg "0600"). |
| | | * |
| | | * Example of connecting to a database in read-only mode: |
| | | * <code> |
| | | * require_once 'DB.php'; |
| | | * |
| | | * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400'; |
| | | * $options = array( |
| | | * 'portability' => DB_PORTABILITY_ALL, |
| | | * ); |
| | | * |
| | | * $db =& DB::connect($dsn, $options); |
| | | * if (PEAR::isError($db)) { |
| | | * die($db->getMessage()); |
| | | * } |
| | | * </code> |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('sqlite')) { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | if ($dsn['database']) { |
| | | if (!file_exists($dsn['database'])) { |
| | | if (!touch($dsn['database'])) { |
| | | return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); |
| | | } |
| | | if (!isset($dsn['mode']) || |
| | | !is_numeric($dsn['mode'])) |
| | | { |
| | | $mode = 0644; |
| | | } else { |
| | | $mode = octdec($dsn['mode']); |
| | | } |
| | | if (!chmod($dsn['database'], $mode)) { |
| | | return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); |
| | | } |
| | | if (!file_exists($dsn['database'])) { |
| | | return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); |
| | | } |
| | | } |
| | | if (!is_file($dsn['database'])) { |
| | | return $this->sqliteRaiseError(DB_ERROR_INVALID); |
| | | } |
| | | if (!is_readable($dsn['database'])) { |
| | | return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); |
| | | } |
| | | } else { |
| | | return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); |
| | | } |
| | | |
| | | $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; |
| | | |
| | | // track_errors must remain on for simpleQuery() |
| | | ini_set('track_errors', 1); |
| | | $php_errormsg = ''; |
| | | |
| | | if (!$this->connection = @$connect_function($dsn['database'])) { |
| | | return $this->raiseError(DB_ERROR_NODBSELECTED, |
| | | null, null, null, |
| | | $php_errormsg); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @sqlite_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * NOTICE: This method needs PHP's track_errors ini setting to be on. |
| | | * It is automatically turned on when connecting to the database. |
| | | * Make sure your scripts don't turn it off. |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | $query = $this->modifyQuery($query); |
| | | |
| | | $php_errormsg = ''; |
| | | |
| | | $result = @sqlite_query($query, $this->connection); |
| | | $this->_lasterror = $php_errormsg ? $php_errormsg : ''; |
| | | |
| | | $this->result = $result; |
| | | if (!$this->result) { |
| | | return $this->sqliteRaiseError(null); |
| | | } |
| | | |
| | | // sqlite_query() seems to allways return a resource |
| | | // so cant use that. Using $ismanip instead |
| | | if (!$ismanip) { |
| | | $numRows = $this->numRows($result); |
| | | if (is_object($numRows)) { |
| | | // we've got PEAR_Error |
| | | return $numRows; |
| | | } |
| | | return $result; |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal sqlite result pointer to the next available result |
| | | * |
| | | * @param resource $result the valid sqlite result resource |
| | | * |
| | | * @return bool true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@sqlite_seek($this->result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | $arr = @sqlite_fetch_array($result, SQLITE_ASSOC); |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @sqlite_fetch_array($result, SQLITE_NUM); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | /* |
| | | * Even though this DBMS already trims output, we do this because |
| | | * a field might have intentional whitespace at the end that |
| | | * gets removed by DB_PORTABILITY_RTRIM under another driver. |
| | | */ |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult(&$result) |
| | | { |
| | | // XXX No native free? |
| | | if (!is_resource($result)) { |
| | | return false; |
| | | } |
| | | $result = null; |
| | | return true; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @sqlite_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->sqliteRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @sqlite_num_rows($result); |
| | | if ($rows === null) { |
| | | return $this->sqliteRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affected() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | return @sqlite_changes($this->connection); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_sqlite::nextID(), DB_sqlite::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_sqlite::nextID(), DB_sqlite::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | $query = 'CREATE TABLE ' . $seqname . |
| | | ' (id INTEGER UNSIGNED PRIMARY KEY) '; |
| | | $result = $this->query($query); |
| | | if (DB::isError($result)) { |
| | | return($result); |
| | | } |
| | | $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname |
| | | BEGIN |
| | | DELETE FROM $seqname WHERE id<LAST_INSERT_ROWID(); |
| | | END "; |
| | | $result = $this->query($query); |
| | | if (DB::isError($result)) { |
| | | return($result); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_sqlite::createSequence(), DB_sqlite::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | |
| | | do { |
| | | $repeat = 0; |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)"); |
| | | $this->popErrorHandling(); |
| | | if ($result === DB_OK) { |
| | | $id = @sqlite_last_insert_rowid($this->connection); |
| | | if ($id != 0) { |
| | | return $id; |
| | | } |
| | | } elseif ($ondemand && DB::isError($result) && |
| | | $result->getCode() == DB_ERROR_NOSUCHTABLE) |
| | | { |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } else { |
| | | $repeat = 1; |
| | | } |
| | | } |
| | | } while ($repeat); |
| | | |
| | | return $this->raiseError($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getDbFileStats() |
| | | |
| | | /** |
| | | * Get the file stats for the current database |
| | | * |
| | | * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, |
| | | * atime, mtime, ctime, blksize, blocks or a numeric key between |
| | | * 0 and 12. |
| | | * |
| | | * @param string $arg the array key for stats() |
| | | * |
| | | * @return mixed an array on an unspecified key, integer on a passed |
| | | * arg and false at a stats error |
| | | */ |
| | | function getDbFileStats($arg = '') |
| | | { |
| | | $stats = stat($this->dsn['database']); |
| | | if ($stats == false) { |
| | | return false; |
| | | } |
| | | if (is_array($stats)) { |
| | | if (is_numeric($arg)) { |
| | | if (((int)$arg <= 12) & ((int)$arg >= 0)) { |
| | | return false; |
| | | } |
| | | return $stats[$arg ]; |
| | | } |
| | | if (array_key_exists(trim($arg), $stats)) { |
| | | return $stats[$arg ]; |
| | | } |
| | | } |
| | | return $stats; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ escapeSimple() |
| | | |
| | | /** |
| | | * Escapes a string according to the current DBMS's standards |
| | | * |
| | | * In SQLite, this makes things safe for inserts/updates, but may |
| | | * cause problems when performing text comparisons against columns |
| | | * containing binary data. See the |
| | | * {@link http://php.net/sqlite_escape_string PHP manual} for more info. |
| | | * |
| | | * @param string $str the string to be escaped |
| | | * |
| | | * @return string the escaped string |
| | | * |
| | | * @since Method available since Release 1.6.1 |
| | | * @see DB_common::escapeSimple() |
| | | */ |
| | | function escapeSimple($str) |
| | | { |
| | | return @sqlite_escape_string($str); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyLimitQuery() |
| | | |
| | | /** |
| | | * Adds LIMIT clauses to a query string according to current DBMS standards |
| | | * |
| | | * @param string $query the query to modify |
| | | * @param int $from the row to start to fetching (0 = the first row) |
| | | * @param int $count the numbers of rows to fetch |
| | | * @param mixed $params array, string or numeric data to be used in |
| | | * execution of the statement. Quantity of items |
| | | * passed must match quantity of placeholders in |
| | | * query: meaning 1 placeholder for non-array |
| | | * parameters or 1 placeholder per array element. |
| | | * |
| | | * @return string the query string with LIMIT clauses added |
| | | * |
| | | * @access protected |
| | | */ |
| | | function modifyLimitQuery($query, $from, $count, $params = array()) |
| | | { |
| | | return "$query LIMIT $count OFFSET $from"; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ modifyQuery() |
| | | |
| | | /** |
| | | * Changes a query string for various DBMS specific reasons |
| | | * |
| | | * This little hack lets you know how many rows were deleted |
| | | * when running a "DELETE FROM table" query. Only implemented |
| | | * if the DB_PORTABILITY_DELETE_COUNT portability option is on. |
| | | * |
| | | * @param string $query the query string to modify |
| | | * |
| | | * @return string the modified query string |
| | | * |
| | | * @access protected |
| | | * @see DB_common::setOption() |
| | | */ |
| | | function modifyQuery($query) |
| | | { |
| | | if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { |
| | | if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { |
| | | $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', |
| | | 'DELETE FROM \1 WHERE 1=1', $query); |
| | | } |
| | | } |
| | | return $query; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ sqliteRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_sqlite::errorNative(), DB_sqlite::errorCode() |
| | | */ |
| | | function sqliteRaiseError($errno = null) |
| | | { |
| | | $native = $this->errorNative(); |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode($native); |
| | | } |
| | | |
| | | $errorcode = @sqlite_last_error($this->connection); |
| | | $userinfo = "$errorcode ** $this->last_query"; |
| | | |
| | | return $this->raiseError($errno, null, null, $userinfo, $native); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error message produced by the last query |
| | | * |
| | | * {@internal This is used to retrieve more meaningfull error messages |
| | | * because sqlite_last_error() does not provide adequate info.}} |
| | | * |
| | | * @return string the DBMS' error message |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return $this->_lasterror; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Determines PEAR::DB error code from the database's text error message |
| | | * |
| | | * @param string $errormsg the error message returned from the database |
| | | * |
| | | * @return integer the DB error number |
| | | */ |
| | | function errorCode($errormsg) |
| | | { |
| | | static $error_regexps; |
| | | if (!isset($error_regexps)) { |
| | | $error_regexps = array( |
| | | '/^no such table:/' => DB_ERROR_NOSUCHTABLE, |
| | | '/^no such index:/' => DB_ERROR_NOT_FOUND, |
| | | '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS, |
| | | '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, |
| | | '/is not unique/' => DB_ERROR_CONSTRAINT, |
| | | '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT, |
| | | '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, |
| | | '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '/^no such column:/' => DB_ERROR_NOSUCHFIELD, |
| | | '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD, |
| | | '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX, |
| | | '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | ); |
| | | } |
| | | foreach ($error_regexps as $regexp => $code) { |
| | | if (preg_match($regexp, $errormsg)) { |
| | | return $code; |
| | | } |
| | | } |
| | | // Fall back to DB_ERROR if there was no mapping. |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table |
| | | * |
| | | * @param string $result a string containing the name of a table |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | * @since Method available since Release 1.7.0 |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | $id = @sqlite_array_query($this->connection, |
| | | "PRAGMA table_info('$result');", |
| | | SQLITE_ASSOC); |
| | | $got_string = true; |
| | | } else { |
| | | $this->last_query = ''; |
| | | return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null, |
| | | 'This DBMS can not obtain tableInfo' . |
| | | ' from result sets'); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = count($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | if (strpos($id[$i]['type'], '(') !== false) { |
| | | $bits = explode('(', $id[$i]['type']); |
| | | $type = $bits[0]; |
| | | $len = rtrim($bits[1],')'); |
| | | } else { |
| | | $type = $id[$i]['type']; |
| | | $len = 0; |
| | | } |
| | | |
| | | $flags = ''; |
| | | if ($id[$i]['pk']) { |
| | | $flags .= 'primary_key '; |
| | | } |
| | | if ($id[$i]['notnull']) { |
| | | $flags .= 'not_null '; |
| | | } |
| | | if ($id[$i]['dflt_value'] !== null) { |
| | | $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']); |
| | | } |
| | | $flags = trim($flags); |
| | | |
| | | $res[$i] = array( |
| | | 'table' => $case_func($result), |
| | | 'name' => $case_func($id[$i]['name']), |
| | | 'type' => $type, |
| | | 'len' => $len, |
| | | 'flags' => $flags, |
| | | ); |
| | | |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * @param array $args SQLITE DRIVER ONLY: a private array of arguments |
| | | * used by the getSpecialQuery(). Do not use |
| | | * this directly. |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type, $args = array()) |
| | | { |
| | | if (!is_array($args)) { |
| | | return $this->raiseError('no key specified', null, null, null, |
| | | 'Argument has to be an array.'); |
| | | } |
| | | |
| | | switch ($type) { |
| | | case 'master': |
| | | return 'SELECT * FROM sqlite_master;'; |
| | | case 'tables': |
| | | return "SELECT name FROM sqlite_master WHERE type='table' " |
| | | . 'UNION ALL SELECT name FROM sqlite_temp_master ' |
| | | . "WHERE type='table' ORDER BY name;"; |
| | | case 'schema': |
| | | return 'SELECT sql FROM (SELECT * FROM sqlite_master ' |
| | | . 'UNION ALL SELECT * FROM sqlite_temp_master) ' |
| | | . "WHERE type!='meta' " |
| | | . 'ORDER BY tbl_name, type DESC, name;'; |
| | | case 'schemax': |
| | | case 'schema_x': |
| | | /* |
| | | * Use like: |
| | | * $res = $db->query($db->getSpecialQuery('schema_x', |
| | | * array('table' => 'table3'))); |
| | | */ |
| | | return 'SELECT sql FROM (SELECT * FROM sqlite_master ' |
| | | . 'UNION ALL SELECT * FROM sqlite_temp_master) ' |
| | | . "WHERE tbl_name LIKE '{$args['table']}' " |
| | | . "AND type!='meta' " |
| | | . 'ORDER BY type DESC, name;'; |
| | | case 'alter': |
| | | /* |
| | | * SQLite does not support ALTER TABLE; this is a helper query |
| | | * to handle this. 'table' represents the table name, 'rows' |
| | | * the news rows to create, 'save' the row(s) to keep _with_ |
| | | * the data. |
| | | * |
| | | * Use like: |
| | | * $args = array( |
| | | * 'table' => $table, |
| | | * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", |
| | | * 'save' => "NULL, titel, content, datetime" |
| | | * ); |
| | | * $res = $db->query( $db->getSpecialQuery('alter', $args)); |
| | | */ |
| | | $rows = strtr($args['rows'], $this->keywords); |
| | | |
| | | $q = array( |
| | | 'BEGIN TRANSACTION', |
| | | "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", |
| | | "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", |
| | | "DROP TABLE {$args['table']}", |
| | | "CREATE TABLE {$args['table']} ({$args['rows']})", |
| | | "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", |
| | | "DROP TABLE {$args['table']}_backup", |
| | | 'COMMIT', |
| | | ); |
| | | |
| | | /* |
| | | * This is a dirty hack, since the above query will not get |
| | | * executed with a single query call so here the query method |
| | | * will be called directly and return a select instead. |
| | | */ |
| | | foreach ($q as $query) { |
| | | $this->query($query); |
| | | } |
| | | return "SELECT * FROM {$args['table']};"; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * Provides an object interface to a table row |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <stig@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB class so it can be extended from |
| | | */ |
| | | require_once 'DB.php'; |
| | | |
| | | /** |
| | | * Provides an object interface to a table row |
| | | * |
| | | * It lets you add, delete and change rows using objects rather than SQL |
| | | * statements. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Stig Bakken <stig@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_storage extends PEAR |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** the name of the table (or view, if the backend database supports |
| | | updates in views) we hold data from */ |
| | | var $_table = null; |
| | | |
| | | /** which column(s) in the table contains primary keys, can be a |
| | | string for single-column primary keys, or an array of strings |
| | | for multiple-column primary keys */ |
| | | var $_keycolumn = null; |
| | | |
| | | /** DB connection handle used for all transactions */ |
| | | var $_dbh = null; |
| | | |
| | | /** an assoc with the names of database fields stored as properties |
| | | in this object */ |
| | | var $_properties = array(); |
| | | |
| | | /** an assoc with the names of the properties in this object that |
| | | have been changed since they were fetched from the database */ |
| | | var $_changes = array(); |
| | | |
| | | /** flag that decides if data in this object can be changed. |
| | | objects that don't have their table's key column in their |
| | | property lists will be flagged as read-only. */ |
| | | var $_readonly = false; |
| | | |
| | | /** function or method that implements a validator for fields that |
| | | are set, this validator function returns true if the field is |
| | | valid, false if not */ |
| | | var $_validator = null; |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * Constructor |
| | | * |
| | | * @param $table string the name of the database table |
| | | * |
| | | * @param $keycolumn mixed string with name of key column, or array of |
| | | * strings if the table has a primary key of more than one column |
| | | * |
| | | * @param $dbh object database connection object |
| | | * |
| | | * @param $validator mixed function or method used to validate |
| | | * each new value, called with three parameters: the name of the |
| | | * field/column that is changing, a reference to the new value and |
| | | * a reference to this object |
| | | * |
| | | */ |
| | | function DB_storage($table, $keycolumn, &$dbh, $validator = null) |
| | | { |
| | | $this->PEAR('DB_Error'); |
| | | $this->_table = $table; |
| | | $this->_keycolumn = $keycolumn; |
| | | $this->_dbh = $dbh; |
| | | $this->_readonly = false; |
| | | $this->_validator = $validator; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _makeWhere() |
| | | |
| | | /** |
| | | * Utility method to build a "WHERE" clause to locate ourselves in |
| | | * the table. |
| | | * |
| | | * XXX future improvement: use rowids? |
| | | * |
| | | * @access private |
| | | */ |
| | | function _makeWhere($keyval = null) |
| | | { |
| | | if (is_array($this->_keycolumn)) { |
| | | if ($keyval === null) { |
| | | for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { |
| | | $keyval[] = $this->{$this->_keycolumn[$i]}; |
| | | } |
| | | } |
| | | $whereclause = ''; |
| | | for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { |
| | | if ($i > 0) { |
| | | $whereclause .= ' AND '; |
| | | } |
| | | $whereclause .= $this->_keycolumn[$i]; |
| | | if (is_null($keyval[$i])) { |
| | | // there's not much point in having a NULL key, |
| | | // but we support it anyway |
| | | $whereclause .= ' IS NULL'; |
| | | } else { |
| | | $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]); |
| | | } |
| | | } |
| | | } else { |
| | | if ($keyval === null) { |
| | | $keyval = @$this->{$this->_keycolumn}; |
| | | } |
| | | $whereclause = $this->_keycolumn; |
| | | if (is_null($keyval)) { |
| | | // there's not much point in having a NULL key, |
| | | // but we support it anyway |
| | | $whereclause .= ' IS NULL'; |
| | | } else { |
| | | $whereclause .= ' = ' . $this->_dbh->quote($keyval); |
| | | } |
| | | } |
| | | return $whereclause; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ setup() |
| | | |
| | | /** |
| | | * Method used to initialize a DB_storage object from the |
| | | * configured table. |
| | | * |
| | | * @param $keyval mixed the key[s] of the row to fetch (string or array) |
| | | * |
| | | * @return int DB_OK on success, a DB error if not |
| | | */ |
| | | function setup($keyval) |
| | | { |
| | | $whereclause = $this->_makeWhere($keyval); |
| | | $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause; |
| | | $sth = $this->_dbh->query($query); |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | $row = $sth->fetchRow(DB_FETCHMODE_ASSOC); |
| | | if (DB::isError($row)) { |
| | | return $row; |
| | | } |
| | | if (!$row) { |
| | | return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null, |
| | | $query, null, true); |
| | | } |
| | | foreach ($row as $key => $value) { |
| | | $this->_properties[$key] = true; |
| | | $this->$key = $value; |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ insert() |
| | | |
| | | /** |
| | | * Create a new (empty) row in the configured table for this |
| | | * object. |
| | | */ |
| | | function insert($newpk) |
| | | { |
| | | if (is_array($this->_keycolumn)) { |
| | | $primarykey = $this->_keycolumn; |
| | | } else { |
| | | $primarykey = array($this->_keycolumn); |
| | | } |
| | | settype($newpk, "array"); |
| | | for ($i = 0; $i < sizeof($primarykey); $i++) { |
| | | $pkvals[] = $this->_dbh->quote($newpk[$i]); |
| | | } |
| | | |
| | | $sth = $this->_dbh->query("INSERT INTO $this->_table (" . |
| | | implode(",", $primarykey) . ") VALUES(" . |
| | | implode(",", $pkvals) . ")"); |
| | | if (DB::isError($sth)) { |
| | | return $sth; |
| | | } |
| | | if (sizeof($newpk) == 1) { |
| | | $newpk = $newpk[0]; |
| | | } |
| | | $this->setup($newpk); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ toString() |
| | | |
| | | /** |
| | | * Output a simple description of this DB_storage object. |
| | | * @return string object description |
| | | */ |
| | | function toString() |
| | | { |
| | | $info = strtolower(get_class($this)); |
| | | $info .= " (table="; |
| | | $info .= $this->_table; |
| | | $info .= ", keycolumn="; |
| | | if (is_array($this->_keycolumn)) { |
| | | $info .= "(" . implode(",", $this->_keycolumn) . ")"; |
| | | } else { |
| | | $info .= $this->_keycolumn; |
| | | } |
| | | $info .= ", dbh="; |
| | | if (is_object($this->_dbh)) { |
| | | $info .= $this->_dbh->toString(); |
| | | } else { |
| | | $info .= "null"; |
| | | } |
| | | $info .= ")"; |
| | | if (sizeof($this->_properties)) { |
| | | $info .= " [loaded, key="; |
| | | $keyname = $this->_keycolumn; |
| | | if (is_array($keyname)) { |
| | | $info .= "("; |
| | | for ($i = 0; $i < sizeof($keyname); $i++) { |
| | | if ($i > 0) { |
| | | $info .= ","; |
| | | } |
| | | $info .= $this->$keyname[$i]; |
| | | } |
| | | $info .= ")"; |
| | | } else { |
| | | $info .= $this->$keyname; |
| | | } |
| | | $info .= "]"; |
| | | } |
| | | if (sizeof($this->_changes)) { |
| | | $info .= " [modified]"; |
| | | } |
| | | return $info; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dump() |
| | | |
| | | /** |
| | | * Dump the contents of this object to "standard output". |
| | | */ |
| | | function dump() |
| | | { |
| | | foreach ($this->_properties as $prop => $foo) { |
| | | print "$prop = "; |
| | | print htmlentities($this->$prop); |
| | | print "<br />\n"; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ &create() |
| | | |
| | | /** |
| | | * Static method used to create new DB storage objects. |
| | | * @param $data assoc. array where the keys are the names |
| | | * of properties/columns |
| | | * @return object a new instance of DB_storage or a subclass of it |
| | | */ |
| | | function &create($table, &$data) |
| | | { |
| | | $classname = strtolower(get_class($this)); |
| | | $obj =& new $classname($table); |
| | | foreach ($data as $name => $value) { |
| | | $obj->_properties[$name] = true; |
| | | $obj->$name = &$value; |
| | | } |
| | | return $obj; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ loadFromQuery() |
| | | |
| | | /** |
| | | * Loads data into this object from the given query. If this |
| | | * object already contains table data, changes will be saved and |
| | | * the object re-initialized first. |
| | | * |
| | | * @param $query SQL query |
| | | * |
| | | * @param $params parameter list in case you want to use |
| | | * prepare/execute mode |
| | | * |
| | | * @return int DB_OK on success, DB_WARNING_READ_ONLY if the |
| | | * returned object is read-only (because the object's specified |
| | | * key column was not found among the columns returned by $query), |
| | | * or another DB error code in case of errors. |
| | | */ |
| | | // XXX commented out for now |
| | | /* |
| | | function loadFromQuery($query, $params = null) |
| | | { |
| | | if (sizeof($this->_properties)) { |
| | | if (sizeof($this->_changes)) { |
| | | $this->store(); |
| | | $this->_changes = array(); |
| | | } |
| | | $this->_properties = array(); |
| | | } |
| | | $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params); |
| | | if (DB::isError($rowdata)) { |
| | | return $rowdata; |
| | | } |
| | | reset($rowdata); |
| | | $found_keycolumn = false; |
| | | while (list($key, $value) = each($rowdata)) { |
| | | if ($key == $this->_keycolumn) { |
| | | $found_keycolumn = true; |
| | | } |
| | | $this->_properties[$key] = true; |
| | | $this->$key = &$value; |
| | | unset($value); // have to unset, or all properties will |
| | | // refer to the same value |
| | | } |
| | | if (!$found_keycolumn) { |
| | | $this->_readonly = true; |
| | | return DB_WARNING_READ_ONLY; |
| | | } |
| | | return DB_OK; |
| | | } |
| | | */ |
| | | |
| | | // }}} |
| | | // {{{ set() |
| | | |
| | | /** |
| | | * Modify an attriute value. |
| | | */ |
| | | function set($property, $newvalue) |
| | | { |
| | | // only change if $property is known and object is not |
| | | // read-only |
| | | if ($this->_readonly) { |
| | | return $this->raiseError(null, DB_WARNING_READ_ONLY, null, |
| | | null, null, null, true); |
| | | } |
| | | if (@isset($this->_properties[$property])) { |
| | | if (empty($this->_validator)) { |
| | | $valid = true; |
| | | } else { |
| | | $valid = @call_user_func($this->_validator, |
| | | $this->_table, |
| | | $property, |
| | | $newvalue, |
| | | $this->$property, |
| | | $this); |
| | | } |
| | | if ($valid) { |
| | | $this->$property = $newvalue; |
| | | if (empty($this->_changes[$property])) { |
| | | $this->_changes[$property] = 0; |
| | | } else { |
| | | $this->_changes[$property]++; |
| | | } |
| | | } else { |
| | | return $this->raiseError(null, DB_ERROR_INVALID, null, |
| | | null, "invalid field: $property", |
| | | null, true); |
| | | } |
| | | return true; |
| | | } |
| | | return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null, |
| | | null, "unknown field: $property", |
| | | null, true); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ &get() |
| | | |
| | | /** |
| | | * Fetch an attribute value. |
| | | * |
| | | * @param string attribute name |
| | | * |
| | | * @return attribute contents, or null if the attribute name is |
| | | * unknown |
| | | */ |
| | | function &get($property) |
| | | { |
| | | // only return if $property is known |
| | | if (isset($this->_properties[$property])) { |
| | | return $this->$property; |
| | | } |
| | | $tmp = null; |
| | | return $tmp; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _DB_storage() |
| | | |
| | | /** |
| | | * Destructor, calls DB_storage::store() if there are changes |
| | | * that are to be kept. |
| | | */ |
| | | function _DB_storage() |
| | | { |
| | | if (sizeof($this->_changes)) { |
| | | $this->store(); |
| | | } |
| | | $this->_properties = array(); |
| | | $this->_changes = array(); |
| | | $this->_table = null; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ store() |
| | | |
| | | /** |
| | | * Stores changes to this object in the database. |
| | | * |
| | | * @return DB_OK or a DB error |
| | | */ |
| | | function store() |
| | | { |
| | | foreach ($this->_changes as $name => $foo) { |
| | | $params[] = &$this->$name; |
| | | $vars[] = $name . ' = ?'; |
| | | } |
| | | if ($vars) { |
| | | $query = 'UPDATE ' . $this->_table . ' SET ' . |
| | | implode(', ', $vars) . ' WHERE ' . |
| | | $this->_makeWhere(); |
| | | $stmt = $this->_dbh->prepare($query); |
| | | $res = $this->_dbh->execute($stmt, $params); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | $this->_changes = array(); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ remove() |
| | | |
| | | /** |
| | | * Remove the row represented by this object from the database. |
| | | * |
| | | * @return mixed DB_OK or a DB error |
| | | */ |
| | | function remove() |
| | | { |
| | | if ($this->_readonly) { |
| | | return $this->raiseError(null, DB_WARNING_READ_ONLY, null, |
| | | null, null, null, true); |
| | | } |
| | | $query = 'DELETE FROM ' . $this->_table .' WHERE '. |
| | | $this->_makeWhere(); |
| | | $res = $this->_dbh->query($query); |
| | | if (DB::isError($res)) { |
| | | return $res; |
| | | } |
| | | foreach ($this->_properties as $prop => $foo) { |
| | | unset($this->$prop); |
| | | } |
| | | $this->_properties = array(); |
| | | $this->_changes = array(); |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |
New file |
| | |
| | | <?php |
| | | |
| | | /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ |
| | | |
| | | /** |
| | | * The PEAR DB driver for PHP's sybase extension |
| | | * for interacting with Sybase databases |
| | | * |
| | | * PHP versions 4 and 5 |
| | | * |
| | | * LICENSE: This source file is subject to version 3.0 of the PHP license |
| | | * that is available through the world-wide-web at the following URI: |
| | | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
| | | * the PHP License and are unable to obtain it through the web, please |
| | | * send a note to license@php.net so we can mail you a copy immediately. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Sterling Hughes <sterling@php.net> |
| | | * @author Antônio Carlos Venâncio Júnior <floripa@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version CVS: $Id$ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | |
| | | /** |
| | | * Obtain the DB_common class so it can be extended from |
| | | */ |
| | | require_once 'DB/common.php'; |
| | | |
| | | /** |
| | | * The methods PEAR DB uses to interact with PHP's sybase extension |
| | | * for interacting with Sybase databases |
| | | * |
| | | * These methods overload the ones declared in DB_common. |
| | | * |
| | | * WARNING: This driver may fail with multiple connections under the |
| | | * same user/pass/host and different databases. |
| | | * |
| | | * @category Database |
| | | * @package DB |
| | | * @author Sterling Hughes <sterling@php.net> |
| | | * @author Antônio Carlos Venâncio Júnior <floripa@php.net> |
| | | * @author Daniel Convissor <danielc@php.net> |
| | | * @copyright 1997-2005 The PHP Group |
| | | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
| | | * @version Release: @package_version@ |
| | | * @link http://pear.php.net/package/DB |
| | | */ |
| | | class DB_sybase extends DB_common |
| | | { |
| | | // {{{ properties |
| | | |
| | | /** |
| | | * The DB driver type (mysql, oci8, odbc, etc.) |
| | | * @var string |
| | | */ |
| | | var $phptype = 'sybase'; |
| | | |
| | | /** |
| | | * The database syntax variant to be used (db2, access, etc.), if any |
| | | * @var string |
| | | */ |
| | | var $dbsyntax = 'sybase'; |
| | | |
| | | /** |
| | | * The capabilities of this DB implementation |
| | | * |
| | | * The 'new_link' element contains the PHP version that first provided |
| | | * new_link support for this DBMS. Contains false if it's unsupported. |
| | | * |
| | | * Meaning of the 'limit' element: |
| | | * + 'emulate' = emulate with fetch row by number |
| | | * + 'alter' = alter the query |
| | | * + false = skip rows |
| | | * |
| | | * @var array |
| | | */ |
| | | var $features = array( |
| | | 'limit' => 'emulate', |
| | | 'new_link' => false, |
| | | 'numrows' => true, |
| | | 'pconnect' => true, |
| | | 'prepare' => false, |
| | | 'ssl' => false, |
| | | 'transactions' => true, |
| | | ); |
| | | |
| | | /** |
| | | * A mapping of native error codes to DB error codes |
| | | * @var array |
| | | */ |
| | | var $errorcode_map = array( |
| | | ); |
| | | |
| | | /** |
| | | * The raw database connection created by PHP |
| | | * @var resource |
| | | */ |
| | | var $connection; |
| | | |
| | | /** |
| | | * The DSN information for connecting to a database |
| | | * @var array |
| | | */ |
| | | var $dsn = array(); |
| | | |
| | | |
| | | /** |
| | | * Should data manipulation queries be committed automatically? |
| | | * @var bool |
| | | * @access private |
| | | */ |
| | | var $autocommit = true; |
| | | |
| | | /** |
| | | * The quantity of transactions begun |
| | | * |
| | | * {@internal While this is private, it can't actually be designated |
| | | * private in PHP 5 because it is directly accessed in the test suite.}} |
| | | * |
| | | * @var integer |
| | | * @access private |
| | | */ |
| | | var $transaction_opcount = 0; |
| | | |
| | | /** |
| | | * The database specified in the DSN |
| | | * |
| | | * It's a fix to allow calls to different databases in the same script. |
| | | * |
| | | * @var string |
| | | * @access private |
| | | */ |
| | | var $_db = ''; |
| | | |
| | | |
| | | // }}} |
| | | // {{{ constructor |
| | | |
| | | /** |
| | | * This constructor calls <kbd>$this->DB_common()</kbd> |
| | | * |
| | | * @return void |
| | | */ |
| | | function DB_sybase() |
| | | { |
| | | $this->DB_common(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ connect() |
| | | |
| | | /** |
| | | * Connect to the database server, log in and open the database |
| | | * |
| | | * Don't call this method directly. Use DB::connect() instead. |
| | | * |
| | | * PEAR DB's sybase driver supports the following extra DSN options: |
| | | * + appname The application name to use on this connection. |
| | | * Available since PEAR DB 1.7.0. |
| | | * + charset The character set to use on this connection. |
| | | * Available since PEAR DB 1.7.0. |
| | | * |
| | | * @param array $dsn the data source name |
| | | * @param bool $persistent should the connection be persistent? |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function connect($dsn, $persistent = false) |
| | | { |
| | | if (!PEAR::loadExtension('sybase') && |
| | | !PEAR::loadExtension('sybase_ct')) |
| | | { |
| | | return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); |
| | | } |
| | | |
| | | $this->dsn = $dsn; |
| | | if ($dsn['dbsyntax']) { |
| | | $this->dbsyntax = $dsn['dbsyntax']; |
| | | } |
| | | |
| | | $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; |
| | | $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false; |
| | | $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false; |
| | | $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false; |
| | | |
| | | $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; |
| | | |
| | | if ($dsn['username']) { |
| | | $this->connection = @$connect_function($dsn['hostspec'], |
| | | $dsn['username'], |
| | | $dsn['password'], |
| | | $dsn['charset'], |
| | | $dsn['appname']); |
| | | } else { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | 'The DSN did not contain a username.'); |
| | | } |
| | | |
| | | if (!$this->connection) { |
| | | return $this->raiseError(DB_ERROR_CONNECT_FAILED, |
| | | null, null, null, |
| | | @sybase_get_last_message()); |
| | | } |
| | | |
| | | if ($dsn['database']) { |
| | | if (!@sybase_select_db($dsn['database'], $this->connection)) { |
| | | return $this->raiseError(DB_ERROR_NODBSELECTED, |
| | | null, null, null, |
| | | @sybase_get_last_message()); |
| | | } |
| | | $this->_db = $dsn['database']; |
| | | } |
| | | |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ disconnect() |
| | | |
| | | /** |
| | | * Disconnects from the database server |
| | | * |
| | | * @return bool TRUE on success, FALSE on failure |
| | | */ |
| | | function disconnect() |
| | | { |
| | | $ret = @sybase_close($this->connection); |
| | | $this->connection = null; |
| | | return $ret; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ simpleQuery() |
| | | |
| | | /** |
| | | * Sends a query to the database server |
| | | * |
| | | * @param string the SQL query string |
| | | * |
| | | * @return mixed + a PHP result resrouce for successful SELECT queries |
| | | * + the DB_OK constant for other successful queries |
| | | * + a DB_Error object on failure |
| | | */ |
| | | function simpleQuery($query) |
| | | { |
| | | $ismanip = DB::isManip($query); |
| | | $this->last_query = $query; |
| | | if (!@sybase_select_db($this->_db, $this->connection)) { |
| | | return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $query = $this->modifyQuery($query); |
| | | if (!$this->autocommit && $ismanip) { |
| | | if ($this->transaction_opcount == 0) { |
| | | $result = @sybase_query('BEGIN TRANSACTION', $this->connection); |
| | | if (!$result) { |
| | | return $this->sybaseRaiseError(); |
| | | } |
| | | } |
| | | $this->transaction_opcount++; |
| | | } |
| | | $result = @sybase_query($query, $this->connection); |
| | | if (!$result) { |
| | | return $this->sybaseRaiseError(); |
| | | } |
| | | if (is_resource($result)) { |
| | | return $result; |
| | | } |
| | | // Determine which queries that should return data, and which |
| | | // should return an error code only. |
| | | return $ismanip ? DB_OK : $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextResult() |
| | | |
| | | /** |
| | | * Move the internal sybase result pointer to the next available result |
| | | * |
| | | * @param a valid sybase result resource |
| | | * |
| | | * @access public |
| | | * |
| | | * @return true if a result is available otherwise return false |
| | | */ |
| | | function nextResult($result) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ fetchInto() |
| | | |
| | | /** |
| | | * Places a row from the result set into the given array |
| | | * |
| | | * Formating of the array and the data therein are configurable. |
| | | * See DB_result::fetchInto() for more information. |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::fetchInto() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result the query result resource |
| | | * @param array $arr the referenced array to put the data in |
| | | * @param int $fetchmode how the resulting array should be indexed |
| | | * @param int $rownum the row number to fetch (0 = first row) |
| | | * |
| | | * @return mixed DB_OK on success, NULL when the end of a result set is |
| | | * reached or on failure |
| | | * |
| | | * @see DB_result::fetchInto() |
| | | */ |
| | | function fetchInto($result, &$arr, $fetchmode, $rownum = null) |
| | | { |
| | | if ($rownum !== null) { |
| | | if (!@sybase_data_seek($result, $rownum)) { |
| | | return null; |
| | | } |
| | | } |
| | | if ($fetchmode & DB_FETCHMODE_ASSOC) { |
| | | if (function_exists('sybase_fetch_assoc')) { |
| | | $arr = @sybase_fetch_assoc($result); |
| | | } else { |
| | | if ($arr = @sybase_fetch_array($result)) { |
| | | foreach ($arr as $key => $value) { |
| | | if (is_int($key)) { |
| | | unset($arr[$key]); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { |
| | | $arr = array_change_key_case($arr, CASE_LOWER); |
| | | } |
| | | } else { |
| | | $arr = @sybase_fetch_row($result); |
| | | } |
| | | if (!$arr) { |
| | | return null; |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { |
| | | $this->_rtrimArrayValues($arr); |
| | | } |
| | | if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { |
| | | $this->_convertNullArrayValuesToEmpty($arr); |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ freeResult() |
| | | |
| | | /** |
| | | * Deletes the result set and frees the memory occupied by the result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::free() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return bool TRUE on success, FALSE if $result is invalid |
| | | * |
| | | * @see DB_result::free() |
| | | */ |
| | | function freeResult($result) |
| | | { |
| | | return @sybase_free_result($result); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numCols() |
| | | |
| | | /** |
| | | * Gets the number of columns in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numCols() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of columns. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numCols() |
| | | */ |
| | | function numCols($result) |
| | | { |
| | | $cols = @sybase_num_fields($result); |
| | | if (!$cols) { |
| | | return $this->sybaseRaiseError(); |
| | | } |
| | | return $cols; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ numRows() |
| | | |
| | | /** |
| | | * Gets the number of rows in a result set |
| | | * |
| | | * This method is not meant to be called directly. Use |
| | | * DB_result::numRows() instead. It can't be declared "protected" |
| | | * because DB_result is a separate object. |
| | | * |
| | | * @param resource $result PHP's query result resource |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | * |
| | | * @see DB_result::numRows() |
| | | */ |
| | | function numRows($result) |
| | | { |
| | | $rows = @sybase_num_rows($result); |
| | | if ($rows === false) { |
| | | return $this->sybaseRaiseError(); |
| | | } |
| | | return $rows; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ affectedRows() |
| | | |
| | | /** |
| | | * Determines the number of rows affected by a data maniuplation query |
| | | * |
| | | * 0 is returned for queries that don't manipulate data. |
| | | * |
| | | * @return int the number of rows. A DB_Error object on failure. |
| | | */ |
| | | function affectedRows() |
| | | { |
| | | if (DB::isManip($this->last_query)) { |
| | | $result = @sybase_affected_rows($this->connection); |
| | | } else { |
| | | $result = 0; |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ nextId() |
| | | |
| | | /** |
| | | * Returns the next free id in a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence |
| | | * @param boolean $ondemand when true, the seqence is automatically |
| | | * created if it does not exist |
| | | * |
| | | * @return int the next id number in the sequence. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::nextID(), DB_common::getSequenceName(), |
| | | * DB_sybase::createSequence(), DB_sybase::dropSequence() |
| | | */ |
| | | function nextId($seq_name, $ondemand = true) |
| | | { |
| | | $seqname = $this->getSequenceName($seq_name); |
| | | if (!@sybase_select_db($this->_db, $this->connection)) { |
| | | return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $repeat = 0; |
| | | do { |
| | | $this->pushErrorHandling(PEAR_ERROR_RETURN); |
| | | $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); |
| | | $this->popErrorHandling(); |
| | | if ($ondemand && DB::isError($result) && |
| | | ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) |
| | | { |
| | | $repeat = 1; |
| | | $result = $this->createSequence($seq_name); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | } elseif (!DB::isError($result)) { |
| | | $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); |
| | | $repeat = 0; |
| | | } else { |
| | | $repeat = false; |
| | | } |
| | | } while ($repeat); |
| | | if (DB::isError($result)) { |
| | | return $this->raiseError($result); |
| | | } |
| | | $result = $result->fetchRow(DB_FETCHMODE_ORDERED); |
| | | return $result[0]; |
| | | } |
| | | |
| | | /** |
| | | * Creates a new sequence |
| | | * |
| | | * @param string $seq_name name of the new sequence |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::createSequence(), DB_common::getSequenceName(), |
| | | * DB_sybase::nextID(), DB_sybase::dropSequence() |
| | | */ |
| | | function createSequence($seq_name) |
| | | { |
| | | return $this->query('CREATE TABLE ' |
| | | . $this->getSequenceName($seq_name) |
| | | . ' (id numeric(10, 0) IDENTITY NOT NULL,' |
| | | . ' vapor int NULL)'); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ dropSequence() |
| | | |
| | | /** |
| | | * Deletes a sequence |
| | | * |
| | | * @param string $seq_name name of the sequence to be deleted |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::dropSequence(), DB_common::getSequenceName(), |
| | | * DB_sybase::nextID(), DB_sybase::createSequence() |
| | | */ |
| | | function dropSequence($seq_name) |
| | | { |
| | | return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ autoCommit() |
| | | |
| | | /** |
| | | * Enables or disables automatic commits |
| | | * |
| | | * @param bool $onoff true turns it on, false turns it off |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object if the driver |
| | | * doesn't support auto-committing transactions. |
| | | */ |
| | | function autoCommit($onoff = false) |
| | | { |
| | | // XXX if $this->transaction_opcount > 0, we should probably |
| | | // issue a warning here. |
| | | $this->autocommit = $onoff ? true : false; |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ commit() |
| | | |
| | | /** |
| | | * Commits the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function commit() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if (!@sybase_select_db($this->_db, $this->connection)) { |
| | | return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $result = @sybase_query('COMMIT', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->sybaseRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ rollback() |
| | | |
| | | /** |
| | | * Reverts the current transaction |
| | | * |
| | | * @return int DB_OK on success. A DB_Error object on failure. |
| | | */ |
| | | function rollback() |
| | | { |
| | | if ($this->transaction_opcount > 0) { |
| | | if (!@sybase_select_db($this->_db, $this->connection)) { |
| | | return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $result = @sybase_query('ROLLBACK', $this->connection); |
| | | $this->transaction_opcount = 0; |
| | | if (!$result) { |
| | | return $this->sybaseRaiseError(); |
| | | } |
| | | } |
| | | return DB_OK; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ sybaseRaiseError() |
| | | |
| | | /** |
| | | * Produces a DB_Error object regarding the current problem |
| | | * |
| | | * @param int $errno if the error is being manually raised pass a |
| | | * DB_ERROR* constant here. If this isn't passed |
| | | * the error information gathered from the DBMS. |
| | | * |
| | | * @return object the DB_Error object |
| | | * |
| | | * @see DB_common::raiseError(), |
| | | * DB_sybase::errorNative(), DB_sybase::errorCode() |
| | | */ |
| | | function sybaseRaiseError($errno = null) |
| | | { |
| | | $native = $this->errorNative(); |
| | | if ($errno === null) { |
| | | $errno = $this->errorCode($native); |
| | | } |
| | | return $this->raiseError($errno, null, null, null, $native); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorNative() |
| | | |
| | | /** |
| | | * Gets the DBMS' native error message produced by the last query |
| | | * |
| | | * @return string the DBMS' error message |
| | | */ |
| | | function errorNative() |
| | | { |
| | | return @sybase_get_last_message(); |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ errorCode() |
| | | |
| | | /** |
| | | * Determines PEAR::DB error code from the database's text error message. |
| | | * |
| | | * @param string $errormsg error message returned from the database |
| | | * @return integer an error number from a DB error constant |
| | | */ |
| | | function errorCode($errormsg) |
| | | { |
| | | static $error_regexps; |
| | | if (!isset($error_regexps)) { |
| | | $error_regexps = array( |
| | | '/Incorrect syntax near/' |
| | | => DB_ERROR_SYNTAX, |
| | | '/^Unclosed quote before the character string [\"\'].*[\"\']\./' |
| | | => DB_ERROR_SYNTAX, |
| | | '/Implicit conversion (from datatype|of NUMERIC value)/i' |
| | | => DB_ERROR_INVALID_NUMBER, |
| | | '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' |
| | | => DB_ERROR_NOSUCHTABLE, |
| | | '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/^.+ permission denied on object .+, database .+, owner .+/' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/^.* permission denied, database .+, owner .+/' |
| | | => DB_ERROR_ACCESS_VIOLATION, |
| | | '/[^.*] not found\./' |
| | | => DB_ERROR_NOSUCHTABLE, |
| | | '/There is already an object named/' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/Invalid column name/' |
| | | => DB_ERROR_NOSUCHFIELD, |
| | | '/does not allow null values/' |
| | | => DB_ERROR_CONSTRAINT_NOT_NULL, |
| | | '/Command has been aborted/' |
| | | => DB_ERROR_CONSTRAINT, |
| | | '/^Cannot drop the index .* because it doesn\'t exist/i' |
| | | => DB_ERROR_NOT_FOUND, |
| | | '/^There is already an index/i' |
| | | => DB_ERROR_ALREADY_EXISTS, |
| | | '/^There are fewer columns in the INSERT statement than values specified/i' |
| | | => DB_ERROR_VALUE_COUNT_ON_ROW, |
| | | ); |
| | | } |
| | | |
| | | foreach ($error_regexps as $regexp => $code) { |
| | | if (preg_match($regexp, $errormsg)) { |
| | | return $code; |
| | | } |
| | | } |
| | | return DB_ERROR; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ tableInfo() |
| | | |
| | | /** |
| | | * Returns information about a table or a result set |
| | | * |
| | | * NOTE: only supports 'table' and 'flags' if <var>$result</var> |
| | | * is a table name. |
| | | * |
| | | * @param object|string $result DB_result object from a query or a |
| | | * string containing the name of a table. |
| | | * While this also accepts a query result |
| | | * resource identifier, this behavior is |
| | | * deprecated. |
| | | * @param int $mode a valid tableInfo mode |
| | | * |
| | | * @return array an associative array with the information requested. |
| | | * A DB_Error object on failure. |
| | | * |
| | | * @see DB_common::tableInfo() |
| | | * @since Method available since Release 1.6.0 |
| | | */ |
| | | function tableInfo($result, $mode = null) |
| | | { |
| | | if (is_string($result)) { |
| | | /* |
| | | * Probably received a table name. |
| | | * Create a result resource identifier. |
| | | */ |
| | | if (!@sybase_select_db($this->_db, $this->connection)) { |
| | | return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); |
| | | } |
| | | $id = @sybase_query("SELECT * FROM $result WHERE 1=0", |
| | | $this->connection); |
| | | $got_string = true; |
| | | } elseif (isset($result->result)) { |
| | | /* |
| | | * Probably received a result object. |
| | | * Extract the result resource identifier. |
| | | */ |
| | | $id = $result->result; |
| | | $got_string = false; |
| | | } else { |
| | | /* |
| | | * Probably received a result resource identifier. |
| | | * Copy it. |
| | | * Deprecated. Here for compatibility only. |
| | | */ |
| | | $id = $result; |
| | | $got_string = false; |
| | | } |
| | | |
| | | if (!is_resource($id)) { |
| | | return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA); |
| | | } |
| | | |
| | | if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { |
| | | $case_func = 'strtolower'; |
| | | } else { |
| | | $case_func = 'strval'; |
| | | } |
| | | |
| | | $count = @sybase_num_fields($id); |
| | | $res = array(); |
| | | |
| | | if ($mode) { |
| | | $res['num_fields'] = $count; |
| | | } |
| | | |
| | | for ($i = 0; $i < $count; $i++) { |
| | | $f = @sybase_fetch_field($id, $i); |
| | | // column_source is often blank |
| | | $res[$i] = array( |
| | | 'table' => $got_string |
| | | ? $case_func($result) |
| | | : $case_func($f->column_source), |
| | | 'name' => $case_func($f->name), |
| | | 'type' => $f->type, |
| | | 'len' => $f->max_length, |
| | | 'flags' => '', |
| | | ); |
| | | if ($res[$i]['table']) { |
| | | $res[$i]['flags'] = $this->_sybase_field_flags( |
| | | $res[$i]['table'], $res[$i]['name']); |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDER) { |
| | | $res['order'][$res[$i]['name']] = $i; |
| | | } |
| | | if ($mode & DB_TABLEINFO_ORDERTABLE) { |
| | | $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
| | | } |
| | | } |
| | | |
| | | // free the result only if we were called on a table |
| | | if ($got_string) { |
| | | @sybase_free_result($id); |
| | | } |
| | | return $res; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _sybase_field_flags() |
| | | |
| | | /** |
| | | * Get the flags for a field |
| | | * |
| | | * Currently supports: |
| | | * + <samp>unique_key</samp> (unique index, unique check or primary_key) |
| | | * + <samp>multiple_key</samp> (multi-key index) |
| | | * |
| | | * @param string $table the table name |
| | | * @param string $column the field name |
| | | * |
| | | * @return string space delimited string of flags. Empty string if none. |
| | | * |
| | | * @access private |
| | | */ |
| | | function _sybase_field_flags($table, $column) |
| | | { |
| | | static $tableName = null; |
| | | static $flags = array(); |
| | | |
| | | if ($table != $tableName) { |
| | | $flags = array(); |
| | | $tableName = $table; |
| | | |
| | | // get unique/primary keys |
| | | $res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC); |
| | | |
| | | if (!isset($res[0]['index_description'])) { |
| | | return ''; |
| | | } |
| | | |
| | | foreach ($res as $val) { |
| | | $keys = explode(', ', trim($val['index_keys'])); |
| | | |
| | | if (sizeof($keys) > 1) { |
| | | foreach ($keys as $key) { |
| | | $this->_add_flag($flags[$key], 'multiple_key'); |
| | | } |
| | | } |
| | | |
| | | if (strpos($val['index_description'], 'unique')) { |
| | | foreach ($keys as $key) { |
| | | $this->_add_flag($flags[$key], 'unique_key'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | if (array_key_exists($column, $flags)) { |
| | | return(implode(' ', $flags[$column])); |
| | | } |
| | | |
| | | return ''; |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ _add_flag() |
| | | |
| | | /** |
| | | * Adds a string to the flags array if the flag is not yet in there |
| | | * - if there is no flag present the array is created |
| | | * |
| | | * @param array $array reference of flags array to add a value to |
| | | * @param mixed $value value to add to the flag array |
| | | * |
| | | * @return void |
| | | * |
| | | * @access private |
| | | */ |
| | | function _add_flag(&$array, $value) |
| | | { |
| | | if (!is_array($array)) { |
| | | $array = array($value); |
| | | } elseif (!in_array($value, $array)) { |
| | | array_push($array, $value); |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | // {{{ getSpecialQuery() |
| | | |
| | | /** |
| | | * Obtains the query string needed for listing a given type of objects |
| | | * |
| | | * @param string $type the kind of objects you want to retrieve |
| | | * |
| | | * @return string the SQL query string or null if the driver doesn't |
| | | * support the object type requested |
| | | * |
| | | * @access protected |
| | | * @see DB_common::getListOf() |
| | | */ |
| | | function getSpecialQuery($type) |
| | | { |
| | | switch ($type) { |
| | | case 'tables': |
| | | return "SELECT name FROM sysobjects WHERE type = 'U'" |
| | | . ' ORDER BY name'; |
| | | case 'views': |
| | | return "SELECT name FROM sysobjects WHERE type = 'V'"; |
| | | default: |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | // }}} |
| | | |
| | | } |
| | | |
| | | /* |
| | | * Local variables: |
| | | * tab-width: 4 |
| | | * c-basic-offset: 4 |
| | | * End: |
| | | */ |
| | | |
| | | ?> |