00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 define( 'TABLE_METADATA', 'ezdbfile' );
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 require_once( 'lib/ezutils/classes/ezdebugsetting.php' );
00050 require_once( 'lib/ezutils/classes/ezdebug.php' );
00051
00052 class eZDBFileHandlerPgsqlBackend
00053 {
00054 function _connect()
00055 {
00056 if ( !isset( $GLOBALS['eZDBFileHandlerPgsqlBackend_dbparams'] ) )
00057 {
00058 $fileINI = eZINI::instance( 'file.ini' );
00059
00060 $params['host'] = $fileINI->variable( 'ClusteringSettings', 'DBHost' );
00061 $params['port'] = $fileINI->variable( 'ClusteringSettings', 'DBPort' );
00062 $params['dbname'] = $fileINI->variable( 'ClusteringSettings', 'DBName' );
00063 $params['user'] = $fileINI->variable( 'ClusteringSettings', 'DBUser' );
00064 $params['pass'] = $fileINI->variable( 'ClusteringSettings', 'DBPassword' );
00065 $params['chunk_size'] = $fileINI->variable( 'ClusteringSettings', 'DBChunkSize' );
00066
00067 $GLOBALS['eZDBFileHandlerPgsqlBackend_dbparams'] = $params;
00068 }
00069 else
00070 $params = $GLOBALS['eZDBFileHandlerPgsqlBackend_dbparams'];
00071
00072 $connStr = "host=$params[host] ";
00073 $connStr .= "port=$params[port] ";
00074 $connStr .= "dbname=$params[dbname] ";
00075 $connStr .= "user=$params[user] ";
00076 $connStr .= "password=$params[pass]";
00077 if ( !$this->db = pg_connect( $connStr ) )
00078 $this->_die( "Unable to connect to storage server." );
00079
00080 $this->dbparams = $params;
00081 }
00082
00083 function _delete( $filePath, $insideOfTransaction = false )
00084 {
00085 if ( !$metaData = $this->_fetchMetadata( $filePath ) )
00086 return false;
00087
00088 $result = true;
00089
00090 $insideOfTransaction || pg_query( $this->db, 'BEGIN' );
00091
00092 if ( !pg_query( "DELETE FROM " . TABLE_METADATA . " WHERE id=" . $metaData['id'] ) )
00093 {
00094 eZDebug::writeError( "Failed to delete file metadata: $filePath: " . pg_last_error( $this->db ) );
00095 $result = false;
00096 }
00097
00098 if ( !pg_lo_unlink( $metaData['lob_id'] ) )
00099 {
00100 eZDebug::writeError( "Failed to remove large object while deleting file '$filePath': " . pg_last_error( $this->db ) );
00101 $result = false;
00102 }
00103
00104 $insideOfTransaction || pg_query( $this->db, $result ? 'COMMIT' : 'ROLLBACK' );
00105
00106 return $result;
00107 }
00108
00109 function _deleteByRegex( $regex )
00110 {
00111 $regex = pg_escape_string( $regex );
00112 $sql = "SELECT name FROM " . TABLE_METADATA . " WHERE name ~ '$regex'";
00113 if ( !$res = pg_query( $this->db, $sql ) )
00114 {
00115 eZDebug::writeError( "Failed to delete files by regex: '$regex'" );
00116 return false;
00117 }
00118
00119 if ( !pg_num_rows( $res ) )
00120 {
00121 pg_free_result( $res );
00122 return true;
00123 }
00124
00125 while ( $row = pg_fetch_row( $res ) )
00126 {
00127 $deleteFilename = $row[0];
00128 $this->_delete( $deleteFilename );
00129 }
00130
00131 pg_free_result( $res );
00132 return true;
00133 }
00134
00135 function _deleteByWildcard( $wildcard )
00136 {
00137
00138 $regex = '^' . pg_escape_string( $wildcard ) . '$';
00139
00140 $regex = str_replace( array( '.' ),
00141 array( '\.' ),
00142 $regex );
00143
00144 $regex = str_replace( array( '?', '*', '{', '}', ',' ),
00145 array( '.', '.*', '(', ')', '|' ),
00146 $regex );
00147
00148 $sql = "SELECT name FROM " . TABLE_METADATA . " WHERE name ~ '$regex'" ;
00149 if ( !$res = pg_query( $this->db, $sql ) )
00150 {
00151 eZDebug::writeError( "Failed to delete files by wildcard: '$wildcard'" );
00152 return false;
00153 }
00154
00155 if ( !pg_num_rows( $res ) )
00156 {
00157 pg_free_result( $res );
00158 return true;
00159 }
00160
00161 while ( $row = pg_fetch_row( $res ) )
00162 {
00163 $deleteFilename = $row[0];
00164 $this->_delete( $deleteFilename );
00165 }
00166
00167 pg_free_result( $res );
00168 return true;
00169 }
00170
00171 function _deleteByLike( $like )
00172 {
00173 $like = pg_escape_string( $like );
00174 $sql = "SELECT name FROM " . TABLE_METADATA . " WHERE name like '$like'" ;
00175 if ( !$res = pg_query( $this->db, $sql ) )
00176 {
00177 eZDebug::writeError( "Failed to delete files by like: '$like'" );
00178 return false;
00179 }
00180
00181 if ( !pg_num_rows( $res ) )
00182 {
00183 pg_free_result( $res );
00184 return true;
00185 }
00186
00187 while ( $row = pg_fetch_row( $res ) )
00188 {
00189 $deleteFilename = $row[0];
00190 $this->_delete( $deleteFilename );
00191 }
00192
00193 pg_free_result( $res );
00194 return true;
00195 }
00196
00197 function _deleteByDirList( $dirList, $commonPath, $commonSuffix )
00198 {
00199
00200 foreach ( $dirList as $dirItem )
00201 {
00202 $sql = "SELECT name FROM " . TABLE_METADATA . " WHERE name like '$commonPath/$dirItem/$commonSuffix%'" ;
00203 if ( !$res = pg_query( $this->db, $sql ) )
00204 {
00205 eZDebug::writeError( "Failed to delete files in dir: '$commonPath/$dirItem/$commonSuffix%'" );
00206 return false;
00207 }
00208
00209 if ( !pg_num_rows( $res ) )
00210 {
00211 pg_free_result( $res );
00212 continue;
00213 }
00214
00215 while ( $row = pg_fetch_row( $res ) )
00216 {
00217 $deleteFilename = $row[0];
00218 $this->_delete( $deleteFilename );
00219 }
00220
00221 pg_free_result( $res );
00222 }
00223 return true;
00224 }
00225
00226 function _exists( $filePath )
00227 {
00228 $filePathHash = md5( $filePath );
00229 $res = pg_query( "SELECT COUNT(*) AS count FROM " . TABLE_METADATA . " WHERE name_hash='$filePathHash'" );
00230 $row = pg_fetch_row( $res );
00231 $count = $row[0];
00232 pg_free_result( $res );
00233 return $count;
00234 }
00235
00236 function __mkdir_p( $dir )
00237 {
00238
00239 $dirElements = explode( '/', $dir );
00240 if ( count( $dirElements ) == 0 )
00241 return true;
00242
00243 $result = true;
00244 $currentDir = $dirElements[0];
00245
00246 if ( $currentDir != '' && !file_exists( $currentDir ) && !mkdir( $currentDir, '0777' ))
00247 return false;
00248
00249 for ( $i = 1; $i < count( $dirElements ); ++$i )
00250 {
00251 $dirElement = $dirElements[$i];
00252 if ( strlen( $dirElement ) == 0 )
00253 continue;
00254
00255 $currentDir .= '/' . $dirElement;
00256
00257 if ( !file_exists( $currentDir ) && !mkdir( $currentDir, 0777 ) )
00258 return false;
00259
00260 $result = true;
00261 }
00262
00263 return $result;
00264 }
00265
00266 function _fetch( $filePath, $uniqueName = false )
00267 {
00268 $metaData = $this->_fetchMetadata( $filePath );
00269 if ( !$metaData )
00270 {
00271 eZDebug::writeNotice( "File '$filePath' does not exists while trying to fetch." );
00272 return false;
00273 }
00274
00275
00276 $tmpFilePath = substr_replace( $filePath, getmypid().'tmp', strrpos( $filePath, '.' ), 0 );
00277
00278 $this->__mkdir_p( dirname( $tmpFilePath ) );
00279 if ( !( $fp = fopen( $tmpFilePath, 'wb' ) ) )
00280 {
00281 eZDebug::writeError( "Cannot write to '$tmpFilePath' while fetching file." );
00282 return false;
00283 }
00284
00285
00286 pg_query( $this->db, "BEGIN" );
00287 $chunkSize = $this->dbparams['chunk_size'];
00288 $lobHandle = pg_lo_open( $this->db, $metaData['lob_id'], 'r' );
00289 while ( $chunk = pg_lo_read( $lobHandle, $chunkSize ) )
00290 fwrite( $fp, $chunk );
00291 pg_lo_close( $lobHandle );
00292 pg_query( $this->db, "COMMIT" );
00293
00294
00295 fclose( $fp );
00296 if ( ! $uniqueName === true )
00297 {
00298 include_once( 'lib/ezfile/classes/ezfile.php' );
00299 eZFile::rename( $tmpFilePath, $filePath );
00300 }
00301 else
00302 {
00303 $filePath = $tmpFilePath;
00304 }
00305 return $filePath;
00306 }
00307
00308 function _fetchContents( $filePath )
00309 {
00310 $metaData = $this->_fetchMetadata( $filePath );
00311 if ( !$metaData )
00312 {
00313 eZDebug::writeNotice( "File '$filePath' does not exists while trying to fetch its contents." );
00314 return false;
00315 }
00316
00317
00318 $contents = '';
00319 $chunkSize = $this->dbparams['chunk_size'];
00320 pg_query( $this->db, "BEGIN" );
00321 $lobHandle = pg_lo_open( $this->db, $metaData['lob_id'], 'r' );
00322 while ( $chunk = pg_lo_read( $lobHandle, $chunkSize ) )
00323 $contents .= $chunk;
00324 pg_lo_close( $lobHandle );
00325 pg_query( $this->db, "COMMIT" );
00326
00327 return $contents;
00328 }
00329
00330 function _fetchMetadata( $filePath )
00331 {
00332 $filePathHash = md5( $filePath );
00333 if( !( $res = pg_query( $this->db, "SELECT * FROM " . TABLE_METADATA . " WHERE name_hash='$filePathHash'" ) ) )
00334 {
00335 eZDebug::writeError( "Error fetching file metadata: " . pg_last_error( $this->db ) );
00336 return false;
00337 }
00338 if ( ( $nrows = pg_num_rows( $res ) ) > 1 )
00339 eZDebug::writeError( "Duplicate file '$filePath' found." );
00340 elseif ( $nrows == 0 )
00341 return false;
00342 $row = pg_fetch_array( $res, null, PGSQL_ASSOC );
00343 pg_free_result( $res );
00344
00345 return $row;
00346 }
00347
00348 function _store( $filePath, $datatype, $scope )
00349 {
00350 if ( !is_readable( $filePath ) )
00351 {
00352 eZDebug::writeError( "Unable to store file '$filePath' since it is not readable.", 'ezdbfilehandlerpgsqlbackend' );
00353 return;
00354 }
00355
00356 if ( !$fp = @fopen( $filePath, 'rb' ) )
00357 {
00358 eZDebug::writeError( "Cannot read '$filePath'.", 'ezdbfilehandlerpgsqlbackend' );
00359 return;
00360 }
00361
00362
00363 $filePathHash = md5( $filePath );
00364 $filePathEscaped = pg_escape_string( $filePath );
00365 $datatype = pg_escape_string( $datatype );
00366 $scope = pg_escape_string( $scope );
00367 $fileMTime = (int) filemtime( $filePath );
00368 $contentLength = (int) filesize( $filePath );
00369
00370
00371 pg_query( $this->db, 'BEGIN' );
00372
00373
00374 $lobOid = pg_lo_create( $this->db );
00375 $lobHandle = pg_lo_open( $this->db, $lobOid, 'w');
00376 $chunkSize = $this->dbparams['chunk_size'];
00377 while ( !feof( $fp ) )
00378 {
00379 $chunk = fread( $fp, $chunkSize );
00380
00381 if ( pg_lo_write( $lobHandle, $chunk ) === false )
00382 {
00383 eZDebug::writeNotice( "Failed to insert file data chunk while storing. Possible race condition: " . $sql );
00384 pg_query( $this->db, 'ROLLBACK' );
00385 return;
00386 }
00387 }
00388 pg_lo_close( $lobHandle );
00389 fclose( $fp );
00390
00391
00392 if ( $row = $this->_fetchMetadata( $filePath ) )
00393 {
00394
00395
00396 if ( pg_lo_unlink( $row['lob_id'] ) === false )
00397 {
00398 eZDebug::writeError( "Error removing large object while storing file: " . pg_last_error( $this->db ) );
00399 pg_query( $this->db, 'ROLLBACK' );
00400 return;
00401 }
00402
00403 $sql = "UPDATE " . TABLE_METADATA . " SET ";
00404 $sql .= "name='$filePathEscaped', name_hash='$filePathHash', ";
00405 $sql .= "datatype='$datatype', scope='$scope', ";
00406 $sql .= "size=$contentLength, mtime=$fileMTime, lob_id=$lobOid ";
00407 $sql .= "WHERE id=" . $row['id'];
00408 }
00409 else
00410 {
00411
00412 $sql = "INSERT INTO " . TABLE_METADATA . " (name, name_hash, datatype, scope, size, mtime, lob_id) ";
00413 $sql .= "VALUES ('$filePathEscaped', '$filePathHash', '$datatype', ";
00414 $sql .= "'$scope', '$contentLength', '$fileMTime', $lobOid)";
00415 }
00416
00417 if ( !pg_query( $this->db, $sql ) )
00418 {
00419 eZDebug::writeError( "Error storing file '$filePath': " . pg_last_error( $this->db ), 'ezdbfilehandlerpgsqlbackend' );
00420 pg_query( $this->db, 'ROLLBACK');
00421 return;
00422 }
00423
00424
00425 pg_query( $this->db, 'COMMIT');
00426 }
00427
00428 function _storeContents( $filePath, $contents, $scope, $datatype )
00429 {
00430
00431
00432
00433 $filePathHash = md5( $filePath );
00434 $filePathEscaped = pg_escape_string( $filePath );
00435 $datatype = pg_escape_string( $datatype );
00436 $scope = pg_escape_string( $scope );
00437 $fileMTime = time();
00438 $contentLength = strlen( $contents );
00439
00440
00441 pg_query( $this->db, 'BEGIN' );
00442
00443
00444 $lobOid = pg_lo_create( $this->db );
00445 $lobHandle = pg_lo_open( $this->db, $lobOid, 'w');
00446 $chunkSize = $this->dbparams['chunk_size'];
00447 for ( $pos = 0; $pos < $contentLength; $pos += $chunkSize )
00448 {
00449 $chunk = substr( $contents, $pos, $chunkSize );
00450
00451 if ( pg_lo_write( $lobHandle, $chunk ) === false )
00452 {
00453 eZDebug::writeNotice( "Failed to insert file data chunk while storing contents. Possible race condition: " . $sql );
00454 pg_query( $this->db, 'ROLLBACK' );
00455 return;
00456 }
00457 }
00458 pg_lo_close( $lobHandle );
00459
00460
00461 if ( $row = $this->_fetchMetadata( $filePath ) )
00462 {
00463
00464
00465 if ( pg_lo_unlink( $row['lob_id'] ) === false )
00466 {
00467 eZDebug::writeError( "Error removing large object while storing file contents: " . pg_last_error( $this->db ) );
00468 pg_query( $this->db, 'ROLLBACK' );
00469 return;
00470 }
00471
00472 $sql = "UPDATE " . TABLE_METADATA . " SET ";
00473 $sql .= "name='$filePathEscaped', name_hash='$filePathHash', ";
00474 $sql .= "datatype='$datatype', scope='$scope', ";
00475 $sql .= "size=$contentLength, mtime=$fileMTime, lob_id=$lobOid ";
00476 $sql .= "WHERE id=" . $row['id'];
00477 }
00478 else
00479 {
00480
00481 $sql = "INSERT INTO " . TABLE_METADATA . " (name, name_hash, datatype, scope, size, mtime, lob_id) ";
00482 $sql .= "VALUES ('$filePathEscaped', '$filePathHash', '$datatype', ";
00483 $sql .= "'$scope', '$contentLength', '$fileMTime', $lobOid)";
00484 }
00485
00486 if ( !pg_query( $this->db, $sql ) )
00487 {
00488 eZDebug::writeError( $sql, "Error storing file '$filePath' contents: " .
00489 pg_last_error( $this->db ), 'ezdbfilehandlerpgsqlbackend' );
00490 pg_query( $this->db, 'ROLLBACK');
00491 return;
00492 }
00493
00494
00495 pg_query( $this->db, 'COMMIT');
00496 }
00497
00498 function _copy( $srcFilePath, $dstFilePath )
00499 {
00500
00501 $srcMetadata = $this->_fetchMetadata( $srcFilePath );
00502 if ( !$srcMetadata )
00503 return false;
00504
00505 pg_query( $this->db, 'BEGIN' );
00506
00507
00508
00509 if ( $this->_exists( $dstFilePath ) )
00510 $this->_delete( $dstFilePath, true );
00511
00512
00513 $dstLobOid = pg_lo_create( $this->db );
00514 $dstLobHandle = pg_lo_open( $this->db, $dstLobOid, 'w');
00515 $srcLobHandle = pg_lo_open( $this->db, $srcMetadata['lob_id'], 'r' );
00516 $chunkSize = $this->dbparams['chunk_size'];
00517 while ( $chunk = pg_lo_read( $srcLobHandle, $chunkSize ) )
00518 {
00519 if ( pg_lo_write( $dstLobHandle, $chunk ) === false )
00520 {
00521 eZDebug::writeError( "Failed to write data chunk while copying file '$srcFilePath': " .
00522 pg_last_error( $this->db ), 'ezdbfilehandlerpgsqlbackend' );
00523 pg_lo_close( $srcLobHandle );
00524 pg_lo_close( $dstLobHandle );
00525 pg_query( $this->db, 'ROLLBACK' );
00526 return false;
00527 }
00528 }
00529 pg_lo_close( $srcLobHandle );
00530 pg_lo_close( $dstLobHandle );
00531
00532
00533 $sql = "INSERT INTO " . TABLE_METADATA . " (name, name_hash, datatype, scope, size, mtime, lob_id) VALUES";
00534 $sql .= sprintf( "('%s', '%s', '%s', '%s', %d, %d, %d)",
00535
00536 pg_escape_string( $dstFilePath ), md5( $dstFilePath ),
00537 $srcMetadata['datatype'], $srcMetadata['scope'], $srcMetadata['size'], $srcMetadata['mtime'],
00538 $dstLobOid );
00539 if ( !$res = pg_query( $this->db, $sql ) )
00540 {
00541 eZDebug::writeError( $srcFilePath, "Failed to insert file metadata on copying." );
00542 pg_query( $this->db, 'ROLLBACK' );
00543 return false;
00544 }
00545
00546 pg_query( $this->db, 'COMMIT' );
00547
00548 return true;
00549 }
00550
00551 function _linkCopy( $srcPath, $dstPath )
00552 {
00553 return $this->_copy( $srcPath, $dstPath );
00554 }
00555
00556 function _rename( $srcFilePath, $dstFilePath )
00557 {
00558
00559
00560 pg_query( $this->db, 'BEGIN' );
00561
00562
00563 $srcMetadata = $this->_fetchMetadata( $srcFilePath );
00564 if ( !$srcMetadata )
00565 {
00566
00567 pg_query( $this->db, 'ROLLBACK' );
00568 eZDebug::writeWarning( "File '$srcFilePath' to rename does not exist",
00569 'ezdbfilehandlerpgsqlbackend' );
00570 return false;
00571 }
00572
00573
00574 $dstMetadata = $this->_fetchMetadata( $dstFilePath );
00575 if ( $dstMetadata )
00576 $this->_delete( $dstFilePath, true );
00577
00578
00579 $sql = sprintf( "UPDATE %s SET name='%s', name_hash='%s' WHERE id=%d",
00580 TABLE_METADATA,
00581 pg_escape_string( $dstFilePath ), md5( $dstFilePath ),
00582 $srcMetadata['id'] );
00583
00584 if ( !pg_query( $this->db, $sql ) )
00585 {
00586 eZDebug::writeError( "Error renaming file '$srcFilePath': " .
00587 pg_last_error( $this->db ), 'ezdbfilehandlerpgsqlbackend' );
00588 pg_query( $this->db, 'ROLLBACK');
00589 return false;
00590 }
00591
00592 pg_query( $this->db, 'COMMIT' );
00593 return true;
00594 }
00595
00596 function _passThrough( $filePath )
00597 {
00598 $metaData = $this->_fetchMetadata( $filePath );
00599 if ( !$metaData )
00600 return false;
00601
00602 pg_query( $this->db, "BEGIN" );
00603 $lobHandle = pg_lo_open( $this->db, $metaData['lob_id'], 'r' );
00604 $chunkSize = $this->dbparams['chunk_size'];
00605 while ( $chunk = pg_lo_read( $lobHandle, $chunkSize ) )
00606 echo $chunk;
00607 pg_lo_close( $lobHandle );
00608 pg_query( $this->db, "COMMIT" );
00609
00610 return true;
00611 }
00612
00613 function _getFileList( $skipBinaryFiles, $skipImages )
00614 {
00615 $query = 'SELECT name FROM ' . TABLE_METADATA;
00616
00617
00618 $filters = array();
00619 if ( $skipBinaryFiles )
00620 $filters[] = "'binaryfile'";
00621 if ( $skipImages )
00622 $filters[] = "'image'";
00623 if ( $filters )
00624 $query .= ' WHERE scope NOT IN (' . join( ', ', $filters ) . ')';
00625
00626 $rslt = pg_query( $this->db, $query );
00627 if ( !$rslt )
00628 {
00629 eZDebug::writeError( pg_last_error( $this->db ) );
00630 return false;
00631 }
00632
00633 $filePathList = array();
00634 while ( $row = pg_fetch_row( $rslt ) )
00635 $filePathList[] = $row[0];
00636
00637 pg_free_result( $rslt );
00638
00639 return $filePathList;
00640 }
00641
00642 function _die( $msg, $sql = null )
00643 {
00644 eZDebug::writeError( $sql, "$msg: " . pg_last_error( $this->db ) );
00645
00646 if( @include_once( '../bt.php' ) )
00647 {
00648 bt();
00649 die( $msg );
00650 }
00651 }
00652
00653 var $db = null;
00654 }
00655
00656 ?>