Commit b965d031 authored by Antony T Curtis's avatar Antony T Curtis

Merge OQGraph3 into MariaDB 5.5.27

parents a0efc4bd 0fee6ce7
/* 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,142 @@ ...@@ -27,22 +27,142 @@
#ifndef oq_graphcore_graph_h_ #ifndef oq_graphcore_graph_h_
#define oq_graphcore_graph_h_ #define oq_graphcore_graph_h_
typedef adjacency_list #include "oqgraph_shim.h"
<
vecS, #include <boost/graph/two_bit_color_map.hpp>
vecS,
bidirectionalS, namespace boost
VertexInfo, {
EdgeInfo typedef oqgraph3::graph Graph;
> Graph;
template<typename IndexMap = identity_property_map>
#define GRAPH_WEIGHTMAP(G) get(&EdgeInfo::weight, G) struct two_bit_judy_map
typedef property_map<Graph, EdgeWeight EdgeInfo::*>::type weightmap_type; {
typedef typename property_traits<IndexMap>::key_type key_type;
#define GRAPH_INDEXMAP(G) get(vertex_index, G) typedef two_bit_color_type value_type;
typedef property_map<Graph, vertex_index_t>::type indexmap_type; typedef void reference;
typedef read_write_property_map_tag category;
#define GRAPH_IDMAP(G) get(&VertexInfo::id, G)
typedef property_map<Graph, VertexID VertexInfo::*>::type idmap_type; 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); }
}
#endif #endif
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#ifndef oq_graphcore_types_h_ #ifndef oq_graphcore_types_h_
#define oq_graphcore_types_h_ #define oq_graphcore_types_h_
namespace open_query namespace open_query
{ {
...@@ -33,4 +34,9 @@ namespace open_query ...@@ -33,4 +34,9 @@ namespace open_query
typedef double EdgeWeight; typedef double EdgeWeight;
} }
class Field;
typedef struct st_table TABLE;
#endif #endif
...@@ -30,14 +30,13 @@ ...@@ -30,14 +30,13 @@
#include <boost/config.hpp> #include <boost/config.hpp>
#include "graphcore-graph.h"
#include <set> #include <set>
#include <stack> #include <stack>
#include <boost/property_map/property_map.hpp> #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/breadth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp> #include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/iteration_macros.hpp> #include <boost/graph/iteration_macros.hpp>
...@@ -46,6 +45,8 @@ ...@@ -46,6 +45,8 @@
#include "graphcore.h" #include "graphcore.h"
#include <boost/unordered_map.hpp>
using namespace open_query; using namespace open_query;
using namespace boost; using namespace boost;
...@@ -53,46 +54,6 @@ static const row empty_row = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ...@@ -53,46 +54,6 @@ static const row empty_row = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
namespace open_query 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>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge; typedef graph_traits<Graph>::edge_descriptor Edge;
...@@ -261,18 +222,15 @@ namespace open_query { ...@@ -261,18 +222,15 @@ namespace open_query {
public: public:
Graph g; Graph g;
weightmap_type weightmap;
idmap_type idmap;
indexmap_type indexmap;
optional<Vertex> find_vertex(VertexID id) const; optional<Vertex> find_vertex(VertexID id) const;
optional<Edge> find_edge(Vertex, Vertex) const; optional<Edge> find_edge(Vertex, Vertex) const;
inline oqgraph_share() throw() inline oqgraph_share(
: g(), TABLE* table,
weightmap(GRAPH_WEIGHTMAP(g)), Field* origid,
idmap(GRAPH_IDMAP(g)), Field* destid,
indexmap(GRAPH_INDEXMAP(g)) Field* weight) throw()
: g(table, origid, destid, weight)
{ } { }
inline ~oqgraph_share() inline ~oqgraph_share()
{ } { }
...@@ -360,13 +318,13 @@ namespace open_query { ...@@ -360,13 +318,13 @@ namespace open_query {
} }
}; };
template <typename P, typename D>
struct GRAPHCORE_INTERNAL oqgraph_visit_dist 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; typedef on_finish_vertex event_filter;
oqgraph_visit_dist(std::vector<Vertex>::iterator p, oqgraph_visit_dist(const P& p, const D& d,
std::vector<EdgeWeight>::iterator d,
stack_cursor *cursor) stack_cursor *cursor)
: seq(0), m_cursor(*cursor), m_p(p), m_d(d) : seq(0), m_cursor(*cursor), m_p(p), m_d(d)
{ assert(cursor); } { assert(cursor); }
...@@ -374,22 +332,28 @@ namespace open_query { ...@@ -374,22 +332,28 @@ namespace open_query {
template<class T, class Graph> template<class T, class Graph>
void operator()(T u, Graph &g) 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: private:
int seq; int seq;
stack_cursor &m_cursor; stack_cursor &m_cursor;
std::vector<Vertex>::iterator m_p; P m_p;
std::vector<EdgeWeight>::iterator m_d; 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 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; 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) stack_cursor *cursor)
: m_goal(goal), m_cursor(*cursor), m_p(p) : m_goal(goal), m_cursor(*cursor), m_p(p)
{ assert(cursor); } { assert(cursor); }
...@@ -400,17 +364,16 @@ namespace open_query { ...@@ -400,17 +364,16 @@ namespace open_query {
if (u == m_goal) if (u == m_goal)
{ {
int seq= 0; int seq= 0;
indexmap_type indexmap= GRAPH_INDEXMAP(g);
for (Vertex q, v= u;; v = q, seq++) for (Vertex q, v= u;; v = q, seq++)
if ((q= m_p[ indexmap[v] ]) == v) if ((q= m_p[ v ]) == v)
break; break;
for (Vertex v= u;; u= v) for (Vertex v= u;; u= v)
{ {
optional<Edge> edge; optional<Edge> edge;
optional<EdgeWeight> weight; optional<EdgeWeight> weight;
v= m_p[ indexmap[u] ]; v= m_p[ u ];
if (record_weight && u != v) if (record_weight && u != v)
{ {
typename graph_traits<Graph>::out_edge_iterator ei, ei_end; typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
...@@ -419,7 +382,7 @@ namespace open_query { ...@@ -419,7 +382,7 @@ namespace open_query {
if (target(*ei, g) == u) if (target(*ei, g) == u)
{ {
edge= *ei; edge= *ei;
weight= GRAPH_WEIGHTMAP(g)[*ei]; weight= get(boost::edge_weight, g, *ei);
break; break;
} }
} }
...@@ -437,8 +400,14 @@ namespace open_query { ...@@ -437,8 +400,14 @@ namespace open_query {
private: private:
Vertex m_goal; Vertex m_goal;
stack_cursor &m_cursor; 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 namespace open_query
...@@ -468,9 +437,14 @@ namespace open_query ...@@ -468,9 +437,14 @@ namespace open_query
return new (std::nothrow) oqgraph(share); 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> optional<Edge>
...@@ -479,14 +453,14 @@ namespace open_query ...@@ -479,14 +453,14 @@ namespace open_query
if (in_degree(dest, g) >= out_degree(orig, g)) if (in_degree(dest, g) >= out_degree(orig, g))
{ {
graph_traits<Graph>::out_edge_iterator ei, ei_end; graph_traits<Graph>::out_edge_iterator ei, ei_end;
boost::tuples::tie(ei, ei_end)= out_edges(orig, g); tie(ei, ei_end)= out_edges(orig, g);
if ((ei= find_if(ei, ei_end, target_equals(dest, g))) != ei_end) if ((ei= find_if(ei, ei_end, target_equals(dest, g))) != ei_end)
return *ei; return *ei;
} }
else else
{ {
graph_traits<Graph>::in_edge_iterator ei, ei_end; graph_traits<Graph>::in_edge_iterator ei, ei_end;
boost::tuples::tie(ei, ei_end)= in_edges(dest, g); tie(ei, ei_end)= in_edges(dest, g);
if ((ei= find_if(ei, ei_end, source_equals(orig, g))) != ei_end) if ((ei= find_if(ei, ei_end, source_equals(orig, g))) != ei_end)
return *ei; return *ei;
} }
...@@ -496,9 +470,10 @@ namespace open_query ...@@ -496,9 +470,10 @@ namespace open_query
optional<Vertex> optional<Vertex>
oqgraph_share::find_vertex(VertexID id) const 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() int oqgraph::delete_all() throw()
{ {
share->g.clear(); share->g.clear();
...@@ -598,8 +573,9 @@ namespace open_query ...@@ -598,8 +573,9 @@ namespace open_query
optional<Vertex> orig= source(*edge, share->g), optional<Vertex> orig= source(*edge, share->g),
dest= target(*edge, share->g); dest= target(*edge, share->g);
bool orig_neq= orig_id ? share->idmap[*orig] != *orig_id : 0; bool orig_neq= orig_id ? get(boost::vertex_index, share->g, *orig) != *orig_id : 0;
bool dest_neq= dest_id ? share->idmap[*dest] != *dest_id : 0; bool dest_neq= dest_id ? get(boost::vertex_index, share->g, *dest) != *dest_id : 0;
if (orig_neq || dest_neq) if (orig_neq || dest_neq)
{ {
optional<Edge> new_edge; optional<Edge> new_edge;
...@@ -675,7 +651,6 @@ namespace open_query ...@@ -675,7 +651,6 @@ namespace open_query
return OK; return OK;
} }
int oqgraph::delete_edge(VertexID orig_id, VertexID dest_id) throw() int oqgraph::delete_edge(VertexID orig_id, VertexID dest_id) throw()
{ {
optional<Vertex> orig, dest; optional<Vertex> orig, dest;
...@@ -694,6 +669,7 @@ namespace open_query ...@@ -694,6 +669,7 @@ namespace open_query
remove_vertex(*dest, share->g); remove_vertex(*dest, share->g);
return OK; return OK;
} }
#endif
int oqgraph::search(int *latch, VertexID *orig_id, VertexID *dest_id) throw() int oqgraph::search(int *latch, VertexID *orig_id, VertexID *dest_id) throw()
...@@ -731,7 +707,8 @@ namespace open_query ...@@ -731,7 +707,8 @@ namespace open_query
{ {
Vertex v= target(*ei, share->g); Vertex v= target(*ei, share->g);
static_cast<stack_cursor*>(cursor)-> 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 */ /* fall through */
...@@ -745,7 +722,8 @@ namespace open_query ...@@ -745,7 +722,8 @@ namespace open_query
{ {
Vertex v= source(*ei, share->g); Vertex v= source(*ei, share->g);
static_cast<stack_cursor*>(cursor)-> 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; break;
...@@ -757,27 +735,29 @@ namespace open_query ...@@ -757,27 +735,29 @@ namespace open_query
case DIJKSTRAS | HAVE_ORIG | HAVE_DEST: case DIJKSTRAS | HAVE_ORIG | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest) if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
{ {
std::vector<Vertex> p(num_vertices(share->g)); boost::unordered_map<Vertex, Vertex> p;
std::vector<EdgeWeight> d(num_vertices(share->g)); boost::unordered_map<Vertex, EdgeWeight> d;
oqgraph_goal<true, on_finish_vertex> p[ *orig ]= *orig;
vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor)); d[ *orig ] = EdgeWeight();
p[share->indexmap[*orig]]= *orig;
try try
{ {
dijkstra_shortest_paths(share->g, *orig, dijkstra_shortest_paths_no_init(share->g, *orig,
weight_map( make_lazy_property_map(p, identity_initializer<Vertex>()),
share->weightmap make_lazy_property_map(d, value_initializer<EdgeWeight>(
). (std::numeric_limits<EdgeWeight>::max)())),
distance_map( get(edge_weight, share->g),
make_iterator_property_map(d.begin(), share->indexmap) get(vertex_index, share->g),
). std::less<EdgeWeight>(),
predecessor_map( closed_plus<EdgeWeight>(),
make_iterator_property_map(p.begin(), share->indexmap) EdgeWeight(),
). make_dijkstra_visitor(
visitor( make_oqgraph_goal<true, on_finish_vertex>(
make_dijkstra_visitor(vis) *dest,
boost::make_assoc_property_map(p),
static_cast<stack_cursor*>(cursor)
) )
); ),
make_two_bit_judy_map(get(vertex_index, share->g)));
} }
catch (...) catch (...)
{ /* printf("found\n"); */ } { /* printf("found\n"); */ }
...@@ -787,23 +767,25 @@ namespace open_query ...@@ -787,23 +767,25 @@ namespace open_query
case BREADTH_FIRST | HAVE_ORIG | HAVE_DEST: case BREADTH_FIRST | HAVE_ORIG | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest) if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
{ {
std::vector<Vertex> p(num_vertices(share->g)); boost::unordered_map<Vertex, Vertex> p;
oqgraph_goal<false, on_discover_vertex> boost::queue<Vertex> Q;
vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor)); p[ *orig ]= *orig;
p[share->indexmap[*orig]]= *orig;
try try
{ {
breadth_first_search(share->g, *orig, breadth_first_visit(share->g, *orig, Q,
visitor(make_bfs_visitor( make_bfs_visitor(
std::make_pair( std::make_pair(
record_predecessors( record_predecessors(
make_iterator_property_map(p.begin(), share->indexmap), boost::make_assoc_property_map(p),
on_tree_edge() 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 (...) catch (...)
{ /* printf("found\n"); */ } { /* printf("found\n"); */ }
...@@ -814,109 +796,121 @@ namespace open_query ...@@ -814,109 +796,121 @@ namespace open_query
case BREADTH_FIRST | HAVE_ORIG: case BREADTH_FIRST | HAVE_ORIG:
if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest)) if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
{ {
std::vector<Vertex> p(num_vertices(share->g)); boost::unordered_map<Vertex, Vertex> p;
std::vector<EdgeWeight> d(num_vertices(share->g)); boost::unordered_map<Vertex, EdgeWeight> d;
oqgraph_visit_dist vis(p.begin(), d.begin(), boost::queue<Vertex> Q;
static_cast<stack_cursor*>(cursor)); p[ *orig ]= *orig;
p[share->indexmap[*orig]]= *orig; d[ *orig ] = EdgeWeight();
switch (ALGORITHM & op) switch (ALGORITHM & op)
{ {
case DIJKSTRAS: case DIJKSTRAS:
dijkstra_shortest_paths(share->g, *orig, dijkstra_shortest_paths_no_init(share->g, *orig,
weight_map( make_lazy_property_map(p, identity_initializer<Vertex>()),
share->weightmap make_lazy_property_map(d, value_initializer<EdgeWeight>(
). (std::numeric_limits<EdgeWeight>::max)())),
distance_map( get(edge_weight, share->g),
make_iterator_property_map(d.begin(), share->indexmap) get(vertex_index, share->g),
). std::less<EdgeWeight>(),
predecessor_map( closed_plus<EdgeWeight>(),
make_iterator_property_map(p.begin(), share->indexmap) EdgeWeight(),
). make_dijkstra_visitor(
visitor( make_oqgraph_visit_dist(
make_dijkstra_visitor(vis) boost::make_assoc_property_map(p), d,
static_cast<stack_cursor*>(cursor)
) )
); ),
make_two_bit_judy_map(get(vertex_index, share->g)));
break; break;
case BREADTH_FIRST: case BREADTH_FIRST:
breadth_first_search(share->g, *orig, breadth_first_visit(share->g, *orig, Q,
visitor(make_bfs_visitor( make_bfs_visitor(
std::make_pair( std::make_pair(
record_predecessors( record_predecessors(
make_iterator_property_map(p.begin(), boost::make_assoc_property_map(p),
share->indexmap),
on_tree_edge() on_tree_edge()
), ),
std::make_pair( std::make_pair(
record_distances( record_distances(
make_iterator_property_map(d.begin(), boost::make_assoc_property_map(d),
share->indexmap),
on_tree_edge() 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; break;
default: default:
abort(); abort();
} }
} }
break; break;
#if 0
case BREADTH_FIRST | HAVE_DEST: case BREADTH_FIRST | HAVE_DEST:
case DIJKSTRAS | HAVE_DEST: case DIJKSTRAS | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest)) if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
{ {
std::vector<Vertex> p(num_vertices(share->g)); boost::unordered_map<Vertex, Vertex> p;
std::vector<EdgeWeight> d(num_vertices(share->g)); boost::unordered_map<Vertex, EdgeWeight> d;
oqgraph_visit_dist vis(p.begin(), d.begin(), boost::queue<Vertex> Q;
static_cast<stack_cursor*>(cursor));
reverse_graph<Graph> r(share->g); reverse_graph<Graph> r(share->g);
p[share->indexmap[*dest]]= *dest; p[ *dest ]= *dest;
d[ *dest ] = EdgeWeight();
switch (ALGORITHM & op) switch (ALGORITHM & op)
{ {
case DIJKSTRAS: case DIJKSTRAS:
dijkstra_shortest_paths(r.m_g, *dest, dijkstra_shortest_paths_no_init(share->g, *dest,
weight_map( make_lazy_property_map(p, identity_initializer<Vertex>()),
share->weightmap make_lazy_property_map(d, value_initializer<EdgeWeight>(
). (std::numeric_limits<EdgeWeight>::max)())),
distance_map( get(edge_weight, share->g),
make_iterator_property_map(d.begin(), share->indexmap) get(vertex_index, share->g),
). std::less<EdgeWeight>(),
predecessor_map( closed_plus<EdgeWeight>(),
make_iterator_property_map(p.begin(), share->indexmap) EdgeWeight(),
). make_dijkstra_visitor(
visitor( make_dijkstra_visitor(
make_dijkstra_visitor(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; break;
case BREADTH_FIRST: case BREADTH_FIRST:
breadth_first_search(r, *dest, breadth_first_visit(share->g, *dest, Q,
visitor(make_bfs_visitor( make_bfs_visitor(
std::make_pair( std::make_pair(
record_predecessors( record_predecessors(
make_iterator_property_map(p.begin(), boost::make_assoc_property_map(p),
share->indexmap),
on_tree_edge() on_tree_edge()
), ),
std::make_pair( std::make_pair(
record_distances( record_distances(
make_iterator_property_map(d.begin(), boost::make_assoc_property_map(d),
share->indexmap),
on_tree_edge() 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; break;
default: default:
abort(); abort();
} }
} }
break; break;
#endif
default: default:
break; break;
} }
...@@ -1006,7 +1000,7 @@ int stack_cursor::fetch_row(const row &row_info, row &result, ...@@ -1006,7 +1000,7 @@ int stack_cursor::fetch_row(const row &row_info, row &result,
if ((result.seq_indicator= seq= last.sequence())) if ((result.seq_indicator= seq= last.sequence()))
result.seq= *seq; result.seq= *seq;
if ((result.link_indicator= v= last.vertex())) 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())) if ((result.weight_indicator= w= last.weight()))
result.weight= *w; result.weight= *w;
return oqgraph::OK; return oqgraph::OK;
...@@ -1040,7 +1034,7 @@ int vertices_cursor::fetch_row(const row &row_info, row &result, ...@@ -1040,7 +1034,7 @@ int vertices_cursor::fetch_row(const row &row_info, row &result,
if (v) if (v)
{ {
result.link_indicator= 1; result.link_indicator= 1;
result.link= share->idmap[*v]; result.link= get(boost::vertex_index, share->g, *v);
#ifdef DISPLAY_VERTEX_INFO #ifdef DISPLAY_VERTEX_INFO
result.seq_indicator= 1; result.seq_indicator= 1;
if ((result.seq= degree(*v, share->g))) if ((result.seq= degree(*v, share->g)))
...@@ -1048,10 +1042,10 @@ int vertices_cursor::fetch_row(const row &row_info, row &result, ...@@ -1048,10 +1042,10 @@ int vertices_cursor::fetch_row(const row &row_info, row &result,
EdgeWeight weight= 0; EdgeWeight weight= 0;
graph_traits<Graph>::in_edge_iterator iei, iei_end; graph_traits<Graph>::in_edge_iterator iei, iei_end;
for (tie(iei, iei_end)= in_edges(*v, share->g); iei != iei_end; ++iei) 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; graph_traits<Graph>::out_edge_iterator oei, oei_end;
for (tie(oei, oei_end)= out_edges(*v, share->g); oei != oei_end; ++oei) 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_indicator= 1;
result.weight= weight / result.seq; result.weight= weight / result.seq;
} }
...@@ -1066,9 +1060,8 @@ int edges_cursor::fetch_row(const row &row_info, row &result) ...@@ -1066,9 +1060,8 @@ int edges_cursor::fetch_row(const row &row_info, row &result)
{ {
edge_iterator it, end; edge_iterator it, end;
reference ref; reference ref;
size_t count= position; tie(it, end)= edges(share->g);
for (boost::tuples::tie(it, end)= edges(share->g); count && it != end; ++it, --count) it+= position;
;
if (it != end) if (it != end)
ref= reference(position+1, *it); ref= reference(position+1, *it);
if (int res= fetch_row(row_info, result, ref)) if (int res= fetch_row(row_info, result, ref))
...@@ -1085,9 +1078,9 @@ int edges_cursor::fetch_row(const row &row_info, row &result, ...@@ -1085,9 +1078,9 @@ int edges_cursor::fetch_row(const row &row_info, row &result,
{ {
result= row_info; result= row_info;
result.orig_indicator= result.dest_indicator= result.weight_indicator= 1; result.orig_indicator= result.dest_indicator= result.weight_indicator= 1;
result.orig= share->idmap[ source( *edge, share->g ) ]; result.orig= get(boost::vertex_index, share->g, source( *edge, share->g ) );
result.dest= share->idmap[ target( *edge, share->g ) ]; result.dest= get(boost::vertex_index, share->g, target( *edge, share->g ) );
result.weight= share->weightmap[ *edge ]; result.weight= get(boost::edge_weight, share->g, *edge);
return oqgraph::OK; return oqgraph::OK;
} }
return oqgraph::NO_MORE_DATA; return oqgraph::NO_MORE_DATA;
......
...@@ -104,7 +104,7 @@ namespace open_query ...@@ -104,7 +104,7 @@ namespace open_query
void row_ref(void*) throw(); void row_ref(void*) throw();
static oqgraph* create(oqgraph_share*) 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*) throw();
static void free(oqgraph_share*) throw(); static void free(oqgraph_share*) throw();
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#endif #endif
#ifdef HAVE_OQGRAPH #ifdef HAVE_OQGRAPH
#include <stdarg.h>
#include <stdio.h>
#include <mysql_version.h> #include <mysql_version.h>
#include "ha_oqgraph.h" #include "ha_oqgraph.h"
...@@ -46,19 +48,27 @@ ...@@ -46,19 +48,27 @@
using namespace open_query; using namespace open_query;
struct oqgraph_info_st struct oqgraph_table_option_struct
{ {
THR_LOCK lock; char *table_name;
oqgraph_share *graph;
uint use_count; char *origid; // name of the origin id column
uint key_stat_version; char *destid; // name of the target id column
uint records; char *weight; // name of the weight column (optional)
bool dropped; };
char name[FN_REFLEN+1];
#define ha_table_option_struct oqgraph_table_option_struct
ha_create_table_option oqgraph_table_option_list[]=
{
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[]= static const char oqgraph_description[]=
"Open Query Graph Computation Engine, stored in memory " "Open Query Graph Computation Engine "
"(http://openquery.com/graph)"; "(http://openquery.com/graph)";
#if MYSQL_VERSION_ID < 50100 #if MYSQL_VERSION_ID < 50100
...@@ -96,27 +106,10 @@ statistic_increment(table->in_use->status_var.X, &LOCK_status) ...@@ -96,27 +106,10 @@ statistic_increment(table->in_use->status_var.X, &LOCK_status)
#define STATISTIC_INCREMENT(X) /* nothing */ #define STATISTIC_INCREMENT(X) /* nothing */
#define MOVE(X) move_field_offset(X) #define MOVE(X) move_field_offset(X)
#define RECORDS stats.records #define RECORDS stats.records
#endif
static HASH oqgraph_open_tables;
static pthread_mutex_t LOCK_oqgraph;
static bool oqgraph_init_done= 0; 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, static handler* oqgraph_create_handler(handlerton *hton, TABLE_SHARE *table,
MEM_ROOT *mem_root) MEM_ROOT *mem_root)
{ {
...@@ -130,7 +123,6 @@ static bool oqgraph_init() ...@@ -130,7 +123,6 @@ static bool oqgraph_init()
{ {
if (have_oqgraph == SHOW_OPTION_DISABLED) if (have_oqgraph == SHOW_OPTION_DISABLED)
return 1; return 1;
#endif
if (pthread_mutex_init(&LOCK_oqgraph, MY_MUTEX_INIT_FAST)) if (pthread_mutex_init(&LOCK_oqgraph, MY_MUTEX_INIT_FAST))
goto error; goto error;
if (my_hash_init(&oqgraph_open_tables, &my_charset_bin, 32, 0, 0, if (my_hash_init(&oqgraph_open_tables, &my_charset_bin, 32, 0, 0,
...@@ -144,17 +136,11 @@ static bool oqgraph_init() ...@@ -144,17 +136,11 @@ static bool oqgraph_init()
hton->db_type= DB_TYPE_AUTOASSIGN; hton->db_type= DB_TYPE_AUTOASSIGN;
hton->create= oqgraph_create_handler; hton->create= oqgraph_create_handler;
hton->flags= HTON_NO_FLAGS; hton->flags= HTON_NO_FLAGS;
#endif hton->table_options= oqgraph_table_option_list;
oqgraph_init_done= TRUE; oqgraph_init_done= TRUE;
return 0; 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 *) static int oqgraph_fini(void *)
{ {
my_hash_free(&oqgraph_open_tables); my_hash_free(&oqgraph_open_tables);
...@@ -264,6 +250,9 @@ static int error_code(int res) ...@@ -264,6 +250,9 @@ static int error_code(int res)
KEY (latch, origid, destid) USING HASH, KEY (latch, origid, destid) USING HASH,
KEY (latch, destid, origid) USING HASH KEY (latch, destid, origid) USING HASH
) ENGINE=OQGRAPH ) ENGINE=OQGRAPH
READ_TABLE=bar
ORIGID=src_id
DESTID=tgt_id
*/ */
static int oqgraph_check_table_structure (TABLE *table_arg) static int oqgraph_check_table_structure (TABLE *table_arg)
...@@ -331,16 +320,15 @@ static int oqgraph_check_table_structure (TABLE *table_arg) ...@@ -331,16 +320,15 @@ static int oqgraph_check_table_structure (TABLE *table_arg)
** OQGRAPH tables ** OQGRAPH tables
*****************************************************************************/ *****************************************************************************/
#if MYSQL_VERSION_ID >= 50100
ha_oqgraph::ha_oqgraph(handlerton *hton, TABLE_SHARE *table_arg) ha_oqgraph::ha_oqgraph(handlerton *hton, TABLE_SHARE *table_arg)
: handler(hton, table_arg), : handler(hton, table_arg)
#else , graph_share(0)
ha_oqgraph::ha_oqgraph(TABLE *table_arg) , graph(0)
: handler(&oqgraph_hton, table_arg), , error_message("", 0, &my_charset_latin1)
#endif
share(0), graph(0), records_changed(0), key_stat_version(0)
{ } { }
ha_oqgraph::~ha_oqgraph()
{ }
static const char *ha_oqgraph_exts[] = static const char *ha_oqgraph_exts[] =
{ {
...@@ -352,11 +340,7 @@ const char **ha_oqgraph::bas_ext() const ...@@ -352,11 +340,7 @@ const char **ha_oqgraph::bas_ext() const
return ha_oqgraph_exts; return ha_oqgraph_exts;
} }
#if MYSQL_VERSION_ID >= 50100
ulonglong ha_oqgraph::table_flags() const ulonglong ha_oqgraph::table_flags() const
#else
ulong ha_oqgraph::table_flags() const
#endif
{ {
return (HA_NO_BLOBS | HA_NULL_IN_KEY | return (HA_NO_BLOBS | HA_NULL_IN_KEY |
HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED | HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED |
...@@ -368,278 +352,251 @@ ulong ha_oqgraph::index_flags(uint inx, uint part, bool all_parts) const ...@@ -368,278 +352,251 @@ ulong ha_oqgraph::index_flags(uint inx, uint part, bool all_parts) const
return HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR; 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 (error < 0)
if ((share = get_share(name, table)))
{ {
ref_length= oqgraph::sizeof_ref; buf->append(error_message);
buf->c_ptr_safe();
error_message.length(0);
} }
return false;
if (share)
{
/* 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;
}
pthread_mutex_unlock(&LOCK_oqgraph);
return (share ? 0 : 1);
} }
int ha_oqgraph::close(void) void ha_oqgraph::print_error(const char* fmt, ...)
{ {
pthread_mutex_lock(&LOCK_oqgraph); va_list ap;
oqgraph::free(graph); graph= 0; va_start(ap, fmt);
int res= free_share(share); error_message.reserve(256);
pthread_mutex_unlock(&LOCK_oqgraph); size_t len = error_message.length();
return error_code(res); 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++) THD* thd = current_thd;
{ oqgraph_table_option_struct *options=
KEY *key=table->key_info+i; reinterpret_cast<oqgraph_table_option_struct*>(table->s->option_struct);
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;
}
error_message.length(0);
int ha_oqgraph::write_row(byte * buf) const char* p= strend(name)-1;
{ while (p > name && *p != '\\' && *p != '/')
int res= oqgraph::MISC_FAIL; --p;
Field ** const field= table->field;
STATISTIC_INCREMENT(ha_write_count);
#if MYSQL_VERSION_ID >= 50100 init_tmp_table_share(
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); thd, share, table->s->db.str, table->s->db.length,
#endif options->table_name, "");
my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff) size_t tlen= strlen(options->table_name);
size_t plen= (int)(p - name) + tlen;
share->path.str= (char*)
alloc_root(&share->mem_root, plen + 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;
origid= destid= weight= 0;
while (open_table_def(thd, share, 0))
{ {
field[1]->MOVE(ptrdiff); if (thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE)
field[2]->MOVE(ptrdiff); {
field[3]->MOVE(ptrdiff); free_table_share(share);
return thd->main_da.sql_errno();
} }
if (!field[1]->is_null() && !field[2]->is_null()) if (ha_create_table_from_engine(thd, table->s->db.str, options->table_name))
{ {
VertexID orig_id= (VertexID) field[1]->val_int(); free_table_share(share);
VertexID dest_id= (VertexID) field[2]->val_int(); return thd->main_da.sql_errno();
EdgeWeight weight= 1; }
mysql_reset_errors(thd, 1);
if (!field[3]->is_null()) thd->clear_error();
weight= (EdgeWeight) field[3]->val_real(); continue;
}
if (!(res= graph->insert_edge(orig_id, dest_id, weight, replace_dups))) if (int err= share->error)
{ {
++records_changed; open_table_error(share, share->error, share->open_errno, share->errarg);
share->records++; free_table_share(share);
} return err;
if (res == oqgraph::DUPLICATE_EDGE && ignore_dups && !insert_dups)
res= oqgraph::OK;
} }
if (ptrdiff) if (share->is_view)
{ {
field[1]->MOVE(-ptrdiff); open_table_error(share, 1, EMFILE, 0);
field[2]->MOVE(-ptrdiff); free_table_share(share);
field[3]->MOVE(-ptrdiff); print_error("VIEWs are not supported for a backing store");
return -1;
} }
#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 (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))
{ {
/* open_table_error(share, err, EMFILE, 0);
We can perform this safely since only one writer at the time is free_table_share(share);
allowed on the table. return -1;
*/
share->key_stat_version++;
} }
return error_code(res); edges->reginfo.lock_type= TL_READ;
}
int ha_oqgraph::update_row(const byte * old, byte * buf) edges->tablenr= thd->current_tablenr++;
{ edges->status= STATUS_NO_RECORD;
int res= oqgraph::MISC_FAIL; edges->file->ha_start_of_new_statement();
VertexID orig_id, dest_id; edges->file->ft_handler= 0;
EdgeWeight weight= 1; edges->pos_in_table_list= 0;
Field **field= table->field; edges->clear_column_bitmaps();
STATISTIC_INCREMENT(ha_update_count); bfill(table->record[0], table->s->null_bytes, 255);
bfill(table->record[1], table->s->null_bytes, 255);
#if MYSQL_VERSION_ID >= 50100 // We expect fields origid, destid and optionally weight
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); origid= destid= weight= 0;
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff) if (!edges->file)
{ {
field[0]->MOVE(ptrdiff); print_error("Some error occurred opening table '%s'", options->table_name);
field[1]->MOVE(ptrdiff); free_table_share(share);
field[2]->MOVE(ptrdiff); return -1;
field[3]->MOVE(ptrdiff);
} }
if (inited == INDEX || inited == RND) for (Field **field= edges->field; *field; ++field)
{ {
VertexID *origp= 0, *destp= 0; if (strcmp(options->origid, (*field)->field_name))
EdgeWeight *weightp= 0; continue;
if (!field[1]->is_null()) if ((*field)->cmp_type() != INT_RESULT ||
*(origp= &orig_id)= (VertexID) field[1]->val_int(); !((*field)->flags & NOT_NULL_FLAG))
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 (!origp == field[1]->is_null() && print_error("Column '%s.%s' is not a not-null integer type",
*origp == (VertexID) field[1]->val_int()) options->table_name, options->origid);
origp= 0; closefrm(edges, 0);
if (!destp == field[2]->is_null() && free_table_share(share);
*destp == (VertexID) field[2]->val_int()) return -1;
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;
} }
origid = *field;
field[0]->MOVE(-ptrdiff2); break;
field[1]->MOVE(-ptrdiff2);
field[2]->MOVE(-ptrdiff2);
field[3]->MOVE(-ptrdiff2);
} }
if (ptrdiff) for (Field **field= edges->field; *field; ++field)
{ {
field[0]->MOVE(-ptrdiff); if (strcmp(options->destid, (*field)->field_name))
field[1]->MOVE(-ptrdiff); continue;
field[2]->MOVE(-ptrdiff); if ((*field)->type() != origid->type() ||
field[3]->MOVE(-ptrdiff); !((*field)->flags & NOT_NULL_FLAG))
}
#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)
{ {
/* print_error("Column '%s.%s' is not a not-null integer type",
We can perform this safely since only one writer at the time is options->table_name, options->destid);
allowed on the table. closefrm(edges, 0);
*/ free_table_share(share);
share->key_stat_version++; return -1;
}
destid = *field;
break;
} }
return error_code(res);
}
int ha_oqgraph::delete_row(const byte * buf)
{
int res= oqgraph::EDGE_NOT_FOUND;
Field **field= table->field;
STATISTIC_INCREMENT(ha_delete_count);
if (inited == INDEX || inited == RND) for (Field **field= edges->field; options->weight && *field; ++field)
{ {
if ((res= graph->delete_edge(oqgraph::current_row())) == oqgraph::OK) if (strcmp(options->weight, (*field)->field_name))
continue;
if ((*field)->result_type() != REAL_RESULT ||
!((*field)->flags & NOT_NULL_FLAG))
{ {
++records_changed; print_error("Column '%s.%s' is not a not-null real type",
share->records--; options->table_name, options->weight);
closefrm(edges, 0);
free_table_share(share);
return -1;
} }
weight = *field;
break;
} }
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) if (!origid || !destid || (!weight && options->weight))
{ {
field[0]->MOVE(ptrdiff); print_error("Data columns missing on table '%s'", options->table_name);
field[1]->MOVE(ptrdiff); closefrm(edges, 0);
field[2]->MOVE(ptrdiff); free_table_share(share);
return -1;
} }
if (field[0]->is_null() && !field[1]->is_null() && !field[2]->is_null()) if (!(graph_share = oqgraph::create(edges, origid, destid, weight)))
{ {
VertexID orig_id= (VertexID) field[1]->val_int(); print_error("Unable to create graph instance.");
VertexID dest_id= (VertexID) field[2]->val_int(); closefrm(edges, 0);
free_table_share(share);
return -1;
}
ref_length= oqgraph::sizeof_ref;
graph = oqgraph::create(graph_share);
return 0;
}
if ((res= graph->delete_edge(orig_id, dest_id)) == oqgraph::OK) int ha_oqgraph::close(void)
{
oqgraph::free(graph); graph= 0;
oqgraph::free(graph_share); graph_share= 0;
if (share)
{ {
++records_changed; if (edges->file)
share->records--; closefrm(edges, 0);
} free_table_share(share);
} }
return 0;
}
if (ptrdiff) void ha_oqgraph::update_key_stats()
{
for (uint i= 0; i < table->s->keys; i++)
{ {
field[0]->MOVE(-ptrdiff); KEY *key=table->key_info+i;
field[1]->MOVE(-ptrdiff); if (!key->rec_per_key)
field[2]->MOVE(-ptrdiff); 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)
uint
no_records= 2;
key->rec_per_key[key->key_parts-1]= no_records;
} }
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
} }
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); /* At the end of update_key_stats() we can proudly claim they are OK. */
//skey_stat_version= share->key_stat_version;
}
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, int ha_oqgraph::index_read(byte * buf, const byte * key, uint key_len,
...@@ -677,9 +634,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key, ...@@ -677,9 +634,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key,
bmove_align(buf, table->s->default_values, table->s->reclength); bmove_align(buf, table->s->default_values, table->s->reclength);
key_restore(buf, (byte*) key, key_info, key_len); 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); my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0]; my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff) if (ptrdiff)
...@@ -713,9 +668,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key, ...@@ -713,9 +668,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key,
field[1]->MOVE(-ptrdiff); field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff); field[2]->MOVE(-ptrdiff);
} }
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map); dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
res= graph->search(latchp, orig_idp, dest_idp); res= graph->search(latchp, orig_idp, dest_idp);
...@@ -731,9 +684,7 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row) ...@@ -731,9 +684,7 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row)
bmove_align(record, table->s->default_values, table->s->reclength); 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); my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
#endif
my_ptrdiff_t ptrdiff= record - table->record[0]; my_ptrdiff_t ptrdiff= record - table->record[0];
if (ptrdiff) if (ptrdiff)
...@@ -792,15 +743,14 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row) ...@@ -792,15 +743,14 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row)
field[4]->MOVE(-ptrdiff); field[4]->MOVE(-ptrdiff);
field[5]->MOVE(-ptrdiff); field[5]->MOVE(-ptrdiff);
} }
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->write_set, old_map); dbug_tmp_restore_column_map(table->write_set, old_map);
#endif
return 0; return 0;
} }
int ha_oqgraph::rnd_init(bool scan) int ha_oqgraph::rnd_init(bool scan)
{ {
edges->prepare_for_position();
return error_code(graph->random(scan)); return error_code(graph->random(scan));
} }
...@@ -838,75 +788,31 @@ int ha_oqgraph::cmp_ref(const byte *ref1, const byte *ref2) ...@@ -838,75 +788,31 @@ int ha_oqgraph::cmp_ref(const byte *ref1, const byte *ref2)
int ha_oqgraph::info(uint flag) int ha_oqgraph::info(uint flag)
{ {
RECORDS= graph->vertices_count() + graph->edges_count(); RECORDS= 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
/* /*
If info() is called for the first time after open(), we will still 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 have to update the key statistics. Hoping that a table lock is now
in place. in place.
*/ */
if (key_stat_version != share->key_stat_version) // if (key_stat_version != share->key_stat_version)
update_key_stats(); // update_key_stats();
return 0; return 0;
} }
int ha_oqgraph::extra(enum ha_extra_function operation) int ha_oqgraph::extra(enum ha_extra_function operation)
{ {
switch (operation) return edges->file->extra(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;
} }
int ha_oqgraph::delete_all_rows() int ha_oqgraph::delete_all_rows()
{ {
int res; return ER_OPEN_AS_READONLY;
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);
} }
int ha_oqgraph::external_lock(THD *thd, int lock_type) int ha_oqgraph::external_lock(THD *thd, int lock_type)
{ {
return 0; // No external locking return edges->file->ha_external_lock(thd, lock_type);
} }
...@@ -914,10 +820,7 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd, ...@@ -914,10 +820,7 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd,
THR_LOCK_DATA **to, THR_LOCK_DATA **to,
enum thr_lock_type lock_type) enum thr_lock_type lock_type)
{ {
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) return edges->file->store_lock(thd, to, lock_type);
lock.type=lock_type;
*to++= &lock;
return to;
} }
/* /*
...@@ -925,20 +828,12 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd, ...@@ -925,20 +828,12 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd,
not when doing a CREATE on the table. 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; return 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);
} }
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); pthread_mutex_lock(&LOCK_oqgraph);
if (OQGRAPH_INFO *share= get_share(from)) if (OQGRAPH_INFO *share= get_share(from))
...@@ -980,8 +875,9 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key, ...@@ -980,8 +875,9 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key,
return RECORDS; return RECORDS;
/* Assert that info() did run. We need current statistics here. */ /* Assert that info() did run. We need current statistics here. */
DBUG_ASSERT(key_stat_version == share->key_stat_version); //DBUG_ASSERT(key_stat_version == share->key_stat_version);
ha_rows result= key->rec_per_key[key->key_parts-1]; //ha_rows result= key->rec_per_key[key->key_parts-1];
ha_rows result= 10;
return result; return result;
} }
...@@ -990,24 +886,14 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key, ...@@ -990,24 +886,14 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key,
int ha_oqgraph::create(const char *name, TABLE *table_arg, int ha_oqgraph::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info) HA_CREATE_INFO *create_info)
{ {
int res = -1; oqgraph_table_option_struct *options=
OQGRAPH_INFO *share; reinterpret_cast<oqgraph_table_option_struct*>(table->s->option_struct);
pthread_mutex_lock(&LOCK_oqgraph); if (int res = oqgraph_check_table_structure(table_arg))
if ((share= get_share(name)))
{
free_share(share);
}
else
{
if (!oqgraph_check_table_structure(table_arg))
res= 0;;
}
pthread_mutex_unlock(&LOCK_oqgraph);
if (this->share)
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
return error_code(res); return error_code(res);
(void)(options);
return 0;
} }
...@@ -1018,7 +904,6 @@ void ha_oqgraph::update_create_info(HA_CREATE_INFO *create_info) ...@@ -1018,7 +904,6 @@ void ha_oqgraph::update_create_info(HA_CREATE_INFO *create_info)
// create_info->auto_increment_value= auto_increment_value; // create_info->auto_increment_value= auto_increment_value;
} }
#if MYSQL_VERSION_ID >= 50100
struct st_mysql_storage_engine oqgraph_storage_engine= struct st_mysql_storage_engine oqgraph_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION }; { MYSQL_HANDLERTON_INTERFACE_VERSION };
...@@ -1032,7 +917,7 @@ maria_declare_plugin(oqgraph) ...@@ -1032,7 +917,7 @@ maria_declare_plugin(oqgraph)
PLUGIN_LICENSE_GPL, PLUGIN_LICENSE_GPL,
(int (*)(void*)) oqgraph_init, /* Plugin Init */ (int (*)(void*)) oqgraph_init, /* Plugin Init */
oqgraph_fini, /* Plugin Deinit */ oqgraph_fini, /* Plugin Deinit */
0x0200, /* Version: 2.0 */ 0x0300, /* Version: 3s.0 */
NULL, /* status variables */ NULL, /* status variables */
NULL, /* system variables */ NULL, /* system variables */
"2.0", "2.0",
......
...@@ -37,19 +37,21 @@ namespace open_query ...@@ -37,19 +37,21 @@ namespace open_query
{ {
struct row; struct row;
class oqgraph; class oqgraph;
class oqgraph_share;
} }
/* class for the the Open Query Graph handler */ /* class for the the Open Query Graph handler */
class ha_oqgraph: public 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; 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&); int fill_record(byte*, const open_query::row&);
...@@ -61,7 +63,7 @@ class ha_oqgraph: public handler ...@@ -61,7 +63,7 @@ class ha_oqgraph: public handler
ha_oqgraph(TABLE *table); ha_oqgraph(TABLE *table);
Table_flags table_flags() const; Table_flags table_flags() const;
#endif #endif
~ha_oqgraph() {} ~ha_oqgraph();
const char *table_type() const const char *table_type() const
{ {
return "OQGRAPH"; return "OQGRAPH";
...@@ -107,6 +109,12 @@ class ha_oqgraph: public handler ...@@ -107,6 +109,12 @@ class ha_oqgraph: public handler
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type); enum thr_lock_type lock_type);
int cmp_ref(const byte *ref1, const byte *ref2); int cmp_ref(const byte *ref1, const byte *ref2);
bool get_error_message(int error, String* buf);
void print_error(const char* fmt, ...);
private: private:
void update_key_stats(); 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& open_query::judy_bitset::set(const judy_bitset& src)
{
if (!src.empty())
{
for (size_type pos= src.find_first(); pos != npos; pos= src.find_next(pos))
{
set(pos);
}
}
return *this;
}
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(const judy_bitset& src)
: array(0)
{
set(src);
}
~judy_bitset()
{ clear(); }
judy_bitset& operator=(const judy_bitset& src)
{
clear();
return set(src);
}
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& set(const judy_bitset& src);
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"
bool oqgraph3::edge_iterator::seek()
{
if (!_graph->_cursor ||
_graph->_rnd_pos > _offset ||
_graph->_cursor != _graph->_rnd_cursor.operator->())
{
_graph->_rnd_pos= 0;
_graph->_rnd_cursor= new cursor(_graph);
if (_graph->_rnd_cursor->seek_to(boost::none, boost::none))
_graph->_rnd_pos= size_t(-1);
}
while (_graph->_rnd_pos < _offset)
{
if (_graph->_rnd_cursor->seek_next())
{
_offset = size_t(-1);
return true;
}
_graph->_rnd_pos++;
}
return false;
}
oqgraph3::edge_iterator::value_type oqgraph3::edge_iterator::operator*()
{
seek();
return *_graph->_rnd_cursor;
}
bool oqgraph3::edge_iterator::operator==(const self& x)
{
if (_offset == size_t(-1) && x._offset != size_t(-1))
return const_cast<edge_iterator&>(x).seek();
if (_offset != size_t(-1) && x._offset == size_t(-1))
return seek();
return _offset == x._offset;
}
bool oqgraph3::edge_iterator::operator!=(const self& x)
{
if (_offset == size_t(-1) && x._offset != size_t(-1))
return !const_cast<edge_iterator&>(x).seek();
if (_offset != size_t(-1) && x._offset == size_t(-1))
return !seek();
return _offset != x._offset;
}
/* 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"
#define BOOST_NO_HASH 1
#define BOOST_NO_SLIST 1
#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_info value_type;
typedef edge_info& reference;
typedef edge_info pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
edge_iterator() { }
edge_iterator(const graph_ptr& graph, size_t offset=0)
: _graph(graph)
, _offset(offset) { }
edge_iterator(const edge_iterator& pos)
: _graph(pos._graph)
, _offset(pos._offset) { }
value_type operator*();
self& operator+=(size_t n) { _offset+= n; return *this; }
self& operator++() { ++_offset; return *this; }
self operator++(int)
{ size_t temp= _offset++; return edge_iterator(_graph, temp); }
bool seek();
bool operator==(const self& x);
bool operator!=(const self& x);
graph_ptr _graph;
size_t _offset;
};
struct vertex_iterator
{
typedef vertex_iterator self;
typedef vertex_id value_type;
typedef vertex_id& reference;
typedef vertex_id pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
vertex_iterator() { }
vertex_iterator(const cursor_ptr& pos) : _cursor(pos.operator->()) { }
vertex_id operator*() const
{
edge_info edge(*_cursor);
if (!_seen.test(edge.origid()))
return edge.origid();
else
return edge.destid();
}
self& operator++()
{
edge_info edge(*_cursor);
if (!_seen.test(edge.origid()))
{
_seen.set(edge.origid());
}
else
{
_seen.set(edge.destid());
}
while (_seen.test(edge.origid()) && _seen.test(edge.destid()))
{
if (_cursor->seek_next())
break;
edge= _cursor;
}
return *this;
}
self operator++(int) { cursor* t(new cursor(*_cursor)); ++(*this); return vertex_iterator(t); }
bool operator==(const self& x) { return *_cursor == *x._cursor; }
bool operator!=(const self& x) { return *_cursor != *x._cursor; }
cursor_ptr _cursor;
open_query::judy_bitset _seen;
};
struct out_edge_iterator
{
typedef out_edge_iterator self;
typedef edge_info value_type;
typedef edge_info& reference;
typedef edge_info pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
out_edge_iterator() { }
out_edge_iterator(const cursor_ptr& cursor) : _cursor(cursor) { }
value_type operator*() { return value_type(_cursor); }
self& operator++() { _cursor->seek_next(); return *this; }
self operator++(int)
{ cursor_ptr t(new cursor(*_cursor)); ++(*this); return out_edge_iterator(t); }
bool operator==(const self& x) { return _cursor == x._cursor; }
bool operator!=(const self& x) { return _cursor != x._cursor; }
cursor_ptr _cursor;
};
struct in_edge_iterator
{
typedef in_edge_iterator self;
typedef edge_info value_type;
typedef edge_info& reference;
typedef edge_info pointer;
typedef std::ptrdiff_t difference_type;
typedef std::input_iterator_tag iterator_category;
in_edge_iterator() { }
in_edge_iterator(const cursor_ptr& cursor) : _cursor(cursor) { }
value_type operator*() { return value_type(_cursor); }
self& operator++() { _cursor->seek_next(); return *this; }
self operator++(int)
{ cursor_ptr t(new cursor(*_cursor)); ++(*this); return in_edge_iterator(t); }
bool operator==(const self& x) { return _cursor == x._cursor; }
bool operator!=(const self& x) { return _cursor != x._cursor; }
cursor_ptr _cursor;
};
struct vertex_index_property_map
{
typedef vertex_id value_type;
typedef value_type reference;
typedef vertex_id key_type;
typedef boost::readable_property_map_tag category;
vertex_index_property_map(const graph& g) : _g(g) { }
const graph& _g;
friend inline reference
get(const vertex_index_property_map&, key_type key)
{ return key; }
};
struct edge_weight_property_map
{
typedef weight_t value_type;
typedef value_type reference;
typedef edge_info 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 cursor_ptr value_type;
typedef cursor_ptr reference;
typedef edge_info 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_id vertex_descriptor;
typedef oqgraph3::edge_info edge_descriptor;
typedef boost::adjacency_iterator_generator<
oqgraph3::graph,
oqgraph3::vertex_id,
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 oqgraph3::vertex_id null_vertex()
{ return oqgraph3::vertex_id(-1); }
};
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;
};
#if BOOST_VERSION >= 104601
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;
};
#endif
inline graph_traits<oqgraph3::graph>::vertex_descriptor
source(
const graph_traits<oqgraph3::graph>::edge_descriptor& e,
const oqgraph3::graph&)
{ return e.origid(); }
inline graph_traits<oqgraph3::graph>::vertex_descriptor
target(
const graph_traits<oqgraph3::graph>::edge_descriptor& e,
const oqgraph3::graph&)
{ return e.destid(); }
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& g)
{
oqgraph3::cursor*
end= new oqgraph3::cursor(const_cast<oqgraph3::graph*>(&g));
oqgraph3::cursor*
start= new oqgraph3::cursor(const_cast<oqgraph3::graph*>(&g));
start->seek_to(v, boost::none);
return std::make_pair(
graph_traits<oqgraph3::graph>::out_edge_iterator(start),
graph_traits<oqgraph3::graph>::out_edge_iterator(end));
}
inline graph_traits<oqgraph3::graph>::degree_size_type
out_degree(
graph_traits<oqgraph3::graph>::vertex_descriptor v,
const oqgraph3::graph& g)
{
std::size_t count = 0;
for (std::pair<
graph_traits<oqgraph3::graph>::out_edge_iterator,
graph_traits<oqgraph3::graph>::out_edge_iterator> i= out_edges(v, g);
i.first != i.second; ++i.first)
{
++count;
}
return count;
}
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& g)
{
oqgraph3::cursor*
end= new oqgraph3::cursor(const_cast<oqgraph3::graph*>(&g));
oqgraph3::cursor*
start= new oqgraph3::cursor(const_cast<oqgraph3::graph*>(&g));
start->seek_to(boost::none, v);
return std::make_pair(
graph_traits<oqgraph3::graph>::in_edge_iterator(start),
graph_traits<oqgraph3::graph>::in_edge_iterator(end));
}
inline graph_traits<oqgraph3::graph>::degree_size_type
in_degree(
graph_traits<oqgraph3::graph>::vertex_descriptor v,
const oqgraph3::graph& g)
{
std::size_t count = 0;
for (std::pair<
graph_traits<oqgraph3::graph>::in_edge_iterator,
graph_traits<oqgraph3::graph>::in_edge_iterator> it= in_edges(v, g);
it.first != it.second; ++it.first)
{
++count;
}
return count;
}
// EdgeListGraph concepts
inline std::pair<
graph_traits<oqgraph3::graph>::edge_iterator,
graph_traits<oqgraph3::graph>::edge_iterator>
edges(const oqgraph3::graph& g)
{
std::size_t end= std::size_t(-1);
std::size_t start= end;
if (g.num_edges())
start= 0;
return std::make_pair(
graph_traits<oqgraph3::graph>::edge_iterator(
const_cast<oqgraph3::graph*>(&g), start),
graph_traits<oqgraph3::graph>::edge_iterator(
const_cast<oqgraph3::graph*>(&g), end));
}
inline std::pair<
graph_traits<oqgraph3::graph>::vertex_iterator,
graph_traits<oqgraph3::graph>::vertex_iterator>
vertices(const oqgraph3::graph& g)
{
oqgraph3::cursor*
start= new oqgraph3::cursor(const_cast<oqgraph3::graph*>(&g));
start->seek_to(boost::none, boost::none);
return std::make_pair(
graph_traits<oqgraph3::graph>::vertex_iterator(start),
graph_traits<oqgraph3::graph>::vertex_iterator(
new oqgraph3::cursor(const_cast<oqgraph3::graph*>(&g))));
}
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 key.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(const property_map<oqgraph3::graph,
edge_weight_t>::const_type& p,
const property_map<
oqgraph3::graph,
edge_weight_t>::const_type::key_type& key)
{ return key.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._cursor; }
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(const property_map<oqgraph3::graph, edge_index_t>::const_type&,
const property_map<oqgraph3::graph,
edge_index_t>::const_type::key_type& key)
{ return key._cursor; }
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; }
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 optional<graph_traits<oqgraph3::graph>::vertex_descriptor>
find_vertex(oqgraph3::vertex_id id, const oqgraph3::graph& g)
{
return id;
}
}
/* 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"
static int debugid = 0;
oqgraph3::vertex_id oqgraph3::edge_info::origid() const
{ return _cursor->get_origid(); }
oqgraph3::vertex_id oqgraph3::edge_info::destid() const
{ return _cursor->get_destid(); }
oqgraph3::weight_t oqgraph3::edge_info::weight() const
{ return _cursor->get_weight(); }
bool oqgraph3::cursor_ptr::operator==(const cursor_ptr& x) const
{
if (get() == x.get())
return true;
return (*this)->record_position() == x->_position;
}
bool oqgraph3::cursor_ptr::operator!=(const cursor_ptr& x) const
{
if (get() == x.get())
return false;
return (*this)->record_position() != x->_position;
}
oqgraph3::cursor::cursor(const graph_ptr& graph)
: _ref_count(0)
, _graph(graph)
, _index(-1)
, _parts(0)
, _key()
, _position()
, _debugid(++debugid)
{ }
oqgraph3::cursor::cursor(const cursor& src)
: _ref_count(0)
, _graph(src._graph)
, _index(src._index)
, _parts(src._parts)
, _key(src._key)
, _position(src.record_position())
, _debugid(++debugid)
{ }
oqgraph3::cursor::~cursor()
{
if (this == _graph->_cursor)
{
if (_graph->_cursor->_index >= 0)
_graph->_table->file->ha_index_end();
else
_graph->_table->file->ha_rnd_end();
_graph->_cursor= 0;
_graph->_stale= false;
}
}
const std::string& oqgraph3::cursor::record_position() const
{
if (_graph->_stale && _graph->_cursor)
{
TABLE& table= *_graph->_table;
table.file->position(table.record[0]);
_graph->_cursor->_position.assign(
(const char*) table.file->ref, table.file->ref_length);
if (_graph->_cursor->_index >= 0)
{
key_copy((uchar*) _graph->_cursor->_key.data(), table.record[0],
table.s->key_info + _index, table.s->key_info[_index].key_length, true);
}
_graph->_stale= false;
}
return _position;
}
void oqgraph3::cursor::clear_position()
{
_position.clear();
if (this == _graph->_cursor)
{
_graph->_cursor= 0;
_graph->_stale= false;
}
}
void oqgraph3::cursor::save_position()
{
record_position();
if (this == _graph->_cursor)
{
TABLE& table= *_graph->_table;
if (_graph->_cursor->_index >= 0)
table.file->ha_index_end();
else
table.file->ha_rnd_end();
_graph->_cursor= 0;
_graph->_stale= false;
}
}
int oqgraph3::cursor::restore_position()
{
TABLE& table= *_graph->_table;
if (!_position.size())
return ENOENT;
if (this == _graph->_cursor)
return 0;
if (_graph->_cursor)
_graph->_cursor->save_position();
if (_origid || _destid)
{
if (int rc= table.file->ha_index_init(_index, 1))
return rc;
restore_record(&table, s->default_values);
if (_origid)
{
bitmap_set_bit(table.write_set, _graph->_source->field_index);
_graph->_source->store(*_origid, 1);
bitmap_clear_bit(table.write_set, _graph->_source->field_index);
}
if (_destid)
{
bitmap_set_bit(table.write_set, _graph->_target->field_index);
_graph->_target->store(*_destid, 1);
bitmap_clear_bit(table.write_set, _graph->_target->field_index);
}
if (int rc= table.file->ha_index_init(_index, 1))
return rc;
if (int rc= table.file->ha_index_read_map(
table.record[0], (const uchar*) _key.data(),
(key_part_map)(1 << _parts) - 1,
table.s->key_info[_index].key_parts == _parts ?
HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT))
{
table.file->ha_index_end();
return rc;
}
update_virtual_fields(table.in_use, &table);
table.file->position(table.record[0]);
while (memcmp(table.file->ref, _position.data(), table.file->ref_length))
{
if (int rc= table.file->ha_index_next(table.record[0]))
{
table.file->ha_index_end();
return rc;
}
update_virtual_fields(table.in_use, &table);
if ((_origid && vertex_id(_graph->_source->val_int()) != *_origid) ||
(_destid && vertex_id(_graph->_target->val_int()) != *_destid))
{
table.file->ha_index_end();
return ENOENT;
}
table.file->position(table.record[0]);
}
update_virtual_fields(table.in_use, &table);
}
else
{
if (int rc= table.file->ha_rnd_init(1))
return rc;
if (int rc= table.file->ha_rnd_pos(
table.record[0], (uchar*) _position.data()))
{
table.file->ha_rnd_end();
return rc;
}
update_virtual_fields(table.in_use, &table);
}
_graph->_cursor= this;
_graph->_stale= false;
return 0;
}
oqgraph3::vertex_id oqgraph3::cursor::get_origid()
{
if (_origid)
return *_origid;
if (this != _graph->_cursor)
{
if (restore_position())
return -1;
}
return static_cast<vertex_id>(_graph->_source->val_int());
}
oqgraph3::vertex_id oqgraph3::cursor::get_destid()
{
if (_destid)
return *_destid;
if (this != _graph->_cursor)
{
if (restore_position())
return -1;
}
return static_cast<vertex_id>(_graph->_target->val_int());
}
oqgraph3::weight_t oqgraph3::cursor::get_weight()
{
if (!_graph->_weight)
return 1.0;
if (this != _graph->_cursor)
{
if (restore_position())
return -1;
}
return static_cast<vertex_id>(_graph->_weight->val_int());
}
int oqgraph3::cursor::seek_next()
{
if (this != _graph->_cursor)
{
if (int rc= restore_position())
return rc;
}
TABLE& table= *_graph->_table;
if (_index < 0)
{
if (int rc= table.file->ha_rnd_next(table.record[0]))
{
table.file->ha_rnd_end();
return clear_position(rc);
}
return 0;
}
if (int rc= table.file->ha_index_next(table.record[0]))
{
table.file->ha_index_end();
return clear_position(rc);
}
update_virtual_fields(table.in_use, &table);
_graph->_stale= true;
if ((_origid && vertex_id(_graph->_source->val_int()) != *_origid) ||
(_destid && vertex_id(_graph->_target->val_int()) != *_destid))
{
table.file->ha_index_end();
return clear_position(ENOENT);
}
return 0;
}
int oqgraph3::cursor::seek_prev()
{
if (this != _graph->_cursor)
{
if (int rc= restore_position())
return rc;
}
TABLE& table= *_graph->_table;
if (_index < 0)
{
return -1; // not supported
}
if (int rc= table.file->ha_index_prev(table.record[0]))
{
table.file->ha_index_end();
return clear_position(rc);
}
update_virtual_fields(table.in_use, &table);
_graph->_stale= true;
if ((_origid && vertex_id(_graph->_source->val_int()) != *_origid) ||
(_destid && vertex_id(_graph->_target->val_int()) != *_destid))
{
table.file->ha_index_end();
return clear_position(ENOENT);
}
return 0;
}
int oqgraph3::cursor::seek_to(
boost::optional<vertex_id> origid,
boost::optional<vertex_id> destid)
{
if (_graph->_cursor && this != _graph->_cursor)
_graph->_cursor->save_position();
TABLE& table= *_graph->_table;
_index= -1;
_origid= origid;
_destid= destid;
if (origid || destid)
{
Field *source= _graph->_source;
Field *target= _graph->_target;
uint source_fieldpos= _graph->_source->offset(table.record[0]);
uint target_fieldpos= _graph->_target->offset(table.record[0]);
if (!destid)
{
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)
continue;
if (table.file->ha_index_init(i, 1))
continue;
restore_record(&table, s->default_values);
bitmap_set_bit(table.write_set, source->field_index);
source->store(*_origid, 1);
bitmap_clear_bit(table.write_set, source->field_index);
uchar* buff= (uchar*) my_alloca(source->pack_length());
source->get_key_image(buff, source->pack_length(), Field::itRAW);
_key.clear();
_key.append((char*) buff, source->pack_length());
_key.resize(key_info->key_length, '\0');
my_afree(buff);
_parts= 1;
_index= i;
break;
}
}
else if (!origid)
{
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)
continue;
if (table.file->ha_index_init(i, 1))
continue;
restore_record(&table, s->default_values);
bitmap_set_bit(table.write_set, target->field_index);
target->store(*_destid, 1);
bitmap_clear_bit(table.write_set, target->field_index);
uchar* buff= (uchar*) my_alloca(target->pack_length());
target->get_key_image(buff, target->pack_length(), Field::itRAW);
_key.clear();
_key.append((char*) buff, target->pack_length());
_key.resize(key_info->key_length, '\0');
my_afree(buff);
_parts= 1;
_index= i;
break;
}
}
else
{
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 == target_fieldpos &&
key_info->key_part[1].offset == source_fieldpos) ||
(key_info->key_part[1].offset == target_fieldpos &&
key_info->key_part[0].offset == source_fieldpos)))
continue;
if (table.file->ha_index_init(i, 1))
continue;
restore_record(&table, s->default_values);
bitmap_set_bit(table.write_set, source->field_index);
source->store(*_origid, 1);
bitmap_clear_bit(table.write_set, source->field_index);
bitmap_set_bit(table.write_set, target->field_index);
target->store(*_destid, 1);
bitmap_clear_bit(table.write_set, target->field_index);
Field* first=
key_info->key_part[0].offset == source_fieldpos ?
source : target;
Field* second=
key_info->key_part[0].offset == target_fieldpos ?
target : source;
uchar* buff= (uchar*) my_alloca(
source->pack_length() + target->pack_length());
first->get_key_image(buff, first->pack_length(), Field::itRAW);
second->get_key_image(buff + first->pack_length(), second->pack_length(), Field::itRAW);
_key.clear();
_key.append((char*) buff, source->pack_length() + target->pack_length());
_key.resize(key_info->key_length, '\0');
my_afree(buff);
_parts= 2;
_index= i;
break;
}
}
if (_index < 0)
{
// no suitable index found
return clear_position(ENXIO);
}
if (int rc= table.file->ha_index_read_map(
table.record[0], (uchar*) _key.data(),
(key_part_map) ((1U << _parts) - 1),
table.s->key_info[_index].key_parts == _parts ?
HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT))
{
table.file->ha_index_end();
return clear_position(rc);
}
update_virtual_fields(table.in_use, &table);
if ((_origid && vertex_id(_graph->_source->val_int()) != *_origid) ||
(_destid && vertex_id(_graph->_target->val_int()) != *_destid))
{
table.file->ha_index_end();
return clear_position(ENOENT);
}
}
else
{
if (int rc= table.file->ha_rnd_init(true))
return clear_position(rc);
if (int rc= table.file->ha_rnd_next(table.record[0]))
{
table.file->ha_rnd_end();
return clear_position(rc);
}
}
_graph->_cursor= this;
_graph->_stale= true;
return 0;
}
bool oqgraph3::cursor::operator==(const cursor& x) const
{
return record_position() == x._position;
}
bool oqgraph3::cursor::operator!=(const cursor& x) const
{
return record_position() != x._position;
}
oqgraph3::graph::graph(
::TABLE* table,
::Field* source,
::Field* target,
::Field* weight)
: _ref_count(0)
, _cursor(0)
, _stale(false)
, _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::edges_size_type oqgraph3::graph::num_edges() const
{
return _table->file->stats.records;
}
/* 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 cursor;
typedef boost::intrusive_ptr<graph> graph_ptr;
struct cursor_ptr : public boost::intrusive_ptr<cursor>
{
cursor_ptr() : boost::intrusive_ptr<cursor>() { }
cursor_ptr(cursor* pos) : boost::intrusive_ptr<cursor>(pos) { }
operator const std::string&() const;
bool operator==(const cursor_ptr&) const;
bool operator!=(const cursor_ptr&) const;
};
struct edge_info
{
cursor_ptr _cursor;
edge_info() : _cursor(0) { }
explicit edge_info(const cursor_ptr& pos) : _cursor(pos) { }
edge_info& operator=(const cursor_ptr& pos) { _cursor= pos; return *this; }
vertex_id origid() const;
vertex_id destid() const;
weight_t weight() const;
bool operator==(const edge_info&) const;
bool operator!=(const edge_info&) const;
};
struct cursor
{
mutable int _ref_count;
graph_ptr _graph;
int _index;
unsigned _parts;
std::string _key;
std::string _position;
int _debugid;
boost::optional<vertex_id> _origid;
boost::optional<vertex_id> _destid;
cursor(const graph_ptr& graph);
cursor(const cursor& src);
~cursor();
operator bool() const
{ return !_position.empty(); }
operator edge_info() const
{ return edge_info(const_cast<cursor*>(this)); }
vertex_id get_origid();
vertex_id get_destid();
weight_t get_weight();
int seek_to(
boost::optional<vertex_id> origid,
boost::optional<vertex_id> destid);
int seek_next();
int seek_prev();
void save_position();
int restore_position();
const std::string& record_position() const;
void clear_position();
int clear_position(int rc) { clear_position(); return rc; }
bool operator==(const cursor& x) const;
bool operator!=(const cursor& x) const;
friend void intrusive_ptr_add_ref(cursor* ptr)
{ ++ptr->_ref_count; }
friend void intrusive_ptr_release(cursor* ptr)
{ if (!--(ptr->_ref_count)) delete ptr; }
};
struct graph
{
mutable int _ref_count;
cursor* _cursor;
bool _stale;
cursor_ptr _rnd_cursor;
size_t _rnd_pos;
::TABLE* _table;
::Field* _source;
::Field* _target;
::Field* _weight;
graph(
::TABLE* table,
::Field* source,
::Field* target,
::Field* weight= 0);
~graph();
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--; }
};
}
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