00001 <?php
00015 class IBM_DB2Field {
00016 private $name = '';
00017 private $tablename = '';
00018 private $type = '';
00019 private $nullable = false;
00020 private $max_length = 0;
00021
00029 static function fromText($db, $table, $field) {
00030 global $wgDBmwschema;
00031
00032 $q = <<<SQL
00033 SELECT
00034 lcase(coltype) AS typname,
00035 nulls AS attnotnull, length AS attlen
00036 FROM sysibm.syscolumns
00037 WHERE tbcreator=%s AND tbname=%s AND name=%s;
00038 SQL;
00039 $res = $db->query(sprintf($q,
00040 $db->addQuotes($wgDBmwschema),
00041 $db->addQuotes($table),
00042 $db->addQuotes($field)));
00043 $row = $db->fetchObject($res);
00044 if (!$row)
00045 return null;
00046 $n = new IBM_DB2Field;
00047 $n->type = $row->typname;
00048 $n->nullable = ($row->attnotnull == 'N');
00049 $n->name = $field;
00050 $n->tablename = $table;
00051 $n->max_length = $row->attlen;
00052 return $n;
00053 }
00058 function name() { return $this->name; }
00063 function tableName() { return $this->tablename; }
00068 function type() { return $this->type; }
00073 function nullable() { return $this->nullable; }
00078 function maxLength() { return $this->max_length; }
00079 }
00080
00085 class IBM_DB2Blob {
00086 private $mData;
00087
00088 public function __construct($data) {
00089 $this->mData = $data;
00090 }
00091
00092 public function getData() {
00093 return $this->mData;
00094 }
00095
00096 public function __toString()
00097 {
00098 return $this->mData;
00099 }
00100 }
00101
00106 class DatabaseIbm_db2 extends DatabaseBase {
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00126 protected $mPort = null;
00128 protected $mCataloged = null;
00130 protected $mSchema = null;
00132 protected $mSchemaSet = false;
00134 protected $mLastResult = null;
00136 protected $mAffectedRows = null;
00138 protected $mNumRows = null;
00139
00141 public $mConnOptions = array();
00143 public $mStmtOptions = array();
00144
00145
00146 const CATALOGED = "cataloged";
00147 const UNCATALOGED = "uncataloged";
00148 const USE_GLOBAL = "get from global";
00149
00150 const NONE_OPTION = 0x00;
00151 const CONN_OPTION = 0x01;
00152 const STMT_OPTION = 0x02;
00153
00154 const REGULAR_MODE = 'regular';
00155 const INSTALL_MODE = 'install';
00156
00157
00158 protected $mMode = self::REGULAR_MODE;
00159
00161 protected $mInsertId = null;
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321 ######################################
00322 # Getters and Setters
00323 ######################################
00324
00328 function cascadingDeletes() {
00329 return true;
00330 }
00331
00335 function cleanupTriggers() {
00336 return true;
00337 }
00338
00343 function strictIPs() {
00344 return true;
00345 }
00346
00350 function realTimestamps() {
00351 return true;
00352 }
00353
00357 function implicitGroupby() {
00358 return false;
00359 }
00360
00365 function implicitOrderby() {
00366 return false;
00367 }
00368
00373 function searchableIPs() {
00374 return true;
00375 }
00376
00380 function functionalIndexes() {
00381 return true;
00382 }
00383
00387 function getWikiID() {
00388 if( $this->mSchema ) {
00389 return "{$this->mDBname}-{$this->mSchema}";
00390 } else {
00391 return $this->mDBname;
00392 }
00393 }
00394
00395 function getType() {
00396 return 'ibm_db2';
00397 }
00398
00399 ######################################
00400 # Setup
00401 ######################################
00402
00403
00414 public function DatabaseIbm_db2($server = false, $user = false, $password = false,
00415 $dbName = false, $failFunction = false, $flags = 0,
00416 $schema = self::USE_GLOBAL )
00417 {
00418
00419 global $wgOut, $wgDBmwschema;
00420 # Can't get a reference if it hasn't been set yet
00421 if ( !isset( $wgOut ) ) {
00422 $wgOut = null;
00423 }
00424 $this->mOut =& $wgOut;
00425 $this->mFailFunction = $failFunction;
00426 $this->mFlags = DBO_TRX | $flags;
00427
00428 if ( $schema == self::USE_GLOBAL ) {
00429 $this->mSchema = $wgDBmwschema;
00430 }
00431 else {
00432 $this->mSchema = $schema;
00433 }
00434
00435
00436 $this->setDB2Option('db2_attr_case', 'DB2_CASE_LOWER', self::CONN_OPTION | self::STMT_OPTION);
00437 $this->setDB2Option('deferred_prepare', 'DB2_DEFERRED_PREPARE_ON', self::STMT_OPTION);
00438 $this->setDB2Option('rowcount', 'DB2_ROWCOUNT_PREFETCH_ON', self::STMT_OPTION);
00439
00440 $this->open( $server, $user, $password, $dbName);
00441 }
00442
00449 private function setDB2Option($name, $const, $type) {
00450 if (defined($const)) {
00451 if ($type & self::CONN_OPTION) $this->mConnOptions[$name] = constant($const);
00452 if ($type & self::STMT_OPTION) $this->mStmtOptions[$name] = constant($const);
00453 }
00454 else {
00455 $this->installPrint("$const is not defined. ibm_db2 version is likely too low.");
00456 }
00457 }
00458
00463 private function installPrint($string) {
00464 wfDebug("$string");
00465 if ($this->mMode == self::INSTALL_MODE) {
00466 print "<li>$string</li>";
00467 flush();
00468 }
00469 }
00470
00480 public function open( $server, $user, $password, $dbName )
00481 {
00482
00483 global $wgDBport_db2, $wgDBcataloged;
00484 wfProfileIn( __METHOD__ );
00485
00486
00487 if (!@extension_loaded('ibm_db2')) {
00488 @dl('ibm_db2.so');
00489 }
00490
00491 if ( !function_exists( 'db2_connect' ) ) {
00492 $error = "DB2 functions missing, have you enabled the ibm_db2 extension for PHP?\n";
00493 $this->installPrint($error);
00494 $this->reportConnectionError($error);
00495 }
00496
00497 if (!strlen($user)) {
00498 return null;
00499 }
00500
00501
00502 $this->close();
00503
00504 $this->mServer = $server;
00505 $this->mPort = $port = $wgDBport_db2;
00506 $this->mUser = $user;
00507 $this->mPassword = $password;
00508 $this->mDBname = $dbName;
00509 $this->mCataloged = $cataloged = $wgDBcataloged;
00510
00511 if ( $cataloged == self::CATALOGED ) {
00512 $this->openCataloged($dbName, $user, $password);
00513 }
00514 elseif ( $cataloged == self::UNCATALOGED ) {
00515 $this->openUncataloged($dbName, $user, $password, $server, $port);
00516 }
00517
00518 db2_set_option($this->mConn, $this->mConnOptions, 1);
00519
00520
00521 db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
00522
00523 if ( $this->mConn == false ) {
00524 $this->installPrint( "DB connection error\n" );
00525 $this->installPrint( "Server: $server, Database: $dbName, User: $user, Password: " . substr( $password, 0, 3 ) . "...\n" );
00526 $this->installPrint( $this->lastError()."\n" );
00527 return null;
00528 }
00529
00530 $this->mOpened = true;
00531 $this->applySchema();
00532
00533 wfProfileOut( __METHOD__ );
00534 return $this->mConn;
00535 }
00536
00540 protected function openCataloged( $dbName, $user, $password )
00541 {
00542 @$this->mConn = db2_connect($dbName, $user, $password);
00543 }
00544
00548 protected function openUncataloged( $dbName, $user, $password, $server, $port )
00549 {
00550 $str = "DRIVER={IBM DB2 ODBC DRIVER};";
00551 $str .= "DATABASE=$dbName;";
00552 $str .= "HOSTNAME=$server;";
00553 if ($port) $str .= "PORT=$port;";
00554 $str .= "PROTOCOL=TCPIP;";
00555 $str .= "UID=$user;";
00556 $str .= "PWD=$password;";
00557
00558 @$this->mConn = db2_connect($str, $user, $password);
00559 }
00560
00565 public function close() {
00566 $this->mOpened = false;
00567 if ( $this->mConn ) {
00568 if ($this->trxLevel() > 0) {
00569 $this->commit();
00570 }
00571 return db2_close( $this->mConn );
00572 }
00573 else {
00574 return true;
00575 }
00576 }
00577
00589 static function newFromParams( $server, $user, $password, $dbName, $failFunction = false, $flags = 0)
00590 {
00591 return new DatabaseIbm_db2( $server, $user, $password, $dbName, $failFunction, $flags );
00592 }
00593
00598 public function lastError() {
00599 $connerr = db2_conn_errormsg();
00600 if ($connerr) {
00601
00602 return $connerr;
00603 }
00604 $stmterr = db2_stmt_errormsg();
00605 if ($stmterr) {
00606
00607 return $stmterr;
00608 }
00609
00610 return false;
00611 }
00612
00618 public function lastErrno() {
00619 $connerr = db2_conn_error();
00620 if ($connerr) return $connerr;
00621 $stmterr = db2_stmt_error();
00622 if ($stmterr) return $stmterr;
00623 return 0;
00624 }
00625
00630 public function isOpen() { return $this->mOpened; }
00631
00638
00639 public function doQuery( $sql ) {
00640
00641
00642 $this->applySchema();
00643
00644 $ret = db2_exec( $this->mConn, $sql, $this->mStmtOptions );
00645 if( !$ret ) {
00646 print "<br><pre>";
00647 print $sql;
00648 print "</pre><br>";
00649 $error = db2_stmt_errormsg();
00650 throw new DBUnexpectedError($this, 'SQL error: ' . htmlspecialchars( $error ) );
00651 }
00652 $this->mLastResult = $ret;
00653 $this->mAffectedRows = null;
00654 return $ret;
00655 }
00656
00660 public function getServerVersion() {
00661 $info = db2_server_info( $this->mConn );
00662 return $info->DBMS_VER;
00663 }
00664
00669 public function tableExists( $table ) {
00670 $schema = $this->mSchema;
00671 $sql = <<< EOF
00672 SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST
00673 WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema'
00674 EOF;
00675 $res = $this->query( $sql );
00676 if (!$res) return false;
00677
00678
00679 @$row = $this->fetchRow($res);
00680 $count = $row[0];
00681 if ($count == '1' or $count == 1) {
00682 return true;
00683 }
00684
00685 return false;
00686 }
00687
00697 public function fetchObject( $res ) {
00698 if ( $res instanceof ResultWrapper ) {
00699 $res = $res->result;
00700 }
00701 @$row = db2_fetch_object( $res );
00702 if( $this->lastErrno() ) {
00703 throw new DBUnexpectedError( $this, 'Error in fetchObject(): ' . htmlspecialchars( $this->lastError() ) );
00704 }
00705 return $row;
00706 }
00707
00716 public function fetchRow( $res ) {
00717 if ( $res instanceof ResultWrapper ) {
00718 $res = $res->result;
00719 }
00720 @$row = db2_fetch_array( $res );
00721 if ( $this->lastErrno() ) {
00722 throw new DBUnexpectedError( $this, 'Error in fetchRow(): ' . htmlspecialchars( $this->lastError() ) );
00723 }
00724 return $row;
00725 }
00726
00730 public function initial_setup() {
00731
00732 }
00733
00737 public function setup_database() {
00738
00739
00740
00741 try {
00742
00743
00744
00745 $this->applySchema();
00746 $this->begin();
00747
00748 $res = $this->sourceFile( "../maintenance/ibm_db2/tables.sql" );
00749 if ($res !== true) {
00750 print " <b>FAILED</b>: " . htmlspecialchars( $res ) . "</li>";
00751 } else {
00752 print " done</li>";
00753 }
00754 $res = null;
00755
00756
00757
00758
00759
00760 if ($this->lastError()) {
00761 print "<li>Errors encountered during table creation -- rolled back</li>\n";
00762 print "<li>Please install again</li>\n";
00763 $this->rollback();
00764 }
00765 else {
00766 $this->commit();
00767 }
00768 }
00769 catch (MWException $mwe)
00770 {
00771 print "<br><pre>$mwe</pre><br>";
00772 }
00773 }
00774
00781 public function addQuotes( $s ) {
00782
00783 if ( is_null( $s ) ) {
00784 return "NULL";
00785 } else if ($s instanceof Blob) {
00786 return "'".$s->fetch($s)."'";
00787 } else if ($s instanceof IBM_DB2Blob) {
00788 return "'".$this->decodeBlob($s)."'";
00789 }
00790 $s = $this->strencode($s);
00791 if ( is_numeric($s) ) {
00792 return $s;
00793 }
00794 else {
00795 return "'$s'";
00796 }
00797 }
00798
00804 public function is_numeric_type( $type ) {
00805 switch (strtoupper($type)) {
00806 case 'SMALLINT':
00807 case 'INTEGER':
00808 case 'INT':
00809 case 'BIGINT':
00810 case 'DECIMAL':
00811 case 'REAL':
00812 case 'DOUBLE':
00813 case 'DECFLOAT':
00814 return true;
00815 }
00816 return false;
00817 }
00818
00824 public function strencode( $s ) {
00825
00826
00827
00828 $s = db2_escape_string($s);
00829
00830 $s = utf8_encode($s);
00831
00832 $from = array("\\\\", "\\'", '\\n', '\\t', '\\"', '\\r');
00833 $to = array("\\", "''", "\n", "\t", '"', "\r");
00834 $s = str_replace($from, $to, $s);
00835 return $s;
00836 }
00837
00841 protected function applySchema() {
00842 if ( !($this->mSchemaSet) ) {
00843 $this->mSchemaSet = true;
00844 $this->begin();
00845 $this->doQuery("SET SCHEMA = $this->mSchema");
00846 $this->commit();
00847 }
00848 }
00849
00853 public function begin( $fname = 'DatabaseIbm_db2::begin' ) {
00854
00855 db2_autocommit($this->mConn, DB2_AUTOCOMMIT_OFF);
00856 $this->mTrxLevel = 1;
00857 }
00858
00863 public function commit( $fname = 'DatabaseIbm_db2::commit' ) {
00864 db2_commit($this->mConn);
00865
00866 db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
00867 $this->mTrxLevel = 0;
00868 }
00869
00873 public function rollback( $fname = 'DatabaseIbm_db2::rollback' ) {
00874 db2_rollback($this->mConn);
00875
00876
00877 db2_autocommit($this->mConn, DB2_AUTOCOMMIT_ON);
00878 $this->mTrxLevel = 0;
00879 }
00880
00890 public function makeList( $a, $mode = LIST_COMMA ) {
00891 if ( !is_array( $a ) ) {
00892 throw new DBUnexpectedError( $this, 'Database::makeList called with incorrect parameters' );
00893 }
00894
00895 $first = true;
00896 $list = '';
00897 foreach ( $a as $field => $value ) {
00898 if ( !$first ) {
00899 if ( $mode == LIST_AND ) {
00900 $list .= ' AND ';
00901 } elseif($mode == LIST_OR) {
00902 $list .= ' OR ';
00903 } else {
00904 $list .= ',';
00905 }
00906 } else {
00907 $first = false;
00908 }
00909 if ( ($mode == LIST_AND || $mode == LIST_OR) && is_numeric( $field ) ) {
00910 $list .= "($value)";
00911 } elseif ( ($mode == LIST_SET) && is_numeric( $field ) ) {
00912 $list .= "$value";
00913 } elseif ( ($mode == LIST_AND || $mode == LIST_OR) && is_array($value) ) {
00914 if( count( $value ) == 0 ) {
00915 throw new MWException( __METHOD__.': empty input' );
00916 } elseif( count( $value ) == 1 ) {
00917
00918
00919
00920 $value = array_values( $value );
00921 $list .= $field." = ".$this->addQuotes( $value[0] );
00922 } else {
00923 $list .= $field." IN (".$this->makeList($value).") ";
00924 }
00925 } elseif( is_null($value) ) {
00926 if ( $mode == LIST_AND || $mode == LIST_OR ) {
00927 $list .= "$field IS ";
00928 } elseif ( $mode == LIST_SET ) {
00929 $list .= "$field = ";
00930 }
00931 $list .= 'NULL';
00932 } else {
00933 if ( $mode == LIST_AND || $mode == LIST_OR || $mode == LIST_SET ) {
00934 $list .= "$field = ";
00935 }
00936 if ( $mode == LIST_NAMES ) {
00937 $list .= $value;
00938 }
00939
00940
00941 else if ( is_numeric($value) ) {
00942 $list .= $value;
00943 }
00944 else {
00945 $list .= $this->addQuotes( $value );
00946 }
00947 }
00948 }
00949 return $list;
00950 }
00951
00959 public function limitResult($sql, $limit, $offset=false) {
00960 if( !is_numeric($limit) ) {
00961 throw new DBUnexpectedError( $this, "Invalid non-numeric limit passed to limitResult()\n" );
00962 }
00963 if( $offset ) {
00964 $this->installPrint("Offset parameter not supported in limitResult()\n");
00965 }
00966
00967
00968 return "$sql FETCH FIRST $limit ROWS ONLY ";
00969 }
00970
00976 public function tableName( $name ) {
00977 # Replace reserved words with better ones
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987 return $name;
00988 }
00989
00995 public function timestamp( $ts=0 ) {
00996
00997 return wfTimestamp(TS_DB2,$ts);
00998 }
00999
01005 public function nextSequenceValue( $seqName ) {
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016 return null;
01017 }
01018
01023 public function insertId() {
01024 return $this->mInsertId;
01025 }
01026
01034 private function calcInsertId($table, $primaryKey, $stmt) {
01035 if ($primaryKey) {
01036 $id_row = $this->fetchRow($stmt);
01037 $this->mInsertId = $id_row[0];
01038 }
01039 }
01040
01054 public function insert( $table, $args, $fname = 'DatabaseIbm_db2::insert', $options = array() ) {
01055 if ( !count( $args ) ) {
01056 return true;
01057 }
01058
01059 $table = $this->tableName( $table );
01060
01061 if ( !is_array( $options ) ) $options = array( $options );
01062
01063 if ( !( isset( $args[0] ) && is_array( $args[0] ) ) ) {
01064 $args = array($args);
01065 }
01066
01067 list($args, $primaryKeys) = $this->removeNullPrimaryKeys($table, $args);
01068
01069
01070 $primaryKey = false;
01071 if (count($primaryKeys) == 1) {
01072 $primaryKey = $primaryKeys[0];
01073 }
01074
01075
01076 $keys = array_keys( $args[0] );
01077 $key_count = count($keys);
01078
01079
01080 $ignore = in_array( 'IGNORE', $options ) ? 'mw' : '';
01081
01082
01083 $res = true;
01084
01085 $didbegin = 0;
01086 if (! $this->mTrxLevel) {
01087 $this->begin();
01088 $didbegin = 1;
01089 }
01090
01091 $sql = "INSERT INTO $table (" . implode( ',', $keys ) . ') VALUES ';
01092 switch($key_count) {
01093
01094 case 1:
01095 $sql .= '(?)';
01096 break;
01097 default:
01098 $sql .= '(?' . str_repeat(',?', $key_count-1) . ')';
01099 }
01100
01101 if ($primaryKey) {
01102 $sql = "SELECT $primaryKey FROM FINAL TABLE($sql)";
01103 }
01104 $stmt = $this->prepare($sql);
01105
01106
01107 $this->begin();
01108
01109 if ( !$ignore ) {
01110 $first = true;
01111 foreach ( $args as $row ) {
01112
01113 $res = $res & $this->execute($stmt, $row);
01114
01115 $this->calcInsertId($table, $primaryKey, $stmt);
01116 }
01117 }
01118 else {
01119 $olde = error_reporting( 0 );
01120
01121
01122 $numrowsinserted = 0;
01123
01124
01125 $res = true;
01126
01127 foreach ( $args as $row ) {
01128 $overhead = "SAVEPOINT $ignore ON ROLLBACK RETAIN CURSORS";
01129 db2_exec($this->mConn, $overhead, $this->mStmtOptions);
01130
01131 $res2 = $this->execute($stmt, $row);
01132
01133 $this->calcInsertId($table, $primaryKey, $stmt);
01134
01135 $errNum = $this->lastErrno();
01136 if ($errNum) {
01137 db2_exec( $this->mConn, "ROLLBACK TO SAVEPOINT $ignore", $this->mStmtOptions );
01138 }
01139 else {
01140 db2_exec( $this->mConn, "RELEASE SAVEPOINT $ignore", $this->mStmtOptions );
01141 $numrowsinserted++;
01142 }
01143 }
01144
01145 $olde = error_reporting( $olde );
01146
01147 $this->mAffectedRows = $numrowsinserted;
01148 }
01149
01150 $this->commit();
01151
01152 return $res;
01153 }
01154
01163 private function removeNullPrimaryKeys($table, $args) {
01164 $schema = $this->mSchema;
01165
01166 $keyres = db2_primary_keys($this->mConn, null, strtoupper($schema), strtoupper($table));
01167 $keys = array();
01168 for ($row = $this->fetchObject($keyres); $row != null; $row = $this->fetchRow($keyres)) {
01169 $keys[] = strtolower($row->column_name);
01170 }
01171
01172 foreach ($args as $ai => $row) {
01173 foreach ($keys as $ki => $key) {
01174 if ($row[$key] == null) {
01175 unset($row[$key]);
01176 }
01177 }
01178 $args[$ai] = $row;
01179 }
01180
01181 return array($args, $keys);
01182 }
01183
01196 public function update( $table, $values, $conds, $fname = 'Database::update', $options = array() ) {
01197 $table = $this->tableName( $table );
01198 $opts = $this->makeUpdateOptions( $options );
01199 $sql = "UPDATE $opts $table SET " . $this->makeList( $values, LIST_SET );
01200 if ( $conds != '*' ) {
01201 $sql .= " WHERE " . $this->makeList( $conds, LIST_AND );
01202 }
01203 return $this->query( $sql, $fname );
01204 }
01205
01211 public function delete( $table, $conds, $fname = 'Database::delete' ) {
01212 if ( !$conds ) {
01213 throw new DBUnexpectedError( $this, 'Database::delete() called with no conditions' );
01214 }
01215 $table = $this->tableName( $table );
01216 $sql = "DELETE FROM $table";
01217 if ( $conds != '*' ) {
01218 $sql .= ' WHERE ' . $this->makeList( $conds, LIST_AND );
01219 }
01220 return $this->query( $sql, $fname );
01221 }
01222
01227 public function affectedRows() {
01228 if ( !is_null( $this->mAffectedRows ) ) {
01229
01230 return $this->mAffectedRows;
01231 }
01232 if( empty( $this->mLastResult ) )
01233 return 0;
01234 return db2_num_rows( $this->mLastResult );
01235 }
01236
01245 function replace( $table, $uniqueIndexes, $rows, $fname = 'DatabaseIbm_db2::replace' ) {
01246 $table = $this->tableName( $table );
01247
01248 if (count($rows)==0) {
01249 return;
01250 }
01251
01252 # Single row case
01253 if ( !is_array( reset( $rows ) ) ) {
01254 $rows = array( $rows );
01255 }
01256
01257 foreach( $rows as $row ) {
01258 # Delete rows which collide
01259 if ( $uniqueIndexes ) {
01260 $sql = "DELETE FROM $table WHERE ";
01261 $first = true;
01262 foreach ( $uniqueIndexes as $index ) {
01263 if ( $first ) {
01264 $first = false;
01265 $sql .= "(";
01266 } else {
01267 $sql .= ') OR (';
01268 }
01269 if ( is_array( $index ) ) {
01270 $first2 = true;
01271 foreach ( $index as $col ) {
01272 if ( $first2 ) {
01273 $first2 = false;
01274 } else {
01275 $sql .= ' AND ';
01276 }
01277 $sql .= $col.'=' . $this->addQuotes( $row[$col] );
01278 }
01279 } else {
01280 $sql .= $index.'=' . $this->addQuotes( $row[$index] );
01281 }
01282 }
01283 $sql .= ')';
01284 $this->query( $sql, $fname );
01285 }
01286
01287 # Now insert the row
01288 $sql = "INSERT INTO $table (" . $this->makeList( array_keys( $row ), LIST_NAMES ) .') VALUES (' .
01289 $this->makeList( $row, LIST_COMMA ) . ')';
01290 $this->query( $sql, $fname );
01291 }
01292 }
01293
01300 public function numRows( $res ) {
01301 if ( $res instanceof ResultWrapper ) {
01302 $res = $res->result;
01303 }
01304 if ( $this->mNumRows ) {
01305 return $this->mNumRows;
01306 }
01307 else {
01308 return 0;
01309 }
01310 }
01311
01318 public function dataSeek( $res, $row ) {
01319 if ( $res instanceof ResultWrapper ) {
01320 $res = $res->result;
01321 }
01322 return db2_fetch_row( $res, $row );
01323 }
01324
01325 ###
01326 # Fix notices in Block.php
01327 ###
01328
01334 public function freeResult( $res ) {
01335 if ( $res instanceof ResultWrapper ) {
01336 $res = $res->result;
01337 }
01338 if ( !@db2_free_result( $res ) ) {
01339 throw new DBUnexpectedError($this, "Unable to free DB2 result\n" );
01340 }
01341 }
01342
01348 public function numFields( $res ) {
01349 if ( $res instanceof ResultWrapper ) {
01350 $res = $res->result;
01351 }
01352 return db2_num_fields( $res );
01353 }
01354
01361 public function fieldName( $res, $n ) {
01362 if ( $res instanceof ResultWrapper ) {
01363 $res = $res->result;
01364 }
01365 return db2_field_name( $res, $n );
01366 }
01367
01381 public function select( $table, $vars, $conds='', $fname = 'DatabaseIbm_db2::select', $options = array(), $join_conds = array() )
01382 {
01383 $res = parent::select( $table, $vars, $conds, $fname, $options, $join_conds );
01384
01385
01386 if ( isset( $options['LIMIT'] ) ) {
01387 if ( isset ($options['OFFSET'] ) ) {
01388 $limit = $options['LIMIT'];
01389 $offset = $options['OFFSET'];
01390 }
01391 }
01392
01393
01394
01395
01396
01397
01398
01399 $vars2 = array('count(*) as num_rows');
01400
01401 $options2 = array();
01402 if ( isset( $options['LIMIT'] ) ) $options2['LIMIT'] = $options['LIMIT'];
01403
01404 if ( isset( $options['GROUP BY'] ) ) return $res;
01405
01406 $res2 = parent::select( $table, $vars2, $conds, $fname, $options2, $join_conds );
01407 $obj = $this->fetchObject($res2);
01408 $this->mNumRows = $obj->num_rows;
01409
01410
01411 return $res;
01412 }
01413
01424 function makeSelectOptions( $options ) {
01425 $preLimitTail = $postLimitTail = '';
01426 $startOpts = '';
01427
01428 $noKeyOptions = array();
01429 foreach ( $options as $key => $option ) {
01430 if ( is_numeric( $key ) ) {
01431 $noKeyOptions[$option] = true;
01432 }
01433 }
01434
01435 if ( isset( $options['GROUP BY'] ) ) $preLimitTail .= " GROUP BY {$options['GROUP BY']}";
01436 if ( isset( $options['HAVING'] ) ) $preLimitTail .= " HAVING {$options['HAVING']}";
01437 if ( isset( $options['ORDER BY'] ) ) $preLimitTail .= " ORDER BY {$options['ORDER BY']}";
01438
01439 if ( isset( $noKeyOptions['DISTINCT'] ) || isset( $noKeyOptions['DISTINCTROW'] ) ) $startOpts .= 'DISTINCT';
01440
01441 return array( $startOpts, '', $preLimitTail, $postLimitTail );
01442 }
01443
01448 public function getSoftwareLink() {
01449 return "[http://www.ibm.com/software/data/db2/express/?s_cmp=ECDDWW01&s_tact=MediaWiki IBM DB2]";
01450 }
01451
01458 public function getSearchEngine() {
01459 return "SearchIBM_DB2";
01460 }
01461
01466 public function wasDeadlock() {
01467
01468 $err = $this->lastErrno();
01469 switch($err) {
01470 case '40001':
01471 case '57011':
01472 case '57033':
01473 $this->installPrint("In a deadlock because of SQLSTATE $err");
01474 return true;
01475 }
01476 return false;
01477 }
01478
01484 public function ping() {
01485
01486
01487 $this->close();
01488 if ($this->mCataloged == null) {
01489 return false;
01490 }
01491 else if ($this->mCataloged) {
01492 $this->mConn = $this->openCataloged($this->mDBName, $this->mUser, $this->mPassword);
01493 }
01494 else if (!$this->mCataloged) {
01495 $this->mConn = $this->openUncataloged($this->mDBName, $this->mUser, $this->mPassword, $this->mServer, $this->mPort);
01496 }
01497 return false;
01498 }
01499 ######################################
01500 # Unimplemented and not applicable
01501 ######################################
01502
01507 public function getStatus( $which="%" ) { $this->installPrint('Not implemented for DB2: getStatus()'); return ''; }
01517 public function setFakeSlaveLag( $lag ) { $this->installPrint('Not implemented for DB2: setFakeSlaveLag()'); }
01522 public function setFakeMaster( $enabled = true ) { $this->installPrint('Not implemented for DB2: setFakeMaster()'); }
01528 public function limitResultForUpdate($sql, $num) { $this->installPrint('Not implemented for DB2: limitResultForUpdate()'); return $sql; }
01529
01534 public function fillPreparedArg( $matches ) { $this->installPrint('Not useful for DB2: fillPreparedArg()'); return ''; }
01535
01536 ######################################
01537 # Reflection
01538 ######################################
01539
01546 public function fieldExists( $table, $field, $fname = 'DatabaseIbm_db2::fieldExists' ) {
01547 $table = $this->tableName( $table );
01548 $schema = $this->mSchema;
01549 $etable = preg_replace("/'/", "''", $table);
01550 $eschema = preg_replace("/'/", "''", $schema);
01551 $ecol = preg_replace("/'/", "''", $field);
01552 $sql = <<<SQL
01553 SELECT 1 as fieldexists
01554 FROM sysibm.syscolumns sc
01555 WHERE sc.name='$ecol' AND sc.tbname='$etable' AND sc.tbcreator='$eschema'
01556 SQL;
01557 $res = $this->query( $sql, $fname );
01558 $count = $res ? $this->numRows($res) : 0;
01559 if ($res)
01560 $this->freeResult( $res );
01561 return $count;
01562 }
01563
01572 public function indexInfo( $table, $index, $fname = 'DatabaseIbm_db2::indexExists' ) {
01573 $table = $this->tableName( $table );
01574 $sql = <<<SQL
01575 SELECT name as indexname
01576 FROM sysibm.sysindexes si
01577 WHERE si.name='$index' AND si.tbname='$table' AND sc.tbcreator='$this->mSchema'
01578 SQL;
01579 $res = $this->query( $sql, $fname );
01580 if ( !$res ) {
01581 return null;
01582 }
01583 $row = $this->fetchObject( $res );
01584 if ($row != null) return $row;
01585 else return false;
01586 }
01587
01594 public function fieldInfo( $table, $field ) {
01595 return IBM_DB2Field::fromText($this, $table, $field);
01596 }
01597
01604 public function fieldType( $res, $index ) {
01605 if ( $res instanceof ResultWrapper ) {
01606 $res = $res->result;
01607 }
01608 return db2_field_type( $res, $index );
01609 }
01610
01618 public function indexUnique ($table, $index, $fname = 'Database::indexUnique' ) {
01619 $table = $this->tableName( $table );
01620 $sql = <<<SQL
01621 SELECT si.name as indexname
01622 FROM sysibm.sysindexes si
01623 WHERE si.name='$index' AND si.tbname='$table' AND sc.tbcreator='$this->mSchema'
01624 AND si.uniquerule IN ('U', 'P')
01625 SQL;
01626 $res = $this->query( $sql, $fname );
01627 if ( !$res ) {
01628 return null;
01629 }
01630 if ($this->fetchObject( $res )) {
01631 return true;
01632 }
01633 return false;
01634
01635 }
01636
01643 public function textFieldSize( $table, $field ) {
01644 $table = $this->tableName( $table );
01645 $sql = <<<SQL
01646 SELECT length as size
01647 FROM sysibm.syscolumns sc
01648 WHERE sc.name='$field' AND sc.tbname='$table' AND sc.tbcreator='$this->mSchema'
01649 SQL;
01650 $res = $this->query($sql);
01651 $row = $this->fetchObject($res);
01652 $size = $row->size;
01653 $this->freeResult( $res );
01654 return $size;
01655 }
01656
01666 public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds, $fname = "DatabaseIbm_db2::deleteJoin" ) {
01667 if ( !$conds ) {
01668 throw new DBUnexpectedError($this, 'Database::deleteJoin() called with empty $conds' );
01669 }
01670
01671 $delTable = $this->tableName( $delTable );
01672 $joinTable = $this->tableName( $joinTable );
01673 $sql = "DELETE FROM $delTable WHERE $delVar IN (SELECT $joinVar FROM $joinTable ";
01674 if ( $conds != '*' ) {
01675 $sql .= 'WHERE ' . $this->makeList( $conds, LIST_AND );
01676 }
01677 $sql .= ')';
01678
01679 $this->query( $sql, $fname );
01680 }
01681
01687 public function encodeBlob($b) {
01688 return new IBM_DB2Blob($b);
01689 }
01690
01696 public function decodeBlob($b) {
01697 return $b->getData();
01698 }
01699
01705 public function buildConcat( $stringList ) {
01706
01707
01708 return implode( ' || ', $stringList );
01709 }
01710
01716 public function extractUnixEpoch( $column ) {
01717
01718
01719 }
01720
01721 ######################################
01722 # Prepared statements
01723 ######################################
01724
01737 public function prepare( $sql, $func = 'DB2::prepare' ) {
01738 $stmt = db2_prepare($this->mConn, $sql, $this->mStmtOptions);
01739 return $stmt;
01740 }
01741
01746 public function freePrepared( $prepared ) {
01747 return db2_free_stmt($prepared);
01748 }
01749
01756 public function execute( $prepared, $args = null ) {
01757 if( !is_array( $args ) ) {
01758 # Pull the var args
01759 $args = func_get_args();
01760 array_shift( $args );
01761 }
01762 $res = db2_execute($prepared, $args);
01763 return $res;
01764 }
01765
01772 public function safeQuery( $query, $args = null ) {
01773
01774 $prepared = $this->prepare( $query, 'DB2::safeQuery' );
01775 if( !is_array( $args ) ) {
01776 # Pull the var args
01777 $args = func_get_args();
01778 array_shift( $args );
01779 }
01780 $retval = $this->execute( $prepared, $args );
01781 $this->freePrepared( $prepared );
01782 return $retval;
01783 }
01784
01792 public function fillPrepared( $preparedQuery, $args ) {
01793 reset( $args );
01794 $this->preparedArgs =& $args;
01795
01796 foreach ($args as $i => $arg) {
01797 db2_bind_param($preparedQuery, $i+1, $args[$i]);
01798 }
01799
01800 return $preparedQuery;
01801 }
01802
01806 public function setMode($mode) {
01807 $old = $this->mMode;
01808 $this->mMode = $mode;
01809 return $old;
01810 }
01811
01818 function bitNot($field) {
01819
01820 return 'BITNOT('.$bitField.')';
01821 }
01822
01830 function bitAnd($fieldLeft, $fieldRight) {
01831 return 'BITAND('.$fieldLeft.', '.$fieldRight.')';
01832 }
01833
01841 function bitOr($fieldLeft, $fieldRight) {
01842 return 'BITOR('.$fieldLeft.', '.$fieldRight.')';
01843 }
01844 }