#include "mymalloc/mymalloc.h" /* * 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 . */ /************************************************************* * * * PURPOSE: * Code with embedded SQL for PostgreSQL DBMS * * * COMMENTS: * - blobs contain 13c as first byte; this is not required here, * but kept for data compatibility with other base DBMSs. * ************************************************************/ // PG stuff: #include "libpq-fe.h" /* C interface to PgSQL */ #include "libpq/libpq-fs.h" /* large object (lo) api */ #include "debug-srv.hh" // general embedded SQL related definitions EXEC SQL include "../reladminif/sqlglobals.h"; #include "hierindex.hh" #include "raslib/rmdebug.hh" #include "reladminif/sqlerror.hh" #include "relblobif/blobtile.hh" #include "indexmgr/keyobject.hh" // libpg connection maintenance extern PGconn *pgConn; void DBHierIndex::insertInDb() throw (r_Error) { RMDBGENTER(5, RMDebug::module_indexif, "DBHierIndex", "insertInDb() " << myOId); ENTER( "DBHierIndex::insertInDb(), myOId=" << myOId ); #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; */ #endif // NOTYET double id2; long dimension2; long size2; double parentid2; short indexsubtype2; Oid blobOid; char pgQuery[SQL_QUERY_BUFFER_SIZE]; // prelim PGresult *pgResult = NULL; // prelim #ifdef NOTYET // should be in future /* EXEC SQL END DECLARE SECTION; */ #endif // NOTYET // (0) --- prepare variables id2 = myOId; dimension2 = myDomain.dimension(); // size2 = myKeyObjects.size(); size2 = getSize(); indexsubtype2 = _isNode; if (parent.getType() == OId::INVALID) parentid2 = 0; else parentid2 = parent; // (1) -­- set all buffers //number of bytes for bounds for "size" entries and mydomain r_Bytes boundssize = sizeof(r_Range) * (size2 + 1) * dimension2; //number of bytes for fixes for "size" entries and mydomain r_Bytes fixessize = sizeof(char) * (size2 + 1) * dimension2; //number of bytes for ids of entries r_Bytes idssize = sizeof(OId::OIdCounter) * size2; //number of bytes for types of entries r_Bytes typessize = sizeof(char) * size2; //number of bytes for the dynamic data, plus 1 starter byte (see below) r_Bytes completesize = 1 + boundssize * 2 + fixessize * 2 + idssize + typessize; char* completebuffer = (char*)mymalloc(completesize); if (completebuffer == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } r_Range* upperboundsbuf = (r_Range*)mymalloc(boundssize); if (upperboundsbuf == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } r_Range* lowerboundsbuf = (r_Range*)mymalloc(boundssize); if (lowerboundsbuf == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } char* upperfixedbuf = (char*)mymalloc(fixessize); if (upperfixedbuf == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } char* lowerfixedbuf = (char*)mymalloc(fixessize); if (lowerfixedbuf == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } OId::OIdCounter* entryidsbuf = (OId::OIdCounter*)mymalloc(idssize); if (entryidsbuf == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } char* entrytypesbuf = (char*)mymalloc(typessize); if (entrytypesbuf == NULL) { RMInit::logOut << "DBHierIndex::insertInDb() cannot malloc buffer" << endl; LEAVE( "DBHierIndex::insertInDb() cannot malloc buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } RMDBGMIDDLE(8, RMDebug::module_indexif, "DBHierIndex", "complete=" << completesize << " bounds=" << boundssize << " fixes=" << fixessize << " ids=" << idssize << " types=" << typessize << ", size=" << size2 << " dimension=" << dimension2 ); myDomain.insertInDb(&(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0])); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBHierIndex", "domain " << myDomain << " stored as " << InlineMinterval(dimension2, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0]))); //populate the buffers with data KeyObjectVector::iterator it = myKeyObjects.begin(); InlineMinterval indom; for (unsigned int i = 0; i < size2; i++, it++) { indom = (*it).getDomain(); indom.insertInDb(&(lowerboundsbuf[(i+1)*dimension2]), &(upperboundsbuf[(i+1)*dimension2]), &(lowerfixedbuf[(i+1)*dimension2]), &(upperfixedbuf[(i+1)*dimension2])); entryidsbuf[i] = (*it).getObject().getOId().getCounter(); entrytypesbuf[i] = (char)(*it).getObject().getOId().getType(); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBHierIndex", "entry " << entryidsbuf[i] << " " << (OId::OIdType)entrytypesbuf[i] << " at " << InlineMinterval(dimension2, &(lowerboundsbuf[(i+1)*dimension2]), &(upperboundsbuf[(i+1)*dimension2]), &(lowerfixedbuf[(i+1)*dimension2]), &(upperfixedbuf[(i+1)*dimension2]))); } // write the buffers in the complete buffer, free all unnecessary buffers // this indirection is neccessary because of memory alignement of longs... completebuffer[0] = 13; //the first char must not be a \0 (void) memcpy( &completebuffer[1], lowerboundsbuf, boundssize); free(lowerboundsbuf); (void) memcpy( &completebuffer[boundssize + 1], upperboundsbuf, boundssize); free(upperboundsbuf); (void) memcpy( &completebuffer[boundssize * 2 + 1], lowerfixedbuf, fixessize); free(lowerfixedbuf); (void) memcpy( &completebuffer[boundssize * 2 + fixessize + 1], upperfixedbuf, fixessize); free(upperfixedbuf); (void) memcpy( &completebuffer[boundssize * 2 + fixessize * 2 + 1], entryidsbuf, idssize); free(entryidsbuf); (void) memcpy( &completebuffer[boundssize * 2 + fixessize * 2 + idssize + 1], entrytypesbuf, typessize); free(entrytypesbuf); #ifdef RMANDEBUG // dump low-level blob byte string { char printbuf[10000]; (void) sprintf( printbuf, "XXX DBHierIndex::insertInDb(): [%d]", completesize ); char bytebuf[3]; for (unsigned int i = 0; i < completesize; i++) { (void) sprintf( bytebuf, " %2X", (unsigned char) completebuffer[i] ); strcat( printbuf, bytebuf ); } TALK( printbuf ); } #endif // RMANDEBUG // (2) --- create, 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 << "DBHierIndex::insertInDb() cannot create blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBHierIndex::insertInDb(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 << " with " << completesize << " bytes" ); int loResult = lo_write( pgConn, fd, completebuffer, completesize ); if (loResult < 0) { RMInit::logOut << "DBHierIndex::insertInDb() cannot write blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBHierIndex::insertInDb() cannot write blob, error " << PQerrorMessage(pgConn) ); generateException(); } else if (loResult != completesize) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") insert: wrote " << loResult << " instead of " << completesize << " bytes" << endl; LEAVE( "DBHierIndex::insertInDb() wrote " << loResult << " instead of " << completesize << " 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 << "DBHierIndex::insertInDb() ignoring lo_close() error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBHierIndex::insertInDb() cannot lo_close(): " << PQerrorMessage(pgConn) ); generateException(); } free(completebuffer); // free main buffer // (3) --- insert HIERIX tuple into db #ifdef NOTYET // should be in future /* TALK( "EXEC SQL INSERT INTO RAS_HIERIX ( MDDObjIxOId, NumEntries, Dimension, ParentOId, IndexSubType, DynData ) VALUES ( " << id2 << "," << size2 << "," << dimension2 << "," << parentid2 << "," << indexsubtype2 << ", " << blobOid << " )" ); EXEC SQL INSERT INTO RAS_HIERIX ( MDDObjIxOId, NumEntries, Dimension, ParentOId, IndexSubType, DynData) VALUES ( :id2, :size2, :dimension2, :parentid2, :indexsubtype2, :blobOid); if (SQLCODE != SQLOK) { check("DBHierIndex::insertInDb() insert into RAS_HIERIX\0"); LEAVE( "DBHierIndex::insertInDb(): db access error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "INSERT INTO RAS_HIERIX ( MDDObjIxOId, NumEntries, Dimension, ParentOId, IndexSubType, DynData ) VALUES ( %f, %d, %d, %f, %d, %d )", id2, size2, dimension2, parentid2, indexsubtype2, blobOid ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "DBHierIndex::insertInDb() libpq 'insert RAS_HIERIX' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); // (4) --- dbobject insert DBObject::insertInDb(); RMDBGEXIT(5, RMDebug::module_indexif, "DBHierIndex", "insertInDb() " << myOId); LEAVE( "DBHierIndex::insertInDb(), myOId=" << myOId ); } // insertInDb() void DBHierIndex::readFromDb() throw (r_Error) { RMDBGENTER(5, RMDebug::module_indexif, "DBHierIndex", "readFromDb() " << myOId); ENTER( "DBHierIndex::readFromDb(), myOId=" << myOId ); #ifdef RMANBENCHMARK DBObject::readTimer.resume(); #endif #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; */ #endif // NOTYET double id1; double parentid1; long dimension1; long size1; short indexsubtype1; short lowerfind; Oid blobOid; char pgQuery[SQL_QUERY_BUFFER_SIZE]; // prelim PGresult *pgResult = NULL; // prelim #ifdef NOTYET // should be in future /* EXEC SQL END DECLARE SECTION; */ #endif // NOTYET // (0) --- prepare variables id1 = myOId; // (1) --- fetch tuple from database #ifdef NOTYET // should be in future /* TALK( "EXEC SQL SELECT NumEntries, Dimension, ParentOId, IndexSubType, DynData INTO :size1, :dimension1, :parentid1, :indexsubtype1, blobOid FROM RAS_HIERIX WHERE MDDObjIxOId = " << id1 ); EXEC SQL SELECT NumEntries, Dimension, ParentOId, IndexSubType, DynData INTO :size1, :dimension1, :parentid1, :indexsubtype1, :blobOid FROM RAS_HIERIX WHERE MDDObjIxOId = :id1; if (SQLCODE != SQLOK) { check("DBHierIndex::readFromDb() select from RAS_HIERIX"); LEAVE( "DBHierIndex::insertInDb() 'select from RAS_HIERIX' error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT NumEntries, Dimension, ParentOId, IndexSubType, DynData FROM RAS_HIERIX WHERE MDDObjIxOId = %f", id1 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "DBHierIndex::insertInDb() libpq 'select RAS_HIERIX' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } size1 = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value #1 from result dimension1 = atoi( PQgetvalue( pgResult, 0, 1 ) ); // extract value #2 from result parentid1 = atoi( PQgetvalue( pgResult, 0, 2 ) ); // extract value #3 from result indexsubtype1 = atoi( PQgetvalue( pgResult, 0, 3 ) ); // extract value #4 from result blobOid = atoi( PQgetvalue( pgResult, 0, 4 ) ); // extract value #5 from result PQclear( pgResult ); // (2) --- fill variables and buffers _isNode = indexsubtype1; if (parentid1) parent = OId(parentid1); else parent = OId(0, OId::INVALID); //number of bytes for bounds for "size" entries and mydomain r_Bytes boundssize = sizeof(r_Range) * (size1 + 1) * dimension1; //number of bytes for fixes for "size" entries and mydomain r_Bytes fixessize = sizeof(char) * (size1 + 1) * dimension1; //number of bytes for ids of entries r_Bytes idssize = sizeof(OId::OIdCounter) * size1; //number of bytes for types of entries r_Bytes typessize = sizeof(char) * size1; //number of bytes for the dynamic data (1 starter byte!) r_Bytes completesize = 1 + boundssize * 2 + fixessize * 2 + idssize + typessize; RMDBGMIDDLE(8, RMDebug::module_indexif, "DBHierIndex", "complete=" << completesize << " bounds=" << boundssize << " fixes=" << fixessize << " ids=" << idssize << " types=" << typessize << ", size=" << size1 << " dimension=" << dimension1 ); char* completebuffer = (char*)mymalloc(completesize); r_Range* upperboundsbuf = (r_Range*)mymalloc(boundssize); r_Range* lowerboundsbuf = (r_Range*)mymalloc(boundssize); char* upperfixedbuf = (char*)mymalloc(fixessize); char* lowerfixedbuf = (char*)mymalloc(fixessize); OId::OIdCounter* entryidsbuf = (OId::OIdCounter*)mymalloc(idssize); char* entrytypesbuf = (char*)mymalloc(typessize); // (3) --- 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" ); int bytesToDo = 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 TALK( "lo_read() for " << completesize << " bytes" ); // read blob int loResult = lo_read( pgConn, fd, completebuffer, completesize ); if (loResult < 0) { RMInit::logOut << "DBHierIndex::readFromDb() cannot read blob, error: " << pgResult << endl; LEAVE( "DBHierIndex::readFromDb: cannot read blob, error " << pgResult ); throw r_Error( r_Error::r_Error_BaseDBMSFailed ); } else if (loResult != bytesToDo) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") read: want to read (" << bytesToDo << " bytes, but got " << loResult << " bytes" << endl; LEAVE( "DBHierIndex::readFromDb: want to read " << bytesToDo << " bytes, but got " << loResult << " bytes" ); throw r_Error( r_Error::r_Error_LimitsMismatch ); } else if (completesize != bytesToDo) // this because I don't trust computations { RMInit::dbgOut << "BLOB (" << myOId << ") read: completesize=" << completesize << ", but bytesToDo=" << bytesToDo << endl; LEAVE( "DBHierIndex::readFromDb: completesize=" << completesize << ", but bytesToDo=" << bytesToDo ); throw r_Error( r_Error::r_Error_LimitsMismatch ); } 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 << "DBHierIndex::readFromDb() ignoring lo_close() error: " << ignoredPgResult << endl; TALK( "DBHierIndex::readFromDb: ignoring lo_close() error: " << ignoredPgResult ); } #ifdef RMANDEBUG // dump low-level blob byte string { char printbuf[10000]; (void) sprintf( printbuf, "XXX DBHierIndex::readFromDb(): [%d]", completesize ); char bytebuf[3]; for (unsigned int i = 0; i < completesize; i++) { (void) sprintf( bytebuf, " %2X", (unsigned char) completebuffer[i] ); strcat( printbuf, bytebuf ); } TALK( printbuf ); } #endif // RMANDEBUG // (4) --- copy data into buffers // all dynamic data ( plus starter byte) is in completebuffer, put that stuff in the correct buffers memcpy(lowerboundsbuf, &completebuffer[1], boundssize); memcpy(upperboundsbuf, &completebuffer[boundssize + 1], boundssize); memcpy(lowerfixedbuf, &completebuffer[boundssize * 2 + 1], fixessize); memcpy(upperfixedbuf, &completebuffer[boundssize * 2 + fixessize + 1], fixessize); memcpy(entryidsbuf, &completebuffer[boundssize * 2 + fixessize * 2 + 1], idssize); memcpy(entrytypesbuf, &completebuffer[boundssize * 2 + fixessize * 2 + idssize + 1], typessize); // now all dynamic data is in its private buffer free (completebuffer); completebuffer = NULL; // rebuild the attributes from the buffers int i = 0; myDomain = InlineMinterval(dimension1, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[i*dimension1])); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBHierIndex", "domain " << myDomain << " constructed from " << InlineMinterval(dimension1, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0]))); KeyObject theKey = KeyObject(DBObjectId(), myDomain); for (i = 0; i < size1; i++) { theKey.setDomain(InlineMinterval(dimension1, &(lowerboundsbuf[(i+1)*dimension1]), &(upperboundsbuf[(i+1)*dimension1]), &(lowerfixedbuf[(i+1)*dimension1]), &(upperfixedbuf[(i+1)*dimension1]))); theKey.setObject(OId(entryidsbuf[i], (OId::OIdType)entrytypesbuf[i])); myKeyObjects.push_back(theKey); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBHierIndex", "entry " << entryidsbuf[i] << " " << (OId::OIdType)entrytypesbuf[i] << " at " << InlineMinterval(dimension1, &(lowerboundsbuf[(i+1)*dimension1]), &(upperboundsbuf[(i+1)*dimension1]), &(lowerfixedbuf[(i+1)*dimension1]), &(upperfixedbuf[(i+1)*dimension1]))); } free(upperboundsbuf); free(lowerboundsbuf); free(upperfixedbuf); free(lowerfixedbuf); free(entryidsbuf); free(entrytypesbuf); #ifdef RMANBENCHMARK DBObject::readTimer.pause(); #endif // (5) --- fill dbobject DBObject::readFromDb(); RMDBGEXIT(5, RMDebug::module_indexif, "DBHierIndex", "readFromDb() " << myOId); LEAVE( "DBHierIndex::readFromDb(), myOId=" << myOId ); } // readFromDb() void DBHierIndex::updateInDb() throw (r_Error) { RMDBGENTER(5, RMDebug::module_indexif, "DBHierIndex", "updateInDb() " << myOId); ENTER( "DBHierIndex::updateInDb(), myOId=" << myOId ); #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; */ #endif // NOTYET double id4; long dimension4; long size4; double parentid4; short count4; short indexsubtype4; Oid blobOid; char pgQuery[SQL_QUERY_BUFFER_SIZE]; // prelim PGresult *pgResult = NULL; // prelim #ifdef NOTYET // should be in future /* EXEC SQL END DECLARE SECTION; */ #endif // NOTYET // (0) --- prepare variables id4 = myOId; indexsubtype4 = _isNode; dimension4 = myDomain.dimension(); size4 = myKeyObjects.size(); if (parent.getType() == OId::INVALID) parentid4 = 0; else parentid4 = parent; // (1) --- prepare buffer //number of bytes for bounds for "size" entries and mydomain r_Bytes boundssize = sizeof(r_Range) * (size4 + 1) * dimension4; //number of bytes for fixes for "size" entries and mydomain r_Bytes fixessize = sizeof(char) * (size4 + 1) * dimension4; //number of bytes for ids of entries r_Bytes idssize = sizeof(OId::OIdCounter) * size4; //number of bytes for types of entries r_Bytes typessize = sizeof(char) * size4; //number of bytes for the dynamic data; 1 starter byte! r_Bytes completesize = 1 + boundssize * 2 + fixessize * 2 + idssize + typessize; char* completebuffer = (char*)mymalloc(completesize); r_Range* upperboundsbuf = (r_Range*)mymalloc(boundssize); r_Range* lowerboundsbuf = (r_Range*)mymalloc(boundssize); char* upperfixedbuf = (char*)mymalloc(fixessize); char* lowerfixedbuf = (char*)mymalloc(fixessize); OId::OIdCounter* entryidsbuf = (OId::OIdCounter*)mymalloc(idssize); char* entrytypesbuf = (char*)mymalloc(typessize); // populate the buffers with data myDomain.insertInDb(&(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0])); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBHierIndex", "domain " << myDomain << " stored as " << InlineMinterval(dimension4, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0]))); KeyObjectVector::iterator it = myKeyObjects.begin(); InlineMinterval indom; for (unsigned int i = 0; i < size4; i++, it++) { indom = (*it).getDomain(); indom.insertInDb(&(lowerboundsbuf[(i+1)*dimension4]), &(upperboundsbuf[(i+1)*dimension4]), &(lowerfixedbuf[(i+1)*dimension4]), &(upperfixedbuf[(i+1)*dimension4])); entryidsbuf[i] = (*it).getObject().getOId().getCounter(); entrytypesbuf[i] = (char)(*it).getObject().getOId().getType(); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBHierIndex", "entry " << entryidsbuf[i] << " " << (OId::OIdType)entrytypesbuf[i] << " at " << InlineMinterval(dimension4, &(lowerboundsbuf[(i+1)*dimension4]), &(upperboundsbuf[(i+1)*dimension4]), &(lowerfixedbuf[(i+1)*dimension4]), &(upperfixedbuf[(i+1)*dimension4]))); } RMDBGMIDDLE(8, RMDebug::module_indexif, "DBHierIndex", "complete=" << completesize << " bounds=" << boundssize << " fixes=" << fixessize << " ids=" << idssize << " types=" << typessize << ", size=" << size4 << " dimension=" << dimension4 ); // write the buffers in the complete buffer, plus starter byte // this indirection is neccessary because of memory alignement of longs... completebuffer[0] = 13; // write dummy starter byte memcpy( &completebuffer[1], lowerboundsbuf, boundssize); free(lowerboundsbuf); memcpy( &completebuffer[boundssize + 1], upperboundsbuf, boundssize); free(upperboundsbuf); memcpy( &completebuffer[boundssize * 2 + 1], lowerfixedbuf, fixessize); free(lowerfixedbuf); memcpy( &completebuffer[boundssize * 2 + fixessize + 1], upperfixedbuf, fixessize); free(upperfixedbuf); memcpy( &completebuffer[boundssize * 2 + fixessize * 2 + 1], entryidsbuf, idssize); free(entryidsbuf); memcpy( &completebuffer[boundssize * 2 + fixessize * 2 + idssize + 1], entrytypesbuf, typessize); free(entrytypesbuf); #ifdef RMANDEBUG // dump low-level blob byte string { char printbuf[10000]; (void) sprintf( printbuf, "XXX DBHierIndex::updateInDb(): [%d]", completesize ); char bytebuf[3]; for (unsigned int i = 0; i < completesize; i++) { (void) sprintf( bytebuf, " %2X", (unsigned char) completebuffer[i] ); strcat( printbuf, bytebuf ); } TALK( printbuf ); } #endif // RMANDEBUG // (2) --- write complete buffer to new database blob // create, 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 << "DBHierIndex::updateInDb() cannot create blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBHierIndex::updateInDb(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 << " for " << completesize << " bytes" ); int loResult = lo_write( pgConn, fd, completebuffer, completesize ); if (loResult < 0) { RMInit::logOut << "DBHierIndex::updateInDb() cannot write blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBHierIndex::updateInDb() cannot write blob, error " << PQerrorMessage(pgConn) ); generateException(); } else if (loResult != completesize) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") update: wrote " << loResult << " instead of " << completesize << " bytes" << endl; LEAVE( "DBHierIndex::updateInDb() wrote " << loResult << " instead of " << completesize << " 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 << "DBHierIndex::updateInDb() ignoring lo_close() error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBHierIndex::updateInDb() cannot lo_close(): " << PQerrorMessage(pgConn) ); generateException(); } free(completebuffer); completebuffer = NULL; // (3) -- update HierIx entry #ifdef NOTYET // should be in future /* TALK( "EXEC SQL UPDATE RAS_HIERIX SET NumEntries = " << size4 << ", Dimension = " << dimension4 << ", ParentOId = " << parentid4 << ", IndexSubType = " << indexsubtype4 << ", DynData = " << blobOid ); EXEC SQL UPDATE RAS_HIERIX SET NumEntries = :size4, Dimension = :dimension4, ParentOId = :parentid4, IndexSubType = :indexsubtype4, DynData = :blobOid WHERE MDDObjIxOId = :id4; if (SQLCODE != SQLOK) { check("DBHierIndex::updateInDb() update RAS_HIERIX"); LEAVE( "DBHierIndex::updateInDb(): db access error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "UPDATE RAS_HIERIX SET NumEntries = %d, Dimension = %d, ParentOId = %f, IndexSubType = %d, DynData = %d WHERE MDDObjIxOId = %f", size4, dimension4, parentid4, indexsubtype4, blobOid, id4 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "DBHierIndex::updateInDb() libpq 'UPDATE HierIx' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); // (4) --- dbobject update DBObject::updateInDb(); RMDBGEXIT(5, RMDebug::module_indexif, "DBHierIndex", "updateInDb() " << myOId); LEAVE( "DBHierIndex::updateInDb(), myOId=" << myOId ); } // updateInDb() void DBHierIndex::deleteFromDb() throw (r_Error) { RMDBGENTER(8, RMDebug::module_indexif, "DBHierIndex", "deleteFromDb() " << myOId); ENTER( "DBHierIndex::deleteFromDb(), myOId=" << myOId ); #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; */ #endif // NOTYET double id3; Oid blobOid; char pgQuery[SQL_QUERY_BUFFER_SIZE]; // prelim PGresult *pgResult = NULL; // prelim #ifdef NOTYET // should be in future /* EXEC SQL END DECLARE SECTION; */ #endif // NOTYET OId oi; DBObjectId dbo; while (!myKeyObjects.empty()) { oi = myKeyObjects.begin()->getObject().getOId(); DBObjectId dbo; if ((oi.getType() == OId::BLOBOID) || (oi.getType() == OId::INLINETILEOID)) { BLOBTile::kill(oi); } else { dbo = DBObjectId(oi); if (!dbo.is_null()) { dbo->setCached(false); dbo->setPersistent(false); dbo = (unsigned int)0; } } myKeyObjects.erase(myKeyObjects.begin()); } id3 = myOId; // (1) --- get blob oid #ifdef NOTYET // should be in future /* TALK( "EXEC SQL SELECT DynData INTO :blobOid FROM RAS_HIERIX WHERE MDDObjIxOId = " << id3 ); EXEC SQL SELECT DynData INTO :blobOid FROM RAS_HIERIX WHERE MDDObjIxOId = :id3; if (SQLCODE != SQLOK) { check("DBHierIndex::deleteFromDb() select from RAS_HIERIX"); LEAVE( "DBHierIndex::deleteFromDb() select from RAS_HIERIX: error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT DynData FROM RAS_HIERIX WHERE MDDObjIxOId = %f", id3 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "DBHierIndex::deleteFromDb() libpq 'select RAS_HIERIX' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result PQclear( pgResult ); // (2) --- delete blob int loResult = lo_unlink( pgConn, blobOid ); if (loResult < 0) // no disaster if we can't so no exception { TALK( "DBHierIndex::deleteFromDb() warning: libpq 'unlink blob' error: " << PQerrorMessage(pgConn) ); } // (3) --- delete tuple #ifdef NOTYET // should be in future /* TALK( "EXEC SQL DELETE FROM RAS_HIERIX WHERE MDDObjIxOId = " << id3 ); EXEC SQL DELETE FROM RAS_HIERIX WHERE MDDObjIxOId = :id3; if (SQLCODE != SQLOK) { check("DBHierIndex::deleteFromDb() RAS_HIERIX"); LEAVE( "DBHierIndex::deleteFromDb(): db access error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "DELETE FROM RAS_HIERIX WHERE MDDObjIxOId = %f", id3 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "DBHierIndex::updateInDb() libpq 'DELETE HierIx' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); // (4) --- dbobject delete DBObject::deleteFromDb(); RMDBGEXIT(8, RMDebug::module_indexif, "DBHierIndex", "deleteFromDb() " << myOId); LEAVE( "DBHierIndex::deleteFromDb(), myOId=" << myOId ); } // deleteFromDb()