/* * This file is part of rasdaman community. * * Rasdaman community is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Rasdaman community is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rasdaman community. If not, see . * * Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / rasdaman GmbH. * * For more information please see * or contact Peter Baumann via . */ #include "mymalloc/mymalloc.h" // This is -*- C++ -*- /************************************************************* * * * PURPOSE: * implements blobtile interface using the PostgreSQL DBMS. * * * COMMENTS: * - uses LO blob method, this has been tested to be faster than bytea * - RMDBG macros generate so weird output that I added DEBUG macros * although this is very ugly (2 trace facilities in parallel) * - exceptions thrown are r_Error, either directly or through common * function generateException() (reladminif) which throws r_Ebase_dbms * ************************************************************/ using namespace std; static const char rcsid[] = "@(#)blobif,BLOBTile: $Id: blobtile.ec,v 1.8 2003/12/27 23:19:03 rasdev Exp $"; #include #include /* atoi */ // PG stuff: #include "libpq-fe.h" /* C interface to PgSQL */ #include "libpq/libpq-fs.h" /* large object (lo) api */ // simple trace facility // trace macros are activated through externally defined compile variable DEBUG #include "debug-srv.hh" // general embedded SQL related definitions EXEC SQL include "../reladminif/sqlglobals.h"; // libpg connection maintenance // must have been initiated before use (see databasif.pgc) extern PGconn *pgConn; // all the DBMS independent code is factored out and // will be included in the resulting .c file //#include "blobtile.cc" #include "blobtile.hh" #include "raslib/rmdebug.hh" #include "reladminif/objectbroker.hh" #include "reladminif/dbref.hh" #include "reladminif/sqlerror.hh" #include "inlinetile.hh" // update blob in ras_tiles table, identified by variable myOId (from blobtile.cc), update map ref void BLOBTile::updateInDb() throw (r_Error) { RMDBGENTER(3, RMDebug::module_blobif, "BLOBTile", "updateInDb() " << myOId); ENTER( "BLOBTile::updateInDb" ); #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; Oid tile; long indbmyoId; long indbmyoid = 0; short dataformat = 0; EXEC SQL END DECLARE SECTION; dataformat = dataFormat; indbmyoid = myOId.getCounter(); // (1) --- get tuple TALK( "EXEC SQL SELECT Tile INTO :tile FROM RAS_TILES WHERE BlobId = " << indbmyoid ); EXEC SQL SELECT TILE INTO :tile FROM RAS_TILES WHERE BlobId = :indbmyoid; if (SQLCODE < 0) { RMInit::logOut << "BLOBTile::updateInDb(): error: cannot get blob, SQLCODE " << SQLCODE << endl; LEAVE( "BLOBTile::updateInDb(): error: cannot get blob, SQLCODE " << SQLCODE ); check("Select blob oid"); generateException(); } else if (SQLCODE == SQLNODATAFOUND) { RMInit::logOut << "BLOBTile::updateInDb(): error: object not in database" << endl; LEAVE( "BLOBTile::updateInDb(): error: object not in database" ); throw r_Error(r_Error::r_Error_ObjectUnknown); } */ #endif //NOTYET // alternative solution for now: Oid tile; long indbmyoId = 0; long indbmyoid = 0; short dataformat = 0; char pgQuery[SQL_QUERY_BUFFER_SIZE]; PGresult *pgResult = NULL; // (1) --- get tuple dataformat = dataFormat; indbmyoid = myOId.getCounter(); (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT Tile FROM RAS_TILES WHERE BlobId = %d", indbmyoid ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "BLOBTile::updateInDb() libpq 'select tile' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } else if (PQntuples(pgResult) != 1) { LEAVE( "BLOBTile::updateInDb() libpq 'select' did not yield 1 result but " << PQntuples(pgResult) ); PQclear( pgResult ); generateException(); } tile = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result PQclear( pgResult ); // (2) --- open, write, close blob TALK( "lo_open()" ); int fd = lo_open( pgConn, tile, INV_WRITE ); // no error code mentioned in manual TALK( "lo_write() for fd " << fd << " and " << size << " bytes" ); int loResult = lo_write( pgConn, fd, cells, size ); if (loResult < 0) { RMInit::logOut << "BLOBTile::updateInDb() cannot write blob, error: " << loResult << endl; LEAVE( "BLOBTile::updateInDb: cannot write blob, error " << loResult ); generateException(); } else if (loResult != size) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") insert: wrote " << loResult << " instead of " << size << " bytes" << endl; LEAVE( "BLOBTile::updateInDb: wrote " << loResult << " instead of " << size << " bytes" ); generateException(); } TALK( "lo_close()" ); loResult = lo_close( pgConn, fd ); if (loResult < 0) // can't close, don't know if data are written { RMInit::logOut << "BLOBTile::updateInDb() ignoring lo_close() error: " << PQerrorMessage(pgConn) << endl; LEAVE( "BLOBTile::updateInDb: cannot lo_close(): " << PQerrorMessage(pgConn) ); generateException(); } #ifdef NOTYET // should be in future /* // (3) --- update data format // NOTE: we assume the blob already exists and do not change the oid TALK( "EXEC SQL UPDATE RAS_TILES SET DataFormat = " << dataformat << " WHERE BlobId = " << indbmyoid ); EXEC SQL UPDATE RAS_TILES SET DataFormat = :dataformat WHERE BlobId = :indbmyoid; if (check("Update Dataformat")) generateException(); */ #endif //NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "UPDATE RAS_TILES SET DataFormat = %d WHERE BlobId = %d", dataformat, indbmyoid ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "BLOBTile::updateInDb() libpq 'update dataformat' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } // (4) --- update map ref DBObject::updateInDb(); LEAVE( "BLOBTile::updateInDb, myOId=" << myOId ); RMDBGEXIT(3, RMDebug::module_blobif, "BLOBTile", "updateInDb() " << myOId); } // updateInDb() // insert new blob into ras_tiles table, update map ref // tuple is identified by blobtile.cc var myOId // data is taken from buffer 'cells' containing 'size' bytes void BLOBTile::insertInDb() throw (r_Error) { RMDBGENTER(3, RMDebug::module_blobif, "BLOBTile", "insertInDb() " << myOId); ENTER( "BLOBTile::insertInDb" ); /* #ifdef NOTYET EXEC SQL BEGIN DECLARE SECTION; #endif NOTYET */ Oid blobOid; long indbmyOId2; short dataformat2; Oid tile2; /* #ifdef NOTYET EXEC SQL END DECLARE SECTION; #endif NOTYET */ dataformat2 = dataFormat; // prelim: char pgQuery[SQL_QUERY_BUFFER_SIZE]; PGresult *pgResult = NULL; indbmyOId2 = myOId.getCounter(); TALK( "myOId.getCounter = " << indbmyOId2 ); // (1) --- open, write, close blob; generates new 'oid' for subsequent storage in tuple TALK( "lo_creat()" ); blobOid = lo_creat( pgConn, INV_READ|INV_WRITE ); // create -- not clear what INV_* here means so indicate all if (blobOid == 0) { RMInit::logOut << "BLOBTile::insertInDb() cannot create blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "BLOBTile::insertInDb(): cannot create blob, error " << PQerrorMessage(pgConn) ); generateException(); } TALK( "lo_open() for oid " << blobOid ); int fd = lo_open( pgConn, blobOid, INV_WRITE ); // no error code indicated, 0 seems to be no error TALK( "lo_write() for fd " << fd << " and " << size << " bytes" ); int loResult = lo_write( pgConn, fd, cells, size ); if (loResult < 0) { RMInit::logOut << "BLOBTile::insertInDb() cannot write blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "BLOBTile::insertInDb(): cannot write blob, error " << PQerrorMessage(pgConn) ); generateException(); } else if (loResult != size) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") insert: wrote " << loResult << " instead of " << size << " bytes" << endl; LEAVE( "BLOBTile::insertInDb(): wrote " << loResult << " instead of " << size << " bytes" ); generateException(); } TALK( "lo_close()" ); loResult = lo_close( pgConn, fd ); if (loResult < 0) // cannot close, don't know if data are there { RMInit::logOut << "BLOBTile::insertInDb() ignoring lo_close() error: " << PQerrorMessage(pgConn) << endl; LEAVE( "BLOBTile::insertInDb(): ignoring lo_close() error: " << PQerrorMessage(pgConn) ); generateException(); } /* #ifdef NOTYET // (2) --- insert tuple into db TALK( "EXEC SQL INSERT INTO RAS_TILES ( BlobId, DataFormat, Tile ) VALUES (" << indbmyOId2 << "," << dataformat2 << ", " << blobOid << ")" ); EXEC SQL INSERT INTO RAS_TILES ( BlobId, DataFormat, Tile) VALUES ( :indbmyOId2, :dataformat2, :blobOid ); #endif NOTYET */ // alternative solution for now: // (2) --- insert tuple into db (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "INSERT INTO RAS_TILES ( BlobId, DataFormat, Tile) VALUES ( %d, %d, %d )", indbmyOId2, dataformat2, blobOid ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "BLOBTile::insertInDb() libpq 'insert' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); // FIXME: other sources have here an updateInDb(); // (3) --- update map ref DBObject::insertInDb(); LEAVE( "BLOBTile::insertInDb(), myOId=" << myOId ); RMDBGEXIT(3, RMDebug::module_blobif, "BLOBTile", "insertInDb() " << myOId); } // insertInDb() // delete one tuple from ras_tiles table, update map ref // tuple is identified by blobtile.cc var myOId void BLOBTile::deleteFromDb() throw (r_Error) { RMDBGENTER(3, RMDebug::module_blobif, "BLOBTile", "deleteFromDb() " << myOId); ENTER( "BLOBTile::deleteFromDb" ); /* #ifdef NOTYET EXEC SQL BEGIN DECLARE SECTION; #endif NOTYET */ long blobId; // blob tuple primary key Oid blobOid; // blob oid "ptr" /* #ifdef NOTYET EXEC SQL END DECLARE SECTION; #endif NOTYET */ // get counter value (primary key) from oid blobId = myOId.getCounter(); /* #ifdef NOTYET // (1) --- get tile tuple TALK( "EXEC SQL SELECT Tile INTO :blobOid FROM RAS_TILES WHERE BlobId = " << blobId ); EXEC SQL SELECT Tile INTO :blobOid FROM RAS_TILES WHERE BlobId = :blobId; if (SQLCODE < 0) { LEAVE( "BLOBTile::deleteFromDb(): Fatal error: cannot find tuple for oid " << blobId ); check("BLOBTile::deleteFromDb() - find tuple\0"); generateException(); } #endif NOTYET */ char pgQuery[SQL_QUERY_BUFFER_SIZE]; (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT Tile FROM RAS_TILES WHERE BlobId = %d", blobId ); TALK( pgQuery ); PGresult *pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult)) { LEAVE( "BLOBTile::deleteFromDb() libpq 'select tile' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } else if (PQntuples(pgResult) != 1) { LEAVE( "BLOBTile::deleteFromDb() libpq 'select' did not yield 1 result but " << PQntuples(pgResult) ); PQclear( pgResult ); generateException(); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result PQclear( pgResult ); // FIXME: other sources have here an updateInDb(); // (2) --- delete blob identified by blobOid TALK( "lo_unlink()" ); int loResult = lo_unlink( pgConn, blobOid ); if (loResult < 0) { LEAVE( "BLOBTile::deleteFromDb(): cannot unlink blob with id '" << blobOid << "' for oid " << blobId << PQerrorMessage(pgConn) ); generateException(); } /* #ifdef NOTYET // (3) --- delete tuple from table TALK( "EXEC SQL DELETE FROM RAS_TILES WHERE BlobId = " << blobId ); EXEC SQL DELETE FROM RAS_TILES WHERE BlobId = :blobId; if (SQLCODE < 0) { LEAVE( "BLOBTile::deleteFromDb: Fatal error: cannot delete tuple for oid " << blobId << ", error: " << SQLCODE ); check("BLOBTile::deleteFromDb() - delete tuple\0"); generateException(); } #endif NOTYET */ (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "DELETE FROM RAS_TILES WHERE BlobId = ", blobOid ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "BLOBTile::deleteFromDb() libpq 'delete tuple' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); // FIXME: other sources have here an updateInDb(); // update map ref DBObject::deleteFromDb(); LEAVE( "BLOBTile::deleteFromDb, myOId=" << myOId ); RMDBGEXIT(3, RMDebug::module_blobif, "BLOBTile", "deleteFromDb() " << myOId); } // delete a range of tuple(s) from ras_tiles table, update map ref // tuples are identified by target and a range void BLOBTile::kill(const OId& target, unsigned int range) { RMDBGENTER(0, RMDebug::module_blobif, "BLOBTile", "kill(" << target << ", " << range <<")"); ENTER( "BLOBTile::kill, target=" << target << ", range=" << range ); /* #ifdef NOTYET EXEC SQL BEGIN DECLARE SECTION; #endif NOTYET */ long indbmyOId5; long indbmyOId6; long blobId; // blob tuple primary key Oid blobOid; // blob oid "ptr" /* #ifdef NOTYET EXEC SQL END DECLARE SECTION; #endif NOTYET */ char pgQuery[SQL_QUERY_BUFFER_SIZE]; PGresult *pgResult = NULL; // query result DBObject* targetobj = NULL; if (range == 0) // single tuple { // (1) --- delete form cache targetobj = ObjectBroker::isInMemory(target); if (targetobj) { targetobj->setPersistent(false); } // (2) --- free blob indbmyOId5 = target.getCounter(); /* #ifdef NOTYET TALK( "EXEC SQL SELECT Tile INTO :blobOid FROM RAS_TILES WHERE BlobId = " << indbmyOId5 ); EXEC SQL SELECT Tile INTO :blobOid FROM RAS_TILES WHERE BlobId = :indbmyOId5; if (SQLCODE < 0) { LEAVE( "BLOBTile::kill(): Fatal error: cannot find tuple for oid " << blobId ); check("BLOBTile::kill() - find tuple\0"); generateException(); } else if (SQLCODE != SQLNODATAFOUND) // we've got a tuple { // delete blob identified by blobOid } // else there is no tuple - this is inconsistent, but nothing to delete #endif NOTYET */ (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT Tile FROM RAS_TILES WHERE BlobId = %d", indbmyOId5 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "BLOBTile::kill() libpq 'select tile' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } else if (PQntuples(pgResult) != 1) { LEAVE( "BLOBTile::kill() libpq 'select' did not yield 1 result but " << PQntuples(pgResult) ); PQclear( pgResult ); generateException(); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result PQclear( pgResult ); // delete blob identified by blobOid int loResult = lo_unlink( pgConn, blobOid ); if (loResult < 0) { LEAVE( "BLOBTile::kill(): cannot unlink blob with id '" << blobOid << "' for oid " << blobId << ": " << PQerrorMessage(pgConn) ); check("BLOBTile::kill() - unlink blob\0"); generateException(); } /* #ifdef NOTYET // (3) --- delete tuple TALK( "EXEC SQL DELETE FROM RAS_TILES WHERE BlobId = " << indbmyOId5 ); EXEC SQL DELETE FROM RAS_TILES WHERE BlobId = :indbmyOId5; if (SQLCODE != SQLOK) { if (SQLCODE == SQLNODATAFOUND) // was: NODATA -- PB 2005-feb-13 { if (target.getType() == OId::INLINETILEOID) { InlineTileId t(target); if (!t.is_null()) t->setPersistent(false); } //else: this tile has been deleted before } else { check("BLOBTile::kill()\0"); generateException(); } } #endif NOTYET */ (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "DELETE FROM RAS_TILES WHERE BlobId = %d", indbmyOId5 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "BLOBTile::kill() libpq 'delete single' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } else if (PQntuples(pgResult) == 0) { if (target.getType() == OId::INLINETILEOID) { InlineTileId t(target); if (!t.is_null()) t->setPersistent(false); } //else: this tile has been deleted before } PQclear( pgResult ); } else { // (1) --- iterate over cache and remove DBObjectPMap& mapRef = ObjectBroker::getMap(target.getType()); DBObjectPMap::iterator it = mapRef.begin(); DBObjectPMap::iterator theEnd = mapRef.end(); OId end(target.getCounter() + range, target.getType()); while (it != theEnd) { if (target <= (const OId&)(*it).first && (*it).first <= (const OId&)end) { (*it).second->setPersistent(false); } } // (2) --- iterate over db and remove /* #ifdef NOTYET TALK( "EXEC SQL DECLARE DeleteLoop CURSOR FOR SELECT Tile FROM RAS_TILES WHERE " << indbmyOId5 << " <= BlobId AND BlobId <= " << indbmyOId6 ); EXEC SQL DECLARE DeleteLoop CURSOR FOR SELECT Tile FROM RAS_TILES WHERE :indbmyOId5 <= BlobId AND BlobId <= :indbmyOId6; if (SQLCODE < 0) { LEAVE( "BLOBTile::kill: Fatal error: cannot prepare cursor for oid " << blobId << " to " << indbmyOId6 ); check("BLOBTile::kill() - prepare cursor\0"); generateException(); } #endif NOTYET */ (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "DECLARE DeleteLoop CURSOR FOR SELECT Tile FROM RAS_TILES WHERE %d <= BlobId AND BlobId <= %d", indbmyOId5, indbmyOId6 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "BLOBTile::kill() libpq 'select for deleteLoop' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } // loop over elements & delete each one do { /* #ifdef NOTYET TALK( "EXEC SQL FETCH NEXT FROM DeleteLoop INTO :blobOid" ); EXEC SQL FETCH NEXT FROM DeleteLoop INTO :blobOid; if (SQLCODE < 0) { LEAVE( "BLOBTile::kill: Fatal error: cannot fetch next tuple" ); check("BLOBTile::kill() - fetch next tuple\0"); generateException(); } #endif NOTYET */ (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "FETCH NEXT FROM DeleteLoop" ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "BLOBTile::kill() libpq 'fetch next' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } else if (PQntuples(pgResult) != 1) { LEAVE( "BLOBTile::kill() libpq 'fetch' did not yield 1 result but " << PQntuples(pgResult) ); PQclear( pgResult ); generateException(); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result // delete blob identified by blobOid int loResult = lo_unlink( pgConn, blobOid ); if (loResult < 0) { LEAVE( "BLOBTile::kill: Fatal error: cannot unlink blob with id '" << blobOid << "' for oid " << blobId ); check("BLOBTile::kill() - unlink blob\0"); generateException(); } /* #ifdef NOTYET } while (SQLCODE == SQLOK || SQLCODE < 0); #endif NOTYET */ // FIXME: what about errors? continue?? } while (PQresultStatus(pgResult) != PGRES_TUPLES_OK); PQclear( pgResult ); // (3) --- delete tuples in db indbmyOId5 = target.getCounter(); indbmyOId6 = end.getCounter(); /* #ifdef NOTYET TALK( "EXEC SQL DELETE FROM RAS_TILES WHERE " << indbmyOId5 << " <= BlobId AND BlobId <= " << indbmyOId6 ); EXEC SQL DELETE FROM RAS_TILES WHERE :indbmyOId5 <= BlobId AND BlobId <= :indbmyOId6; if ((SQLCODE != SQLOK) && (SQLCODE != SQLNODATAFOUND)) { check("BLOBTile::kill()\0"); generateException(); } #endif NOTYET */ (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "DELETE FROM RAS_TILES WHERE %d <= BlobId AND BlobId <= %d", indbmyOId5, indbmyOId6 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "BLOBTile::kill() libpq 'delete range' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); } LEAVE( "BLOBTile::kill" ); RMDBGEXIT(0, RMDebug::module_blobif, "BLOBTile", "kill(" << target << " " << target.getType() << ")"); } // read tuple from ras_tiles, identified by blobtile.cc var myOId // allocates necessary mem into ptr 'cells' and fills it; must be freed elsewhere // external var 'size' is set to the number of bytes read void BLOBTile::readFromDb() throw (r_Error) { RMDBGENTER(3, RMDebug::module_blobif, "BLOBTile", "readFromDb() " << myOId); ENTER( "BLOBTile::readFromDb" ); #ifdef RMANBENCHMARK DBObject::readTimer.resume(); #endif /* #ifdef NOTYET EXEC SQL BEGIN DECLARE SECTION; #endif NOTYET */ Oid blobOid; long indbmyOId3; short dataformat3; short indicatorr3; /* #ifdef NOTYET EXEC SQL END DECLARE SECTION; #endif NOTYET */ PGresult *pgResult = NULL; // PostgreSQL call return values indbmyOId3 = myOId.getCounter(); // (1) --- access tuple /* #ifdef NOTYET TALK( "EXEC SQL SELECT Tile, DataFormat INTO :blobOid, :dataformat3 FROM RAS_TILES WHERE BlobId = " << indbmyOId3 ); EXEC SQL SELECT Tile, DataFormat INTO :blobOid, :dataformat3 FROM RAS_TILES WHERE BlobId = :indbmyOId3; if (SQLCODE < 0) { RMInit::logOut << "BLOBTile::readFromDb() Fatal error during RAS_TILES read: got SQLCODE " << SQLCODE << " for oid " << indbmyOId3 << endl; LEAVE( "BLOBTile::readFromDb: Fatal error during RAS_TILES read: got SQLCODE " << SQLCODE << " for oid " << indbmyOId3 ); check("BLOBTile::readFromDb() - find tuple\0"); generateException(); } else if (SQLCODE == SQLNODATAFOUND) // we've got no tuple { RMInit::logOut << "BLOBTile::readFromDb() error: object unknown for oid " << indbmyOId3 << endl; LEAVE( "BLOBTile::readFromDb: Error: object unknown for oid " << indbmyOId3 ); throw r_Error(r_Error::r_Error_ObjectUnknown); } #endif NOTYET */ char pgQuery[SQL_QUERY_BUFFER_SIZE]; (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT Tile, DataFormat FROM RAS_TILES WHERE BlobId = %d", indbmyOId3 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "BLOBTile::readFromDb() libpq 'select' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } else if (PQntuples(pgResult) != 1) { LEAVE( "BLOBTile::readFromDb() libpq 'select' did not yield 1 result but " << PQntuples(pgResult) ); PQclear( pgResult ); throw r_Error(r_Error::r_Error_ObjectUnknown); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract 1st value from result dataformat3 = atoi( PQgetvalue( pgResult, 0, 1 ) ); // extract 2nd value from result PQclear( pgResult ); // we have a tuple, extract data format dataFormat = (r_Data_Format)dataformat3; currentFormat = (r_Data_Format)dataformat3; TALK( "got dataFormat " << dataFormat ); // (2) --- open, read, close blob TALK( "lo_open()" ); int fd = lo_open( pgConn, blobOid, INV_READ ); // open; manual tells no error indication TALK( "lo_lseek() end" ); size = lo_lseek( pgConn, fd, 0, SEEK_END ); // determine blob size; FIXME: more efficient method?? TALK( "lo_lseek() start" ); (void) lo_lseek( pgConn, fd, 0, SEEK_SET ); // rewind for reading cells = (char*) mymalloc( size * sizeof(char) ); // allocate buffer for blob if (cells == NULL) { RMInit::logOut << "BLOBTile::readFromDb() error: cannot allocate " << size << " bytes " << endl; LEAVE( "BLOBTile::readFromDb: error: cannot allocate " << size << " bytes" ); generateException(); } TALK( "lo_read()" ); // read blob int loResult = lo_read( pgConn, fd, cells, size ); if (loResult < 0) { RMInit::logOut << "BLOBTile::readFromDb() cannot read blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "BLOBTile::readFromDb: cannot read blob, error " << PQerrorMessage(pgConn) ); generateException(); } else if (loResult != size) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") read: want to read (" << size << " bytes, but got " << loResult << " bytes" << endl; LEAVE( "BLOBTile::readFromDb: want to read " << size << " bytes, but got " << loResult << " bytes" ); generateException(); } TALK( "lo_close()" ); int ignoredPgResult = lo_close( pgConn, fd ); // close blob if (ignoredPgResult < 0) // we note, but ignore errors, as we have the data { RMInit::logOut << "BLOBTile::readFromDb() ignoring lo_close() error: " << ignoredPgResult << endl; TALK( "BLOBTile::readFromDb: ignoring lo_close() error: " << ignoredPgResult ); } RMDBGIF(20, RMDebug::module_blobif, "BLOBTileOutput", for (int a = 0; a < size; a++)\ RMInit::dbgOut << " " << hex << (int)(cells[a]); RMInit::dbgOut << dec << endl;) DBObject::readFromDb(); #ifdef RMANBENCHMARK DBObject::readTimer.pause(); #endif LEAVE( "BLOBTile::readFromDb" ); RMDBGEXIT(3, RMDebug::module_blobif, "BLOBTile", "readFromDb() " << myOId); }