From 8f27e65bddd7d4b8515ce620fb485fdd78fcdf89 Mon Sep 17 00:00:00 2001 From: Constantin Jucovschi Date: Fri, 24 Apr 2009 07:20:22 -0400 Subject: Initial commit --- relblobif/blobtile.pgc | 765 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 765 insertions(+) create mode 100644 relblobif/blobtile.pgc (limited to 'relblobif/blobtile.pgc') diff --git a/relblobif/blobtile.pgc b/relblobif/blobtile.pgc new file mode 100644 index 0000000..7f3aef4 --- /dev/null +++ b/relblobif/blobtile.pgc @@ -0,0 +1,765 @@ +/* +* 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); +} + -- cgit