summaryrefslogtreecommitdiffstats
path: root/storage/oqgraph/graphcore.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/oqgraph/graphcore.cc')
-rw-r--r--storage/oqgraph/graphcore.cc1216
1 files changed, 1216 insertions, 0 deletions
diff --git a/storage/oqgraph/graphcore.cc b/storage/oqgraph/graphcore.cc
new file mode 100644
index 00000000..92796538
--- /dev/null
+++ b/storage/oqgraph/graphcore.cc
@@ -0,0 +1,1216 @@
+/* Copyright (C) 2007-2013 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/* ======================================================================
+ Open Query Graph Computation Engine, based on a concept by Arjen Lentz
+ v3 implementation by Antony Curtis, Arjen Lentz, Andrew McDonnell
+ For more information, documentation, support, enhancement engineering,
+ see http://openquery.com/graph or contact graph@openquery.com
+ ======================================================================
+*/
+
+#include <string.h>
+#include <cstdlib>
+
+#include "graphcore-graph.h"
+
+#include <set>
+#include <stack>
+
+#include <boost/property_map/property_map.hpp>
+
+#include <boost/graph/breadth_first_search.hpp>
+#include <boost/graph/dijkstra_shortest_paths.hpp>
+#include <boost/graph/iteration_macros.hpp>
+#include <boost/graph/reverse_graph.hpp>
+#include <boost/graph/graph_utility.hpp>
+
+#include "graphcore.h"
+
+#include <boost/unordered_map.hpp>
+#include <boost/version.hpp>
+
+using namespace open_query;
+using namespace boost;
+
+static const row empty_row = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+extern "C" const char* const oqgraph_boost_version= BOOST_LIB_VERSION;
+
+namespace open_query
+{
+
+ typedef graph_traits<Graph>::vertex_descriptor Vertex;
+ typedef graph_traits<Graph>::edge_descriptor Edge;
+
+ typedef std::list<std::pair<Vertex,optional<EdgeWeight> > > shortest_path_list;
+ typedef shortest_path_list::iterator shortest_path_iterator;
+
+ template<typename ID, typename IDMap>
+ class id_equals_t
+ {
+ public:
+ id_equals_t(ID id, IDMap map)
+ : m_id(id), m_map(map)
+ { }
+ template<typename V>
+ bool operator()(V u) const
+ {
+ return m_map[u] == m_id;
+ }
+ private:
+ ID m_id;
+ IDMap m_map;
+ };
+
+ template<typename ID, typename IDMap>
+ inline id_equals_t<ID,IDMap>
+ id_equals(ID id, IDMap idmap)
+ {
+ return id_equals_t<ID,IDMap>(id, idmap);
+ }
+
+ template<typename T, typename Graph>
+ class target_equals_t
+ {
+ public:
+ target_equals_t(T target, Graph &g)
+ : m_target(target), m_g(g)
+ { }
+ template<typename V>
+ bool operator()(V u) const
+ {
+ return target(u, m_g) == m_target;
+ }
+ private:
+ T m_target;
+ Graph &m_g;
+ };
+
+ template<typename T, typename Graph>
+ inline target_equals_t<T,Graph>
+ target_equals(T target, Graph &g)
+ {
+ return target_equals_t<T,Graph>(target, g);
+ }
+
+ template<typename T, typename Graph>
+ class source_equals_t
+ {
+ public:
+ source_equals_t(T source, Graph &g)
+ : m_source(source), m_g(g)
+ { }
+ template<typename V>
+ bool operator()(V u) const
+ {
+ return source(u, m_g) == m_source;
+ }
+ private:
+ T m_source;
+ Graph &m_g;
+ };
+
+ template<typename T, typename Graph>
+ inline source_equals_t<T,Graph>
+ source_equals(T source, Graph &g)
+ {
+ return source_equals_t<T,Graph>(source, g);
+ }
+
+ struct reference
+ {
+ int m_flags;
+ int m_sequence;
+ Vertex m_vertex;
+ Edge m_edge;
+ EdgeWeight m_weight;
+
+ enum
+ {
+ HAVE_SEQUENCE = 1,
+ HAVE_WEIGHT = 2,
+ HAVE_EDGE = 4,
+ };
+
+ inline reference()
+ : m_flags(0), m_sequence(0),
+ m_vertex(graph_traits<Graph>::null_vertex()),
+ m_edge(), m_weight(0)
+ { }
+
+ inline reference(int s, Edge e)
+ : m_flags(HAVE_SEQUENCE | HAVE_EDGE), m_sequence(s),
+ m_vertex(graph_traits<Graph>::null_vertex()),
+ m_edge(e), m_weight(0)
+ { }
+
+ inline reference(int s, Vertex v, const optional<Edge> &e,
+ const optional<EdgeWeight> &w)
+ : m_flags(HAVE_SEQUENCE | (w ? HAVE_WEIGHT : 0) | (e ? HAVE_EDGE : 0)),
+ m_sequence(s), m_vertex(v),
+ m_edge(), m_weight(0)
+ {
+ if (w) m_weight= *w;
+ if (e) m_edge= *e;
+ }
+
+ inline reference(int s, Vertex v, Edge e, EdgeWeight w)
+ : m_flags(HAVE_SEQUENCE | HAVE_WEIGHT | HAVE_EDGE),
+ m_sequence(s), m_vertex(v), m_edge(e), m_weight(w)
+ { }
+
+ inline reference(int s, Vertex v, EdgeWeight w)
+ : m_flags(HAVE_SEQUENCE | HAVE_WEIGHT),
+ m_sequence(s), m_vertex(v), m_edge(), m_weight(w)
+ { }
+
+ inline reference(int s, Vertex v)
+ : m_flags(HAVE_SEQUENCE), m_sequence(s), m_vertex(v), m_edge(),
+ m_weight(0)
+ { }
+
+ optional<int> sequence() const
+ {
+ if (m_flags & HAVE_SEQUENCE)
+ {
+ return m_sequence;
+ }
+ return optional<int>();
+ }
+
+ optional<Vertex> vertex() const
+ {
+ if (m_vertex != graph_traits<Graph>::null_vertex())
+ return m_vertex;
+ return optional<Vertex>();
+ }
+
+ optional<Edge> edge() const
+ {
+ if (m_flags & HAVE_EDGE)
+ return m_edge;
+ return optional<Edge>();
+ };
+
+ optional<EdgeWeight> weight() const
+ {
+ if (m_flags & HAVE_WEIGHT)
+ return m_weight;
+ return optional<EdgeWeight>();
+ }
+ };
+}
+
+namespace open_query {
+ class GRAPHCORE_INTERNAL oqgraph_share
+ {
+ public:
+ Graph g;
+
+ optional<Vertex> find_vertex(VertexID id) const;
+ optional<Edge> find_edge(Vertex, Vertex) const;
+
+ inline oqgraph_share(
+ TABLE* table,
+ Field* origid,
+ Field* destid,
+ Field* weight) throw()
+ : g(table, origid, destid, weight)
+ { }
+ inline ~oqgraph_share()
+ { }
+ };
+
+ class GRAPHCORE_INTERNAL oqgraph_cursor
+ {
+ public:
+ oqgraph_share *const share;
+
+ inline oqgraph_cursor(oqgraph_share *arg)
+ : share(arg)
+ { }
+ virtual ~oqgraph_cursor()
+ { }
+
+ virtual int fetch_row(const row &, row&) = 0;
+ virtual int fetch_row(const row &, row&, const reference&) = 0;
+ virtual void current(reference& ref) const = 0;
+ };
+}
+
+namespace open_query {
+ class GRAPHCORE_INTERNAL stack_cursor : public oqgraph_cursor
+ {
+ private:
+ optional<EdgeWeight> no_weight;
+ public:
+ int sequence;
+ std::stack<reference> results;
+ reference last;
+
+ inline stack_cursor(oqgraph_share *arg)
+ : oqgraph_cursor(arg), no_weight(), sequence(0), results(), last()
+ { }
+
+ int fetch_row(const row &, row&);
+ int fetch_row(const row &, row&, const reference&);
+
+ void current(reference& ref) const
+ {
+ ref= last;
+ }
+ };
+
+ class GRAPHCORE_INTERNAL vertices_cursor : public oqgraph_cursor
+ {
+ typedef graph_traits<Graph>::vertex_iterator vertex_iterator;
+
+ size_t position;
+ reference last;
+ public:
+ inline vertices_cursor(oqgraph_share *arg)
+ : oqgraph_cursor(arg), position(0)
+ { }
+
+ int fetch_row(const row &, row&);
+ int fetch_row(const row &, row&, const reference&);
+
+ void current(reference& ref) const
+ {
+ ref= last;
+ }
+
+ };
+
+ class GRAPHCORE_INTERNAL edges_cursor : public oqgraph_cursor
+ {
+ typedef graph_traits<Graph>::edge_iterator edge_iterator;
+ typedef edge_iterator::difference_type edge_difference;
+
+ edge_difference position;
+ reference last;
+ public:
+ inline edges_cursor(oqgraph_share *arg)
+ : oqgraph_cursor(arg), position(0), last()
+ { }
+
+ int fetch_row(const row &, row&);
+ int fetch_row(const row &, row&, const reference&);
+
+ void current(reference& ref) const
+ {
+ ref= last;
+ }
+ };
+
+ template <typename P, typename D>
+ struct GRAPHCORE_INTERNAL oqgraph_visit_leaves
+ : public base_visitor< oqgraph_visit_leaves<P,D> >
+ {
+ typedef on_finish_vertex event_filter;
+
+ oqgraph_visit_leaves(const P& p, const D& d,
+ stack_cursor *cursor)
+ : seq(0), m_cursor(*cursor), m_p(p), m_d(d)
+ { assert(cursor); }
+
+ template<class T, class Graph>
+ void operator()(T u, Graph &g)
+ {
+ typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
+ boost::tuples::tie(ei, ei_end) = out_edges(u, g);
+ if (ei == ei_end)
+ {
+ m_cursor.results.push(reference(++seq, u, m_d[ u ]));
+ }
+ }
+ private:
+ int seq;
+ stack_cursor &m_cursor;
+ P m_p;
+ D m_d;
+ };
+
+ template <typename P, typename D>
+ oqgraph_visit_leaves<P,D>
+ make_oqgraph_visit_leaves(const P& p, const D& d, stack_cursor *cursor)
+ { return oqgraph_visit_leaves<P,D>(p, d, cursor); }
+
+
+ template <typename P, typename D>
+ struct GRAPHCORE_INTERNAL oqgraph_visit_dist
+ : public base_visitor< oqgraph_visit_dist<P,D> >
+ {
+ typedef on_finish_vertex event_filter;
+
+ oqgraph_visit_dist(const P& p, const D& d,
+ stack_cursor *cursor)
+ : seq(0), m_cursor(*cursor), m_p(p), m_d(d)
+ { assert(cursor); }
+
+ template<class T, class Graph>
+ void operator()(T u, Graph &g)
+ {
+ m_cursor.results.push(reference(++seq, u, m_d[ u ]));
+ }
+ private:
+ int seq;
+ stack_cursor &m_cursor;
+ P m_p;
+ D m_d;
+ };
+
+ template <typename P, typename D>
+ oqgraph_visit_dist<P,D>
+ make_oqgraph_visit_dist(const P& p, const D& d, stack_cursor *cursor)
+ { return oqgraph_visit_dist<P,D>(p, d, cursor); }
+
+
+ template<bool record_weight, typename goal_filter, typename P>
+ struct GRAPHCORE_INTERNAL oqgraph_goal
+ : public base_visitor<oqgraph_goal<record_weight,goal_filter,P> >
+ {
+ typedef goal_filter event_filter;
+
+ oqgraph_goal(const Vertex& goal, const P& p,
+ stack_cursor *cursor)
+ : m_goal(goal), m_cursor(*cursor), m_p(p)
+ { assert(cursor); }
+
+ template<class T, class Graph>
+ void operator()(T u, Graph &g)
+ {
+ if (u == m_goal)
+ {
+ int seq= 0;
+
+ for (Vertex q, v= u;; v = q, seq++)
+ if ((q= m_p[ v ]) == v)
+ break;
+
+ for (Vertex v= u;; u= v)
+ {
+ optional<Edge> edge;
+ optional<EdgeWeight> weight;
+ v= m_p[ u ];
+ if (record_weight && u != v)
+ {
+ typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
+ for (boost::tuples::tie(ei, ei_end)= out_edges(v, g); ei != ei_end; ++ei)
+ {
+ if (target(*ei, g) == u)
+ {
+ edge= *ei;
+ weight= get(boost::edge_weight, g, *ei);
+ break;
+ }
+ }
+ }
+ else if (u != v)
+ weight= 1;
+ m_cursor.results.push(reference(seq--, u, edge, weight));
+ if (u == v)
+ break;
+ }
+ throw this;
+ }
+ }
+
+ private:
+ Vertex m_goal;
+ stack_cursor &m_cursor;
+ 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
+{
+ inline oqgraph::oqgraph(oqgraph_share *arg) throw()
+ : share(arg), cursor(0), lastRetainedLatch(NULL)
+ { }
+
+ inline oqgraph::~oqgraph() throw()
+ {
+ std::free(lastRetainedLatch);
+ delete cursor;
+ }
+
+ unsigned oqgraph::edges_count() const throw()
+ {
+ return num_edges(share->g);
+ }
+
+ unsigned oqgraph::vertices_count() const throw()
+ {
+ return num_vertices(share->g);
+ }
+
+ THD* oqgraph::get_thd() { return share->g.get_table_thd(); }
+ void oqgraph::set_thd(THD* thd) { share->g.set_table_thd(thd); }
+
+ oqgraph* oqgraph::create(oqgraph_share *share) throw()
+ {
+ assert(share != NULL);
+ return new (std::nothrow) oqgraph(share);
+ }
+
+ oqgraph_share* oqgraph::create(
+ TABLE* table,
+ Field* origid,
+ Field* destid,
+ Field* weight) throw()
+ {
+ return new (std::nothrow) oqgraph_share(
+ table, origid, destid, weight);
+ }
+
+ optional<Edge>
+ oqgraph_share::find_edge(Vertex orig, Vertex dest) const
+ {
+ if (in_degree(dest, g) >= out_degree(orig, g))
+ {
+ graph_traits<Graph>::out_edge_iterator ei, ei_end;
+ tie(ei, ei_end)= out_edges(orig, g);
+ if ((ei= std::find_if(ei, ei_end, target_equals(dest, g))) != ei_end)
+ return *ei;
+ }
+ else
+ {
+ graph_traits<Graph>::in_edge_iterator ei, ei_end;
+ tie(ei, ei_end)= in_edges(dest, g);
+ if ((ei= std::find_if(ei, ei_end, source_equals(orig, g))) != ei_end)
+ return *ei;
+ }
+ return optional<Edge>();
+ }
+
+ optional<Vertex>
+ oqgraph_share::find_vertex(VertexID id) const
+ {
+ return oqgraph3::find_vertex(id, g);
+ }
+
+#if 0
+ int oqgraph::delete_all() throw()
+ {
+ share->g.clear();
+ return 0;
+ }
+
+ int oqgraph::insert_edge(
+ VertexID orig_id, VertexID dest_id, EdgeWeight weight, bool replace) throw()
+ {
+ optional<Vertex> orig, dest;
+ optional<Edge> edge;
+ bool inserted= 0;
+
+ if (weight < 0)
+ return INVALID_WEIGHT;
+ if (!(orig= share->find_vertex(orig_id)))
+ {
+ try
+ {
+ orig= add_vertex(VertexInfo(orig_id), share->g);
+ if (orig == graph_traits<Graph>::null_vertex())
+ return CANNOT_ADD_VERTEX;
+ }
+ catch (...)
+ {
+ return CANNOT_ADD_VERTEX;
+ }
+ }
+ if (!(dest= share->find_vertex(dest_id)))
+ {
+ try
+ {
+ dest= add_vertex(VertexInfo(dest_id), share->g);
+ if (dest == graph_traits<Graph>::null_vertex())
+ return CANNOT_ADD_VERTEX;
+ }
+ catch (...)
+ {
+ return CANNOT_ADD_VERTEX;
+ }
+ }
+ if (!(edge= share->find_edge(*orig, *dest)))
+ {
+ try
+ {
+ tie(edge, inserted)= add_edge(*orig, *dest, share->g);
+ if (!inserted)
+ return CANNOT_ADD_EDGE;
+ }
+ catch (...)
+ {
+ return CANNOT_ADD_EDGE;
+ }
+ }
+ else
+ {
+ if (!replace)
+ return DUPLICATE_EDGE;
+ }
+ share->weightmap[*edge]= weight;
+ return OK;
+ }
+
+ int oqgraph::delete_edge(current_row_st) throw()
+ {
+ reference ref;
+ if (cursor)
+ return EDGE_NOT_FOUND;
+ cursor->current(ref);
+ optional<Edge> edge;
+ if (!(edge= ref.edge()))
+ return EDGE_NOT_FOUND;
+ Vertex orig= source(*edge, share->g);
+ Vertex dest= target(*edge, share->g);
+ remove_edge(*edge, share->g);
+ if (!degree(orig, share->g))
+ remove_vertex(orig, share->g);
+ if (!degree(dest, share->g))
+ remove_vertex(dest, share->g);
+ return OK;
+ }
+
+ int oqgraph::modify_edge(current_row_st,
+ VertexID *orig_id, VertexID *dest_id, EdgeWeight *weight,
+ bool replace) throw()
+ {
+ if (!cursor)
+ return EDGE_NOT_FOUND;
+ reference ref;
+ cursor->current(ref);
+ optional<Edge> edge;
+ if (!(edge= ref.edge()))
+ return EDGE_NOT_FOUND;
+ if (weight && *weight < 0)
+ return INVALID_WEIGHT;
+
+ optional<Vertex> orig= source(*edge, share->g),
+ dest= target(*edge, share->g);
+
+ bool orig_neq= orig_id ? get(boost::vertex_index, share->g, *orig) != *orig_id : 0;
+ bool dest_neq= dest_id ? get(boost::vertex_index, share->g, *dest) != *dest_id : 0;
+
+ if (orig_neq || dest_neq)
+ {
+ optional<Edge> new_edge;
+ if (orig_neq && !(orig= share->find_vertex(*orig_id)))
+ {
+ try
+ {
+ orig= add_vertex(VertexInfo(*orig_id), share->g);
+ if (orig == graph_traits<Graph>::null_vertex())
+ return CANNOT_ADD_VERTEX;
+ }
+ catch (...)
+ {
+ return CANNOT_ADD_VERTEX;
+ }
+ }
+ if (dest_neq && !(dest= share->find_vertex(*dest_id)))
+ {
+ try
+ {
+ dest= add_vertex(VertexInfo(*dest_id), share->g);
+ if (dest == graph_traits<Graph>::null_vertex())
+ return CANNOT_ADD_VERTEX;
+ }
+ catch (...)
+ {
+ return CANNOT_ADD_VERTEX;
+ }
+ }
+ if (!(new_edge= share->find_edge(*orig, *dest)))
+ {
+ try
+ {
+ bool inserted;
+ tie(new_edge, inserted)= add_edge(*orig, *dest, share->g);
+ if (!inserted)
+ return CANNOT_ADD_EDGE;
+ }
+ catch (...)
+ {
+ return CANNOT_ADD_EDGE;
+ }
+ }
+ else
+ {
+ if (!replace)
+ return DUPLICATE_EDGE;
+ }
+ share->weightmap[*new_edge]= share->weightmap[*edge];
+ remove_edge(*edge, share->g);
+ edge= new_edge;
+ }
+ if (weight)
+ share->weightmap[*edge]= *weight;
+ return OK;
+ }
+
+ int oqgraph::modify_edge(
+ VertexID orig_id, VertexID dest_id, EdgeWeight weight) throw()
+ {
+ optional<Vertex> orig, dest;
+ optional<Edge> edge;
+
+ if (weight < 0)
+ return INVALID_WEIGHT;
+ if (!(orig= share->find_vertex(orig_id)))
+ return EDGE_NOT_FOUND;
+ if (!(dest= share->find_vertex(dest_id)))
+ return EDGE_NOT_FOUND;
+ if (!(edge= share->find_edge(*orig, *dest)))
+ return EDGE_NOT_FOUND;
+ share->weightmap[*edge]= weight;
+ return OK;
+ }
+
+ int oqgraph::delete_edge(VertexID orig_id, VertexID dest_id) throw()
+ {
+ optional<Vertex> orig, dest;
+ optional<Edge> edge;
+
+ if (!(orig= share->find_vertex(orig_id)))
+ return EDGE_NOT_FOUND;
+ if (!(dest= share->find_vertex(dest_id)))
+ return EDGE_NOT_FOUND;
+ if (!(edge= share->find_edge(*orig, *dest)))
+ return EDGE_NOT_FOUND;
+ remove_edge(*edge, share->g);
+ if (!degree(*orig, share->g))
+ remove_vertex(*orig, share->g);
+ if (!degree(*dest, share->g))
+ remove_vertex(*dest, share->g);
+ return OK;
+ }
+#endif
+
+ // THIS IS UGLY - refactor later
+ // Update the retained latch string value, for later retrieval by
+ // fetch_row() as a workaround for making sure we return the correct
+ // string to match the latch='' clause
+ // (This is a hack for mariadb mysql compatibility)
+ // IT SHOULD ONLY BE CALLED IMMEIDATELY BEFORE search)(
+ void oqgraph::retainLatchFieldValue(const char *retainedLatch)
+ {
+ // attempting to use std::string broke lots of stuff
+ // Probably more efficient to use mysql String class, FIXME later
+ if (lastRetainedLatch) { std::free(lastRetainedLatch); lastRetainedLatch = NULL; }
+ if (retainedLatch) { lastRetainedLatch = strdup(retainedLatch); }
+ }
+
+ // Because otherwise things can happen and we havent freed a resource since the end of the last query...
+ void oqgraph::release_cursor() throw() {
+ if (share->g._cursor) {
+ // Make sure refs all freed before deleting share->g._cursor
+ share->g._rnd_cursor = 0;
+ delete cursor; cursor = 0;
+ delete share->g._cursor;
+ share->g._cursor = NULL;
+ }
+ row_info= empty_row;
+ }
+
+
+ int oqgraph::search(int *latch, VertexID *orig_id, VertexID *dest_id) throw()
+ {
+ optional<Vertex> orig, dest;
+ int op= 0, seq= 0;
+
+ delete cursor; cursor= 0;
+ row_info= empty_row;
+ if ((row_info.latch_indicator= latch)) {
+ op= ALGORITHM & (row_info.latch= *latch);
+ row_info.latchStringValue = lastRetainedLatch;
+ row_info.latchStringValueLen = strlen(lastRetainedLatch);
+ }
+ if ((row_info.orig_indicator= orig_id) && (op|= HAVE_ORIG))
+ orig= share->find_vertex((row_info.orig= *orig_id));
+ if ((row_info.dest_indicator= dest_id) && (op|= HAVE_DEST))
+ dest= share->find_vertex((row_info.dest= *dest_id));
+ //try
+ //{
+ switch (op)
+ {
+ case NO_SEARCH | HAVE_ORIG | HAVE_DEST:
+ case NO_SEARCH | HAVE_ORIG:
+ if ((cursor= new (std::nothrow) stack_cursor(share)) && orig)
+ {
+ graph_traits<Graph>::out_edge_iterator ei, ei_end;
+ for (boost::tuples::tie(ei, ei_end)= out_edges(*orig, share->g); ei != ei_end; ++ei)
+ {
+ Vertex v= target(*ei, share->g);
+ static_cast<stack_cursor*>(cursor)->
+ results.push(reference(++seq, v, *ei,
+ get(boost::edge_weight, share->g, *ei)));
+ }
+ }
+ /* fall through */
+ case NO_SEARCH | HAVE_DEST:
+ if ((op & HAVE_DEST) &&
+ (cursor || (cursor= new (std::nothrow) stack_cursor(share))) &&
+ dest)
+ {
+ graph_traits<Graph>::in_edge_iterator ei, ei_end;
+ for (boost::tuples::tie(ei, ei_end)= in_edges(*dest, share->g); ei != ei_end; ++ei)
+ {
+ Vertex v= source(*ei, share->g);
+ static_cast<stack_cursor*>(cursor)->
+ results.push(reference(++seq, v, *ei,
+ get(boost::edge_weight, share->g, *ei)));
+ }
+ }
+ break;
+
+ case NO_SEARCH:
+ cursor= new (std::nothrow) vertices_cursor(share);
+ break;
+
+ case DIJKSTRAS | HAVE_ORIG | HAVE_DEST:
+ if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
+ {
+ boost::unordered_map<Vertex, Vertex> p;
+ boost::unordered_map<Vertex, EdgeWeight> d;
+ p[ *orig ]= *orig;
+ d[ *orig ] = EdgeWeight();
+ try
+ {
+ dijkstra_shortest_paths_no_init(share->g, *orig,
+ make_lazy_property_map(p, identity_initializer<Vertex>()),
+ make_lazy_property_map(d, value_initializer<EdgeWeight>(
+ (std::numeric_limits<EdgeWeight>::max)())),
+ get(edge_weight, share->g),
+ get(vertex_index, share->g),
+ std::less<EdgeWeight>(),
+ closed_plus<EdgeWeight>(),
+ EdgeWeight(),
+ make_dijkstra_visitor(
+ make_oqgraph_goal<true, on_finish_vertex>(
+ *dest,
+ boost::make_assoc_property_map(p),
+ static_cast<stack_cursor*>(cursor)
+ )
+ ),
+ make_two_bit_judy_map(get(vertex_index, share->g)));
+ }
+ catch (...)
+ { /* printf("found\n"); */ }
+ }
+ break;
+
+ case BREADTH_FIRST | HAVE_ORIG | HAVE_DEST:
+ if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
+ {
+ boost::unordered_map<Vertex, Vertex> p;
+ boost::queue<Vertex> Q;
+ p[ *orig ]= *orig;
+ try
+ {
+ breadth_first_visit(share->g, *orig, Q,
+ make_bfs_visitor(
+ std::make_pair(
+ record_predecessors(
+ boost::make_assoc_property_map(p),
+ on_tree_edge()
+ ),
+ make_oqgraph_goal<false, on_discover_vertex>(
+ *dest, boost::make_assoc_property_map(p),
+ static_cast<stack_cursor*>(cursor)
+ )
+ )
+ ),
+ make_two_bit_judy_map(get(vertex_index, share->g)));
+ }
+ catch (...)
+ { /* printf("found\n"); */ }
+ }
+ break;
+
+ case DIJKSTRAS | HAVE_ORIG:
+ case BREADTH_FIRST | HAVE_ORIG:
+ case LEAVES | HAVE_ORIG:
+ if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
+ {
+ boost::unordered_map<Vertex, Vertex> p;
+ boost::unordered_map<Vertex, EdgeWeight> d;
+ boost::queue<Vertex> Q;
+ p[ *orig ]= *orig;
+ d[ *orig ] = EdgeWeight();
+ switch (ALGORITHM & op)
+ {
+ case DIJKSTRAS:
+ dijkstra_shortest_paths_no_init(share->g, *orig,
+ make_lazy_property_map(p, identity_initializer<Vertex>()),
+ make_lazy_property_map(d, value_initializer<EdgeWeight>(
+ (std::numeric_limits<EdgeWeight>::max)())),
+ get(edge_weight, share->g),
+ get(vertex_index, share->g),
+ std::less<EdgeWeight>(),
+ closed_plus<EdgeWeight>(),
+ EdgeWeight(),
+ make_dijkstra_visitor(
+ make_oqgraph_visit_dist(
+ boost::make_assoc_property_map(p),
+ boost::make_assoc_property_map(d),
+ static_cast<stack_cursor*>(cursor)
+ )
+ ),
+ make_two_bit_judy_map(get(vertex_index, share->g)));
+ break;
+ case BREADTH_FIRST:
+ breadth_first_visit(share->g, *orig, Q,
+ make_bfs_visitor(
+ std::make_pair(
+ record_predecessors(
+ boost::make_assoc_property_map(p),
+ on_tree_edge()
+ ),
+ std::make_pair(
+ record_distances(
+ boost::make_assoc_property_map(d),
+ on_tree_edge()
+ ),
+ make_oqgraph_visit_dist(
+ boost::make_assoc_property_map(p),
+ boost::make_assoc_property_map(d),
+ static_cast<stack_cursor*>(cursor)
+ )
+ ))
+ ),
+ make_two_bit_judy_map(get(vertex_index, share->g)));
+ break;
+ case LEAVES:
+ breadth_first_visit(share->g, *orig, Q,
+ make_bfs_visitor(
+ std::make_pair(
+ record_predecessors(
+ boost::make_assoc_property_map(p),
+ on_tree_edge()
+ ),
+ std::make_pair(
+ record_distances(
+ boost::make_assoc_property_map(d),
+ on_tree_edge()
+ ),
+ make_oqgraph_visit_leaves(
+ boost::make_assoc_property_map(p),
+ boost::make_assoc_property_map(d),
+ static_cast<stack_cursor*>(cursor)
+ )
+ ))
+ ),
+ make_two_bit_judy_map(get(vertex_index, share->g)));
+ break;
+ default:
+ abort();
+ }
+ }
+ break;
+ case BREADTH_FIRST | HAVE_DEST:
+ case DIJKSTRAS | HAVE_DEST:
+ case LEAVES | HAVE_DEST:
+ if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
+ {
+ boost::unordered_map<Vertex, Vertex> p;
+ boost::unordered_map<Vertex, EdgeWeight> d;
+ boost::queue<Vertex> Q;
+ const reverse_graph<Graph> r(share->g);
+ p[ *dest ]= *dest;
+ d[ *dest ] = EdgeWeight();
+ switch (ALGORITHM & op)
+ {
+ case DIJKSTRAS:
+ dijkstra_shortest_paths_no_init(r, *dest,
+ make_lazy_property_map(p, identity_initializer<Vertex>()),
+ make_lazy_property_map(d, value_initializer<EdgeWeight>(
+ (std::numeric_limits<EdgeWeight>::max)())),
+ get(edge_weight, r),
+ get(vertex_index, r),
+ std::less<EdgeWeight>(),
+ closed_plus<EdgeWeight>(),
+ EdgeWeight(),
+ make_dijkstra_visitor(
+ make_oqgraph_visit_dist(
+ boost::make_assoc_property_map(p),
+ boost::make_assoc_property_map(d),
+ static_cast<stack_cursor*>(cursor)
+ )
+ ),
+ make_two_bit_judy_map(get(vertex_index, r)));
+ break;
+ case BREADTH_FIRST:
+ breadth_first_visit(r, *dest, Q,
+ make_bfs_visitor(
+ std::make_pair(
+ record_predecessors(
+ boost::make_assoc_property_map(p),
+ on_tree_edge()
+ ),
+ std::make_pair(
+ record_distances(
+ boost::make_assoc_property_map(d),
+ on_tree_edge()
+ ),
+ 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, r)));
+ break;
+ case LEAVES:
+ breadth_first_visit(r, *dest, Q,
+ make_bfs_visitor(
+ std::make_pair(
+ record_predecessors(
+ boost::make_assoc_property_map(p),
+ on_tree_edge()
+ ),
+ std::make_pair(
+ record_distances(
+ boost::make_assoc_property_map(d),
+ on_tree_edge()
+ ),
+ make_oqgraph_visit_leaves(
+ 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, r)));
+ break;
+ default:
+ abort();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+ //}
+ //catch (...)
+ //{
+ // return MISC_FAIL;
+ //}
+ }
+
+ int oqgraph::fetch_row(row& result) throw()
+ {
+ if (!cursor)
+ return NO_MORE_DATA;
+ return cursor->fetch_row(row_info, result);
+ }
+
+ int oqgraph::fetch_row(row& result, const void* ref_ptr) throw()
+ {
+ const reference &ref= *(const reference*) ref_ptr;
+ if (!cursor)
+ return NO_MORE_DATA;
+ return cursor->fetch_row(row_info, result, ref);
+ }
+
+ void oqgraph::row_ref(void *ref_ptr) throw()
+ {
+ reference &ref= *(reference*) ref_ptr;
+ if (cursor)
+ cursor->current(ref);
+ else
+ // Beware: internally this eventually causes a swap by intrusive_ptr, so ref must be initialised to sane on all cases
+ ref = reference();
+ }
+
+ void oqgraph::init_row_ref(void *ref_ptr) throw()
+ {
+ // Placement new will cause a constructor to be called avoiding the assignment operator of intrusive_ptr
+ // This doesnt allocate any memory, assumes ref_ptr is the correct size(!)
+ new (ref_ptr) reference();
+ }
+
+ int oqgraph::random(bool scan) throw()
+ {
+ if (scan || !cursor)
+ {
+ delete cursor; cursor= 0;
+ if (!(cursor= new (std::nothrow) edges_cursor(share)))
+ return MISC_FAIL;
+ }
+ row_info= empty_row;
+ return OK;
+ }
+
+ void oqgraph::free(oqgraph *graph) throw()
+ {
+ delete graph;
+ }
+
+ void oqgraph::free(oqgraph_share *graph) throw()
+ {
+ delete graph;
+ }
+
+ const size_t oqgraph::sizeof_ref= sizeof(reference);
+}
+
+int stack_cursor::fetch_row(const row &row_info, row &result)
+{
+ if (!results.empty())
+ {
+ if (int res= fetch_row(row_info, result, results.top()))
+ return res;
+ results.pop();
+ return oqgraph::OK;
+ }
+ else
+ {
+ last= reference();
+ return oqgraph::NO_MORE_DATA;
+ }
+}
+
+int stack_cursor::fetch_row(const row &row_info, row &result,
+ const reference &ref)
+{
+ last= ref;
+ if (last.vertex())
+ {
+ optional<int> seq;
+ optional<EdgeWeight> w;
+ optional<Vertex> v;
+ result= row_info;
+ if ((result.seq_indicator= static_cast<bool>(seq= last.sequence())))
+ result.seq= *seq;
+ if ((result.link_indicator= static_cast<bool>(v= last.vertex())))
+ result.link= get(boost::vertex_index, share->g, *v);
+ if ((result.weight_indicator= static_cast<bool>(w= last.weight())))
+ result.weight= *w;
+ return oqgraph::OK;
+ }
+ else
+ return oqgraph::NO_MORE_DATA;
+}
+
+
+int vertices_cursor::fetch_row(const row &row_info, row &result)
+{
+ vertex_iterator it, end;
+ reference ref;
+ size_t count= position;
+ for (tie(it, end)= vertices(share->g); count && it != end; ++it, --count)
+ ;
+ if (it != end)
+ ref= reference(position+1, *it);
+ if (int res= fetch_row(row_info, result, ref))
+ return res;
+ position++;
+ return oqgraph::OK;
+}
+
+int vertices_cursor::fetch_row(const row &row_info, row &result,
+ const reference &ref)
+{
+ last= ref;
+ optional<Vertex> v= last.vertex();
+ result= row_info;
+ if (v)
+ {
+ result.link_indicator= 1;
+ result.link= get(boost::vertex_index, share->g, *v);
+#ifdef DISPLAY_VERTEX_INFO
+ result.seq_indicator= 1;
+ if ((result.seq= degree(*v, share->g)))
+ {
+ EdgeWeight weight= 0;
+ graph_traits<Graph>::in_edge_iterator iei, iei_end;
+ for (tie(iei, iei_end)= in_edges(*v, share->g); iei != iei_end; ++iei)
+ weight+= get(boost::edge_weight, share->g, *iei);
+ graph_traits<Graph>::out_edge_iterator oei, oei_end;
+ for (tie(oei, oei_end)= out_edges(*v, share->g); oei != oei_end; ++oei)
+ weight+= get(boost::edge_weight, share->g, *oei);
+ result.weight_indicator= 1;
+ result.weight= weight / result.seq;
+ }
+#endif
+ return oqgraph::OK;
+ }
+ else
+ return oqgraph::NO_MORE_DATA;
+}
+
+int edges_cursor::fetch_row(const row &row_info, row &result)
+{
+ edge_iterator it, end;
+ reference ref;
+ tie(it, end)= edges(share->g);
+ it+= position;
+ if (it != end)
+ ref= reference(position+1, *it);
+ if (int res= fetch_row(row_info, result, ref))
+ return res;
+ ++position;
+ return oqgraph::OK;
+}
+
+int edges_cursor::fetch_row(const row &row_info, row &result,
+ const reference &ref)
+{
+ optional<Edge> edge;
+ if ((edge= (last= ref).edge()))
+ {
+ result= row_info;
+ result.orig_indicator= result.dest_indicator= result.weight_indicator= 1;
+
+ oqgraph3::vertex_id orig = get(boost::vertex_index, share->g, source( *edge, share->g ) );
+ oqgraph3::vertex_id dest = get(boost::vertex_index, share->g, target( *edge, share->g ) );
+
+ // bug 796647c - may be symptomatic of a bigger problem with representation
+ // but origid and destid can be -1 indicating no such record, NULL? but oqgraph3::vertex_id
+ // seems to resolve to VertexID (unsigned) in row
+ // in any case we should check for errors (-1) in origid... because all edges have at least one vertex by definition
+ if (orig == (oqgraph3::vertex_id)-1 && dest == (oqgraph3::vertex_id)-1) {
+ // Select * from graph; -- when backing store is empty (bug MDEV-5891)
+ return oqgraph::NO_MORE_DATA;
+ }
+ // assert( ! ((size_t)orig == (size_t)-1 && (size_t)dest == (size_t)-1));
+ // indicates we havent handle a HA_ERR_RECORD_DELETED somewhere
+
+ result.orig= orig;
+ result.dest= dest;
+ result.weight= get(boost::edge_weight, share->g, *edge);
+ return oqgraph::OK;
+ }
+ return oqgraph::NO_MORE_DATA;
+}
+
+namespace boost {
+ GRAPHCORE_INTERNAL void throw_exception(std::exception const&)
+ {
+ abort();
+ }
+}