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 @@
#ifndef oq_graphcore_graph_h_
#define oq_graphcore_graph_h_
typedef adjacency_list
<
vecS,
vecS,
bidirectionalS,
VertexInfo,
EdgeInfo
> Graph;
#define GRAPH_WEIGHTMAP(G) get(&EdgeInfo::weight, G)
typedef property_map<Graph, EdgeWeight EdgeInfo::*>::type weightmap_type;
#define GRAPH_INDEXMAP(G) get(vertex_index, G)
typedef property_map<Graph, vertex_index_t>::type indexmap_type;
#define GRAPH_IDMAP(G) get(&VertexInfo::id, G)
typedef property_map<Graph, VertexID VertexInfo::*>::type idmap_type;
#include "oqgraph_shim.h"
#include <boost/graph/two_bit_color_map.hpp>
namespace boost
{
typedef oqgraph3::graph Graph;
template<typename IndexMap = identity_property_map>
struct two_bit_judy_map
{
typedef typename property_traits<IndexMap>::key_type key_type;
typedef two_bit_color_type value_type;
typedef void reference;
typedef read_write_property_map_tag category;
open_query::judy_bitset msb;
open_query::judy_bitset lsb;
IndexMap index;
two_bit_judy_map(const IndexMap& i)
: index(i)
{ }
friend two_bit_color_type get(
const two_bit_judy_map<IndexMap>& pm,
typename property_traits<IndexMap>::key_type key)
{
typename property_traits<IndexMap>::value_type i = get(pm.index, key);
return two_bit_color_type((2*int(pm.msb.test(i))) | int(pm.lsb.test(i)));
}
friend void put(
two_bit_judy_map<IndexMap>& pm,
typename property_traits<IndexMap>::key_type key,
two_bit_color_type value)
{
typename property_traits<IndexMap>::value_type i = get(pm.index, key);
pm.msb.set(i, value & 2);
pm.lsb.set(i, value & 1);
}
};
template<typename IndexMap>
inline two_bit_judy_map<IndexMap>
make_two_bit_judy_map(const IndexMap& index)
{
return two_bit_judy_map<IndexMap>(index);
}
template <typename Type>
struct default_lazy_initializer
{
template <typename Key>
Type operator()(const Key&) const { return Type(); }
};
template <typename Type>
struct copy_initializer
{
copy_initializer(const Type& value) : _(value) { }
template <typename Key>
const Type& operator()(const Key&) const { return _; }
const Type& _;
};
template <typename Type>
copy_initializer<Type> make_copy_initializer(const Type& value)
{ return copy_initializer<Type>(value); }
template <typename Type>
struct value_initializer
{
value_initializer(const Type& value) : _(value) { }
template <typename Key>
const Type& operator()(const Key&) const { return _; }
const Type _;
};
template <typename Type>
value_initializer<Type> make_value_initializer(const Type& value)
{ return value_initializer<Type>(value); }
template <typename Key>
struct identity_initializer
{
const Key& operator()(const Key& _) const { return _; }
};
template <class Container, class Generator>
struct lazy_property_map
{
typedef lazy_property_map<Container, Generator> self;
typedef typename Container::key_type key_type;
typedef typename Container::value_type::second_type value_type;
typedef value_type& reference;
typedef lvalue_property_map_tag category;
lazy_property_map(Container& m, Generator g= Generator())
: _m(m)
, _g(g)
{ }
reference operator[](const key_type& k) const
{
typename Container::iterator found= _m.find(k);
if (_m.end() == found)
{
found= _m.insert(std::make_pair(k, _g(k))).first;
}
return found->second;
}
void set(const key_type& k, const value_type& v)
{ _m[k] = v; }
friend reference get(const self& s, const key_type& k)
{
return s[k];
}
friend void put(self& s, const key_type& k, const value_type& v)
{ s.set(k, v); }
Container& _m;
Generator _g;
};
template <class Container, class Generator>
lazy_property_map<Container, Generator>
make_lazy_property_map(Container& c, Generator g)
{ return lazy_property_map<Container, Generator>(c, g); }
}
#endif
......@@ -26,6 +26,7 @@
#ifndef oq_graphcore_types_h_
#define oq_graphcore_types_h_
namespace open_query
{
......@@ -33,4 +34,9 @@ namespace open_query
typedef double EdgeWeight;
}
class Field;
typedef struct st_table TABLE;
#endif
......@@ -30,14 +30,13 @@
#include <boost/config.hpp>
#include "graphcore-graph.h"
#include <set>
#include <stack>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_archetypes.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/iteration_macros.hpp>
......@@ -46,6 +45,8 @@
#include "graphcore.h"
#include <boost/unordered_map.hpp>
using namespace open_query;
using namespace boost;
......@@ -53,46 +54,6 @@ static const row empty_row = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
namespace open_query
{
enum vertex_id_t { vertex_id };
struct VertexInfo {
inline VertexInfo() { }
inline VertexInfo(VertexID _id)
: id(_id) { }
VertexID id;
};
struct EdgeInfo {
EdgeWeight weight;
};
}
namespace boost
{
BOOST_INSTALL_PROPERTY(vertex, id);
namespace graph
{
template<>
struct internal_vertex_name<VertexInfo>
{
typedef multi_index::member<VertexInfo, VertexID, &VertexInfo::id> type;
};
template<>
struct internal_vertex_constructor<VertexInfo>
{
typedef vertex_from_name<VertexInfo> type;
};
}
}
namespace open_query
{
#include "graphcore-graph.h"
typedef graph_traits<Graph>::vertex_descriptor Vertex;
typedef graph_traits<Graph>::edge_descriptor Edge;
......@@ -261,18 +222,15 @@ namespace open_query {
public:
Graph g;
weightmap_type weightmap;
idmap_type idmap;
indexmap_type indexmap;
optional<Vertex> find_vertex(VertexID id) const;
optional<Edge> find_edge(Vertex, Vertex) const;
inline oqgraph_share() throw()
: g(),
weightmap(GRAPH_WEIGHTMAP(g)),
idmap(GRAPH_IDMAP(g)),
indexmap(GRAPH_INDEXMAP(g))
inline oqgraph_share(
TABLE* table,
Field* origid,
Field* destid,
Field* weight) throw()
: g(table, origid, destid, weight)
{ }
inline ~oqgraph_share()
{ }
......@@ -360,13 +318,13 @@ namespace open_query {
}
};
template <typename P, typename D>
struct GRAPHCORE_INTERNAL oqgraph_visit_dist
: public base_visitor<oqgraph_visit_dist>
: public base_visitor< oqgraph_visit_dist<P,D> >
{
typedef on_finish_vertex event_filter;
oqgraph_visit_dist(std::vector<Vertex>::iterator p,
std::vector<EdgeWeight>::iterator d,
oqgraph_visit_dist(const P& p, const D& d,
stack_cursor *cursor)
: seq(0), m_cursor(*cursor), m_p(p), m_d(d)
{ assert(cursor); }
......@@ -374,22 +332,28 @@ namespace open_query {
template<class T, class Graph>
void operator()(T u, Graph &g)
{
m_cursor.results.push(reference(++seq, u, m_d[GRAPH_INDEXMAP(g)[u]]));
m_cursor.results.push(reference(++seq, u, m_d[ u ]));
}
private:
int seq;
stack_cursor &m_cursor;
std::vector<Vertex>::iterator m_p;
std::vector<EdgeWeight>::iterator m_d;
P m_p;
D m_d;
};
template<bool record_weight, typename goal_filter>
template <typename P, typename D>
oqgraph_visit_dist<P,D>
make_oqgraph_visit_dist(const P& p, const D& d, stack_cursor *cursor)
{ return oqgraph_visit_dist<P,D>(p, d, cursor); }
template<bool record_weight, typename goal_filter, typename P>
struct GRAPHCORE_INTERNAL oqgraph_goal
: public base_visitor<oqgraph_goal<record_weight,goal_filter> >
: public base_visitor<oqgraph_goal<record_weight,goal_filter,P> >
{
typedef goal_filter event_filter;
oqgraph_goal(Vertex goal, std::vector<Vertex>::iterator p,
oqgraph_goal(const Vertex& goal, const P& p,
stack_cursor *cursor)
: m_goal(goal), m_cursor(*cursor), m_p(p)
{ assert(cursor); }
......@@ -400,17 +364,16 @@ namespace open_query {
if (u == m_goal)
{
int seq= 0;
indexmap_type indexmap= GRAPH_INDEXMAP(g);
for (Vertex q, v= u;; v = q, seq++)
if ((q= m_p[ indexmap[v] ]) == v)
if ((q= m_p[ v ]) == v)
break;
for (Vertex v= u;; u= v)
{
optional<Edge> edge;
optional<EdgeWeight> weight;
v= m_p[ indexmap[u] ];
v= m_p[ u ];
if (record_weight && u != v)
{
typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
......@@ -419,7 +382,7 @@ namespace open_query {
if (target(*ei, g) == u)
{
edge= *ei;
weight= GRAPH_WEIGHTMAP(g)[*ei];
weight= get(boost::edge_weight, g, *ei);
break;
}
}
......@@ -437,8 +400,14 @@ namespace open_query {
private:
Vertex m_goal;
stack_cursor &m_cursor;
std::vector<Vertex>::iterator m_p;
P m_p;
};
template<bool record_weight, typename goal_filter, typename P>
oqgraph_goal<record_weight, goal_filter, P>
make_oqgraph_goal(const Vertex& goal, const P& p, stack_cursor *cursor)
{ return oqgraph_goal<record_weight, goal_filter, P>(goal, p, cursor); }
}
namespace open_query
......@@ -468,9 +437,14 @@ namespace open_query
return new (std::nothrow) oqgraph(share);
}
oqgraph_share* oqgraph::create() throw()
oqgraph_share* oqgraph::create(
TABLE* table,
Field* origid,
Field* destid,
Field* weight) throw()
{
return new (std::nothrow) oqgraph_share();
return new (std::nothrow) oqgraph_share(
table, origid, destid, weight);
}
optional<Edge>
......@@ -479,14 +453,14 @@ namespace open_query
if (in_degree(dest, g) >= out_degree(orig, g))
{
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)
return *ei;
}
else
{
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)
return *ei;
}
......@@ -496,9 +470,10 @@ namespace open_query
optional<Vertex>
oqgraph_share::find_vertex(VertexID id) const
{
return boost::graph::find_vertex(id, g);
return ::boost::find_vertex(id, g);
}
#if 0
int oqgraph::delete_all() throw()
{
share->g.clear();
......@@ -598,8 +573,9 @@ namespace open_query
optional<Vertex> orig= source(*edge, share->g),
dest= target(*edge, share->g);
bool orig_neq= orig_id ? share->idmap[*orig] != *orig_id : 0;
bool dest_neq= dest_id ? share->idmap[*dest] != *dest_id : 0;
bool orig_neq= orig_id ? get(boost::vertex_index, share->g, *orig) != *orig_id : 0;
bool dest_neq= dest_id ? get(boost::vertex_index, share->g, *dest) != *dest_id : 0;
if (orig_neq || dest_neq)
{
optional<Edge> new_edge;
......@@ -675,7 +651,6 @@ namespace open_query
return OK;
}
int oqgraph::delete_edge(VertexID orig_id, VertexID dest_id) throw()
{
optional<Vertex> orig, dest;
......@@ -694,6 +669,7 @@ namespace open_query
remove_vertex(*dest, share->g);
return OK;
}
#endif
int oqgraph::search(int *latch, VertexID *orig_id, VertexID *dest_id) throw()
......@@ -731,7 +707,8 @@ namespace open_query
{
Vertex v= target(*ei, share->g);
static_cast<stack_cursor*>(cursor)->
results.push(reference(++seq, v, *ei, share->weightmap[*ei]));
results.push(reference(++seq, v, *ei,
get(boost::edge_weight, share->g, *ei)));
}
}
/* fall through */
......@@ -745,7 +722,8 @@ namespace open_query
{
Vertex v= source(*ei, share->g);
static_cast<stack_cursor*>(cursor)->
results.push(reference(++seq, v, *ei, share->weightmap[*ei]));
results.push(reference(++seq, v, *ei,
get(boost::edge_weight, share->g, *ei)));
}
}
break;
......@@ -757,27 +735,29 @@ namespace open_query
case DIJKSTRAS | HAVE_ORIG | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
{
std::vector<Vertex> p(num_vertices(share->g));
std::vector<EdgeWeight> d(num_vertices(share->g));
oqgraph_goal<true, on_finish_vertex>
vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor));
p[share->indexmap[*orig]]= *orig;
boost::unordered_map<Vertex, Vertex> p;
boost::unordered_map<Vertex, EdgeWeight> d;
p[ *orig ]= *orig;
d[ *orig ] = EdgeWeight();
try
{
dijkstra_shortest_paths(share->g, *orig,
weight_map(
share->weightmap
).
distance_map(
make_iterator_property_map(d.begin(), share->indexmap)
).
predecessor_map(
make_iterator_property_map(p.begin(), share->indexmap)
).
visitor(
make_dijkstra_visitor(vis)
dijkstra_shortest_paths_no_init(share->g, *orig,
make_lazy_property_map(p, identity_initializer<Vertex>()),
make_lazy_property_map(d, value_initializer<EdgeWeight>(
(std::numeric_limits<EdgeWeight>::max)())),
get(edge_weight, share->g),
get(vertex_index, share->g),
std::less<EdgeWeight>(),
closed_plus<EdgeWeight>(),
EdgeWeight(),
make_dijkstra_visitor(
make_oqgraph_goal<true, on_finish_vertex>(
*dest,
boost::make_assoc_property_map(p),
static_cast<stack_cursor*>(cursor)
)
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
}
catch (...)
{ /* printf("found\n"); */ }
......@@ -787,23 +767,25 @@ namespace open_query
case BREADTH_FIRST | HAVE_ORIG | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && orig && dest)
{
std::vector<Vertex> p(num_vertices(share->g));
oqgraph_goal<false, on_discover_vertex>
vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor));
p[share->indexmap[*orig]]= *orig;
boost::unordered_map<Vertex, Vertex> p;
boost::queue<Vertex> Q;
p[ *orig ]= *orig;
try
{
breadth_first_search(share->g, *orig,
visitor(make_bfs_visitor(
breadth_first_visit(share->g, *orig, Q,
make_bfs_visitor(
std::make_pair(
record_predecessors(
make_iterator_property_map(p.begin(), share->indexmap),
boost::make_assoc_property_map(p),
on_tree_edge()
),
vis)
make_oqgraph_goal<false, on_discover_vertex>(
*dest, boost::make_assoc_property_map(p),
static_cast<stack_cursor*>(cursor)
)
)
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
}
catch (...)
{ /* printf("found\n"); */ }
......@@ -814,109 +796,121 @@ namespace open_query
case BREADTH_FIRST | HAVE_ORIG:
if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
{
std::vector<Vertex> p(num_vertices(share->g));
std::vector<EdgeWeight> d(num_vertices(share->g));
oqgraph_visit_dist vis(p.begin(), d.begin(),
static_cast<stack_cursor*>(cursor));
p[share->indexmap[*orig]]= *orig;
boost::unordered_map<Vertex, Vertex> p;
boost::unordered_map<Vertex, EdgeWeight> d;
boost::queue<Vertex> Q;
p[ *orig ]= *orig;
d[ *orig ] = EdgeWeight();
switch (ALGORITHM & op)
{
case DIJKSTRAS:
dijkstra_shortest_paths(share->g, *orig,
weight_map(
share->weightmap
).
distance_map(
make_iterator_property_map(d.begin(), share->indexmap)
).
predecessor_map(
make_iterator_property_map(p.begin(), share->indexmap)
).
visitor(
make_dijkstra_visitor(vis)
dijkstra_shortest_paths_no_init(share->g, *orig,
make_lazy_property_map(p, identity_initializer<Vertex>()),
make_lazy_property_map(d, value_initializer<EdgeWeight>(
(std::numeric_limits<EdgeWeight>::max)())),
get(edge_weight, share->g),
get(vertex_index, share->g),
std::less<EdgeWeight>(),
closed_plus<EdgeWeight>(),
EdgeWeight(),
make_dijkstra_visitor(
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p), d,
static_cast<stack_cursor*>(cursor)
)
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
case BREADTH_FIRST:
breadth_first_search(share->g, *orig,
visitor(make_bfs_visitor(
breadth_first_visit(share->g, *orig, Q,
make_bfs_visitor(
std::make_pair(
record_predecessors(
make_iterator_property_map(p.begin(),
share->indexmap),
boost::make_assoc_property_map(p),
on_tree_edge()
),
std::make_pair(
record_distances(
make_iterator_property_map(d.begin(),
share->indexmap),
boost::make_assoc_property_map(d),
on_tree_edge()
),
vis
))
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p),
boost::make_assoc_property_map(d),
static_cast<stack_cursor*>(cursor)
)
))
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
default:
abort();
}
}
break;
#if 0
case BREADTH_FIRST | HAVE_DEST:
case DIJKSTRAS | HAVE_DEST:
if ((cursor= new (std::nothrow) stack_cursor(share)) && (orig || dest))
{
std::vector<Vertex> p(num_vertices(share->g));
std::vector<EdgeWeight> d(num_vertices(share->g));
oqgraph_visit_dist vis(p.begin(), d.begin(),
static_cast<stack_cursor*>(cursor));
boost::unordered_map<Vertex, Vertex> p;
boost::unordered_map<Vertex, EdgeWeight> d;
boost::queue<Vertex> Q;
reverse_graph<Graph> r(share->g);
p[share->indexmap[*dest]]= *dest;
p[ *dest ]= *dest;
d[ *dest ] = EdgeWeight();
switch (ALGORITHM & op)
{
case DIJKSTRAS:
dijkstra_shortest_paths(r.m_g, *dest,
weight_map(
share->weightmap
).
distance_map(
make_iterator_property_map(d.begin(), share->indexmap)
).
predecessor_map(
make_iterator_property_map(p.begin(), share->indexmap)
).
visitor(
make_dijkstra_visitor(vis)
dijkstra_shortest_paths_no_init(share->g, *dest,
make_lazy_property_map(p, identity_initializer<Vertex>()),
make_lazy_property_map(d, value_initializer<EdgeWeight>(
(std::numeric_limits<EdgeWeight>::max)())),
get(edge_weight, share->g),
get(vertex_index, share->g),
std::less<EdgeWeight>(),
closed_plus<EdgeWeight>(),
EdgeWeight(),
make_dijkstra_visitor(
make_dijkstra_visitor(
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p),
boost::make_assoc_property_map(d),
static_cast<stack_cursor*>(cursor)
)
)
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
case BREADTH_FIRST:
breadth_first_search(r, *dest,
visitor(make_bfs_visitor(
breadth_first_visit(share->g, *dest, Q,
make_bfs_visitor(
std::make_pair(
record_predecessors(
make_iterator_property_map(p.begin(),
share->indexmap),
boost::make_assoc_property_map(p),
on_tree_edge()
),
std::make_pair(
record_distances(
make_iterator_property_map(d.begin(),
share->indexmap),
boost::make_assoc_property_map(d),
on_tree_edge()
),
vis
))
make_oqgraph_visit_dist(
boost::make_assoc_property_map(p),
boost::make_assoc_property_map(d),
static_cast<stack_cursor*>(cursor)
)
))
);
),
make_two_bit_judy_map(get(vertex_index, share->g)));
break;
default:
abort();
}
}
break;
#endif
default:
break;
}
......@@ -1006,7 +1000,7 @@ int stack_cursor::fetch_row(const row &row_info, row &result,
if ((result.seq_indicator= seq= last.sequence()))
result.seq= *seq;
if ((result.link_indicator= v= last.vertex()))
result.link= share->idmap[*v];
result.link= get(boost::vertex_index, share->g, *v);
if ((result.weight_indicator= w= last.weight()))
result.weight= *w;
return oqgraph::OK;
......@@ -1040,7 +1034,7 @@ int vertices_cursor::fetch_row(const row &row_info, row &result,
if (v)
{
result.link_indicator= 1;
result.link= share->idmap[*v];
result.link= get(boost::vertex_index, share->g, *v);
#ifdef DISPLAY_VERTEX_INFO
result.seq_indicator= 1;
if ((result.seq= degree(*v, share->g)))
......@@ -1048,10 +1042,10 @@ int vertices_cursor::fetch_row(const row &row_info, row &result,
EdgeWeight weight= 0;
graph_traits<Graph>::in_edge_iterator iei, iei_end;
for (tie(iei, iei_end)= in_edges(*v, share->g); iei != iei_end; ++iei)
weight+= share->weightmap[*iei];
weight+= get(boost::edge_weight, share->g, *iei);
graph_traits<Graph>::out_edge_iterator oei, oei_end;
for (tie(oei, oei_end)= out_edges(*v, share->g); oei != oei_end; ++oei)
weight+= share->weightmap[*oei];
weight+= get(boost::edge_weight, share->g, *oei);
result.weight_indicator= 1;
result.weight= weight / result.seq;
}
......@@ -1066,9 +1060,8 @@ int edges_cursor::fetch_row(const row &row_info, row &result)
{
edge_iterator it, end;
reference ref;
size_t count= position;
for (boost::tuples::tie(it, end)= edges(share->g); count && it != end; ++it, --count)
;
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))
......@@ -1085,9 +1078,9 @@ int edges_cursor::fetch_row(const row &row_info, row &result,
{
result= row_info;
result.orig_indicator= result.dest_indicator= result.weight_indicator= 1;
result.orig= share->idmap[ source( *edge, share->g ) ];
result.dest= share->idmap[ target( *edge, share->g ) ];
result.weight= share->weightmap[ *edge ];
result.orig= get(boost::vertex_index, share->g, source( *edge, share->g ) );
result.dest= get(boost::vertex_index, share->g, target( *edge, share->g ) );
result.weight= get(boost::edge_weight, share->g, *edge);
return oqgraph::OK;
}
return oqgraph::NO_MORE_DATA;
......
......@@ -104,7 +104,7 @@ namespace open_query
void row_ref(void*) throw();
static oqgraph* create(oqgraph_share*) throw();
static oqgraph_share *create() throw();
static oqgraph_share *create(TABLE*,Field*,Field*,Field*) throw();
static void free(oqgraph*) throw();
static void free(oqgraph_share*) throw();
......
......@@ -29,6 +29,8 @@
#endif
#ifdef HAVE_OQGRAPH
#include <stdarg.h>
#include <stdio.h>
#include <mysql_version.h>
#include "ha_oqgraph.h"
......@@ -46,19 +48,27 @@
using namespace open_query;
struct oqgraph_info_st
struct oqgraph_table_option_struct
{
THR_LOCK lock;
oqgraph_share *graph;
uint use_count;
uint key_stat_version;
uint records;
bool dropped;
char name[FN_REFLEN+1];
char *table_name;
char *origid; // name of the origin id column
char *destid; // name of the target id column
char *weight; // name of the weight column (optional)
};
#define ha_table_option_struct oqgraph_table_option_struct
ha_create_table_option oqgraph_table_option_list[]=
{
HA_TOPTION_STRING("data_table", table_name),
HA_TOPTION_STRING("origid", origid),
HA_TOPTION_STRING("destid", destid),
HA_TOPTION_STRING("weight", weight),
HA_TOPTION_END
};
static const char oqgraph_description[]=
"Open Query Graph Computation Engine, stored in memory "
"Open Query Graph Computation Engine "
"(http://openquery.com/graph)";
#if MYSQL_VERSION_ID < 50100
......@@ -96,27 +106,10 @@ statistic_increment(table->in_use->status_var.X, &LOCK_status)
#define STATISTIC_INCREMENT(X) /* nothing */
#define MOVE(X) move_field_offset(X)
#define RECORDS stats.records
#endif
static HASH oqgraph_open_tables;
static pthread_mutex_t LOCK_oqgraph;
static bool oqgraph_init_done= 0;
#if MYSQL_VERSION_ID >= 50130
#define HASH_KEY_LENGTH size_t
#else
#define HASH_KEY_LENGTH uint
#endif
static uchar* get_key(const uchar *ptr, HASH_KEY_LENGTH *length,
my_bool)
{
const OQGRAPH_INFO *share= (const OQGRAPH_INFO*) ptr;
*length= strlen(share->name);
return (uchar*) share->name;
}
#if MYSQL_VERSION_ID >= 50100
static handler* oqgraph_create_handler(handlerton *hton, TABLE_SHARE *table,
MEM_ROOT *mem_root)
{
......@@ -130,7 +123,6 @@ static bool oqgraph_init()
{
if (have_oqgraph == SHOW_OPTION_DISABLED)
return 1;
#endif
if (pthread_mutex_init(&LOCK_oqgraph, MY_MUTEX_INIT_FAST))
goto error;
if (my_hash_init(&oqgraph_open_tables, &my_charset_bin, 32, 0, 0,
......@@ -144,17 +136,11 @@ static bool oqgraph_init()
hton->db_type= DB_TYPE_AUTOASSIGN;
hton->create= oqgraph_create_handler;
hton->flags= HTON_NO_FLAGS;
#endif
hton->table_options= oqgraph_table_option_list;
oqgraph_init_done= TRUE;
return 0;
error:
#if MYSQL_VERSION_ID < 50100
have_oqgraph= SHOW_OPTION_DISABLED;
#endif
return 1;
}
#if MYSQL_VERSION_ID >= 50100
static int oqgraph_fini(void *)
{
my_hash_free(&oqgraph_open_tables);
......@@ -264,6 +250,9 @@ static int error_code(int res)
KEY (latch, origid, destid) USING HASH,
KEY (latch, destid, origid) USING HASH
) ENGINE=OQGRAPH
READ_TABLE=bar
ORIGID=src_id
DESTID=tgt_id
*/
static int oqgraph_check_table_structure (TABLE *table_arg)
......@@ -331,16 +320,15 @@ static int oqgraph_check_table_structure (TABLE *table_arg)
** OQGRAPH tables
*****************************************************************************/
#if MYSQL_VERSION_ID >= 50100
ha_oqgraph::ha_oqgraph(handlerton *hton, TABLE_SHARE *table_arg)
: handler(hton, table_arg),
#else
ha_oqgraph::ha_oqgraph(TABLE *table_arg)
: handler(&oqgraph_hton, table_arg),
#endif
share(0), graph(0), records_changed(0), key_stat_version(0)
: handler(hton, table_arg)
, graph_share(0)
, graph(0)
, error_message("", 0, &my_charset_latin1)
{ }
ha_oqgraph::~ha_oqgraph()
{ }
static const char *ha_oqgraph_exts[] =
{
......@@ -352,11 +340,7 @@ const char **ha_oqgraph::bas_ext() const
return ha_oqgraph_exts;
}
#if MYSQL_VERSION_ID >= 50100
ulonglong ha_oqgraph::table_flags() const
#else
ulong ha_oqgraph::table_flags() const
#endif
{
return (HA_NO_BLOBS | HA_NULL_IN_KEY |
HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED |
......@@ -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;
}
int ha_oqgraph::open(const char *name, int mode, uint test_if_locked)
bool ha_oqgraph::get_error_message(int error, String* buf)
{
pthread_mutex_lock(&LOCK_oqgraph);
if ((share = get_share(name, table)))
if (error < 0)
{
ref_length= oqgraph::sizeof_ref;
buf->append(error_message);
buf->c_ptr_safe();
error_message.length(0);
}
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);
return false;
}
int ha_oqgraph::close(void)
void ha_oqgraph::print_error(const char* fmt, ...)
{
pthread_mutex_lock(&LOCK_oqgraph);
oqgraph::free(graph); graph= 0;
int res= free_share(share);
pthread_mutex_unlock(&LOCK_oqgraph);
return error_code(res);
va_list ap;
va_start(ap, fmt);
error_message.reserve(256);
size_t len = error_message.length();
len += vsnprintf(&error_message[len], 255, fmt, ap);
error_message.length(len);
va_end(ap);
}
void ha_oqgraph::update_key_stats()
int ha_oqgraph::open(const char *name, int mode, uint test_if_locked)
{
for (uint i= 0; i < table->s->keys; i++)
{
KEY *key=table->key_info+i;
if (!key->rec_per_key)
continue;
if (key->algorithm != HA_KEY_ALG_BTREE)
{
if (key->flags & HA_NOSAME)
key->rec_per_key[key->key_parts-1]= 1;
else
{
unsigned vertices= graph->vertices_count();
unsigned edges= graph->edges_count();
uint no_records= vertices ? 2 * (edges + vertices) / vertices : 2;
if (no_records < 2)
no_records= 2;
key->rec_per_key[key->key_parts-1]= no_records;
}
}
}
records_changed= 0;
/* At the end of update_key_stats() we can proudly claim they are OK. */
key_stat_version= share->key_stat_version;
}
THD* thd = current_thd;
oqgraph_table_option_struct *options=
reinterpret_cast<oqgraph_table_option_struct*>(table->s->option_struct);
error_message.length(0);
int ha_oqgraph::write_row(byte * buf)
{
int res= oqgraph::MISC_FAIL;
Field ** const field= table->field;
STATISTIC_INCREMENT(ha_write_count);
const char* p= strend(name)-1;
while (p > name && *p != '\\' && *p != '/')
--p;
#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];
init_tmp_table_share(
thd, share, table->s->db.str, table->s->db.length,
options->table_name, "");
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);
field[2]->MOVE(ptrdiff);
field[3]->MOVE(ptrdiff);
if (thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE)
{
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();
VertexID dest_id= (VertexID) field[2]->val_int();
EdgeWeight weight= 1;
if (!field[3]->is_null())
weight= (EdgeWeight) field[3]->val_real();
free_table_share(share);
return thd->main_da.sql_errno();
}
mysql_reset_errors(thd, 1);
thd->clear_error();
continue;
}
if (!(res= graph->insert_edge(orig_id, dest_id, weight, replace_dups)))
if (int err= share->error)
{
++records_changed;
share->records++;
}
if (res == oqgraph::DUPLICATE_EDGE && ignore_dups && !insert_dups)
res= oqgraph::OK;
open_table_error(share, share->error, share->open_errno, share->errarg);
free_table_share(share);
return err;
}
if (ptrdiff)
if (share->is_view)
{
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
field[3]->MOVE(-ptrdiff);
open_table_error(share, 1, EMFILE, 0);
free_table_share(share);
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))
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
open_table_error(share, err, EMFILE, 0);
free_table_share(share);
return -1;
}
return error_code(res);
}
edges->reginfo.lock_type= TL_READ;
int ha_oqgraph::update_row(const byte * old, byte * buf)
{
int res= oqgraph::MISC_FAIL;
VertexID orig_id, dest_id;
EdgeWeight weight= 1;
Field **field= table->field;
STATISTIC_INCREMENT(ha_update_count);
edges->tablenr= thd->current_tablenr++;
edges->status= STATUS_NO_RECORD;
edges->file->ha_start_of_new_statement();
edges->file->ft_handler= 0;
edges->pos_in_table_list= 0;
edges->clear_column_bitmaps();
bfill(table->record[0], table->s->null_bytes, 255);
bfill(table->record[1], table->s->null_bytes, 255);
#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];
// We expect fields origid, destid and optionally weight
origid= destid= weight= 0;
if (ptrdiff)
if (!edges->file)
{
field[0]->MOVE(ptrdiff);
field[1]->MOVE(ptrdiff);
field[2]->MOVE(ptrdiff);
field[3]->MOVE(ptrdiff);
print_error("Some error occurred opening table '%s'", options->table_name);
free_table_share(share);
return -1;
}
if (inited == INDEX || inited == RND)
for (Field **field= edges->field; *field; ++field)
{
VertexID *origp= 0, *destp= 0;
EdgeWeight *weightp= 0;
if (!field[1]->is_null())
*(origp= &orig_id)= (VertexID) field[1]->val_int();
if (!field[2]->is_null())
*(destp= &dest_id)= (VertexID) field[2]->val_int();
if (!field[3]->is_null())
*(weightp= &weight)= (EdgeWeight) field[3]->val_real();
my_ptrdiff_t ptrdiff2= old - buf;
field[0]->MOVE(ptrdiff2);
field[1]->MOVE(ptrdiff2);
field[2]->MOVE(ptrdiff2);
field[3]->MOVE(ptrdiff2);
if (field[0]->is_null())
if (strcmp(options->origid, (*field)->field_name))
continue;
if ((*field)->cmp_type() != INT_RESULT ||
!((*field)->flags & NOT_NULL_FLAG))
{
if (!origp == field[1]->is_null() &&
*origp == (VertexID) field[1]->val_int())
origp= 0;
if (!destp == field[2]->is_null() &&
*destp == (VertexID) field[2]->val_int())
origp= 0;
if (!weightp == field[3]->is_null() &&
*weightp == (VertexID) field[3]->val_real())
weightp= 0;
if (!(res= graph->modify_edge(oqgraph::current_row(),
origp, destp, weightp, replace_dups)))
++records_changed;
else if (ignore_dups && res == oqgraph::DUPLICATE_EDGE)
res= oqgraph::OK;
print_error("Column '%s.%s' is not a not-null integer type",
options->table_name, options->origid);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
field[0]->MOVE(-ptrdiff2);
field[1]->MOVE(-ptrdiff2);
field[2]->MOVE(-ptrdiff2);
field[3]->MOVE(-ptrdiff2);
origid = *field;
break;
}
if (ptrdiff)
for (Field **field= edges->field; *field; ++field)
{
field[0]->MOVE(-ptrdiff);
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
field[3]->MOVE(-ptrdiff);
}
#if 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 (strcmp(options->destid, (*field)->field_name))
continue;
if ((*field)->type() != origid->type() ||
!((*field)->flags & NOT_NULL_FLAG))
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
print_error("Column '%s.%s' is not a not-null integer type",
options->table_name, options->destid);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
destid = *field;
break;
}
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;
share->records--;
print_error("Column '%s.%s' is not a not-null real type",
options->table_name, options->weight);
closefrm(edges, 0);
free_table_share(share);
return -1;
}
weight = *field;
break;
}
if (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);
field[1]->MOVE(ptrdiff);
field[2]->MOVE(ptrdiff);
print_error("Data columns missing on table '%s'", options->table_name);
closefrm(edges, 0);
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();
VertexID dest_id= (VertexID) field[2]->val_int();
print_error("Unable to create graph instance.");
closefrm(edges, 0);
free_table_share(share);
return -1;
}
ref_length= oqgraph::sizeof_ref;
graph = oqgraph::create(graph_share);
return 0;
}
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;
share->records--;
}
if (edges->file)
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);
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
KEY *key=table->key_info+i;
if (!key->rec_per_key)
continue;
if (key->algorithm != HA_KEY_ALG_BTREE)
{
if (key->flags & HA_NOSAME)
key->rec_per_key[key->key_parts-1]= 1;
else
{
//unsigned vertices= graph->vertices_count();
//unsigned edges= graph->edges_count();
//uint no_records= vertices ? 2 * (edges + vertices) / vertices : 2;
//if (no_records < 2)
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,
......@@ -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);
key_restore(buf, (byte*) key, key_info, key_len);
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
#endif
my_ptrdiff_t ptrdiff= buf - table->record[0];
if (ptrdiff)
......@@ -713,9 +668,7 @@ int ha_oqgraph::index_read_idx(byte * buf, uint index, const byte * key,
field[1]->MOVE(-ptrdiff);
field[2]->MOVE(-ptrdiff);
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->read_set, old_map);
#endif
res= graph->search(latchp, orig_idp, dest_idp);
......@@ -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);
#if MYSQL_VERSION_ID >= 50100
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set);
#endif
my_ptrdiff_t ptrdiff= record - table->record[0];
if (ptrdiff)
......@@ -792,15 +743,14 @@ int ha_oqgraph::fill_record(byte *record, const open_query::row &row)
field[4]->MOVE(-ptrdiff);
field[5]->MOVE(-ptrdiff);
}
#if MYSQL_VERSION_ID >= 50100
dbug_tmp_restore_column_map(table->write_set, old_map);
#endif
return 0;
}
int ha_oqgraph::rnd_init(bool scan)
{
edges->prepare_for_position();
return error_code(graph->random(scan));
}
......@@ -838,75 +788,31 @@ int ha_oqgraph::cmp_ref(const byte *ref1, const byte *ref2)
int ha_oqgraph::info(uint flag)
{
RECORDS= graph->vertices_count() + graph->edges_count();
#if 0
records= hp_info.records;
deleted= hp_info.deleted;
errkey= hp_info.errkey;
mean_rec_length= hp_info.reclength;
data_file_length= hp_info.data_length;
index_file_length= hp_info.index_length;
max_data_file_length= hp_info.max_records* hp_info.reclength;
delete_length= hp_info.deleted * hp_info.reclength;
#endif
RECORDS= graph->edges_count();
/*
If info() is called for the first time after open(), we will still
have to update the key statistics. Hoping that a table lock is now
in place.
*/
if (key_stat_version != share->key_stat_version)
update_key_stats();
// if (key_stat_version != share->key_stat_version)
// update_key_stats();
return 0;
}
int ha_oqgraph::extra(enum ha_extra_function operation)
{
switch (operation)
{
case HA_EXTRA_IGNORE_DUP_KEY:
ignore_dups= true;
break;
case HA_EXTRA_NO_IGNORE_DUP_KEY:
ignore_dups= false;
insert_dups= false;
break;
case HA_EXTRA_WRITE_CAN_REPLACE:
replace_dups= true;
break;
case HA_EXTRA_WRITE_CANNOT_REPLACE:
replace_dups= false;
break;
case HA_EXTRA_INSERT_WITH_UPDATE:
insert_dups= true;
break;
default:
break;
}
return 0;
return edges->file->extra(operation);
}
int ha_oqgraph::delete_all_rows()
{
int res;
if (!(res= graph->delete_all()))
{
share->records= 0;
}
if (!res && table->s->tmp_table == NO_TMP_TABLE)
{
/*
We can perform this safely since only one writer at the time is
allowed on the table.
*/
share->key_stat_version++;
}
return error_code(res);
return ER_OPEN_AS_READONLY;
}
int ha_oqgraph::external_lock(THD *thd, int lock_type)
{
return 0; // No external locking
return edges->file->ha_external_lock(thd, lock_type);
}
......@@ -914,10 +820,7 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
lock.type=lock_type;
*to++= &lock;
return to;
return edges->file->store_lock(thd, to, lock_type);
}
/*
......@@ -925,20 +828,12 @@ THR_LOCK_DATA **ha_oqgraph::store_lock(THD *thd,
not when doing a CREATE on the table.
*/
int ha_oqgraph::delete_table(const char *name)
int ha_oqgraph::delete_table(const char *)
{
int res= 0;
OQGRAPH_INFO *share;
pthread_mutex_lock(&LOCK_oqgraph);
if ((share= get_share(name)))
{
res= free_share(share, true);
}
pthread_mutex_unlock(&LOCK_oqgraph);
return error_code(res);
return 0;
}
int ha_oqgraph::rename_table(const char * from, const char * to)
int ha_oqgraph::rename_table(const char *, const char *)
{
pthread_mutex_lock(&LOCK_oqgraph);
if (OQGRAPH_INFO *share= get_share(from))
......@@ -980,8 +875,9 @@ ha_rows ha_oqgraph::records_in_range(uint inx, key_range *min_key,
return RECORDS;
/* Assert that info() did run. We need current statistics here. */
DBUG_ASSERT(key_stat_version == share->key_stat_version);
ha_rows result= key->rec_per_key[key->key_parts-1];
//DBUG_ASSERT(key_stat_version == share->key_stat_version);
//ha_rows result= key->rec_per_key[key->key_parts-1];
ha_rows result= 10;
return result;
}
......@@ -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,
HA_CREATE_INFO *create_info)
{
int res = -1;
OQGRAPH_INFO *share;
oqgraph_table_option_struct *options=
reinterpret_cast<oqgraph_table_option_struct*>(table->s->option_struct);
pthread_mutex_lock(&LOCK_oqgraph);
if ((share= get_share(name)))
{
free_share(share);
}
else
{
if (!oqgraph_check_table_structure(table_arg))
res= 0;;
}
pthread_mutex_unlock(&LOCK_oqgraph);
if (this->share)
info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
if (int res = oqgraph_check_table_structure(table_arg))
return error_code(res);
(void)(options);
return 0;
}
......@@ -1018,7 +904,6 @@ void ha_oqgraph::update_create_info(HA_CREATE_INFO *create_info)
// create_info->auto_increment_value= auto_increment_value;
}
#if MYSQL_VERSION_ID >= 50100
struct st_mysql_storage_engine oqgraph_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION };
......@@ -1032,7 +917,7 @@ maria_declare_plugin(oqgraph)
PLUGIN_LICENSE_GPL,
(int (*)(void*)) oqgraph_init, /* Plugin Init */
oqgraph_fini, /* Plugin Deinit */
0x0200, /* Version: 2.0 */
0x0300, /* Version: 3s.0 */
NULL, /* status variables */
NULL, /* system variables */
"2.0",
......
......@@ -37,19 +37,21 @@ namespace open_query
{
struct row;
class oqgraph;
class oqgraph_share;
}
/* class for the the Open Query Graph handler */
class ha_oqgraph: public handler
{
OQGRAPH_INFO *share;
TABLE_SHARE share[1];
TABLE edges[1];
Field *origid;
Field *destid;
Field *weight;
open_query::oqgraph_share *graph_share;
open_query::oqgraph *graph;
THR_LOCK_DATA lock;
/* number of records changed since last statistics update */
uint records_changed;
uint key_stat_version;
bool replace_dups, ignore_dups, insert_dups;
int fill_record(byte*, const open_query::row&);
......@@ -61,7 +63,7 @@ class ha_oqgraph: public handler
ha_oqgraph(TABLE *table);
Table_flags table_flags() const;
#endif
~ha_oqgraph() {}
~ha_oqgraph();
const char *table_type() const
{
return "OQGRAPH";
......@@ -107,6 +109,12 @@ class ha_oqgraph: public handler
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
int cmp_ref(const byte *ref1, const byte *ref2);
bool get_error_message(int error, String* buf);
void print_error(const char* fmt, ...);
private:
void update_key_stats();
String error_message;
};
/* Copyright (C) 2009-2011 Arjen G Lentz & Antony T Curtis for Open Query
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* ======================================================================
Open Query Graph Computation Engine, based on a concept by Arjen Lentz
Mk.III implementation by Antony Curtis & Arjen Lentz
For more information, documentation, support, enhancement engineering,
and non-GPL licensing, see http://openquery.com/graph
or contact graph@openquery.com
For packaged binaries, see http://ourdelta.org
======================================================================
*/
#include "oqgraph_judy.h"
#include <Judy.h>
void open_query::judy_bitset::clear()
{
int rc;
J1FA(rc, array);
}
bool open_query::judy_bitset::test(size_type n) const
{
int rc;
J1T(rc, array, n);
return rc == 1;
}
open_query::judy_bitset& open_query::judy_bitset::setbit(size_type n)
{
int rc;
J1S(rc, array, n);
return *this;
}
open_query::judy_bitset& open_query::judy_bitset::reset(size_type n)
{
int rc;
J1U(rc, array, n);
return *this;
}
open_query::judy_bitset& open_query::judy_bitset::flip(size_type n)
{
int rc;
J1U(rc, array, n);
if (!rc)
{
J1S(rc, array, n);
}
return *this;
}
open_query::judy_bitset::size_type open_query::judy_bitset::num_blocks() const
{
Word_t rc;
J1MU(rc, array);
return rc;
}
open_query::judy_bitset::size_type open_query::judy_bitset::size() const
{
int rc;
Word_t index = (Word_t) -1;
J1L(rc, array, index);
if (!rc)
return index;
else
return npos;
}
open_query::judy_bitset::size_type open_query::judy_bitset::count() const
{
Word_t rc;
J1C(rc, array, 0, -1);
return rc;
}
open_query::judy_bitset& 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