#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: * - attribute name 'OId' -> 'UOId' (for UDFs), 'OId' -> 'Id' (for IXs) * to avoid PG name clash with attr type * ************************************************************/ // PG stuff: #include "libpq-fe.h" /* C interface to PgSQL */ #include "libpq/libpq-fs.h" /* large object (lo) api */ #include "debug-srv.hh" #include "dbrcindexds.hh" #include "reladminif/sqlerror.hh" #include "raslib/rmdebug.hh" // general embedded SQL related definitions EXEC SQL include "../reladminif/sqlglobals.h"; // container size for index node // BEWARE: keep these parameters always consistent! #define BYTES_PER_TUPLE 3990 EXEC SQL define SQL_BYTES_PER_TUPLE 3991; // libpg connection maintenance extern PGconn *pgConn; r_Bytes DBRCIndexDS::BytesPerTupel = BYTES_PER_TUPLE; void DBRCIndexDS::insertInDb() throw (r_Error) { RMDBGENTER(5, RMDebug::module_indexif, "DBRCIndexDS", "insertInDb() " << myOId); ENTER( "DBRCIndexDS::insertInDb" ); #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; */ #endif //NOTYET double id2; short count2; 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 // alternative solution for now: // (1) --- prepare buffer id2 = myOId; r_Dimension dimension = myDomain.dimension(); //number of bytes for bounds in 1 minterval r_Bytes boundssize = sizeof(r_Range) * dimension; //number of bytes for fixes in 1 minterval r_Bytes fixessize = sizeof(char) * dimension; //number of bytes for the dynamic data r_Bytes completesize = sizeof(r_Dimension) + sizeof(short) + sizeof(OId::OIdCounter) + sizeof(unsigned int) + boundssize * 2 + fixessize * 2; 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); RMDBGMIDDLE(8, RMDebug::module_indexif, "DBRCIndexDS", "complete " << completesize << " bounds " << boundssize << " fixes " << fixessize); TALK( "DBRCIndexDS: complete " << completesize << " bounds " << boundssize << " fixes " << fixessize); // insert myDomain in buffers myDomain.insertInDb(&(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0])); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBRCIndexDS", "domain " << myDomain << " stored as " << InlineMinterval(dimension, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0]))); TALK( "DBRCIndexDS: domain " << myDomain << " stored as " << InlineMinterval(dimension, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0])) ); char* insertionpointer = completebuffer; // write the buffers in the complete buffer // this indirection is neccessary because of memory alignment of longs... // insert dimension memcpy(insertionpointer, &dimension, sizeof(r_Dimension)); insertionpointer = insertionpointer + sizeof(r_Dimension); // insert oid type memcpy(insertionpointer, &myBaseOIdType, sizeof(short)); insertionpointer = insertionpointer + sizeof(short); // insert oid counter memcpy(insertionpointer, &myBaseCounter, sizeof(OId::OIdCounter)); insertionpointer = insertionpointer + sizeof(OId::OIdCounter); // insert oid counter memcpy(insertionpointer, &mySize, sizeof(unsigned int)); insertionpointer = insertionpointer + sizeof(unsigned int); // insert domains memcpy(insertionpointer, lowerboundsbuf, boundssize); insertionpointer = insertionpointer + boundssize; free(lowerboundsbuf); memcpy(insertionpointer, upperboundsbuf, boundssize); insertionpointer = insertionpointer + boundssize; free(upperboundsbuf); memcpy(insertionpointer, lowerfixedbuf, fixessize); insertionpointer = insertionpointer + fixessize; free(lowerfixedbuf); memcpy(insertionpointer, upperfixedbuf, fixessize); free(upperfixedbuf); #ifdef RMANDEBUG // dump low-level blob byte string { char printbuf[10000]; (void) sprintf( printbuf, "DBRCIndexDS::insertInDb(): [%d]", completesize ); #if 0 // extra verbose output: dump buffer char bytebuf[3]; for (unsigned int i = 0; i < completesize; i++) { (void) sprintf( bytebuf, " %2X", (unsigned char) completebuffer[i] ); strcat( printbuf, bytebuf ); } #endif // 0 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) { free(completebuffer); completebuffer = NULL; RMInit::logOut << "DBRCIndexDS::insertInDb() cannot create blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBRCIndexDS::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) { free(completebuffer); completebuffer = NULL; RMInit::logOut << "DBRCIndexDS::insertInDb() cannot write blob, error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBRCIndexDS::insertInDb() cannot write blob, error " << PQerrorMessage(pgConn) ); generateException(); } else if (loResult != completesize) // did not get all { free(completebuffer); completebuffer = NULL; RMInit::dbgOut << "BLOB (" << myOId << ") insert: wrote " << loResult << " instead of " << completesize << " bytes" << endl; LEAVE( "DBRCIndexDS::insertInDb() wrote " << loResult << " instead of " << completesize << " bytes" ); generateException(); } TALK( "lo_close(), " << completesize << " bytes written" ); loResult = lo_close( pgConn, fd ); if (loResult < 0) // can't close, don't know if data are written { free(completebuffer); completebuffer = NULL; RMInit::logOut << "DBRCIndexDS::insertInDb() ignoring lo_close() error: " << PQerrorMessage(pgConn) << endl; LEAVE( "DBRCIndexDS::insertInDb() cannot lo_close(): " << PQerrorMessage(pgConn) ); generateException(); } free(completebuffer); // (3) --- insert HIERIX tuple into db count2 = 0; // we only have one entry #ifdef NOTYET // should be in future /* TALK( "EXEC SQL INSERT INTO RAS_RCINDEXDYN ( Id, Count, DynData ) VALUES ( " << id2 << "," << count2 << "," << blobOid << " )" ); EXEC SQL INSERT INTO RAS_RCINDEXDYN ( Id, Count, DynData ) VALUES ( :id2, :count2, :blobOid ); if (SQLCODE != SQLOK) { check("DBRCIndexDS::insertInDb() insert into RAS_HIERIXDYN\0"); LEAVE( "DBRCIndexDS::insertInDb(): db access error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "INSERT INTO RAS_RCINDEXDYN ( Id, Count, DynData ) VALUES ( %f, %d, %d )", id2, count2, blobOid ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "DBRCIndexDS::insertInDb() libpq 'insert RAS_HIERIX' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } PQclear( pgResult ); // (4) --- dbobject insert DBObject::insertInDb(); LEAVE( "DBRCIndexDS::insertInDb" ); RMDBGEXIT(5, RMDebug::module_indexif, "DBRCIndexDS", "insertInDb() " << myOId); } void DBRCIndexDS::readFromDb() throw (r_Error) { RMDBGENTER(5, RMDebug::module_indexif, "DBRCIndexDS", "readFromDb() " << myOId); ENTER( "DBRCIndexDS::readFromDb" ); #ifdef RMANBENCHMARK DBObject::readTimer.resume(); #endif #ifdef NOTYET // should be in future /* EXEC SQL BEGIN DECLARE SECTION; */ #endif //NOTYET double id1; 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 // (1) --- prepare variables id1 = myOId; // (2) --- get tuple #ifdef NOTYET // should be in future /* TALK( "EXEC SQL SELECT DynData FROM RAS_RCINDEXDYN WHERE Id = " << id1 ); EXEC SQL SELECT DynData FROM RAS_RCINDEXDYN WHERE Id = :id1; if (SQLCODE != SQLOK) { check("DBRCIndexDS::readFromDb() select from RAS_RCINDEXDYN"); LEAVE("DBRCIndexDS::readFromDb() 'select from RAS_RCINDEXDYN' error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT DynData FROM RAS_RCINDEXDYN WHERE Id = %f", id1 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "DBRCIndexDS::readFromDb() libpq 'insert RAS_HIERIX' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result PQclear( pgResult ); // (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 blobSize = 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 char* completebuffer = (char*)mymalloc(blobSize); // receives blob contents if (completebuffer == NULL) { RMInit::logOut << "DBRCIndexDS::readFromDb() cannot allocate blob buffer" << endl; LEAVE( "DBRCIndexDS::readFromDb: cannot allocate blob buffer" ); throw r_Error( r_Error::r_Error_MemoryAllocation ); } TALK( "lo_read() for " << blobSize << " bytes" ); // read blob int loResult = lo_read( pgConn, fd, completebuffer, blobSize ); if (loResult < 0) { RMInit::logOut << "DBRCIndexDS::readFromDb() cannot read blob, error: " << loResult << endl; LEAVE( "DBRCIndexDS::readFromDb: cannot read blob, error " << loResult ); throw r_Error( r_Error::r_Error_BaseDBMSFailed ); } else if (loResult != blobSize) // did not get all { RMInit::dbgOut << "BLOB (" << myOId << ") read: want to read (" << blobSize << " bytes, but got " << loResult << " bytes" << endl; LEAVE( "DBRCIndexDS::readFromDb: want to read " << blobSize << " bytes, but got " << loResult << " bytes" ); 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 << "DBRCIndexDS::readFromDb() ignoring lo_close() error: " << ignoredPgResult << endl; TALK( "DBRCIndexDS::readFromDb: ignoring lo_close() error: " << ignoredPgResult ); } #ifdef RMANDEBUG // dump low-level blob byte string { char printbuf[10000]; (void) sprintf( printbuf, "XXX DBRCIndexDS::readFromDb(): [%d]", blobSize ); char bytebuf[3]; for (unsigned int i = 0; i < blobSize; i++) { (void) sprintf( bytebuf, " %2X", (unsigned char) completebuffer[i] ); strcat( printbuf, bytebuf ); } TALK( printbuf ); } #endif // RMANDEBUG // (4) --- fill variables and buffers r_Dimension dimension = 0; (void) memcpy(&dimension, completebuffer, sizeof(r_Dimension)); unsigned int bytesdone = sizeof(r_Dimension); (void) memcpy(&myBaseOIdType, &(completebuffer[bytesdone]), sizeof(short)); bytesdone += sizeof(short); (void) memcpy(&myBaseCounter, &(completebuffer[bytesdone]), sizeof(OId::OIdCounter)); bytesdone += sizeof(OId::OIdCounter); (void) memcpy(&mySize, &(completebuffer[bytesdone]), sizeof(unsigned int)); bytesdone += sizeof(unsigned int); r_Bytes boundssize = sizeof(r_Range) * dimension; //number of bytes for bounds in 2 domains r_Bytes fixessize = sizeof(char) * dimension; //number of bytes for fixes in 2 domains r_Bytes completesize = boundssize * 2 + fixessize * 2; //number of bytes for the dynamic data char *dynamicBuffer = &completebuffer[bytesdone]; // ptr to start of dynamic part of buffer // additional plausi check if (blobSize != bytesdone + completesize) { RMInit::logOut << "DBRCIndexDS::readFromDb() blob size inconsistency: blobSize (" << blobSize << " != bytesdone (" << bytesdone << ") + completesize (" << completesize << ")"; TALK( "DBRCIndexDS::readFromDb() blob size inconsistency: blobSize (" << blobSize << " != bytesdone (" << bytesdone << ") + completesize (" << completesize << ")" ); throw r_Error( r_Error::r_Error_LimitsMismatch ); } RMDBGMIDDLE(7, RMDebug::module_indexif, "DBRCIndexDS", "dimension " << dimension << ", base oid type " << myBaseOIdType << ", base counter " << myBaseCounter << ", size " << mySize << ", complete data size " << 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); // all dynamic data is in dynamicBuffer // put that stuff in the correct buffers memcpy(lowerboundsbuf, dynamicBuffer, boundssize); memcpy(upperboundsbuf, &dynamicBuffer[boundssize], boundssize); memcpy(lowerfixedbuf, &dynamicBuffer[boundssize * 2], fixessize); memcpy(upperfixedbuf, &dynamicBuffer[boundssize * 2 + fixessize], fixessize); // all dynamic data is in its buffer free (completebuffer); dynamicBuffer = completebuffer = NULL; // rebuild attributes from buffers myDomain = InlineMinterval(dimension, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0])); RMDBGMIDDLE(5, RMDebug::module_indexif, "DBRCIndexDS", "domain " << myDomain << " constructed from " << InlineMinterval(dimension, &(lowerboundsbuf[0]), &(upperboundsbuf[0]), &(lowerfixedbuf[0]), &(upperfixedbuf[0]))); free(upperboundsbuf); upperboundsbuf = NULL; free(lowerboundsbuf); lowerboundsbuf = NULL; free(upperfixedbuf); upperfixedbuf = NULL; free(lowerfixedbuf); lowerfixedbuf = NULL; #ifdef RMANBENCHMARK DBObject::readTimer.pause(); #endif // (5) --- dbobject read DBObject::readFromDb(); LEAVE( "DBRCIndexDS::readFromDb, myOId=" << myOId ); RMDBGEXIT(5, RMDebug::module_indexif, "DBRCIndexDS", "readFromDb() " << myOId); } void DBRCIndexDS::deleteFromDb() throw (r_Error) { RMDBGENTER(8, RMDebug::module_indexif, "DBRCIndexDS", "deleteFromDb() " << myOId); ENTER( "DBRCIndexDS::deleteFromDb" ); #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 // (1) --- set variables id3 = myOId; // (2) --- fetch blob oid #ifdef NOTYET // should be in future /* TALK( "EXEC SQL SELECT DynData FROM RAS_RCINDEXDYN WHERE Id = " << id3 ); EXEC SQL SELECT DynData FROM RAS_RCINDEXDYN WHERE Id = :id3; if (check("DBRCIndexDS::deleteFromDb() RAS_RCINDEX") != 0) generateException(); */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "SELECT DynData FROM RAS_RCINDEXDYN WHERE Id = %f", id3 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_TUPLES_OK) { LEAVE( "DBRCIndexDS::deleteFromDb() libpq 'select RAS_RCINDEXDYN' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } blobOid = atoi( PQgetvalue( pgResult, 0, 0 ) ); // extract value from result PQclear( pgResult ); // (3) --- delete blob int loResult = lo_unlink( pgConn, blobOid ); if (loResult < 0) // no disaster if we can't so no exception { TALK( "DBRCIndexDS::deleteFromDb() warning: libpq 'unlink blob' error: " << PQerrorMessage(pgConn) ); } // (3) --- delete tuple #ifdef NOTYET // should be in future /* TALK( "EXEC SQL DELETE FROM RAS_RCINDEXDYN WHERE Id = " << id3 ); EXEC SQL DELETE FROM RAS_RCINDEXDYN WHERE Id = :id3; if (SQLCODE != SQLOK) { check("DBRCIndexDS::deleteFromDb() delete from RAS_RCINDEX"); LEAVE("DBRCIndexDS::deleteFromDb() 'delete from RAS_RCINDEX' error: " << SQLCODE ); generateException(); } */ #endif // NOTYET // alternative solution for now: (void) snprintf( pgQuery, (size_t) sizeof(pgQuery), "DELETE FROM RAS_RCINDEXDYN WHERE Id = %f", id3 ); TALK( pgQuery ); pgResult = PQexec( pgConn, pgQuery ); if (PQresultStatus(pgResult) != PGRES_COMMAND_OK) { LEAVE( "DBRCIndexDS::deleteFromDb() libpq 'select RAS_RCINDEXDYN' error: " << PQerrorMessage(pgConn) ); PQclear( pgResult ); generateException(); } // (4) --- dbobject delete DBObject::deleteFromDb(); LEAVE( "DBRCIndexDS::deleteFromDb" ); RMDBGEXIT(8, RMDebug::module_indexif, "DBRCIndexDS", "deleteFromDb() " << myOId); }