Commit 2037f387 authored by Antony T Curtis's avatar Antony T Curtis

Commit work for OQGraph v3

Todo:
 - Add #ifdefs for building in MySQL
 - Lots more testing
parent 64e43e1c
......@@ -54,13 +54,13 @@ EXTRA_DIST = ha_oqgraph.h ha_oqgraph.cc graphcore.cc \
# DTRACEFILES = .libs/libha_oqgraph_la-ha_oqgraph.o
ORIG_CXXFLAGS = @CXXFLAGS@
CXXFLAGS=
CXXFLAGS= -DBOOST_ALL_NO_LIB=1
noinst_HEADERS = ha_oqgraph.h \
graphcore-graph.h graphcore-types.h graphcore.h
# oqgraph_probes.h
noinst_LTLIBRARIES = libgraphcore.la
libgraphcore_la_SOURCES = graphcore.cc
libgraphcore_la_SOURCES = graphcore.cc graphcore-graph.cc oqgraph_shim.cc oqgraph_thunk.cc
libgraphcore_la_CXXFLAGS = $(ORIG_CXXFLAGS) $(BOOST_CXXFLAGS)
if BUILD_OQGRAPH_FOR_MYSQL
......@@ -73,14 +73,14 @@ endif !BUILD_OQGRAPH_STANDALONE
EXTRA_LTLIBRARIES = ha_oqgraph.la
mysqlplugin_LTLIBRARIES = @plugin_oqgraph_shared_target@
ha_oqgraph_la_SOURCES = ha_oqgraph.cc
ha_oqgraph_la_SOURCES = ha_oqgraph.cc oqgraph_judy.cc
ha_oqgraph_la_LIBADD = libgraphcore.la
# if HAVE_DTRACE
# ha_oqgraph_la_LIBADD += oqgraph_probes.o
# endif
ha_oqgraph_la_LDFLAGS = -shared -module -rpath $(mysqlplugindir)
ha_oqgraph_la_LDFLAGS = -shared -module -rpath $(mysqlplugindir) -lJudy -Wl,-flat_namespace
ha_oqgraph_la_CFLAGS = $(ORIG_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN
ha_oqgraph_la_CXXFLAGS = $(ORIG_CXXFLAGS) -DMYSQL_DYNAMIC_PLUGIN
......
/* 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 "graphcore-graph.h"
......@@ -27,22 +27,209 @@
#ifndef oq_graphcore_graph_h_
#define oq_graphcore_graph_h_
typedef adjacency_list
<
vecS,
vecS,
bidirectionalS,
VertexInfo,
EdgeInfo
> Graph;
#define GRAPH_WEIGHTMAP(G) get(&EdgeInfo::weight, G)
typedef property_map<Graph, EdgeWeight EdgeInfo::*>::type weightmap_type;
#define GRAPH_INDEXMAP(G) get(vertex_index, G)
typedef property_map<Graph, vertex_index_t>::type indexmap_type;
#define GRAPH_IDMAP(G) get(&VertexInfo::id, G)
typedef property_map<Graph, VertexID VertexInfo::*>::type idmap_type;
#include "oqgraph_shim.h"
#include <boost/graph/two_bit_color_map.hpp>
namespace boost
{
typedef oqgraph3::graph Graph;
template <>
struct hash<graph_traits<oqgraph3::graph>::vertex_descriptor>
: public std::unary_function<
graph_traits<oqgraph3::graph>::vertex_descriptor, std::size_t>
{
std::size_t operator()(
const graph_traits<oqgraph3::graph>::vertex_descriptor& v) const
{
return boost::hash_value(v->id);
}
};
template<typename IndexMap = identity_property_map>
struct two_bit_judy_map
{
typedef typename property_traits<IndexMap>::key_type key_type;
typedef two_bit_color_type value_type;
typedef void reference;
typedef read_write_property_map_tag category;
open_query::judy_bitset msb;
open_query::judy_bitset lsb;
IndexMap index;
two_bit_judy_map(const IndexMap& i)
: index(i)
{ }
friend two_bit_color_type get(
const two_bit_judy_map<IndexMap>& pm,
typename property_traits<IndexMap>::key_type key)
{
typename property_traits<IndexMap>::value_type i = get(pm.index, key);
return two_bit_color_type((2*int(pm.msb.test(i))) | int(pm.lsb.test(i)));
}
friend void put(
two_bit_judy_map<IndexMap>& pm,
typename property_traits<IndexMap>::key_type key,
two_bit_color_type value)
{
typename property_traits<IndexMap>::value_type i = get(pm.index, key);
pm.msb.set(i, value & 2);
pm.lsb.set(i, value & 1);
}
};
template<typename IndexMap>
inline two_bit_judy_map<IndexMap>
make_two_bit_judy_map(const IndexMap& index)
{
return two_bit_judy_map<IndexMap>(index);
}
template <typename Type>
struct default_lazy_initializer
{
template <typename Key>
Type operator()(const Key&) const { return Type(); }
};
template <typename Type>
struct copy_initializer
{
copy_initializer(const Type& value) : _(value) { }
template <typename Key>
const Type& operator()(const Key&) const { return _; }
const Type& _;
};
template <typename Type>
copy_initializer<Type> make_copy_initializer(const Type& value)
{ return copy_initializer<Type>(value); }
template <typename Type>
struct value_initializer
{
value_initializer(const Type& value) : _(value) { }
template <typename Key>
const Type& operator()(const Key&) const { return _; }
const Type _;
};
template <typename Type>
value_initializer<Type> make_value_initializer(const Type& value)
{ return value_initializer<Type>(value); }
template <typename Key>
struct identity_initializer
{
const Key& operator()(const Key& _) const { return _; }
};
template <class Container, class Generator>
struct lazy_property_map
{
typedef lazy_property_map<Container, Generator> self;
typedef typename Container::key_type key_type;
typedef typename Container::value_type::second_type value_type;
typedef value_type& reference;
typedef lvalue_property_map_tag category;
lazy_property_map(Container& m, Generator g= Generator())
: _m(m)
, _g(g)
{ }
reference operator[](const key_type& k) const
{
typename Container::iterator found= _m.find(k);
if (_m.end() == found)
{
found= _m.insert(std::make_pair(k, _g(k))).first;
}
return found->second;
}
void set(const key_type& k, const value_type& v)
{ _m[k] = v; }
friend reference get(const self& s, const key_type& k)
{
return s[k];
}
friend void put(self& s, const key_type& k, const value_type& v)
{ s.set(k, v); }
Container& _m;
Generator _g;
};
template <class Container, class Generator>
lazy_property_map<Container, Generator>
make_lazy_property_map(Container& c, Generator g)
{ return lazy_property_map<Container, Generator>(c, g); }
#if 0
struct map_wra
struct property_traits< unordered_map_wrapper<K,T,H,P,A
template <class K, class T, class H, class P, class A>
struct property_traits< unordered_map<K,T,H,P,A> > {
typedef T value_type;
typedef K key_type;
typedef T& reference;
struct category
: public read_write_property_map_tag
, public lvalue_property_map_tag
{ };
friend reference get(
unordered_map<K,T,H,P,A>& m,
const key_type& k)
{
return m[k];
}
friend void put(
unordered_map<K,T,H,P,A>& m,
const key_type& k,
const value_type& v)
{
m[k]= v;
}
};
//template <class K, class T, class H, class P, class A>
//property_traits< unordered_map<K,T,H,P,A> >::reference
//get(unordered_map<K,T,H,P,A>& m,
// const property_traits< unordered_map<K,T,H,P,A> >::key_type& k)
//{ return m[ k ]; }
//template <class K, class T, class H, class P, class A>
//void put(
// unordered_map<K,T,H,P,A>& m,
// const property_traits< unordered_map<K,T,H,P,A> >::key_type& k,
// const property_traits< unordered_map<K,T,H,P,A> >::value_type& v)
//{ m[ k ] = v; }
//template <class K, class T, class H, class P, class A>
//property_traits< unordered_map<K,T,H,P,A>::reference
//get(unordered_map<K,T,H,P,A>& m,
// const graph_traits<oqgraph3::graph>::vertex_descriptor& v)
//{ return m[ v->id ]; }
#endif
}
#endif
......@@ -26,6 +26,7 @@
#ifndef oq_graphcore_types_h_
#define oq_graphcore_types_h_
namespace open_query
{
......@@ -33,4 +34,9 @@ namespace open_query
typedef double EdgeWeight;
}
class Field;
typedef struct st_table TABLE;
#endif
......@@ -30,14 +30,13 @@
#include <boost/config.hpp>
#include "graphcore-graph.h"
#include <set>
#include <stack>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_archetypes.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/iteration_macros.hpp>
......@@ -46,54 +45,16 @@
#include "graphcore.h"
#include <boost/unordered_map.hpp>
using namespace open_query;
using namespace boost;
static const row empty_row = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
namespace open_query
{
enum vertex_id_t { vertex_id };
struct VertexInfo {
inline VertexInfo() { }
inline VertexInfo(VertexID _id)
: id(_id) { }
VertexID id;
};
struct EdgeInfo {
EdgeWeight weight;
};
}
namespace boost
{
BOOST_INSTALL_PROPERTY(vertex, id);
namespace graph
{
template<>
struct internal_vertex_name<VertexInfo>
{
typedef multi_index::member<VertexInfo, VertexID, &VertexInfo::id> type;
};
template<>
struct internal_vertex_constructor<VertexInfo>
{
typedef vertex_from_name<VertexInfo> type;
};
}
}
namespace open_query
{
#include "graphcore-graph.h"
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge;
......@@ -261,18 +222,15 @@ namespace open_query {
public:
Graph g;
weightmap_type weightmap;
idmap_type idmap;
indexmap_type indexmap;
optional<Vertex> find_vertex(VertexID id) const;
optional<Edge> find_edge(Vertex, Vertex) const;
inline oqgraph_share() throw()
: g(),
weightmap(GRAPH_WEIGHTMAP(g)),
idmap(GRAPH_IDMAP(g)),
indexmap(GRAPH_INDEXMAP(g))
inline oqgraph_share(
TABLE* table,
Field* origid,
Field* destid,
Field* weight) throw()
: g(table, origid, destid, weight)
{ }
inline ~oqgraph_share()
{ }
......@@ -360,13 +318,13 @@ namespace open_query {
}
};
template <typename P, typename D>
struct GRAPHCORE_INTERNAL oqgraph_visit_dist
: public base_visitor<oqgraph_visit_dist>
: public base_visitor< oqgraph_visit_dist<P,D> >
{
typedef on_finish_vertex event_filter;
oqgraph_visit_dist(std::vector<Vertex>::iterator p,
std::vector<EdgeWeight>::iterator d,
oqgraph_visit_dist(const P& p, const D& d,
stack_cursor *cursor)
: seq(0), m_cursor(*cursor), m_p(p), m_d(d)
{ assert(cursor); }
......@@ -374,22 +332,28 @@ namespace open_query {
template<class T, class Graph>
void operator()(T u, Graph &g)
{
m_cursor.results.push(reference(++seq, u, m_d[GRAPH_INDEXMAP(g)[u]]));
m_cursor.results.push(reference(++seq, u, m_d[ u ]));
}
private:
int seq;
stack_cursor &m_cursor;
std::vector<Vertex>::iterator m_p;
std::vector<EdgeWeight>::iterator m_d;
P m_p;
D m_d;
};
template<bool record_weight, typename goal_filter>
template <typename P, typename D>
oqgraph_visit_dist<P,D>
make_oqgraph_visit_dist(const P& p, const D& d, stack_cursor *cursor)
{ return oqgraph_visit_dist<P,D>(p, d, cursor); }
template<bool record_weight, typename goal_filter, typename P>
struct GRAPHCORE_INTERNAL oqgraph_goal
: public base_visitor<oqgraph_goal<record_weight,goal_filter> >
: public base_visitor<oqgraph_goal<record_weight,goal_filter,P> >
{
typedef goal_filter event_filter;
oqgraph_goal(Vertex goal, std::vector<Vertex>::iterator p,
oqgraph_goal(const Vertex& goal, const P& p,
stack_cursor *cursor)
: m_goal(goal), m_cursor(*cursor), m_p(p)
{ assert(cursor); }
......@@ -400,17 +364,16 @@ namespace open_query {
if (u == m_goal)
{
int seq= 0;
indexmap_type indexmap= GRAPH_INDEXMAP(g);
for (Vertex q, v= u;; v = q, seq++)
if ((q= m_p[ indexmap[v] ]) == v)
if ((q= m_p[ v ]) == v)
break;
for (Vertex v= u;; u= v)
{
optional<Edge> edge;
optional<EdgeWeight> weight;
v= m_p[ indexmap[u] ];
v= m_p[ u ];
if (record_weight && u != v)
{
typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
......@@ -419,7 +382,7 @@ namespace open_query {
if (target(*ei, g) == u)
{
edge= *ei;
weight= GRAPH_WEIGHTMAP(g)[*ei];
weight= get(boost::edge_weight, g, *ei);
break;
}
}
......@@ -437,8 +400,14 @@ namespace open_query {
private:
Vertex m_goal;
stack_cursor &m_cursor;
std::vector<Vertex>::iterator m_p;
P m_p;
};
template<bool record_weight, typename goal_filter, typename P>
oqgraph_goal<record_weight, goal_filter, P>
make_oqgraph_goal(const Vertex& goal, const P& p, stack_cursor *cursor)
{ return oqgraph_goal<record_weight, goal_filter, P>(goal, p, cursor); }
}
namespace open_query
......@@ -468,9 +437,14 @@ namespace open_query
return new (std::nothrow) oqgraph(share);
}
oqgraph_share* oqgraph::create() throw()
oqgraph_share* oqgraph::create(
TABLE* table,
Field* origid,
Field* destid,
Field* weight) throw()
{
return new (std::nothrow) oqgraph_share();
return new (std::nothrow) oqgraph_share(
table, origid, destid, weight);
}
optional<Edge>
......@@ -480,14 +454,14 @@ namespace open_query
{
graph_traits<Graph>::out_edge_iterator ei, ei_end;
tie(ei, ei_end)= out_edges(orig, g);
if ((ei= find_if(ei, ei_end, target_equals(dest, g))) != ei_end)
if ((ei= std::find_if(ei, ei_end, target_equals(dest, g))) != ei_end)
return *ei;
}
else
{
graph_traits<Graph>::in_edge_iterator ei, ei_end;
tie(ei, ei_end)= in_edges(dest, g);
if ((ei= find_if(ei, ei_end, source_equals(orig, g))) != ei_end)
if ((ei= std::find_if(ei, ei_end, source_equals(orig, g))) != ei_end)
return *ei;
}
return optional<Edge>();
......@@ -496,9 +470,10 @@ namespace open_query
optional<Vertex>
oqgraph_share::find_vertex(VertexID id) const
{
return boost::graph::find_vertex(id, g);
return ::boost::find_vertex(id, g);
}
#if 0
int oqgraph::delete_all() throw()
{
share->g.clear();
......@@ -598,8 +573,9 @@ namespace open_query
optional<Vertex> orig= source(*edge, share->g),
dest= target(*edge, share->g);
bool orig_neq= orig_id ? share->idmap[*orig] != *orig_id : 0;
bool dest_neq= dest_id ? share->idmap[*dest] != *dest_id : 0;
bool orig_neq= orig_id ? get(boost::vertex_index, share->g, *orig) != *orig_id : 0;
bool dest_neq= dest_id ? get(boost::vertex_index, share->g, *dest) != *dest_id : 0;
if (orig_neq || dest_neq)
{
optional<Edge> new_edge;
......@@ -675,7 +651,6 @@ namespace open_query
return OK;
}
int oqgraph::delete_edge(VertexID orig_id, VertexID dest_id) throw()
{
optional<Vertex> orig, dest;
......@@ -694,6 +669,7 @@ namespace open_query
remove_vertex(*dest, share->g);
return OK;
}
#endif
int oqgraph::search(int *latch, VertexID *orig_id, VertexID *dest_id) throw()
......@@ -731,7 +707,8 @@ namespace open_query
{
Vertex v= target(*ei, share->g);
static_cast<stack_cursor*>(cursor)->
results.push(reference(++seq, v, *ei, share->weightmap[*ei]));
results.push(reference(++seq, v, *ei,
get(boost::edge_weight, share->g, *ei)));
}
}
/* fall through */
......@@ -745,7 +722,8 @@ namespace open_query
{
Vertex v= source(*ei, share->g);
static_cast<stack_cursor*>(cursor)->
results.push(reference(++seq, v, *ei, share->weightmap[*ei]));
results.push(reference(++seq, v, *ei,
get(boost::edge_weight, share->g, *ei)));
}
}
break;
......@@ -757,27 +735,29 @@ namespace open_query
case DIJKSTRAS | HAVE_ORIG | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
{
std::vector<Vertex> p(num_vertices(share->g));
std::vector<EdgeWeight> d(num_vertices(share->g));
oqgraph_goal<true, on_finish_vertex>
vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor));
p[share->indexmap[*orig]]= *orig;
boost::unordered_map<Vertex, Vertex> p;
boost::unordered_map<Vertex, EdgeWeight> d;
p[ *orig ]= *orig;
d[ *orig ] = EdgeWeight();
try
{
dijkstra_shortest_paths(share->g, *orig,
weight_map(
share->weightmap
).
distance_map(
make_iterator_property_map(d.begin(), share->indexmap)
).
predecessor_map(
make_iterator_property_map(p.begin(), share->indexmap)
).
visitor(
make_dijkstra_visitor(vis)
)
);
dijkstra_shortest_paths_no_init(share->g, *orig,
make_lazy_property_map(p, identity_initializer<Vertex>()),
make_lazy_property_map(d, value_initializer<EdgeWeight>(
(std::numeric_limits<EdgeWeight>::max)())),
get(edge_weight, share->g),
get(vertex_index, share->g),
std::less<EdgeWeight>(),
closed_plus<EdgeWeight>(),
EdgeWeight(),
make_dijkstra_visitor(
make_oqgraph_goal<true, on_finish_vertex>(
*dest,
boost::make_assoc_property_map(p),
static_cast<stack_cursor*>(cursor)
)
),
make_two_bit_judy_map(get(vertex_index, share->g)));
}
catch (...)
{ /* printf("found\n"); */ }
......@@ -787,23 +767,25 @@ namespace open_query
case BREADTH_FIRST | HAVE_ORIG | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
{
std::vector<Vertex> p(num_vertices(share->g));
oqgraph_goal<false, on_discover_vertex>
vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor));
p[share->indexmap[*orig]]= *orig;
boost::unordered_map<Vertex, Vertex> p;
boost::queue<Vertex> Q;
p[ *orig ]= *orig;
try
{
breadth_first_search(share->g, *orig,
visitor(make_bfs_visitor(
breadth_first_visit(share->g, *orig, Q,
make_bfs_visitor(
std::make_pair(
record_predecessors(
make_iterator_property_map(p.begin(), share->indexmap),
boost::make_assoc_property_map(p),
on_tree_edge()
),
vis)
make_oqgraph_goal<false, on_discover_vertex>(
*dest, boost::make_assoc_property_map(p),
static_cast<stack_cursor*>(cursor)
)
)
)
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
}
catch (...)
{ /* printf("found\n"); */ }
......@@ -814,109 +796,121 @@ namespace open_query
case BREADTH_FIRST | HAVE_ORIG:
if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
{
std::vector<Vertex> p(num_vertices(share->g));
std::vector<EdgeWeight> d(num_vertices(share->g));
oqgraph_visit_dist vis(p.begin(), d.begin(),
static_cast<stack_cursor*>(cursor));
p[share->indexmap[*orig]]= *orig;
boost::unordered_map<Vertex, Vertex> p;
boost::unordered_map<Vertex, EdgeWeight> d;
boost::queue<Vertex> Q;
p[ *orig ]= *orig;
d[ *orig ] = EdgeWeight();
switch (ALGORITHM & op)
{
case DIJKSTRAS:
dijkstra_shortest_paths(share->g, *orig,
weight_map(
share->weightmap
).
distance_map(
make_iterator_property_map(d.begin(), share->indexmap)
).
predecessor_map(
make_iterator_property_map(p.begin(), share->indexmap)
).
visitor(
make_dijkstra_visitor(vis)
)
);
break;
dijkstra_shortest_paths_no_init(share->g, *orig,
make_lazy_property_map(p, identity_initializer<Vertex>()),
make_lazy_property_map(d, value_initializer<EdgeWeight>(
(std::numeric_limits<EdgeWeight>::max)())),
get(edge_weight, share->g),
get(vertex_index, share->g),
std::less<EdgeWeight>(),
closed_plus<EdgeWeight>(),
EdgeWeight(),
make_dijkstra_visitor(
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p), d,
static_cast<stack_cursor*>(cursor)
)
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
case BREADTH_FIRST:
breadth_first_search(share->g, *orig,
visitor(make_bfs_visitor(
breadth_first_visit(share->g, *orig, Q,
make_bfs_visitor(
std::make_pair(
record_predecessors(
make_iterator_property_map(p.begin(),
share->indexmap),
boost::make_assoc_property_map(p),
on_tree_edge()
),
std::make_pair(
record_distances(
make_iterator_property_map(d.begin(),
share->indexmap),
boost::make_assoc_property_map(d),
on_tree_edge()
),
vis
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p),
boost::make_assoc_property_map(d),
static_cast<stack_cursor*>(cursor)
)
))
))
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
default:
abort();
}
}
break;
#if 0
case BREADTH_FIRST | HAVE_DEST:
case DIJKSTRAS | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
{
std::vector<Vertex> p(num_vertices(share->g));
std::vector<EdgeWeight> d(num_vertices(share->g));
oqgraph_visit_dist vis(p.begin(), d.begin(),
static_cast<stack_cursor*>(cursor));
boost::unordered_map<Vertex, Vertex> p;
boost::unordered_map<Vertex, EdgeWeight> d;
boost::queue<Vertex> Q;
reverse_graph<Graph> r(share->g);
p[share->indexmap[*dest]]= *dest;
p[ *dest ]= *dest;
d[ *dest ] = EdgeWeight();
switch (ALGORITHM & op)
{
case DIJKSTRAS:
dijkstra_shortest_paths(r, *dest,
weight_map(
share->weightmap
).
distance_map(
make_iterator_property_map(d.begin(), share->indexmap)
).
predecessor_map(
make_iterator_property_map(p.begin(), share->indexmap)
).
visitor(
make_dijkstra_visitor(vis)
)
);
dijkstra_shortest_paths_no_init(share->g, *dest,
make_lazy_property_map(p, identity_initializer<Vertex>()),
make_lazy_property_map(d, value_initializer<EdgeWeight>(
(std::numeric_limits<EdgeWeight>::max)())),
get(edge_weight, share->g),
get(vertex_index, share->g),
std::less<EdgeWeight>(),
closed_plus<EdgeWeight>(),
EdgeWeight(),
make_dijkstra_visitor(
make_dijkstra_visitor(
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p),
boost::make_assoc_property_map(d),
static_cast<stack_cursor*>(cursor)
)
)
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
case BREADTH_FIRST:
breadth_first_search(r, *dest,
visitor(make_bfs_visitor(
breadth_first_visit(share->g, *dest, Q,
make_bfs_visitor(
std::make_pair(
record_predecessors(
make_iterator_property_map(p.begin(),
share->indexmap),
boost::make_assoc_property_map(p),
on_tree_edge()
),
std::make_pair(
record_distances(
make_iterator_property_map(d.begin(),
share->indexmap),
boost::make_assoc_property_map(d),
on_tree_edge()
),
vis
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p),
boost::make_assoc_property_map(d),
static_cast<stack_cursor*>(cursor)
)
))
))
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
default:
abort();
}
}
break;
#endif
default:
break;
}
......@@ -1006,7 +1000,7 @@ int stack_cursor::fetch_row(const row &row_info, row &result,
if ((result.seq_indicator= seq= last.sequence()))
result.seq= *seq;
if ((result.link_indicator= v= last.vertex()))
result.link= share->idmap[*v];
result.link= get(boost::vertex_index, share->g, *v);
if ((result.weight_indicator= w= last.weight()))
result.weight= *w;
return oqgraph::OK;
......@@ -1040,7 +1034,7 @@ int vertices_cursor::fetch_row(const row &row_info, row &result,
if (v)
{
result.link_indicator= 1;
result.link= share->idmap[*v];
result.link= get(boost::vertex_index, share->g, *v);
#ifdef DISPLAY_VERTEX_INFO
result.seq_indicator= 1;
if ((result.seq= degree(*v, share->g)))
......@@ -1048,10 +1042,10 @@ int vertices_cursor::fetch_row(const row &row_info, row &result,
EdgeWeight weight= 0;
graph_traits<Graph>::in_edge_iterator iei, iei_end;
for (tie(iei, iei_end)= in_edges(*v, share->g); iei != iei_end; ++iei)
weight+= share->weightmap[*iei];
weight+= get(boost::edge_weight, share->g, *iei);
graph_traits<Graph>::out_edge_iterator oei, oei_end;
for (tie(oei, oei_end)= out_edges(*v, share->g); oei != oei_end; ++oei)
weight+= share->weightmap[*oei];
weight+= get(boost::edge_weight, share->g, *oei);
result.weight_indicator= 1;
result.weight= weight / result.seq;
}
......@@ -1085,9 +1079,9 @@ int edges_cursor::fetch_row(const row &row_info, row &result,
{
result= row_info;
result.orig_indicator= result.dest_indicator= result.weight_indicator= 1;
result.orig= share->idmap[ source( *edge, share->g ) ];
result.dest= share->idmap[ target( *edge, share->g ) ];
result.weight= share->weightmap[ *edge ];
result.orig= get(boost::vertex_index, share->g, source( *edge, share->g ) );
result.dest= get(boost::vertex_index, share->g, target( *edge, share->g ) );
result.weight= get(boost::edge_weight, share->g, *edge);
return oqgraph::OK;
}
return oqgraph::NO_MORE_DATA;
......
......@@ -104,7 +104,7 @@ namespace open_query
void row_ref(void*) throw();
static oqgraph* create(oqgraph_share*) throw();
static oqgraph_share *create() throw();
static oqgraph_share *create(TABLE*,Field*,Field*,Field*) throw();
static void free(oqgraph*) throw();
static void free(oqgraph_share*) throw();
......
......@@ -28,13 +28,12 @@
#pragma implementation // gcc: Class implementation
#endif
#include <stdarg.h>
#include <stdio.h>
#define MYSQL_SERVER // to have THD
#include "mysql_priv.h"
#if MYSQL_VERSION_ID >= 50100
#include <mysql/plugin.h>
#endif
#ifdef HAVE_OQGRAPH
#include "ha_oqgraph.h"
#include "graphcore.h"
......@@ -44,77 +43,36 @@
using namespace open_query;
struct oqgraph_info_st
struct oqgraph_table_option_struct
{
char *table_name;
char *origid; // name of the origin id column
char *destid; // name of the target id column
char *weight; // name of the weight column (optional)
};
#define ha_table_option_struct oqgraph_table_option_struct
ha_create_table_option oqgraph_table_option_list[]=
{
THR_LOCK lock;
oqgraph_share *graph;
uint use_count;
uint key_stat_version;
uint records;
bool dropped;
char name[FN_REFLEN+1];
HA_TOPTION_STRING("data_table", table_name),
HA_TOPTION_STRING("origid", origid),
HA_TOPTION_STRING("destid", destid),
HA_TOPTION_STRING("weight", weight),
HA_TOPTION_END
};
static const char oqgraph_description[]=
"Open Query Graph Computation Engine, stored in memory "
"Open Query Graph Computation Engine "
"(http://openquery.com/graph)";
#if MYSQL_VERSION_ID < 50100
static bool oqgraph_init();
handlerton oqgraph_hton= {
"OQGRAPH",
SHOW_OPTION_YES,
oqgraph_description,
DB_TYPE_OQGRAPH,
oqgraph_init,
0, /* slot */
0, /* savepoint size. */
NULL, /* close_connection */
NULL, /* savepoint */
NULL, /* rollback to savepoint */
NULL, /* release savepoint */
NULL, /* commit */
NULL, /* rollback */
NULL, /* prepare */
NULL, /* recover */
NULL, /* commit_by_xid */
NULL, /* rollback_by_xid */
NULL, /* create_cursor_read_view */
NULL, /* set_cursor_read_view */
NULL, /* close_cursor_read_view */
HTON_NO_FLAGS
};
#define STATISTIC_INCREMENT(X) \
statistic_increment(table->in_use->status_var.X, &LOCK_status)
#define MOVE(X) move_field(X)
#define RECORDS records
#else
#define STATISTIC_INCREMENT(X) ha_statistic_increment(&SSV::X)
#define MOVE(X) move_field_offset(X)
#define RECORDS stats.records
#endif
static HASH oqgraph_open_tables;
static pthread_mutex_t LOCK_oqgraph;
static bool oqgraph_init_done= 0;
#if MYSQL_VERSION_ID >= 50130
#define HASH_KEY_LENGTH size_t
#else
#define HASH_KEY_LENGTH uint
#endif
static uchar* get_key(const uchar *ptr, HASH_KEY_LENGTH *length,
my_bool)
{
const OQGRAPH_INFO *share= (const OQGRAPH_INFO*) ptr;
*length= strlen(share->name);
return (uchar*) share->name;
}
#if MYSQL_VERSION_ID >= 50100
static handler* oqgraph_create_handler(handlerton *hton, TABLE_SHARE *table,
MEM_ROOT *mem_root)
{
......@@ -123,98 +81,20 @@ static handler* oqgraph_create_handler(handlerton *hton, TABLE_SHARE *table,
static int oqgraph_init(handlerton *hton)
{
#else
static bool oqgraph_init()
{
if (have_oqgraph == SHOW_OPTION_DISABLED)
return 1;
#endif
if (pthread_mutex_init(&LOCK_oqgraph, MY_MUTEX_INIT_FAST))
goto error;
if (hash_init(&oqgraph_open_tables, &my_charset_bin, 32, 0, 0,
get_key, 0, 0))
{
pthread_mutex_destroy(&LOCK_oqgraph);
goto error;
}
#if MYSQL_VERSION_ID >= 50100
hton->state= SHOW_OPTION_YES;
hton->db_type= DB_TYPE_AUTOASSIGN;
hton->create= oqgraph_create_handler;
hton->flags= HTON_NO_FLAGS;
#endif
hton->table_options= oqgraph_table_option_list;
oqgraph_init_done= TRUE;
return 0;
error:
#if MYSQL_VERSION_ID < 50100
have_oqgraph= SHOW_OPTION_DISABLED;
#endif
return 1;
}
#if MYSQL_VERSION_ID >= 50100
static int oqgraph_fini(void *)
{
hash_free(&oqgraph_open_tables);
pthread_mutex_destroy(&LOCK_oqgraph);
oqgraph_init_done= FALSE;
return 0;
}
#endif
static OQGRAPH_INFO *get_share(const char *name, TABLE *table=0)
{
OQGRAPH_INFO *share;
uint length= strlen(name);
safe_mutex_assert_owner(&LOCK_oqgraph);
if (!(share= (OQGRAPH_INFO*) hash_search(&oqgraph_open_tables,
(byte*) name, length)))
{
if (!table ||
!(share= new OQGRAPH_INFO))
return 0;
share->use_count= share->key_stat_version= share->records= 0;
share->dropped= 0;
strmov(share->name, name);
if (!(share->graph= oqgraph::create()))
{
delete share;
return 0;
}
if (my_hash_insert(&oqgraph_open_tables, (byte*) share))
{
oqgraph::free(share->graph);
delete share;
return 0;
}
thr_lock_init(&share->lock);
}
share->use_count++;
return share;
}
static int free_share(OQGRAPH_INFO *share, bool drop=0)
{
safe_mutex_assert_owner(&LOCK_oqgraph);
if (!share)
return 0;
if (drop)
{
share->dropped= true;
hash_delete(&oqgraph_open_tables, (byte*) share);
}
if (!--share->use_count)
{
if (share->dropped)
{
thr_lock_delete(&share->lock);
oqgraph::free(share->graph);
delete share;
}
}
return 0;
}
static int error_code(int res)
{
......@@ -262,6 +142,9 @@ static int error_code(int res)
KEY (latch, origid, destid) USING HASH,
KEY (latch, destid, origid) USING HASH
) ENGINE=OQGRAPH
READ_TABLE=bar
ORIGID=src_id
DESTID=tgt_id
*/
static int oqgraph_check_table_structure (TABLE *table_arg)
......@@ -329,16 +212,15 @@ static int oqgraph_check_table_structure (TABLE *table_arg)
** OQGRAPH tables
*****************************************************************************/
#if MYSQL_VERSION_ID >= 50100
ha_oqgraph::ha_oqgraph(handlerton *hton, TABLE_SHARE *table_arg)
: handler(hton, table_arg),
#else
ha_oqgraph::ha_oqgraph(TABLE *table_arg)
: handler(&oqgraph_hton, table_arg),
#endif
share(0), graph(0), records_changed(0), key_stat_version(0)
: handler(hton, table_arg)
, graph_share(0)
, graph(0)
, error_message("", 0, &my_charset_latin1)
{ }
ha_oqgraph::~ha_oqgraph()
{ }
static const char *ha_oqgraph_exts[] =
{
......@@ -350,11 +232,7 @@ const char **ha_oqgraph::bas_ext() const
return ha_oqgraph_exts;
}
#if MYSQL_VERSION_ID >= 50100
ulonglong ha_oqgraph::table_flags() const
#else
ulong ha_oqgraph::table_flags() const
#endif
{
return (HA_NO_BLOBS | HA_NULL_IN_KEY |
HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED |
......@@ -366,278 +244,251 @@ ulong ha_oqgraph::index_flags(uint inx, uint part, bool all_parts) const
return HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR;
}
int ha_oqgraph::open(const char *name, int mode, uint test_if_locked)
bool ha_oqgraph::get_error_message(int error, String* buf)
{
pthread_mutex_lock(&LOCK_oqgraph);
if ((share = get_share(name, table)))
{
ref_length= oqgraph::sizeof_ref;
}
if (share)
if (error < 0)
{
/* Initialize variables for the opened table */
thr_lock_data_init(&share->lock, &lock, NULL);
graph= oqgraph::create(share->graph);
/*
We cannot run update_key_stats() here because we do not have a
lock on the table. The 'records' count might just be changed
temporarily at this moment and we might get wrong statistics (Bug
#10178). Instead we request for update. This will be done in
ha_oqgraph::info(), which is always called before key statistics are
used.
*/
key_stat_version= share->key_stat_version-1;
buf->append(error_message);
buf->c_ptr_safe();
error_message.length(0);
}
pthread_mutex_unlock(&LOCK_oqgraph);
return (share ? 0 : 1);
return false;
}
int ha_oqgraph::close(void)
void ha_oqgraph::print_error(const char* fmt, ...)
{
pthread_mutex_lock(&LOCK_oqgraph);
oqgraph::free(graph); graph= 0;
int res= free_share(share);
pthread_mutex_unlock(&LOCK_oqgraph);
return error_code(res);
va_list ap;
va_start(ap, fmt);
error_message.reserve(256);
size_t len = error_message.length();
len += vsnprintf(&error_message[len], 255, fmt, ap);
error_message.length(len);
va_end(ap);
}
void ha_oqgraph::update_key_stats()
int ha_oqgraph::open(const char *name, int mode, uint test_if_locked)
{
for (uint i= 0; i < table->s->keys; i++)
{
KEY *key=table->key_info+i;
if (!key->rec_per_key)
continue;
if (key->algorithm != HA_KEY_ALG_BTREE)
{
if (key->flags & HA_NOSAME)
key->rec_per_key[key->key_parts-1]= 1;
else
{
unsigned vertices= graph->vertices_count();
unsigned edges= graph->edges_count();
uint no_records= vertices ? 2 * (edges + vertices) / vertices : 2;
if (no_records < 2)
no_records= 2;
key->rec_per_key[key->key_parts-1]= no_records;
}
}
}
records_changed= 0;
/* At the end of update_key_stats() we can proudly claim they are OK. */
key_stat_version= share->key_stat_version;
}
THD* thd = current_thd;
oqgraph_table_option_struct *options=
reinterpret_cast<oqgraph_table_option_struct*>(table->s->option_struct);
error_message.length(0);
const char* p= strend(name)-1;
while (p > name && *p != '\\' && *p != '/')
--p;
int ha_oqgraph::write_row(byte * buf)
{
int res= oqgraph::MISC_FAIL;
Field ** const field= table->field;
STATISTIC_INCREMENT(ha_write_count);
init_tmp_table_share(
thd, share, table->s->db.str, table->s->db.length,
options->table_name, "");
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0];
size_t tlen= strlen(options->table_name);
size_t plen= (int)(p - name) + tlen;
if (ptrdiff)
{
field[1]->MOVE(ptrdiff);
field[2]->MOVE(ptrdiff);
field[3]->MOVE(ptrdiff);
}
share->path.str= (char*)
alloc_root(&share->mem_root, plen + 1);
if (!field[1]->is_null() && !field[2]->is_null())
{
VertexID orig_id= (VertexID) field[1]->val_int();
VertexID dest_id= (VertexID) field[2]->val_int();
EdgeWeight weight= 1;
strmov(strnmov(share->path.str, name, (int)(p - name) + 1), options->table_name);
share->normalized_path.str= share->path.str;
share->path.length= share->normalized_path.length= plen;
if (!field[3]->is_null())
weight= (EdgeWeight) field[3]->val_real();
origid= destid= weight= 0;
if (!(res= graph->insert_edge(orig_id, dest_id, weight, replace_dups)))
while (open_table_def(thd, share, 0))
{
if (thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE)
{
free_table_share(share);
return thd->main_da.sql_errno();
}
if (ha_create_table_from_engine(thd, table->s->db.str, options->table_name))
{
++records_changed;
share->records++;
free_table_share(share);
return thd->main_da.sql_errno();
}
if (res == oqgraph::DUPLICATE_EDGE && ignore_dups && !insert_dups)
res= oqgraph::OK;
mysql_reset_errors(thd, 1);
thd->clear_error();
continue;
}
if (ptrdiff)
if (int err= share->error)
{
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
field[3]->MOVE(-ptrdiff);
open_table_error(share, share->error, share->open_errno, share->errarg);
free_table_share(share);
return err;
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
if (!res && records_changed*OQGRAPH_STATS_UPDATE_THRESHOLD > share->records)
if (share->is_view)
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
open_table_error(share, 1, EMFILE, 0);
free_table_share(share);
print_error("VIEWs are not supported for a backing store");
return -1;
}
return error_code(res);
}
int ha_oqgraph::update_row(const byte * old, byte * buf)
{
int res= oqgraph::MISC_FAIL;
VertexID orig_id, dest_id;
EdgeWeight weight= 1;
Field **field= table->field;
STATISTIC_INCREMENT(ha_update_count);
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff)
if (int err= open_table_from_share(thd, share, "",
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
HA_GET_INDEX | HA_TRY_READ_ONLY),
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
thd->open_options, edges, FALSE))
{
field[0]->MOVE(ptrdiff);
field[1]->MOVE(ptrdiff);
field[2]->MOVE(ptrdiff);
field[3]->MOVE(ptrdiff);
open_table_error(share, err, EMFILE, 0);
free_table_share(share);
return -1;
}
if (inited == INDEX || inited == RND)
edges->reginfo.lock_type= TL_READ;
edges->tablenr= thd->current_tablenr++;
edges->status= STATUS_NO_RECORD;
edges->file->ha_start_of_new_statement();
edges->file->ft_handler= 0;
edges->pos_in_table_list= 0;
edges->clear_column_bitmaps();
bfill(table->record[0], table->s->null_bytes, 255);
bfill(table->record[1], table->s->null_bytes, 255);
// We expect fields origid, destid and optionally weight
origid= destid= weight= 0;
if (!edges->file)
{
print_error("Some error occurred opening table '%s'", options->table_name);
free_table_share(share);
return -1;
}
for (Field **field= edges->field; *field; ++field)
{
VertexID *origp= 0, *destp= 0;
EdgeWeight *weightp= 0;
if (!field[1]->is_null())
*(origp= &orig_id)= (VertexID) field[1]->val_int();
if (!field[2]->is_null())
*(destp= &dest_id)= (VertexID) field[2]->val_int();
if (!field[3]->is_null())
*(weightp= &weight)= (EdgeWeight) field[3]->val_real();
my_ptrdiff_t ptrdiff2= old - buf;
field[0]->MOVE(ptrdiff2);
field[1]->MOVE(ptrdiff2);
field[2]->MOVE(ptrdiff2);
field[3]->MOVE(ptrdiff2);
if (field[0]->is_null())
if (strcmp(options->origid, (*field)->field_name))
continue;
if ((*field)->cmp_type() != INT_RESULT ||
!((*field)->flags & NOT_NULL_FLAG))
{
if (!origp == field[1]->is_null() &&
*origp == (VertexID) field[1]->val_int())
origp= 0;
if (!destp == field[2]->is_null() &&
*destp == (VertexID) field[2]->val_int())
origp= 0;
if (!weightp == field[3]->is_null() &&
*weightp == (VertexID) field[3]->val_real())
weightp= 0;
if (!(res= graph->modify_edge(oqgraph::current_row(),
origp, destp, weightp, replace_dups)))
++records_changed;
else if (ignore_dups && res == oqgraph::DUPLICATE_EDGE)
res= oqgraph::OK;
print_error("Column '%s.%s' is not a not-null integer type",
options->table_name, options->origid);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
field[0]->MOVE(-ptrdiff2);
field[1]->MOVE(-ptrdiff2);
field[2]->MOVE(-ptrdiff2);
field[3]->MOVE(-ptrdiff2);
origid = *field;
break;
}
if (ptrdiff)
for (Field **field= edges->field; *field; ++field)
{
field[0]->MOVE(-ptrdiff);
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
field[3]->MOVE(-ptrdiff);
if (strcmp(options->destid, (*field)->field_name))
continue;
if ((*field)->type() != origid->type() ||
!((*field)->flags & NOT_NULL_FLAG))
{
print_error("Column '%s.%s' is not a not-null integer type",
options->table_name, options->destid);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
destid = *field;
break;
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
if (!res && records_changed*OQGRAPH_STATS_UPDATE_THRESHOLD > share->records)
for (Field **field= edges->field; options->weight && *field; ++field)
{
if (strcmp(options->weight, (*field)->field_name))
continue;
if ((*field)->result_type() != REAL_RESULT ||
!((*field)->flags & NOT_NULL_FLAG))
{
print_error("Column '%s.%s' is not a not-null real type",
options->table_name, options->weight);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
weight = *field;
break;
}
if (!origid || !destid || (!weight && options->weight))
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
print_error("Data columns missing on table '%s'", options->table_name);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
return error_code(res);
if (!(graph_share = oqgraph::create(edges, origid, destid, weight)))
{
print_error("Unable to create graph instance.");
closefrm(edges, 0);
free_table_share(share);
return -1;
}
ref_length= oqgraph::sizeof_ref;
graph = oqgraph::create(graph_share);
return 0;
}
int ha_oqgraph::delete_row(const byte * buf)
int ha_oqgraph::close(void)
{
int res= oqgraph::EDGE_NOT_FOUND;
Field **field= table->field;
STATISTIC_INCREMENT(ha_delete_count);
oqgraph::free(graph); graph= 0;
oqgraph::free(graph_share); graph_share= 0;
if (inited == INDEX || inited == RND)
if (share)
{
if ((res= graph->delete_edge(oqgraph::current_row())) == oqgraph::OK)
{
++records_changed;
share->records--;
}
if (edges->file)
closefrm(edges, 0);
free_table_share(share);
}
if (res != oqgraph::OK)
{
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff)
{
field[0]->MOVE(ptrdiff);
field[1]->MOVE(ptrdiff);
field[2]->MOVE(ptrdiff);
}
return 0;
}
if (field[0]->is_null() && !field[1]->is_null() && !field[2]->is_null())
void ha_oqgraph::update_key_stats()
{
for (uint i= 0; i < table->s->keys; i++)
{
KEY *key=table->key_info+i;
if (!key->rec_per_key)
continue;
if (key->algorithm != HA_KEY_ALG_BTREE)
{
VertexID orig_id= (VertexID) field[1]->val_int();
VertexID dest_id= (VertexID) field[2]->val_int();
if ((res= graph->delete_edge(orig_id, dest_id)) == oqgraph::OK)
if (key->flags & HA_NOSAME)
key->rec_per_key[key->key_parts-1]= 1;
else
{
++records_changed;
share->records--;
//unsigned vertices= graph->vertices_count();
//unsigned edges= graph->edges_count();
//uint no_records= vertices ? 2 * (edges + vertices) / vertices : 2;
//if (no_records < 2)
uint
no_records= 2;
key->rec_per_key[key->key_parts-1]= no_records;
}
}
if (ptrdiff)
{
field[0]->MOVE(-ptrdiff);
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
}
/* At the end of update_key_stats() we can proudly claim they are OK. */
//skey_stat_version= share->key_stat_version;
}
if (!res && table->s->tmp_table == NO_TMP_TABLE &&
records_changed*OQGRAPH_STATS_UPDATE_THRESHOLD > share->records)
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
}
return error_code(res);
int ha_oqgraph::write_row(byte * buf)
{
return ER_OPEN_AS_READONLY;
}
int ha_oqgraph::update_row(const byte * old, byte * buf)
{
return ER_OPEN_AS_READONLY;
}
int ha_oqgraph::delete_row(const byte * buf)
{
return ER_OPEN_AS_READONLY;
}
int ha_oqgraph::index_read(byte * buf, const byte * key, uint key_len,
......@@ -675,9 +526,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key,
bmove_align(buf, table->s->default_values, table->s->reclength);
key_restore(buf, (byte*) key, key_info, key_len);
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff)
......@@ -711,9 +560,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key,
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
res= graph->search(latchp, orig_idp, dest_idp);
......@@ -729,9 +576,7 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row)
bmove_align(record, table->s->default_values, table->s->reclength);
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
#endif
my_ptrdiff_t ptrdiff= record - table->record[0];
if (ptrdiff)
......@@ -790,15 +635,14 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row)
field[4]->MOVE(-ptrdiff);
field[5]->MOVE(-ptrdiff);
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->write_set, old_map);
#endif
return 0;
}
int ha_oqgraph::rnd_init(bool scan)
{
edges->prepare_for_position();
return error_code(graph->random(scan));
}
......@@ -836,75 +680,31 @@ int ha_oqgraph::cmp_ref(const byte *ref1, const byte *ref2)
int ha_oqgraph::info(uint flag)
{
RECORDS= graph->vertices_count() + graph->edges_count();
#if 0
records= hp_info.records;
deleted= hp_info.deleted;
errkey= hp_info.errkey;
mean_rec_length= hp_info.reclength;
data_file_length= hp_info.data_length;
index_file_length= hp_info.index_length;
max_data_file_length= hp_info.max_records* hp_info.reclength;
delete_length= hp_info.deleted * hp_info.reclength;
#endif
RECORDS= graph->edges_count();
/*
If info() is called for the first time after open(), we will still
have to update the key statistics. Hoping that a table lock is now
in place.
*/
if (key_stat_version != share->key_stat_version)
update_key_stats();
// if (key_stat_version != share->key_stat_version)
// update_key_stats();
return 0;
}
int ha_oqgraph::extra(enum ha_extra_function operation)
{
switch (operation)
{
case HA_EXTRA_IGNORE_DUP_KEY:
ignore_dups= true;
break;
case HA_EXTRA_NO_IGNORE_DUP_KEY:
ignore_dups= false;
insert_dups= false;
break;
case HA_EXTRA_WRITE_CAN_REPLACE:
replace_dups= true;
break;
case HA_EXTRA_WRITE_CANNOT_REPLACE:
replace_dups= false;
break;
case HA_EXTRA_INSERT_WITH_UPDATE:
insert_dups= true;
break;
default:
break;
}
return 0;
return edges->file->extra(operation);
}
int ha_oqgraph::delete_all_rows()
{
int res;
if (!(res= graph->delete_all()))
{
share->records= 0;
}
if (!res && table->s->tmp_table == NO_TMP_TABLE)
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
}
return error_code(res);
return ER_OPEN_AS_READONLY;
}
int ha_oqgraph::external_lock(THD *thd, int lock_type)
{
return 0; // No external locking
return edges->file->ha_external_lock(thd, lock_type);
}
......@@ -912,10 +712,7 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
lock.type=lock_type;
*to++= &lock;
return to;
return edges->file->store_lock(thd, to, lock_type);
}
/*
......@@ -923,29 +720,13 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd,
not when doing a CREATE on the table.
*/
int ha_oqgraph::delete_table(const char *name)
int ha_oqgraph::delete_table(const char *)
{
int res= 0;
OQGRAPH_INFO *share;
pthread_mutex_lock(&LOCK_oqgraph);
if ((share= get_share(name)))
{
res= free_share(share, true);
}
pthread_mutex_unlock(&LOCK_oqgraph);
return error_code(res);
return 0;
}
int ha_oqgraph::rename_table(const char * from, const char * to)
int ha_oqgraph::rename_table(const char *, const char *)
{
pthread_mutex_lock(&LOCK_oqgraph);
if (OQGRAPH_INFO *share= get_share(from))
{
strmov(share->name, to);
hash_update(&oqgraph_open_tables, (byte*) share,
(byte*) from, strlen(from));
}
pthread_mutex_unlock(&LOCK_oqgraph);
return 0;
}
......@@ -978,8 +759,9 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key,
return RECORDS;
/* Assert that info() did run. We need current statistics here. */
DBUG_ASSERT(key_stat_version == share->key_stat_version);
ha_rows result= key->rec_per_key[key->key_parts-1];
//DBUG_ASSERT(key_stat_version == share->key_stat_version);
//ha_rows result= key->rec_per_key[key->key_parts-1];
ha_rows result= 10;
return result;
}
......@@ -988,24 +770,13 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key,
int ha_oqgraph::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
int res = -1;
OQGRAPH_INFO *share;
oqgraph_table_option_struct *options=
reinterpret_cast<oqgraph_table_option_struct*>(table->s->option_struct);
pthread_mutex_lock(&LOCK_oqgraph);
if ((share= get_share(name)))
{
free_share(share);
}
else
{
if (!oqgraph_check_table_structure(table_arg))
res= 0;;
}
pthread_mutex_unlock(&LOCK_oqgraph);
if (int res = oqgraph_check_table_structure(table_arg))
return error_code(res);
if (this->share)
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
return error_code(res);
return 0;
}
......@@ -1016,7 +787,6 @@ void ha_oqgraph::update_create_info(HA_CREATE_INFO *create_info)
// create_info->auto_increment_value= auto_increment_value;
}
#if MYSQL_VERSION_ID >= 50100
struct st_mysql_storage_engine oqgraph_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
......@@ -1030,12 +800,9 @@ mysql_declare_plugin(oqgraph)
PLUGIN_LICENSE_GPL,
(int (*)(void*)) oqgraph_init, /* Plugin Init */
oqgraph_fini, /* Plugin Deinit */
0x0200, /* Version: 2.0 */
0x0300, /* Version: 3s.0 */
NULL, /* status variables */
NULL, /* system variables */
NULL /* config options */
}
mysql_declare_plugin_end;
#endif
#endif
......@@ -39,19 +39,21 @@ namespace open_query
{
struct row;
class oqgraph;
class oqgraph_share;
}
/* class for the the Open Query Graph handler */
class ha_oqgraph: public handler
{
OQGRAPH_INFO *share;
TABLE_SHARE share[1];
TABLE edges[1];
Field *origid;
Field *destid;
Field *weight;
open_query::oqgraph_share *graph_share;
open_query::oqgraph *graph;
THR_LOCK_DATA lock;
/* number of records changed since last statistics update */
uint records_changed;
uint key_stat_version;
bool replace_dups, ignore_dups, insert_dups;
int fill_record(byte*, const open_query::row&);
......@@ -63,7 +65,7 @@ class ha_oqgraph: public handler
ha_oqgraph(TABLE *table);
ulong table_flags() const;
#endif
~ha_oqgraph() {}
~ha_oqgraph();
const char *table_type() const
{
return "OQGRAPH";
......@@ -109,6 +111,12 @@ class ha_oqgraph: public handler
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
int cmp_ref(const byte *ref1, const byte *ref2);
bool get_error_message(int error, String* buf);
void print_error(const char* fmt, ...);
private:
void update_key_stats();
String error_message;
};
/* 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_judy.h"
#include <Judy.h>
void open_query::judy_bitset::clear()
{
int rc;
J1FA(rc, array);
}
bool open_query::judy_bitset::test(size_type n) const
{
int rc;
J1T(rc, array, n);
return rc == 1;
}
open_query::judy_bitset& open_query::judy_bitset::setbit(size_type n)
{
int rc;
J1S(rc, array, n);
return *this;
}
open_query::judy_bitset& open_query::judy_bitset::reset(size_type n)
{
int rc;
J1U(rc, array, n);
return *this;
}
open_query::judy_bitset& open_query::judy_bitset::flip(size_type n)
{
int rc;
J1U(rc, array, n);
if (!rc)
{
J1S(rc, array, n);
}
return *this;
}
open_query::judy_bitset::size_type open_query::judy_bitset::num_blocks() const
{
Word_t rc;
J1MU(rc, array);
return rc;
}
open_query::judy_bitset::size_type open_query::judy_bitset::size() const
{
int rc;
Word_t index = (Word_t) -1;
J1L(rc, array, index);
if (!rc)
return index;
else
return npos;
}
open_query::judy_bitset::size_type open_query::judy_bitset::count() const
{
Word_t rc;
J1C(rc, array, 0, -1);
return rc;
}
open_query::judy_bitset::size_type open_query::judy_bitset::find_first() const
{
int rc;
Word_t index = 0;
J1F(rc, array, index);
if (!rc)
return index;
else
return npos;
}
open_query::judy_bitset::size_type open_query::judy_bitset::find_next(size_type n) const
{
int rc;
Word_t index = (Word_t) n;
J1N(rc, array, index);
if (!rc)
return index;
else
return npos;
}
/* 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
======================================================================
*/
#pragma once
#include <cstddef>
namespace open_query
{
class judy_bitset
{
public:
typedef std::size_t size_type;
enum { npos = (size_type) -1 };
judy_bitset()
: array(0)
{ }
~judy_bitset()
{ clear(); }
void clear();
bool empty() const { return !array; }
bool none() const { return npos == find_first(); }
inline judy_bitset& set(size_type n, bool val = true)
{
if (!val)
return reset(n);
else
return setbit(n);
}
judy_bitset& reset(size_type n);
judy_bitset& flip(size_type n);
bool test(size_type) const;
size_type count() const;
size_type size() const;
size_type num_blocks() const;
class reference
{
friend class judy_bitset;
reference(judy_bitset& array, size_type pos)
: j(array)
, n(pos)
{ }
void operator&(); // not defined
public:
reference& operator=(bool value)
{ j.set(n, value); return *this; }
reference& operator=(const reference& ref)
{ j.set(n, ref); return *this; }
reference& operator|=(bool value)
{ if (value) j.set(n); return *this; }
reference& operator&=(bool value)
{ if (!value) j.reset(n); return *this; }
reference& operator^=(bool value)
{ if (value) j.flip(n); return *this; }
reference& operator-=(bool value)
{ if (value) j.reset(n); return *this; }
bool operator~() const { return !j.test(n); }
operator bool() const { return j.test(n); }
reference& flip() { j.flip(n); return *this; }
private:
judy_bitset& j;
size_type n;
};
reference operator[](size_type n) { return reference(*this, n); }
bool operator[](size_type n) const { return test(n); }
size_type find_first() const;
size_type find_next(size_type n) const;
private:
mutable void* array;
judy_bitset& setbit(size_type n);
};
}
/* 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_shim.h"
/* 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
======================================================================
*/
#pragma once
#include "oqgraph_thunk.h"
#include "oqgraph_judy.h"
#include <boost/graph/directed_graph.hpp>
#include <boost/graph/adjacency_iterator.hpp>
namespace open_query
{
struct OQGraphTraversalCategory
: public boost::bidirectional_graph_tag
, public boost::adjacency_graph_tag
, public boost::edge_list_graph_tag
{ };
}
namespace oqgraph3
{
struct traversal_category
: public boost::adjacency_graph_tag
, public boost::bidirectional_graph_tag
, public boost::edge_list_graph_tag
{ };
struct edge_iterator
{
typedef edge_iterator self;
typedef edge_descriptor value_type;
typedef edge_descriptor& reference;
typedef edge_descriptor pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
edge_iterator() { }
edge_iterator(row_cursor iter) : _iter(iter) { }
edge_descriptor operator*() { return _iter->_current; }
self& operator++() { ++(*_iter); return *this; }
self operator++(int) { self t= *this; ++(*this); return t; }
bool operator==(const self& x) { return _iter == x._iter; }
bool operator!=(const self& x) { return _iter != x._iter; }
boost::optional<row_cursor> _iter;
};
struct vertex_iterator
{
typedef vertex_iterator self;
typedef vertex_descriptor value_type;
typedef vertex_descriptor& reference;
typedef vertex_descriptor pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
vertex_iterator() { }
vertex_iterator(edge_iterator iter) : _iter(iter) { }
vertex_descriptor operator*()
{
if (!_seen.test(_iter._iter->operator*().first))
return _iter._iter->_cache->vertex(_iter._iter->operator*().first);
else
return _iter._iter->_cache->vertex(_iter._iter->operator*().second);
}
self& operator++()
{
if (!_seen.test(_iter._iter->operator*().first))
{
_seen.set(_iter._iter->operator*().first);
}
else
{
_seen.set(_iter._iter->operator*().second);
}
while (_iter._iter->_current &&
_seen.test(_iter._iter->operator*().first) &&
_seen.test(_iter._iter->operator*().second))
{
++_iter;
}
return *this;
}
self operator++(int) { self t= *this; ++(*this); return t; }
bool operator==(const self& x) { return _iter == x._iter; }
bool operator!=(const self& x) { return _iter != x._iter; }
edge_iterator _iter;
open_query::judy_bitset _seen;
};
struct out_edge_iterator
{
typedef out_edge_iterator self;
typedef vertex_info::edge_list_type::const_iterator iter_type;
typedef edge_descriptor value_type;
typedef edge_descriptor& reference;
typedef edge_descriptor pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
out_edge_iterator() { }
out_edge_iterator(iter_type iter, const vertex_descriptor& v)
: _iter(iter), _v(v) { }
edge_descriptor operator*()
{ return _v->_cache->edge(edge_key(*_iter)); }
self& operator++() { ++_iter; return *this; }
self operator++(int) { self t= *this; ++(*this); return t; }
bool operator==(const self& x) { return _iter == x._iter; }
bool operator!=(const self& x) { return _iter != x._iter; }
iter_type _iter;
vertex_descriptor _v;
};
struct in_edge_iterator
{
typedef in_edge_iterator self;
typedef vertex_info::edge_list_type::const_iterator iter_type;
typedef edge_descriptor value_type;
typedef edge_descriptor& reference;
typedef edge_descriptor pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
in_edge_iterator() { }
in_edge_iterator(iter_type iter, const vertex_descriptor& v)
: _iter(iter), _v(v) { }
edge_descriptor operator*()
{ return _v->_cache->edge(edge_key(*_iter)); }
self& operator++() { ++_iter; return *this; }
self operator++(int) { self t= *this; ++(*this); return t; }
bool operator==(const self& x) { return _iter == x._iter; }
bool operator!=(const self& x) { return _iter != x._iter; }
iter_type _iter;
vertex_descriptor _v;
};
struct vertex_index_property_map
{
typedef vertex_id value_type;
typedef value_type reference;
typedef vertex_descriptor key_type;
typedef boost::readable_property_map_tag category;
vertex_index_property_map(const graph& g) : _g(g) { }
const graph& _g;
};
struct edge_weight_property_map
{
typedef weight_t value_type;
typedef value_type reference;
typedef edge_descriptor key_type;
typedef boost::readable_property_map_tag category;
edge_weight_property_map(const graph& g) : _g(g) { }
const graph& _g;
};
struct edge_index_property_map
{
typedef edge_key::ref_type value_type;
typedef const edge_key::ref_type& reference;
typedef edge_descriptor key_type;
typedef boost::readable_property_map_tag category;
edge_index_property_map(const graph& g) : _g(g) { }
const graph& _g;
};
}
namespace boost
{
template<>
struct graph_traits<oqgraph3::graph>
{
typedef oqgraph3::vertex_descriptor vertex_descriptor;
typedef oqgraph3::edge_descriptor edge_descriptor;
typedef boost::adjacency_iterator_generator<
oqgraph3::graph,
oqgraph3::vertex_descriptor,
oqgraph3::out_edge_iterator>::type adjacency_iterator;
typedef oqgraph3::out_edge_iterator out_edge_iterator;
typedef oqgraph3::in_edge_iterator in_edge_iterator;
typedef oqgraph3::vertex_iterator vertex_iterator;
typedef oqgraph3::edge_iterator edge_iterator;
typedef boost::directed_tag directed_category;
typedef boost::allow_parallel_edge_tag edge_parallel_category;
typedef oqgraph3::traversal_category traversal_category;
typedef oqgraph3::vertices_size_type vertices_size_type;
typedef oqgraph3::edges_size_type edges_size_type;
typedef oqgraph3::degree_size_type degree_size_type;
static inline vertex_descriptor null_vertex()
{ return oqgraph3::vertex_descriptor(); }
};
template<>
struct graph_traits<const oqgraph3::graph>
: public graph_traits<oqgraph3::graph>
{ };
template <>
struct graph_property_type<oqgraph3::graph>
{
typedef no_property type;
};
template <>
struct vertex_property_type<oqgraph3::graph>
{
typedef no_property type;
};
template <>
struct edge_property_type<oqgraph3::graph>
{
typedef no_property type;
};
template <>
struct graph_bundle_type<oqgraph3::graph>
{
typedef no_graph_bundle type;
};
template <>
struct vertex_bundle_type<oqgraph3::graph>
{
typedef no_vertex_bundle type;
};
template <>
struct edge_bundle_type<oqgraph3::graph>
{
typedef no_edge_bundle type;
};
inline std::pair<
graph_traits<oqgraph3::graph>::out_edge_iterator,
graph_traits<oqgraph3::graph>::out_edge_iterator>
out_edges(
graph_traits<oqgraph3::graph>::vertex_descriptor v,
const oqgraph3::graph&)
{
const oqgraph3::vertex_info::edge_list_type&
iter= v->out_edges();
return std::make_pair(
graph_traits<oqgraph3::graph>::out_edge_iterator(iter.begin(), v),
graph_traits<oqgraph3::graph>::out_edge_iterator(iter.end(), v));
}
inline std::pair<
graph_traits<oqgraph3::graph>::in_edge_iterator,
graph_traits<oqgraph3::graph>::in_edge_iterator>
in_edges(
graph_traits<oqgraph3::graph>::vertex_descriptor v,
const oqgraph3::graph&)
{
const oqgraph3::vertex_info::edge_list_type&
iter= v->out_edges();
return std::make_pair(
graph_traits<oqgraph3::graph>::in_edge_iterator(iter.begin(), v),
graph_traits<oqgraph3::graph>::in_edge_iterator(iter.end(), v));
}
// EdgeListGraph concepts
inline std::pair<
graph_traits<oqgraph3::graph>::edge_iterator,
graph_traits<oqgraph3::graph>::edge_iterator>
edges(const oqgraph3::graph& _g)
{
oqgraph3::graph& g= const_cast<oqgraph3::graph&>(_g);
return std::make_pair(
graph_traits<oqgraph3::graph>::edge_iterator(
oqgraph3::row_cursor(&g).first()),
graph_traits<oqgraph3::graph>::edge_iterator(
oqgraph3::row_cursor(&g).end()));
}
inline std::pair<
graph_traits<oqgraph3::graph>::vertex_iterator,
graph_traits<oqgraph3::graph>::vertex_iterator>
vertices(const oqgraph3::graph& g)
{
std::pair<
graph_traits<oqgraph3::graph>::edge_iterator,
graph_traits<oqgraph3::graph>::edge_iterator> epair= edges(g);
return std::make_pair(
graph_traits<oqgraph3::graph>::vertex_iterator(epair.first),
graph_traits<oqgraph3::graph>::vertex_iterator(epair.second));
}
inline graph_traits<oqgraph3::graph>::vertices_size_type
num_vertices(const oqgraph3::graph& g)
{
std::size_t count = 0;
for (std::pair<
graph_traits<oqgraph3::graph>::vertex_iterator,
graph_traits<oqgraph3::graph>::vertex_iterator> i= vertices(g);
i.first != i.second; ++i.first)
{
++count;
}
return count;
}
template<>
struct property_map<oqgraph3::graph, edge_weight_t>
{
typedef void type;
typedef oqgraph3::edge_weight_property_map const_type;
};
template<>
struct property_map<oqgraph3::graph, vertex_index_t>
{
typedef void type;
typedef oqgraph3::vertex_index_property_map const_type;
};
template<>
struct property_map<oqgraph3::graph, edge_index_t>
{
typedef void type;
typedef oqgraph3::edge_index_property_map const_type;
};
inline property_map<
oqgraph3::graph,
edge_weight_t>::const_type::reference
get(
edge_weight_t,
const oqgraph3::graph& g,
const property_map<
oqgraph3::graph,
edge_weight_t>::const_type::key_type& key)
{ return oqgraph3::row_cursor(key, const_cast<oqgraph3::graph*>(&g))->_weight; }
inline property_map<
oqgraph3::graph,
edge_weight_t>::const_type
get(edge_weight_t,
const oqgraph3::graph& g)
{ return property_map<oqgraph3::graph, edge_weight_t>::const_type(g); }
inline property_map<
oqgraph3::graph,
edge_weight_t>::const_type::reference
get(property_map<oqgraph3::graph,
edge_weight_t>::const_type p,
const property_map<
oqgraph3::graph,
edge_weight_t>::const_type::key_type& key)
{ return oqgraph3::row_cursor(
key, const_cast<oqgraph3::graph*>(&p._g))->_weight; }
inline property_map<
oqgraph3::graph,
edge_index_t>::const_type::reference
get(edge_index_t,
const oqgraph3::graph&,
const property_map<
oqgraph3::graph,
edge_index_t>::const_type::key_type& key)
{ return key->_ref; }
inline property_map<oqgraph3::graph, edge_index_t>::const_type
get(edge_index_t, const oqgraph3::graph& g)
{ return property_map<oqgraph3::graph, edge_index_t>::const_type(g); }
inline property_map<oqgraph3::graph, edge_index_t>::const_type::reference
get(property_map<oqgraph3::graph, edge_index_t>::const_type,
const property_map<oqgraph3::graph,
edge_index_t>::const_type::key_type& key)
{ return key->_ref; }
inline property_map<oqgraph3::graph, vertex_index_t>::const_type::reference
get(vertex_index_t, const oqgraph3::graph&,
const property_map<oqgraph3::graph,
vertex_index_t>::const_type::key_type& key)
{ return key->id; }
inline property_map<oqgraph3::graph, vertex_index_t>::const_type
get(vertex_index_t, const oqgraph3::graph& g)
{ return property_map<oqgraph3::graph, vertex_index_t>::const_type(g); }
inline property_map<oqgraph3::graph, vertex_index_t>::const_type::reference
get(property_map<oqgraph3::graph, vertex_index_t>::const_type,
const property_map<oqgraph3::graph,
vertex_index_t>::const_type::key_type& key)
{ return key->id; }
inline optional<graph_traits<oqgraph3::graph>::vertex_descriptor>
find_vertex(oqgraph3::vertex_id id, const oqgraph3::graph& g)
{
graph_traits<oqgraph3::graph>::vertex_descriptor tmp(
const_cast<oqgraph3::graph&>(g).vertex(id));
if (tmp)
return tmp;
else
return none;
}
}
#if 0
class OQGraph
{
public:
typedef OQGraph graph_type;
// more commonly used graph types
typedef void stored_vertex;
typedef std::size_t vertices_size_type;
typedef std::size_t edges_size_type;
typedef std::size_t degree_size_type;
typedef oqgraph3::vertex_descriptor vertex_descriptor;
typedef oqgraph3::edge_descriptor edge_descriptor;
// iterator types
typedef boost::adjacency_iterator_generator<
OQGraph,
vertex_descriptor,
out_edge_iterator>::type adjacency_iterator;
// miscellaneous types
typedef boost::directed_graph_tag graph_tag;
typedef boost::directed_tag directed_category;
typedef boost::allow_parallel_edge_tag edge_parallel_category;
typedef OQGraphTraversalCategory traversal_category;
typedef oqgraph3::vertex_id vertex_index_type;
typedef oqgraph3::edge_key::ref_type edge_index_type;
typedef void edge_property_type;
typedef void vertex_property_type;
typedef void graph_property_type;
// Graph concepts
static vertex_descriptor null_vertex()
{ return vertex_descriptor(); }
oqgraph3::graph_cache_ptr _cache;
vertex_descriptor source(edge_descriptor e) const
{ return _cache->vertex(oqgraph3::row_cursor(e)->first); }
vertex_descriptor target(edge_descriptor e) const
{ return _cache->vertex(oqgraph3::row_cursor(e)->second); }
degree_size_type out_degree(vertex_descriptor v) const
{ return v->out_edges().size(); }
std::pair<out_edge_iterator,out_edge_iterator>
out_edges(vertex_descriptor v) const
{
const oqgraph3::vertex_info::edge_list_type&
iter= v->out_edges();
return std::make_pair(
out_edge_iterator(iter.begin(), v),
out_edge_iterator(iter.end(), v));
}
degree_size_type in_degree(vertex_descriptor v) const
{ return v->in_edges().size(); }
std::pair<in_edge_iterator,in_edge_iterator>
in_edges(vertex_descriptor v) const
{
const oqgraph3::vertex_info::edge_list_type&
iter= v->out_edges();
return std::make_pair(
in_edge_iterator(iter.begin(), v),
in_edge_iterator(iter.end(), v));
}
degree_size_type degree(vertex_descriptor v) const
{ return v->degree(); }
vertex_descriptor vertex() const
{ return null_vertex(); }
std::pair<edge_descriptor, bool> edge(
vertex_descriptor u, vertex_descriptor v) const
{ edge_descriptor result= _cache->edge(u, v);
return std::make_pair(result, bool(result)); }
edges_size_type num_edges() const
{ return _cache->num_edges(); }
std::pair<edge_iterator,edge_iterator>
edges() const
{
return std::make_pair(
edge_iterator(oqgraph3::row_cursor(_cache).first()),
edge_iterator(oqgraph3::row_cursor(_cache).end()));
}
};
struct vertex_index_property_map
{
typedef oqgraph3::vertex_id value_type;
typedef value_type reference;
typedef OQGraph::vertex_descriptor key_type;
typedef boost::readable_property_map_tag category;
};
struct edge_weight_property_map
{
typedef oqgraph3::weight_t value_type;
typedef value_type reference;
typedef OQGraph::edge_descriptor key_type;
typedef boost::readable_property_map_tag category;
};
struct edge_index_property_map
{
typedef oqgraph3::edge_key::ref_type value_type;
typedef const oqgraph3::edge_key::ref_type& reference;
typedef OQGraph::edge_descriptor key_type;
typedef boost::readable_property_map_tag category;
};
}
namespace boost
{
template<>
struct property_map<open_query::OQGraph, edge_weight_t>
{
typedef void type;
typedef open_query::edge_weight_property_map const_type;
};
template<>
struct property_map<open_query::OQGraph, vertex_index_t>
{
typedef void type;
typedef open_query::vertex_index_property_map const_type;
};
template<>
struct property_map<open_query::OQGraph, edge_index_t>
{
typedef void type;
typedef open_query::edge_index_property_map const_type;
};
property_map<
open_query::OQGraph,
edge_weight_t>::const_type::reference
get(
edge_weight_t,
const open_query::OQGraph&,
const property_map<
open_query::OQGraph,
edge_weight_t>::const_type::key_type& key)
{ return oqgraph3::row_cursor(key)->_weight; }
property_map<
open_query::OQGraph,
edge_weight_t>::const_type
get(edge_weight_t,
const open_query::OQGraph&)
{ return property_map<open_query::OQGraph, edge_weight_t>::const_type(); }
property_map<
open_query::OQGraph,
edge_weight_t>::const_type::reference
get(property_map<open_query::OQGraph,
edge_weight_t>::const_type,
const property_map<
open_query::OQGraph,
edge_weight_t>::const_type::key_type& key)
{ return oqgraph3::row_cursor(key)->_weight; }
property_map<
open_query::OQGraph,
edge_index_t>::const_type::reference
get(edge_index_t,
const open_query::OQGraph&,
const property_map<
open_query::OQGraph,
edge_index_t>::const_type::key_type& key)
{ return key->_ref; }
property_map<open_query::OQGraph, edge_index_t>::const_type
get(edge_index_t, const open_query::OQGraph&)
{ return property_map<open_query::OQGraph, edge_index_t>::const_type(); }
property_map<open_query::OQGraph, edge_index_t>::const_type::reference
get(property_map<open_query::OQGraph, edge_index_t>::const_type,
const property_map<open_query::OQGraph, edge_index_t>::const_type::key_type& key)
{ return key->_ref; }
property_map<open_query::OQGraph, vertex_index_t>::const_type::reference
get(vertex_index_t, const open_query::OQGraph&,
const property_map<open_query::OQGraph, vertex_index_t>::const_type::key_type& key)
{ return key->id; }
property_map<open_query::OQGraph, vertex_index_t>::const_type
get(vertex_index_t, const open_query::OQGraph&)
{ return property_map<open_query::OQGraph, vertex_index_t>::const_type(); }
property_map<open_query::OQGraph, vertex_index_t>::const_type::reference
get(property_map<open_query::OQGraph, vertex_index_t>::const_type,
const property_map<open_query::OQGraph, vertex_index_t>::const_type::key_type& key)
{ return key->id; }
optional<open_query::OQGraph::vertex_descriptor>
find_vertex(oqgraph3::vertex_id id, const open_query::OQGraph& g)
{ open_query::OQGraph::vertex_descriptor tmp(g._cache->vertex(id));
if (tmp)
return tmp;
else
return none;
}
// IncidenceGraph concepts
inline open_query::OQGraph::vertex_descriptor
source(
open_query::OQGraph::edge_descriptor e,
open_query::OQGraph const& g)
{ return g.source(e); }
inline open_query::OQGraph::vertex_descriptor
target(
open_query::OQGraph::edge_descriptor e,
open_query::OQGraph const& g)
{ return g.target(e); }
inline open_query::OQGraph::degree_size_type
out_degree(
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return g.out_degree(v); }
inline std::pair<
open_query::OQGraph::out_edge_iterator,
open_query::OQGraph::out_edge_iterator
>
out_edges(
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return g.out_edges(v); }
// BidirectionalGraph concepts
inline open_query::OQGraph::degree_size_type
in_degree(
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return g.in_degree(v); }
inline std::pair<
open_query::OQGraph::in_edge_iterator,
open_query::OQGraph::in_edge_iterator
>
in_edges(
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return g.in_edges(v); }
inline open_query::OQGraph::degree_size_type
degree(
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return g.degree(v); }
// AdjacencyGraph concepts
//inline std::pair<
// open_query::OQGraph::adjacency_iterator,
// open_query::OQGraph::adjacency_iterator
// >
//adjacent_vertices(
// open_query::OQGraph::vertex_descriptor v,
// open_query::OQGraph const& g)
//{ return g.adjacent_vertices(v, g.impl()); }
open_query::OQGraph::vertex_descriptor
vertex(
open_query::OQGraph::vertices_size_type n,
open_query::OQGraph const& g)
{ return g.vertex(); }
std::pair<open_query::OQGraph::edge_descriptor, bool>
edge(
open_query::OQGraph::vertex_descriptor u,
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return g.edge(u, v); }
// Vertex index management
inline open_query::OQGraph::vertex_index_type
get_vertex_index(
open_query::OQGraph::vertex_descriptor v,
open_query::OQGraph const& g)
{ return get(boost::vertex_index, g, v); }
// Edge index management
inline const open_query::OQGraph::edge_index_type&
get_edge_index(
open_query::OQGraph::edge_descriptor v,
open_query::OQGraph const& g)
{ return get(boost::edge_index, g, v); }
}
#endif
/* 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);
}
/* 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
======================================================================
*/
#pragma once
#include <list>
#include <queue>
#include <string>
#include <utility>
#include <boost/intrusive_ptr.hpp>
#include <boost/optional.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <boost/pending/queue.hpp>
#include <boost/ptr_container/ptr_deque.hpp>
#include "graphcore-types.h"
namespace oqgraph3
{
typedef open_query::VertexID vertex_id;
typedef open_query::EdgeWeight weight_t;
typedef size_t vertices_size_type;
typedef size_t edges_size_type;
typedef size_t degree_size_type;
struct graph;
struct vertex_info;
typedef boost::intrusive_ptr<graph> graph_ptr;
struct edge_key
{
typedef std::string ref_type;
ref_type _ref;
mutable int _ref_count;
graph_ptr _cache;
mutable bool _on_gc;
operator const ref_type&() const { return _ref; }
explicit edge_key(
std::size_t length,
const graph_ptr& cache= graph_ptr())
: _ref(length, '\0')
, _ref_count(0)
, _cache(cache)
{ }
template <typename C, typename T, typename A>
explicit edge_key(const std::basic_string<C,T,A>& ref)
: _ref(ref.begin(), ref.end())
, _ref_count(0)
, _cache(0)
{ }
edge_key(const edge_key& src)
: _ref(src._ref)
, _ref_count(0)
, _cache(src._cache)
{ }
edge_key(const edge_key& src, const graph_ptr& cache)
: _ref(src._ref)
, _ref_count(0)
, _cache(cache)
{ }
~edge_key();
void release() const;
friend void intrusive_ptr_add_ref(const edge_key* ptr)
{ ++ptr->_ref_count; }
friend void intrusive_ptr_release(const edge_key* ptr)
{ ptr->release(); }
};
struct vertex_info
{
typedef vertex_id vertex_id_type;
typedef std::list<edge_key::ref_type> edge_list_type;
const vertex_id_type id;
mutable int _ref_count;
boost::optional<edge_list_type> _in_edges;
boost::optional<edge_list_type> _out_edges;
graph_ptr _cache;
mutable bool _on_gc;
explicit vertex_info(
vertex_id_type i,
const graph_ptr& cache= graph_ptr())
: id(i)
, _ref_count(0)
, _cache(cache)
, _on_gc(false)
{ }
~vertex_info();
const edge_list_type& out_edges();
const edge_list_type& in_edges();
std::size_t degree();
void release();
friend void intrusive_ptr_add_ref(vertex_info* ptr)
{ ++ptr->_ref_count; }
friend void intrusive_ptr_release(vertex_info* ptr)
{ ptr->release(); }
};
struct vertex_descriptor
: public boost::intrusive_ptr<vertex_info>
{
vertex_descriptor()
: boost::intrusive_ptr<vertex_info>()
{ }
vertex_descriptor(vertex_info* v)
: boost::intrusive_ptr<vertex_info>(v)
{ }
vertex_descriptor(const vertex_descriptor& v)
: boost::intrusive_ptr<vertex_info>(v)
{ }
degree_size_type in_degree() const;
degree_size_type out_degree() const;
friend degree_size_type in_degree(const vertex_descriptor& v, const graph&)
{ return v.in_degree(); }
friend degree_size_type out_degree(const vertex_descriptor& v, const graph&)
{ return v.out_degree(); }
};
struct edge_descriptor
: public boost::intrusive_ptr<const edge_key>
{
edge_descriptor()
: boost::intrusive_ptr<const edge_key>()
{ }
edge_descriptor(const edge_key* e)
: boost::intrusive_ptr<const edge_key>(e)
{ }
edge_descriptor(const edge_descriptor& e)
: boost::intrusive_ptr<const edge_key>(e)
{ }
vertex_descriptor source() const;
vertex_descriptor target() const;
friend vertex_descriptor source(const edge_descriptor& e, const graph&)
{ return e.source(); }
friend vertex_descriptor target(const edge_descriptor& e, const graph&)
{ return e.target(); }
};
struct row_info
: public std::pair<vertex_id, vertex_id>
{
weight_t _weight;
const edge_key::ref_type* _prev;
const edge_key::ref_type* _next;
row_info(
first_type source,
second_type target,
weight_t weight,
const edge_key::ref_type* prev,
const edge_key::ref_type* next)
: std::pair<first_type, second_type>(source, target)
, _weight(weight)
, _prev(prev)
, _next(next)
{ }
};
namespace internal
{
template <typename Value> struct hash
: public std::unary_function<Value, std::size_t>
{
boost::hash<Value> ref_hasher;
std::size_t operator()(const Value& v) const
{
return ref_hasher(v);
}
};
template <typename Value> struct equal_to
: public std::binary_function<Value, Value, bool>
{
std::equal_to<Value> ref_equals;
bool operator()(const Value& x, const Value& y) const
{
return ref_equals(x, y);
}
};
template <> struct hash<edge_key>
: public std::unary_function<edge_key, std::size_t>
{
boost::hash<edge_key::ref_type> ref_hasher;
std::size_t operator()(edge_key const& v) const
{
return ref_hasher(v._ref);
}
};
template <> struct hash<const edge_key*>
: public std::unary_function<const edge_key*, std::size_t>
{
boost::hash<edge_key::ref_type> ref_hasher;
std::size_t operator()(const edge_key* const& v) const
{
return ref_hasher(v->_ref);
}
};
template <> struct hash<vertex_info>
: public std::unary_function<vertex_info, std::size_t>
{
boost::hash<vertex_info::vertex_id_type> id_hasher;
std::size_t operator()(vertex_info const& v) const
{
return id_hasher(v.id);
}
};
template <> struct equal_to<edge_key>
: public std::binary_function<edge_key, edge_key, bool>
{
std::equal_to<edge_key::ref_type> ref_equals;
bool operator()(const edge_key& x, const edge_key& y) const
{
return ref_equals(x._ref, y._ref);
}
};
template <> struct equal_to<const edge_key*>
: public std::binary_function<const edge_key*, edge_key, bool>
{
std::equal_to<edge_key::ref_type> ref_equals;
bool operator()(const edge_key*& x, const edge_key*& y) const
{
return ref_equals(x->_ref, y->_ref);
}
};
template <> struct equal_to<vertex_info>
: public std::binary_function<vertex_info, vertex_info, bool>
{
std::equal_to<vertex_info::vertex_id_type> id_equals;
bool operator()(const vertex_info& x, const vertex_info& y) const
{
return id_equals(x.id, y.id);
}
};
}
struct graph
{
typedef boost::unordered_map<
edge_key,
row_info,
internal::hash<edge_key>,
internal::equal_to<edge_key> > edge_cache_type;
typedef boost::unordered_set<
vertex_info,
internal::hash<vertex_info>,
internal::equal_to<vertex_info> > vertex_cache_type;
bool _gc_running;
mutable int _ref_count;
edge_cache_type _cache_edges;
boost::queue<edge_key*> _gc_edges;
vertex_cache_type _cache_vertices;
boost::queue<vertex_info*> _gc_vertices;
::TABLE* _table;
::Field* _source;
::Field* _target;
::Field* _weight;
graph(
::TABLE* table,
::Field* source,
::Field* target,
::Field* weight= 0);
~graph();
vertex_descriptor vertex(vertex_id);
edge_descriptor edge(const edge_key&);
edge_descriptor edge(
const vertex_descriptor&,
const vertex_descriptor&);
edges_size_type num_edges() const;
friend edges_size_type num_edges(const graph& g)
{ return g.num_edges(); }
friend void intrusive_ptr_add_ref(graph* ptr)
{ ptr->_ref_count++; }
friend void intrusive_ptr_release(graph* ptr)
{ ptr->_ref_count--; }
};
struct row_cursor
{
typedef std::random_access_iterator_tag iterator_category;
typedef graph::edge_cache_type::value_type::second_type value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef graph::edge_cache_type::size_type size_type;
typedef graph::edge_cache_type::difference_type difference_type;
graph_ptr _cache;
edge_descriptor _current;
explicit row_cursor(graph* cache)
: _cache(cache)
{ }
explicit row_cursor(const graph_ptr& cache)
: _cache(cache)
{ }
explicit row_cursor(const edge_descriptor& e, const graph_ptr& cache)
: _cache(cache)
, _current(e)
{ }
reference operator*() const
{ return _cache->_cache_edges.find(*_current)->second; }
pointer operator->() const
{ return &_cache->_cache_edges.find(*_current)->second; }
row_cursor& operator++();
row_cursor operator++(int)
{ row_cursor tmp= *this; ++*this; return tmp; }
row_cursor& operator--();
row_cursor operator--(int)
{ row_cursor tmp= *this; --*this; return tmp; }
row_cursor& operator+=(difference_type delta);
row_cursor operator+(difference_type delta) const
{ row_cursor tmp= *this; return tmp += delta; }
row_cursor& operator-=(difference_type delta);
row_cursor operator-(difference_type delta) const
{ row_cursor tmp= *this; return tmp -= delta; }
reference operator[](difference_type offset) const
{ return *(*this + offset); }
row_cursor& first();
row_cursor& last();
row_cursor& end()
{ _current.reset(); return *this; }
bool operator==(const row_cursor& x) const
{ return _current == x._current; }
bool operator!=(const row_cursor& x) const
{ return _current != x._current; }
};
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment