1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-23 17:33:56 +03:00
one/include/BitMap.h
Ruben S. Montero cbd9aee912
F #3393: Reduce number of updates referencing FTS field. Changed REPLACE
operations to UPDATE to improve DB performance

co-authored-by: Christian González <cgonzalez@opennebula.systems>
2019-06-12 17:20:50 +02:00

394 lines
9.8 KiB
C++

/* -------------------------------------------------------------------------- */
/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */
/* */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
/* not use this file except in compliance with the License. You may obtain */
/* a copy of the License at */
/* */
/* http://www.apache.org/licenses/LICENSE-2.0 */
/* */
/* Unless required by applicable law or agreed to in writing, software */
/* distributed under the License is distributed on an "AS IS" BASIS, */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
/* See the License for the specific language governing permissions and */
/* limitations under the License. */
/* -------------------------------------------------------------------------- */
#ifndef BITMAP_H_
#define BITMAP_H_
#include <bitset>
#include "Attribute.h"
#include "Callbackable.h"
class SqlDB;
/**
* This class represents a generic BitMap
*
*/
template <unsigned int N>
class BitMap : public Callbackable
{
public:
/**
* Creates a new bitmap, it stores a pointer to the DB parameters
* that MUST exists during the object lifetime.
*/
BitMap(const VectorAttribute& bs_conf, int _id, const char * _db_table)
: id(_id), start_bit(0), bs(0), db_table(_db_table)
{
std::string reserved;
bs_conf.vector_value("START", start_bit);
bs_conf.vector_value("RESERVED", reserved);
if (!reserved.empty())
{
set_reserved_bit(reserved);
}
};
virtual ~BitMap()
{
delete bs;
};
/* ---------------------------------------------------------------------- */
/* Database interface */
/* ---------------------------------------------------------------------- */
/**
* Returns a string stream with the SQL bootstrap command for the
* bitmap table
*/
static std::ostringstream& bootstrap(const char * t, std::ostringstream& o)
{
o << "CREATE TABLE IF NOT EXISTS " << t
<< " (id INTEGER, map LONGTEXT, PRIMARY KEY(id))";
return o;
}
/**
* Insert a new zero'ed bitmap into the bitmap table. This function is
* called once to bootstrap the bitmap contents.
* @param id of the set, this will update the id of the bitmap
* @return 0 on success
*/
int insert(int _id, SqlDB * db)
{
id = _id;
bs = new std::bitset<N>;
return insert_replace(db, false);
}
/**
* Loads a bitmap from its string representation. This function is called
* once to load the bitmap from the DB.
* @param id of the set, this will update the id of the bitmap
* @return 0 on success
*/
int select(int _id, SqlDB * db)
{
std::string * uzbs;
id = _id;
if ( select(db, &uzbs) != 0 )
{
return -1;
}
bs = new std::bitset<N>(*uzbs);
delete uzbs;
return 0;
}
int update(SqlDB * db)
{
return insert_replace(db, true);
}
/**
* Deletes a bitmap from the DB.
* @return 0 on success
*/
int drop(SqlDB * db)
{
std::ostringstream oss;
oss << "DELETE FROM " << db_table << " WHERE id = " << id ;
return db->exec_wr(oss);
}
/* ---------------------------------------------------------------------- */
/* BitMap interface */
/* ---------------------------------------------------------------------- */
/*+
* Gets the first 0 bit in the map (and not reserved) and set it.
* @param hint try this bit first, 0 does not use any hint
* @param bit the bit number reserved
* @return -1 in case of error
*/
int get(unsigned int hint, unsigned int& bit)
{
if ( hint != 0 )
{
if ( bs->test(hint) == false && reserved_bit.count(hint) == 0 )
{
bs->set(hint);
bit = hint;
return 0;
}
}
for (bit = start_bit; ; ++bit)
{
try
{
if ( bs->test(bit) == false && reserved_bit.count(bit) == 0)
{
bs->set(bit);
return 0;
}
}
catch (const std::out_of_range& oor)
{
return -1;
}
}
return -1;
}
/**
* Clears a bit in the map and updates DB.
* @param bit to reset
*/
void reset(int bit)
{
try
{
bs->reset(bit);
}
catch(const std::out_of_range& oor){};
}
/**
* Sets a bit in the map and updates DB.
* @param bit to set
* @return 0 on success, -1 if bit was set
*/
int set(int bit)
{
int rc = -1;
try
{
if (bs->test(bit) == false)
{
bs->set(bit);
rc = 0;
}
}
catch(const std::out_of_range& oor){};
return rc;
}
/**
* Return the start_bit of the bitmap
*/
unsigned int get_start_bit()
{
return start_bit;
}
private:
/* ---------------------------------------------------------------------- */
/* Bitmap configuration attributes */
/* ---------------------------------------------------------------------- */
int id;
unsigned int start_bit;
std::set<unsigned int> reserved_bit;
std::bitset<N> * bs;
/* ---------------------------------------------------------------------- */
/* Database implementation */
/* ---------------------------------------------------------------------- */
const char * db_table;
/**
* Select callback from DB engine
*/
int select_cb(void * _bs, int num, char **values, char **names)
{
if ( num == 0 || values == 0 || values[0] == 0 )
{
return -1;
}
*static_cast<std::string*>(_bs) = (const char *) values[0];
return 0;
}
/**
* Loads a the contents of a bitmap from DB
* @param **uzbs, pointer to a string pointer to store the bitmap, must
* be freed by caller.
* @return 0 on success
*/
int select(SqlDB * db, std::string ** uzbs)
{
int rc;
std::ostringstream oss;
std::string zbs;
*uzbs = 0;
set_callback(static_cast<Callbackable::Callback>(&BitMap::select_cb),
static_cast<void *>(&zbs));
oss << "SELECT map FROM " << db_table << " WHERE id = " << id ;
rc = db->exec_rd(oss, this);
unset_callback();
if ( rc != 0 )
{
return rc;
}
else if (zbs.empty())
{
return -1;
}
*uzbs = one_util::zlib_decompress(zbs, true);
if ( *uzbs == 0 )
{
rc = -1;
}
return rc;
}
/**
* Insert a Bitmap in the DB, the bitmap is stored in a compressed (zlib)
* string form.
* @param replace true to replace false to insert
* @return 0 on success
*/
int insert_replace(SqlDB * db, bool replace)
{
std::ostringstream oss;
std::string * zipped = one_util::zlib_compress(bs->to_string(), true);
if (zipped == 0)
{
return -1;
}
char * ezipped64 = db->escape_str(zipped->c_str());
if (replace)
{
oss << "UPDATE " << db_table << " SET "
<< "map = '" << ezipped64 << "' "
<< "WHERE id = " << id;
}
else
{
oss << "INSERT INTO " << db_table << " (id, map) VALUES ("
<< id << ",'" << ezipped64 << "')";
}
int rc = db->exec_wr(oss);
delete zipped;
db->free_str(ezipped64);
return rc;
}
/**
* The reserved bit string is separated by ',' for each element
* and by ':' for ranges.
* @param string with reserved bits
*/
void set_reserved_bit(std::string& reserved)
{
std::vector<std::string> strings;
std::vector<std::string> range;
std::vector<std::string>::const_iterator it;
std::istringstream iss;
unsigned int bit, bit_start, bit_end;
strings = one_util::split(reserved, ',', true);
for (it = strings.begin(); it != strings.end(); it++)
{
// Try to split it by ':'
range = one_util::split(*it, ':', true);
iss.clear();
iss.str(range[0]);
iss >> bit_start;
if ( iss.fail() )
{
continue;
}
if (range.size() == 1)
{
bit_end = bit_start;
}
else if (range.size() == 2)
{
iss.clear();
iss.str(range[1]);
iss >> bit_end;
if ( iss.fail() )
{
continue;
}
}
else
{
continue;
}
for (bit = bit_start; bit <= bit_end; bit++)
{
reserved_bit.insert(bit);
}
}
}
};
#endif /*BITMAP_H_*/