mariadb/storage/oqgraph/oqgraph_thunk.cc
Antony T Curtis 2037f387ee Commit work for OQGraph v3
Todo:
 - Add #ifdefs for building in MySQL
 - Lots more testing
2011-04-12 15:50:32 -07:00

975 lines
26 KiB
C++

/* Copyright (C) 2009-2011 Arjen G Lentz & Antony T Curtis for Open Query
This program 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; version 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* ======================================================================
Open Query Graph Computation Engine, based on a concept by Arjen Lentz
Mk.III implementation by Antony Curtis & Arjen Lentz
For more information, documentation, support, enhancement engineering,
and non-GPL licensing, see http://openquery.com/graph
or contact graph@openquery.com
For packaged binaries, see http://ourdelta.org
======================================================================
*/
#include "oqgraph_thunk.h"
#include <boost/tuple/tuple.hpp>
#define MYSQL_SERVER
#include "mysql_priv.h"
oqgraph3::vertex_info::~vertex_info()
{ }
void oqgraph3::vertex_info::release()
{
if (!--(_ref_count))
{
oqgraph3::graph& cache= *_cache;
oqgraph3::graph::vertex_cache_type::const_iterator it;
if (!_on_gc)
{
cache._gc_vertices.push(this);
_on_gc= true;
}
while (!cache._gc_running && cache._gc_vertices.size() > 64)
{
cache._gc_running= true;
if (cache._gc_vertices.front()->_ref_count ||
(it= cache._cache_vertices.find(*cache._gc_vertices.front()))
== cache._cache_vertices.end())
{
cache._gc_vertices.front()->_on_gc= false;
}
else
{
cache._cache_vertices.quick_erase(it);
}
cache._gc_vertices.pop();
cache._gc_running= false;
}
}
}
oqgraph3::edge_key::~edge_key()
{ }
void oqgraph3::edge_key::release() const
{
if (!--(_ref_count))
{
oqgraph3::graph& cache= *_cache;
oqgraph3::graph::edge_cache_type::const_iterator it;
if (!_on_gc)
{
cache._gc_edges.push(const_cast<edge_key*>(this));
_on_gc= true;
}
while (!cache._gc_running && cache._gc_edges.size() > 512)
{
cache._gc_running= true;
if (cache._gc_edges.front()->_ref_count ||
(it= cache._cache_edges.find(*cache._gc_edges.front()))
== cache._cache_edges.end())
{
cache._gc_edges.front()->_on_gc= false;
}
else
{
cache._cache_edges.quick_erase(it);
}
cache._gc_edges.pop();
cache._gc_running= false;
}
}
}
oqgraph3::graph::graph(
::TABLE* table,
::Field* source,
::Field* target,
::Field* weight)
: _gc_running(false)
, _ref_count(0)
, _table(table)
, _source(source)
, _target(target)
, _weight(weight)
{
bitmap_set_bit(table->read_set, source->field_index);
bitmap_set_bit(table->read_set, target->field_index);
if (weight)
bitmap_set_bit(table->read_set, weight->field_index);
table->file->column_bitmaps_signal();
}
oqgraph3::graph::~graph()
{ }
oqgraph3::row_cursor& oqgraph3::row_cursor::operator++()
{
if (!_current)
return *this;
graph::edge_cache_type::iterator
current= _cache->_cache_edges.find(*_current);
if (current->second._next)
{
graph::edge_cache_type::iterator next=
_cache->_cache_edges.find(edge_key(*current->second._next));
if (_cache->_cache_edges.end() != next)
{
_current.reset(&next->first);
return *this;
}
}
TABLE& table= *_cache->_table;
table.file->ha_index_init(0, 1);
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(current->first._ref.data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_AFTER_KEY))
{
table.file->ha_index_end();
_current.reset(0);
return *this;
}
update_virtual_fields(table.in_use, &table);
table.file->ha_index_end();
edge_key tmp(table.key_info->key_length, _cache);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (_cache->_cache_edges.end() != found)
{
// we already had a row, link them together
found->second._prev= &current->first._ref;
current->second._next= &found->first._ref;
_current.reset(&found->first);
return *this;
}
_current.reset(&_cache->_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_cache->_source->val_int(),
_cache->_target->val_int(),
_cache->_weight ? _cache->_weight->val_real() : 1.0,
&_current->_ref,
0
)
)).first->first);
return *this;
}
oqgraph3::row_cursor& oqgraph3::row_cursor::first()
{
TABLE& table= *_cache->_table;
printf("%s:%d\n", __func__, __LINE__);
table.file->ha_index_init(0, 1);
printf("%s:%d\n", __func__, __LINE__);
if (!table.file->ha_index_first(table.record[0]))
{
printf("%s:%d\n", __func__, __LINE__);
update_virtual_fields(table.in_use, &table);
printf("%s:%d\n", __func__, __LINE__);
edge_key tmp(table.key_info->key_length, _cache);
printf("%s:%d\n", __func__, __LINE__);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
printf("%s:%d\n", __func__, __LINE__);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
printf("%s:%d\n", __func__, __LINE__);
if (found != _cache->_cache_edges.end())
{
// we already had a row
printf("%s:%d\n", __func__, __LINE__);
_current.reset(&found->first);
}
else
{
printf("%s:%d\n", __func__, __LINE__);
_current.reset(&_cache->_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_cache->_source->val_int(),
_cache->_target->val_int(),
_cache->_weight ? _cache->_weight->val_real() : 1.0,
0,
0
)
)).first->first);
}
}
printf("%s:%d\n", __func__, __LINE__);
table.file->ha_index_end();
printf("%s:%d\n", __func__, __LINE__);
return *this;
}
oqgraph3::row_cursor& oqgraph3::row_cursor::last()
{
TABLE& table= *_cache->_table;
table.file->ha_index_init(0, 1);
if (!table.file->ha_index_last(table.record[0]))
{
update_virtual_fields(table.in_use, &table);
edge_key tmp(table.key_info->key_length, _cache);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (found != _cache->_cache_edges.end())
{
// we already had a row
_current.reset(&found->first);
}
else
{
_current.reset(&_cache->_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_cache->_source->val_int(),
_cache->_target->val_int(),
_cache->_weight ? _cache->_weight->val_real() : 1.0,
0,
0
)
)).first->first);
}
}
table.file->ha_index_end();
return *this;
}
oqgraph3::row_cursor& oqgraph3::row_cursor::operator--()
{
if (!_current)
return last();
graph::edge_cache_type::iterator
current= _cache->_cache_edges.find(*_current);
if (current->second._prev)
{
graph::edge_cache_type::iterator prev=
_cache->_cache_edges.find(edge_key(*current->second._prev));
if (_cache->_cache_edges.end() != prev)
{
_current.reset(&prev->first);
return *this;
}
}
TABLE& table= *_cache->_table;
table.file->ha_index_init(0, 1);
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(current->first._ref.data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_BEFORE_KEY))
{
table.file->ha_index_end();
_current.reset();
return *this;
}
update_virtual_fields(table.in_use, &table);
table.file->ha_index_end();
edge_key tmp(table.key_info->key_length, _cache);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (_cache->_cache_edges.end() != found)
{
// we already had a row, link them together
found->second._next= &current->first._ref;
current->second._prev= &found->first._ref;
_current.reset(&found->first);
return *this;
}
_current.reset(&_cache->_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_cache->_source->val_int(),
_cache->_target->val_int(),
_cache->_weight ? _cache->_weight->val_real() : 1.0,
0,
&_current->_ref
)
)).first->first);
return *this;
}
oqgraph3::row_cursor& oqgraph3::row_cursor::operator+=(difference_type delta)
{
if (!delta || !_current)
return *this;
if (delta < 0)
return *this -= (-delta);
graph::edge_cache_type::iterator
current= _cache->_cache_edges.find(*_current);
bool index_started= false;
TABLE& table= *_cache->_table;
while (delta-- > 0)
{
if (_cache->_cache_edges.end() != current)
{
if (current->second._next)
{
graph::edge_cache_type::iterator
next= _cache->_cache_edges.find(edge_key(*current->second._next));
if (_cache->_cache_edges.end() != next)
{
current= next;
continue;
}
if (!index_started)
{
table.file->ha_index_init(0, 1);
index_started= true;
}
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(current->second._next->data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_KEY_OR_NEXT))
{
table.file->ha_index_end();
_current.reset();
return *this;
}
}
else
{
if (!index_started)
{
table.file->ha_index_init(0, 1);
index_started= true;
}
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(current->first._ref.data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_AFTER_KEY))
{
table.file->ha_index_end();
_current.reset();
return *this;
}
}
edge_key tmp(table.key_info->key_length);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (found != _cache->_cache_edges.end())
{
// we already had a row, link them together
found->second._next= &current->first._ref;
current->second._prev= &found->first._ref;
}
current= found;
}
else
{
if (!index_started)
return *this;
if (table.file->ha_index_next(table.record[0]))
{
table.file->ha_index_end();
_current.reset();
return *this;
}
edge_key tmp(table.key_info->key_length);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
current= _cache->_cache_edges.find(tmp);
}
}
if (index_started)
{
table.file->ha_index_end();
}
return *this;
}
oqgraph3::row_cursor& oqgraph3::row_cursor::operator-=(difference_type delta)
{
if (!delta || !_current)
return *this;
if (delta < 0)
return *this += (-delta);
graph::edge_cache_type::iterator
current= _cache->_cache_edges.find(*_current);
bool index_started= false;
TABLE& table= *_cache->_table;
while (delta-- > 0)
{
if (_cache->_cache_edges.end() != current)
{
if (current->second._next)
{
graph::edge_cache_type::iterator next=
_cache->_cache_edges.find(edge_key(*current->second._next));
if (_cache->_cache_edges.end() != next)
{
current= next;
continue;
}
if (!index_started)
{
table.file->ha_index_init(0, 1);
index_started= true;
}
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(current->second._next->data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_KEY_OR_PREV))
{
table.file->ha_index_end();
return first();
}
}
else
{
if (!index_started)
{
table.file->ha_index_init(0, 1);
index_started= true;
}
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(current->first._ref.data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_BEFORE_KEY))
{
table.file->ha_index_end();
return first();
}
}
edge_key tmp(table.key_info->key_length);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (_cache->_cache_edges.end() != found)
{
// we already had a row, link them together
found->second._next= &current->first._ref;
current->second._prev= &found->first._ref;
}
current= found;
}
else
{
if (!index_started)
return first();
if (table.file->ha_index_prev(table.record[0]))
{
table.file->ha_index_end();
return first();
}
edge_key tmp(table.key_info->key_length);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
current= _cache->_cache_edges.find(tmp);
}
}
if (index_started)
{
table.file->ha_index_end();
}
return *this;
}
oqgraph3::vertex_descriptor oqgraph3::graph::vertex(vertex_id id)
{
vertex_cache_type::const_iterator
found= _cache_vertices.find(vertex_info(id));
if (_cache_vertices.end() != found)
return vertex_descriptor(const_cast<vertex_info*>(found.operator->()));
return vertex_descriptor(const_cast<vertex_info*>(
_cache_vertices.insert(vertex_info(id, this)).first.operator->()));
}
oqgraph3::edge_descriptor oqgraph3::graph::edge(const edge_key& key)
{
edge_cache_type::const_iterator
found= _cache_edges.find(key);
if (_cache_edges.end() != found)
return edge_descriptor(&found->first);
TABLE& table= *_table;
table.file->ha_index_init(0, 0);
if (table.file->ha_index_read_map(
table.record[0],
reinterpret_cast<const uchar*>(key._ref.data()),
(key_part_map)((1 << table.key_info->key_parts) - 1),
HA_READ_KEY_EXACT))
{
table.file->ha_index_end();
return edge_descriptor();
}
update_virtual_fields(table.in_use, &table);
table.file->ha_index_end();
return edge_descriptor(&_cache_edges.insert(
std::make_pair(
edge_key(key, this),
row_info(
_source->val_int(),
_target->val_int(),
_weight ? _weight->val_real() : 1.0,
0,
0
)
)).first->first);
}
oqgraph3::edge_descriptor oqgraph3::graph::edge(
const vertex_descriptor& source,
const vertex_descriptor& target)
{
vertex_cache_type::const_iterator xsource= _cache_vertices.find(*source);
if (_cache_vertices.end() != xsource && xsource->_out_edges)
{
const vertex_info::edge_list_type& edges= *xsource->_out_edges;
for (vertex_info::edge_list_type::const_iterator
it= edges.begin(), end= edges.end(); end != it; ++it)
{
edge_cache_type::const_iterator
found= _cache_edges.find(edge_key(*it));
if (_cache_edges.end() != found &&
target->id == found->second.second)
{
return edge_descriptor(&found->first);
}
}
return edge_descriptor();
}
vertex_cache_type::const_iterator xtarget= _cache_vertices.find(*target);
if (_cache_vertices.end() != xtarget && xtarget->_in_edges)
{
const vertex_info::edge_list_type& edges= *xtarget->_in_edges;
for (vertex_info::edge_list_type::const_iterator
it= edges.begin(), end= edges.end(); end != it; ++it)
{
edge_cache_type::const_iterator
found= _cache_edges.find(edge_key(*it));
if (_cache_edges.end() != found &&
source->id == found->second.first)
{
return edge_descriptor(&found->first);
}
}
return edge_descriptor();
}
// If we have an index which has both key parts, we can use that
// to quickly retrieve the edge descriptor.
TABLE& table= *_table;
uint source_fieldpos= _source->offset(table.record[0]);
uint target_fieldpos= _target->offset(table.record[0]);
int i= 0;
for( ::KEY *key_info= table.s->key_info,
*key_end= key_info + table.s->keys;
key_info < key_end; ++key_info, ++i)
{
if (key_info->key_parts < 2)
continue;
if ((key_info->key_part[0].offset == source_fieldpos &&
key_info->key_part[1].offset == target_fieldpos) ||
(key_info->key_part[0].offset == target_fieldpos &&
key_info->key_part[1].offset == source_fieldpos))
{
// we can use this key
restore_record(&table, s->default_values);
bitmap_set_bit(table.write_set, _source->field_index);
bitmap_set_bit(table.write_set, _target->field_index);
_source->store(source->id, 1);
_target->store(target->id, 1);
bitmap_clear_bit(table.write_set, _source->field_index);
bitmap_clear_bit(table.write_set, _target->field_index);
uint key_len= key_info->key_part[0].store_length +
key_info->key_part[1].store_length;
uchar* key_prefix= (uchar*) my_alloca(key_len);
table.file->ha_index_init(i, 0);
key_copy(key_prefix, table.record[0], key_info, key_len);
if (!table.file->ha_index_read_map(
table.record[0], key_prefix, (key_part_map)3, HA_READ_KEY_EXACT))
{
// We have found the edge,
update_virtual_fields(table.in_use, &table);
table.file->ha_index_end();
my_afree(key_prefix);
edge_key tmp(table.key_info->key_length, this);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache_edges.find(tmp);
if (_cache_edges.end() != found)
{
// we already had the edge
return edge_descriptor(&found->first);
}
return edge_descriptor(&_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_source->val_int(),
_target->val_int(),
_weight ? _weight->val_real() : 1.0,
0,
0
)
)).first->first);
}
}
}
const vertex_info::edge_list_type& edges= vertex(source->id)->out_edges();
for (vertex_info::edge_list_type::const_iterator
it= edges.begin(), end= edges
.end(); end != it; ++it)
{
edge_cache_type::const_iterator
found= _cache_edges.find(edge_key(*it));
if (_cache_edges.end() != found &&
target->id == found->second.second)
{
return edge_descriptor(&found->first);
}
}
return edge_descriptor();
}
oqgraph3::edges_size_type oqgraph3::graph::num_edges() const
{
return _table->file->stats.records;
}
const oqgraph3::vertex_info::edge_list_type&
oqgraph3::vertex_info::out_edges()
{
if (!_out_edges)
{
_out_edges = edge_list_type();
TABLE& table= *_cache->_table;
uint source_fieldpos= _cache->_source->offset(table.record[0]);
int i= 0;
for( ::KEY *key_info= table.s->key_info,
*key_end= key_info + table.s->keys;
key_info < key_end; ++key_info, ++i)
{
if (key_info->key_part[0].offset == source_fieldpos)
{
// we can use this key
restore_record(&table, s->default_values);
bitmap_set_bit(table.write_set, _cache->_source->field_index);
_cache->_source->store(id, 1);
bitmap_clear_bit(table.write_set, _cache->_source->field_index);
uint key_len= key_info->key_part[0].store_length;
uchar* key= (uchar*) my_alloca(key_len);
table.file->ha_index_init(i, 1);
key_copy(key, table.record[0], key_info, key_len);
if (!table.file->ha_index_read_map(
table.record[0], key, (key_part_map)1, HA_READ_KEY_EXACT))
{
// We have found an edge,
do
{
update_virtual_fields(table.in_use, &table);
edge_key tmp(table.key_info->key_length, _cache);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (_cache->_cache_edges.end() == found)
{
found= _cache->_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_cache->_source->val_int(),
_cache->_target->val_int(),
_cache->_weight ? _cache->_weight->val_real() : 1.0,
0,
0
)
)).first;
}
_out_edges->push_back(found->first._ref);
}
while (!table.file->ha_index_next_same(
table.record[0], key, key_len));
table.file->ha_index_end();
my_afree(key);
break;
}
table.file->ha_index_end();
}
}
}
return *_out_edges;
}
const oqgraph3::vertex_info::edge_list_type&
oqgraph3::vertex_info::in_edges()
{
if (!_in_edges)
{
_in_edges = edge_list_type();
TABLE& table= *_cache->_table;
uint target_fieldpos= _cache->_target->offset(table.record[0]);
int i= 0;
for( ::KEY *key_info= table.s->key_info,
*key_end= key_info + table.s->keys;
key_info < key_end; ++key_info, ++i)
{
if (key_info->key_part[0].offset == target_fieldpos)
{
// we can use this key
restore_record(&table, s->default_values);
bitmap_set_bit(table.write_set, _cache->_target->field_index);
_cache->_target->store(id, 1);
bitmap_clear_bit(table.write_set, _cache->_target->field_index);
uint key_len= key_info->key_part[0].store_length;
uchar* key= (uchar*) my_alloca(key_len);
table.file->ha_index_init(i, 1);
key_copy(key, table.record[0], key_info, key_len);
if (!table.file->ha_index_read_map(
table.record[0], key, (key_part_map)1, HA_READ_KEY_EXACT))
{
// We have found an edge,
do
{
update_virtual_fields(table.in_use, &table);
edge_key tmp(table.key_info->key_length, _cache);
key_copy(
reinterpret_cast<uchar*>(const_cast<char*>(tmp._ref.data())),
table.record[0],
table.key_info,
tmp._ref.size(), true);
graph::edge_cache_type::iterator
found= _cache->_cache_edges.find(tmp);
if (_cache->_cache_edges.end() == found)
{
found= _cache->_cache_edges.insert(
std::make_pair(
tmp,
row_info(
_cache->_source->val_int(),
_cache->_target->val_int(),
_cache->_weight ? _cache->_weight->val_real() : 1.0,
0,
0
)
)).first;
}
_in_edges->push_back(found->first._ref);
}
while (!table.file->ha_index_next_same(
table.record[0], key, key_len));
table.file->ha_index_end();
my_afree(key);
break;
}
table.file->ha_index_end();
}
}
}
return *_in_edges;
}
std::size_t oqgraph3::vertex_info::degree()
{
return out_edges().size() + in_edges().size();
}
oqgraph3::degree_size_type oqgraph3::vertex_descriptor::in_degree() const
{
return (*this)->in_edges().size();
}
oqgraph3::degree_size_type oqgraph3::vertex_descriptor::out_degree() const
{
return (*this)->out_edges().size();
}
oqgraph3::vertex_descriptor oqgraph3::edge_descriptor::source() const
{
return (*this)->_cache->vertex(
oqgraph3::row_cursor(*this, (*this)->_cache)->first);
}
oqgraph3::vertex_descriptor oqgraph3::edge_descriptor::target() const
{
return (*this)->_cache->vertex(
oqgraph3::row_cursor(*this, (*this)->_cache)->second);
}