Merge lp://staging/~posulliv/oqgraph/drizzle-port into lp://staging/oqgraph
- drizzle-port
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp://staging/~posulliv/oqgraph/drizzle-port |
Merge into: | lp://staging/oqgraph |
Diff against target: |
3255 lines 18 files modified
oqgraph/README (+15/-0) oqgraph/graphcore-graph.h (+56/-0) oqgraph/graphcore-types.h (+36/-0) oqgraph/graphcore.cc (+1034/-0) oqgraph/graphcore.h (+116/-0) oqgraph/graphstore.c (+356/-0) oqgraph/graphstore.h (+90/-0) oqgraph/ha_oqgraph.cc (+884/-0) oqgraph/ha_oqgraph.h (+118/-0) oqgraph/open_tables.cc (+91/-0) oqgraph/open_tables.h (+93/-0) oqgraph/oqgraph_config.h.in (+73/-0) oqgraph/oqgraph_probes.d (+19/-0) oqgraph/oqgraph_probes.h (+45/-0) oqgraph/plugin.am (+67/-0) oqgraph/plugin.ini (+9/-0) oqgraph/tests/r/basic.result (+33/-0) oqgraph/tests/t/basic.test (+25/-0) |
To merge this branch: | bzr merge lp://staging/~posulliv/oqgraph/drizzle-port |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Antony T Curtis | pending/comment | Abstain | |
Arjen Lentz | highlevel review | Abstain | |
Review via email: mp+13910@code.staging.launchpad.net |
Commit message
Description of the change
Arjen Lentz (arjen-lentz) wrote : | # |
Hi Padraig
> I wanted to mess around with this engine and since I work mostly with drizzle,
> I ported it to drizzle. I thought I would submit the work I did to port the
> engine in case it is of any use to you guys.
He wow that was quick! Thanks.
Stewart was interested too but I guess you were faster, all the way to delivery ;-)
I'll let Antony decide on the code changes you made.
Dir structure wise, I think we need to do this differently, so we don't have the same file in multiple places (bzr supports symlinks).
Cheers,
Arjen.
Antony T Curtis (atcurtis) wrote : | # |
> Hi Padraig
>
> > I wanted to mess around with this engine and since I work mostly with
> drizzle,
> > I ported it to drizzle. I thought I would submit the work I did to port the
> > engine in case it is of any use to you guys.
>
> He wow that was quick! Thanks.
> Stewart was interested too but I guess you were faster, all the way to
> delivery ;-)
>
> I'll let Antony decide on the code changes you made.
I need to get my Drizzle build back up to date and then I will examine this in detail.
> Dir structure wise, I think we need to do this differently, so we don't have
> the same file in multiple places (bzr supports symlinks).
I am thinking along the lines of having the Drizzle and MySQL build under the same "roof" but have the build selective upon which is present since the graphcore is pretty much independent of the database abstraction. So we'd have "configure --with-mysql=..." and/or "configure --with-drizzle=..."
Regards,
Antony.
Padraig O'Sullivan (posulliv) wrote : | # |
> > I'll let Antony decide on the code changes you made.
>
> I need to get my Drizzle build back up to date and then I will examine this in
> detail.
>
> > Dir structure wise, I think we need to do this differently, so we don't have
> > the same file in multiple places (bzr supports symlinks).
>
> I am thinking along the lines of having the Drizzle and MySQL build under the
> same "roof" but have the build selective upon which is present since the
> graphcore is pretty much independent of the database abstraction. So we'd have
> "configure --with-mysql=..." and/or "configure --with-drizzle=..."
That makes perfect sense. I wanted to do this but I am not really familiar with MySQL internals and it seems that drizzle has diverged quite a bit at the storage engine layer so I wasn't able to keep things in the same place and utilize conditional compilation. As you said it is just the handler (or Cursor in drizzle) that needs to change for the engine.
Anyway, I hope its useful to see the changes that were needed for porting it. If you guys need any help when you get around to doing porting the engine to drizzle, I'd be happy to help if you want.
-Padraig
>
> Regards,
> Antony.
Unmerged revisions
- 12. By Padraig O'Sullivan
-
Updated the create table method to verify that created tables adhere to the
correct structure for the graph engine. - 11. By Padraig O'Sullivan
-
Added test suite from drizzle.
- 10. By Padraig O'Sullivan
-
Added new class for tracking open tables in the engine. Removed the boost
libraries from the engine. We will make it a build time dependency instead.
If boost is not present then the engine will not be built. - 9. By Padraig O'Sullivan
-
Various updates from debugging the engine on drizzle.
- 8. By Padraig O'Sullivan
-
Updated the handler class to conform to drizzle.
- 7. By Padraig O'Sullivan
-
Updated the build files again for drizzle.
- 6. By Padraig O'Sullivan
-
Porting the handler files to work with drizzle.
- 5. By Padraig O'Sullivan
-
Updated build file for drizzle.
- 4. By Padraig O'Sullivan
-
Added some build related files.
- 3. By Padraig O'Sullivan
-
Initial work on porting the OQGRAPH engine to drizzle.
Preview Diff
1 | === added directory 'oqgraph' |
2 | === added file 'oqgraph/README' |
3 | --- oqgraph/README 1970-01-01 00:00:00 +0000 |
4 | +++ oqgraph/README 2009-10-25 07:55:23 +0000 |
5 | @@ -0,0 +1,15 @@ |
6 | +====================================================================== |
7 | +Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
8 | +Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
9 | +Mk.II implementation by Antony Curtis & Arjen Lentz |
10 | + |
11 | +- Open Query original code licensed under GPLv2+ |
12 | +- MySQL 5.0 derived glue code licensed under GPLv2 |
13 | +- Boost library licensed under Boost Software License (GPL compatible) |
14 | + |
15 | +For more information, documentation, support, enhancement engineering, |
16 | +and non-GPL licensing, see http://openquery.com/graph |
17 | +or contact graph@openquery.com |
18 | + |
19 | +For packaged binaries, see http://ourdelta.org |
20 | +====================================================================== |
21 | |
22 | === added file 'oqgraph/graphcore-graph.h' |
23 | --- oqgraph/graphcore-graph.h 1970-01-01 00:00:00 +0000 |
24 | +++ oqgraph/graphcore-graph.h 2009-10-25 07:55:23 +0000 |
25 | @@ -0,0 +1,56 @@ |
26 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
27 | + |
28 | + This program is free software; you can redistribute it and/or modify |
29 | + it under the terms of the GNU General Public License as published by |
30 | + the Free Software Foundation; version 2 of the License, or |
31 | + (at your option) any later version. |
32 | + |
33 | + This program is distributed in the hope that it will be useful, |
34 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
35 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
36 | + GNU General Public License for more details. |
37 | + |
38 | + You should have received a copy of the GNU General Public License |
39 | + along with this program; if not, write to the Free Software |
40 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
41 | + |
42 | +/* ====================================================================== |
43 | + Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
44 | + Mk.II implementation by Antony Curtis & Arjen Lentz |
45 | + For more information, documentation, support, enhancement engineering, |
46 | + and non-GPL licensing, see http://openquery.com/graph |
47 | + or contact graph@openquery.com |
48 | + For packaged binaries, see http://ourdelta.org |
49 | + ====================================================================== |
50 | +*/ |
51 | + |
52 | +#ifndef oq_graphcore_graph_h_ |
53 | +#define oq_graphcore_graph_h_ |
54 | + |
55 | +typedef struct { |
56 | + VertexID id; |
57 | +} VertexInfo; |
58 | + |
59 | +typedef struct { |
60 | + EdgeWeight weight; |
61 | +} EdgeInfo; |
62 | + |
63 | +typedef adjacency_list |
64 | +< |
65 | + vecS, |
66 | + vecS, |
67 | + bidirectionalS, |
68 | + VertexInfo, |
69 | + EdgeInfo |
70 | +> Graph; |
71 | + |
72 | +#define GRAPH_WEIGHTMAP(G) get(&EdgeInfo::weight, G) |
73 | +typedef property_map<Graph, EdgeWeight EdgeInfo::*>::type weightmap_type; |
74 | + |
75 | +#define GRAPH_INDEXMAP(G) get(vertex_index, G) |
76 | +typedef property_map<Graph, vertex_index_t>::type indexmap_type; |
77 | + |
78 | +#define GRAPH_IDMAP(G) get(&VertexInfo::id, G) |
79 | +typedef property_map<Graph, VertexID VertexInfo::*>::type idmap_type; |
80 | + |
81 | +#endif |
82 | |
83 | === added file 'oqgraph/graphcore-types.h' |
84 | --- oqgraph/graphcore-types.h 1970-01-01 00:00:00 +0000 |
85 | +++ oqgraph/graphcore-types.h 2009-10-25 07:55:23 +0000 |
86 | @@ -0,0 +1,36 @@ |
87 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
88 | + |
89 | + This program is free software; you can redistribute it and/or modify |
90 | + it under the terms of the GNU General Public License as published by |
91 | + the Free Software Foundation; version 2 of the License, or |
92 | + (at your option) any later version. |
93 | + |
94 | + This program is distributed in the hope that it will be useful, |
95 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
96 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
97 | + GNU General Public License for more details. |
98 | + |
99 | + You should have received a copy of the GNU General Public License |
100 | + along with this program; if not, write to the Free Software |
101 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
102 | + |
103 | +/* ====================================================================== |
104 | + Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
105 | + Mk.II implementation by Antony Curtis & Arjen Lentz |
106 | + For more information, documentation, support, enhancement engineering, |
107 | + and non-GPL licensing, see http://openquery.com/graph |
108 | + or contact graph@openquery.com |
109 | + For packaged binaries, see http://ourdelta.org |
110 | + ====================================================================== |
111 | +*/ |
112 | + |
113 | +#ifndef oq_graphcore_types_h_ |
114 | +#define oq_graphcore_types_h_ |
115 | +namespace open_query |
116 | +{ |
117 | + |
118 | + typedef unsigned long long VertexID; |
119 | + typedef double EdgeWeight; |
120 | + |
121 | +} |
122 | +#endif |
123 | |
124 | === added file 'oqgraph/graphcore.cc' |
125 | --- oqgraph/graphcore.cc 1970-01-01 00:00:00 +0000 |
126 | +++ oqgraph/graphcore.cc 2009-10-25 07:55:23 +0000 |
127 | @@ -0,0 +1,1034 @@ |
128 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
129 | + |
130 | + This program is free software; you can redistribute it and/or modify |
131 | + it under the terms of the GNU General Public License as published by |
132 | + the Free Software Foundation; version 2 of the License, or |
133 | + (at your option) any later version. |
134 | + |
135 | + This program is distributed in the hope that it will be useful, |
136 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
137 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
138 | + GNU General Public License for more details. |
139 | + |
140 | + You should have received a copy of the GNU General Public License |
141 | + along with this program; if not, write to the Free Software |
142 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
143 | + |
144 | +/* ====================================================================== |
145 | + Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
146 | + Mk.II implementation by Antony Curtis & Arjen Lentz |
147 | + For more information, documentation, support, enhancement engineering, |
148 | + and non-GPL licensing, see http://openquery.com/graph |
149 | + or contact graph@openquery.com |
150 | + For packaged binaries, see http://ourdelta.org |
151 | + ====================================================================== |
152 | +*/ |
153 | + |
154 | +#include <strings.h> |
155 | + |
156 | +#define BOOST_ALL_NO_LIB 1 |
157 | + |
158 | +#include <boost/config.hpp> |
159 | + |
160 | +#include <set> |
161 | +#include <stack> |
162 | + |
163 | +#include <boost/property_map.hpp> |
164 | + |
165 | +#include <boost/graph/graph_concepts.hpp> |
166 | +#include <boost/graph/graph_archetypes.hpp> |
167 | +#include <boost/graph/adjacency_list.hpp> |
168 | +#include <boost/graph/breadth_first_search.hpp> |
169 | +#include <boost/graph/dijkstra_shortest_paths.hpp> |
170 | +#include <boost/graph/iteration_macros.hpp> |
171 | +#include <boost/graph/reverse_graph.hpp> |
172 | +#include <boost/graph/graph_utility.hpp> |
173 | + |
174 | +#include "graphcore.h" |
175 | + |
176 | +using namespace open_query; |
177 | +using namespace boost; |
178 | + |
179 | +enum vertex_id_t { vertex_id }; |
180 | + |
181 | +static const row empty_row = { 0 }; |
182 | + |
183 | +namespace boost |
184 | +{ |
185 | + BOOST_INSTALL_PROPERTY(vertex, id); |
186 | +} |
187 | + |
188 | +namespace open_query { |
189 | + |
190 | + #include "graphcore-graph.h" |
191 | + |
192 | + typedef graph_traits<Graph>::vertex_descriptor Vertex; |
193 | + typedef graph_traits<Graph>::edge_descriptor Edge; |
194 | + |
195 | + typedef std::list<std::pair<Vertex,optional<EdgeWeight> > > shortest_path_list; |
196 | + typedef shortest_path_list::iterator shortest_path_iterator; |
197 | + |
198 | + template<typename ID, typename IDMap> |
199 | + class id_equals_t |
200 | + { |
201 | + public: |
202 | + id_equals_t(ID id, IDMap map) |
203 | + : m_id(id), m_map(map) |
204 | + { } |
205 | + template<typename V> |
206 | + bool operator()(V u) const |
207 | + { |
208 | + return m_map[u] == m_id; |
209 | + } |
210 | + private: |
211 | + ID m_id; |
212 | + IDMap m_map; |
213 | + }; |
214 | + |
215 | + template<typename ID, typename IDMap> |
216 | + inline id_equals_t<ID,IDMap> |
217 | + id_equals(ID id, IDMap idmap) |
218 | + { |
219 | + return id_equals_t<ID,IDMap>(id, idmap); |
220 | + } |
221 | + |
222 | + template<typename T, typename Graph> |
223 | + class target_equals_t |
224 | + { |
225 | + public: |
226 | + target_equals_t(T target, Graph &g) |
227 | + : m_target(target), m_g(g) |
228 | + { } |
229 | + template<typename V> |
230 | + bool operator()(V u) const |
231 | + { |
232 | + return target(u, m_g) == m_target; |
233 | + } |
234 | + private: |
235 | + T m_target; |
236 | + Graph &m_g; |
237 | + }; |
238 | + |
239 | + template<typename T, typename Graph> |
240 | + inline target_equals_t<T,Graph> |
241 | + target_equals(T target, Graph &g) |
242 | + { |
243 | + return target_equals_t<T,Graph>(target, g); |
244 | + } |
245 | + |
246 | + template<typename T, typename Graph> |
247 | + class source_equals_t |
248 | + { |
249 | + public: |
250 | + source_equals_t(T source, Graph &g) |
251 | + : m_source(source), m_g(g) |
252 | + { } |
253 | + template<typename V> |
254 | + bool operator()(V u) const |
255 | + { |
256 | + return source(u, m_g) == m_source; |
257 | + } |
258 | + private: |
259 | + T m_source; |
260 | + Graph &m_g; |
261 | + }; |
262 | + |
263 | + template<typename T, typename Graph> |
264 | + inline source_equals_t<T,Graph> |
265 | + source_equals(T source, Graph &g) |
266 | + { |
267 | + return source_equals_t<T,Graph>(source, g); |
268 | + } |
269 | + |
270 | + struct reference |
271 | + { |
272 | + int m_flags; |
273 | + int m_sequence; |
274 | + Vertex m_vertex; |
275 | + Edge m_edge; |
276 | + EdgeWeight m_weight; |
277 | + |
278 | + enum |
279 | + { |
280 | + HAVE_SEQUENCE = 1, |
281 | + HAVE_WEIGHT = 2, |
282 | + HAVE_EDGE = 4, |
283 | + }; |
284 | + |
285 | + inline reference() |
286 | + : m_flags(0), m_sequence(0), |
287 | + m_vertex(graph_traits<Graph>::null_vertex()), |
288 | + m_edge(), m_weight(0) |
289 | + { } |
290 | + |
291 | + inline reference(int s, Edge e) |
292 | + : m_flags(HAVE_SEQUENCE | HAVE_EDGE), m_sequence(s), |
293 | + m_vertex(graph_traits<Graph>::null_vertex()), |
294 | + m_edge(e), m_weight(0) |
295 | + { } |
296 | + |
297 | + inline reference(int s, Vertex v, const optional<Edge> &e, |
298 | + const optional<EdgeWeight> &w) |
299 | + : m_flags(HAVE_SEQUENCE | (w ? HAVE_WEIGHT : 0) | (e ? HAVE_EDGE : 0)), |
300 | + m_sequence(s), m_vertex(v) |
301 | + { |
302 | + if (w) m_weight= *w; |
303 | + if (e) m_edge= *e; |
304 | + } |
305 | + |
306 | + inline reference(int s, Vertex v, Edge e, EdgeWeight w) |
307 | + : m_flags(HAVE_SEQUENCE | HAVE_WEIGHT | HAVE_EDGE), |
308 | + m_sequence(s), m_vertex(v), m_edge(e), m_weight(w) |
309 | + { } |
310 | + |
311 | + inline reference(int s, Vertex v, EdgeWeight w) |
312 | + : m_flags(HAVE_SEQUENCE | HAVE_WEIGHT), |
313 | + m_sequence(s), m_vertex(v), m_edge(), m_weight(w) |
314 | + { } |
315 | + |
316 | + inline reference(int s, Vertex v) |
317 | + : m_flags(HAVE_SEQUENCE), m_sequence(s), m_vertex(v), m_edge(), |
318 | + m_weight(0) |
319 | + { } |
320 | + |
321 | + optional<int> sequence() const |
322 | + { |
323 | + if (m_flags & HAVE_SEQUENCE) |
324 | + { |
325 | + return m_sequence; |
326 | + } |
327 | + return optional<int>(); |
328 | + } |
329 | + |
330 | + optional<Vertex> vertex() const |
331 | + { |
332 | + if (m_vertex != graph_traits<Graph>::null_vertex()) |
333 | + return m_vertex; |
334 | + return optional<Vertex>(); |
335 | + } |
336 | + |
337 | + optional<Edge> edge() const |
338 | + { |
339 | + if (m_flags & HAVE_EDGE) |
340 | + return m_edge; |
341 | + return optional<Edge>(); |
342 | + }; |
343 | + |
344 | + optional<EdgeWeight> weight() const |
345 | + { |
346 | + if (m_flags & HAVE_WEIGHT) |
347 | + return m_weight; |
348 | + return optional<EdgeWeight>(); |
349 | + } |
350 | + }; |
351 | +} |
352 | + |
353 | +namespace open_query { |
354 | + class GRAPHCORE_INTERNAL oqgraph_share |
355 | + { |
356 | + public: |
357 | + Graph g; |
358 | + |
359 | + weightmap_type weightmap; |
360 | + idmap_type idmap; |
361 | + indexmap_type indexmap; |
362 | + |
363 | + optional<Vertex> find_vertex(VertexID id) const; |
364 | + optional<Edge> find_edge(Vertex, Vertex) const; |
365 | + |
366 | + inline oqgraph_share() throw() |
367 | + : g(), |
368 | + weightmap(GRAPH_WEIGHTMAP(g)), |
369 | + idmap(GRAPH_IDMAP(g)), |
370 | + indexmap(GRAPH_INDEXMAP(g)) |
371 | + { } |
372 | + inline ~oqgraph_share() |
373 | + { } |
374 | + }; |
375 | + |
376 | + class GRAPHCORE_INTERNAL oqgraph_cursor |
377 | + { |
378 | + public: |
379 | + oqgraph_share *const share; |
380 | + |
381 | + inline oqgraph_cursor(oqgraph_share *arg) |
382 | + : share(arg) |
383 | + { } |
384 | + virtual ~oqgraph_cursor() |
385 | + { } |
386 | + |
387 | + virtual int fetch_row(const row &, row&) = 0; |
388 | + virtual int fetch_row(const row &, row&, const reference&) = 0; |
389 | + virtual void current(reference& ref) const = 0; |
390 | + }; |
391 | +} |
392 | + |
393 | +namespace open_query { |
394 | + class GRAPHCORE_INTERNAL stack_cursor : public oqgraph_cursor |
395 | + { |
396 | + private: |
397 | + optional<EdgeWeight> no_weight; |
398 | + public: |
399 | + int sequence; |
400 | + std::stack<reference> results; |
401 | + reference last; |
402 | + |
403 | + inline stack_cursor(oqgraph_share *arg) |
404 | + : oqgraph_cursor(arg), no_weight(), sequence(0), results(), last() |
405 | + { } |
406 | + |
407 | + int fetch_row(const row &, row&); |
408 | + int fetch_row(const row &, row&, const reference&); |
409 | + |
410 | + void current(reference& ref) const |
411 | + { |
412 | + ref= last; |
413 | + } |
414 | + }; |
415 | + |
416 | + class GRAPHCORE_INTERNAL vertices_cursor : public oqgraph_cursor |
417 | + { |
418 | + typedef graph_traits<Graph>::vertex_iterator vertex_iterator; |
419 | + |
420 | + size_t position; |
421 | + reference last; |
422 | + public: |
423 | + inline vertices_cursor(oqgraph_share *arg) |
424 | + : oqgraph_cursor(arg), position(0) |
425 | + { } |
426 | + |
427 | + int fetch_row(const row &, row&); |
428 | + int fetch_row(const row &, row&, const reference&); |
429 | + |
430 | + void current(reference& ref) const |
431 | + { |
432 | + ref= last; |
433 | + } |
434 | + |
435 | + }; |
436 | + |
437 | + class GRAPHCORE_INTERNAL edges_cursor : public oqgraph_cursor |
438 | + { |
439 | + typedef graph_traits<Graph>::edge_iterator edge_iterator; |
440 | + typedef edge_iterator::difference_type edge_difference; |
441 | + |
442 | + edge_difference position; |
443 | + reference last; |
444 | + public: |
445 | + inline edges_cursor(oqgraph_share *arg) |
446 | + : oqgraph_cursor(arg), position(0), last() |
447 | + { } |
448 | + |
449 | + int fetch_row(const row &, row&); |
450 | + int fetch_row(const row &, row&, const reference&); |
451 | + |
452 | + void current(reference& ref) const |
453 | + { |
454 | + ref= last; |
455 | + } |
456 | + }; |
457 | + |
458 | + struct GRAPHCORE_INTERNAL oqgraph_visit_dist |
459 | + : public base_visitor<oqgraph_visit_dist> |
460 | + { |
461 | + typedef on_finish_vertex event_filter; |
462 | + |
463 | + oqgraph_visit_dist(std::vector<Vertex>::iterator p, |
464 | + std::vector<EdgeWeight>::iterator d, |
465 | + stack_cursor *cursor) |
466 | + : seq(0), m_cursor(*cursor), m_p(p), m_d(d) |
467 | + { assert(cursor); } |
468 | + |
469 | + template<class T, class Graph> |
470 | + void operator()(T u, Graph &g) |
471 | + { |
472 | + m_cursor.results.push(reference(++seq, u, m_d[GRAPH_INDEXMAP(g)[u]])); |
473 | + } |
474 | + private: |
475 | + int seq; |
476 | + stack_cursor &m_cursor; |
477 | + std::vector<Vertex>::iterator m_p; |
478 | + std::vector<EdgeWeight>::iterator m_d; |
479 | + }; |
480 | + |
481 | + template<bool record_weight, typename goal_filter> |
482 | + struct GRAPHCORE_INTERNAL oqgraph_goal |
483 | + : public base_visitor<oqgraph_goal<record_weight,goal_filter> > |
484 | + { |
485 | + typedef goal_filter event_filter; |
486 | + |
487 | + oqgraph_goal(Vertex goal, std::vector<Vertex>::iterator p, |
488 | + stack_cursor *cursor) |
489 | + : m_goal(goal), m_cursor(*cursor), m_p(p) |
490 | + { assert(cursor); } |
491 | + |
492 | + template<class T, class Graph> |
493 | + void operator()(T u, Graph &g) |
494 | + { |
495 | + if (u == m_goal) |
496 | + { |
497 | + int seq= 0; |
498 | + indexmap_type indexmap= GRAPH_INDEXMAP(g); |
499 | + |
500 | + for (Vertex q, v= u;; v = q, seq++) |
501 | + if ((q= m_p[ indexmap[v] ]) == v) |
502 | + break; |
503 | + |
504 | + for (Vertex v= u;; u= v) |
505 | + { |
506 | + optional<Edge> edge; |
507 | + optional<EdgeWeight> weight; |
508 | + v= m_p[ indexmap[u] ]; |
509 | + if (record_weight && u != v) |
510 | + { |
511 | + typename graph_traits<Graph>::out_edge_iterator ei, ei_end; |
512 | + for (tie(ei, ei_end)= out_edges(v, g); ei != ei_end; ++ei) |
513 | + { |
514 | + if (target(*ei, g) == u) |
515 | + { |
516 | + edge= *ei; |
517 | + weight= GRAPH_WEIGHTMAP(g)[*ei]; |
518 | + break; |
519 | + } |
520 | + } |
521 | + } |
522 | + else if (u != v) |
523 | + weight= 1; |
524 | + m_cursor.results.push(reference(seq--, u, edge, weight)); |
525 | + if (u == v) |
526 | + break; |
527 | + } |
528 | + throw this; |
529 | + } |
530 | + } |
531 | + |
532 | + private: |
533 | + Vertex m_goal; |
534 | + stack_cursor &m_cursor; |
535 | + std::vector<Vertex>::iterator m_p; |
536 | + }; |
537 | +} |
538 | + |
539 | +namespace open_query |
540 | +{ |
541 | + inline oqgraph::oqgraph(oqgraph_share *arg) throw() |
542 | + : share(arg), cursor(0) |
543 | + { } |
544 | + |
545 | + inline oqgraph::~oqgraph() throw() |
546 | + { |
547 | + delete cursor; |
548 | + } |
549 | + |
550 | + unsigned oqgraph::edges_count() const throw() |
551 | + { |
552 | + return num_edges(share->g); |
553 | + } |
554 | + |
555 | + unsigned oqgraph::vertices_count() const throw() |
556 | + { |
557 | + return num_vertices(share->g); |
558 | + } |
559 | + |
560 | + oqgraph* oqgraph::create(oqgraph_share *share) throw() |
561 | + { |
562 | + assert(share != NULL); |
563 | + return new oqgraph(share); |
564 | + } |
565 | + |
566 | + oqgraph_share* oqgraph::create() throw() |
567 | + { |
568 | + return new oqgraph_share(); |
569 | + } |
570 | + |
571 | + optional<Edge> |
572 | + oqgraph_share::find_edge(Vertex orig, Vertex dest) const |
573 | + { |
574 | + if (in_degree(dest, g) >= out_degree(orig, g)) |
575 | + { |
576 | + graph_traits<Graph>::out_edge_iterator ei, ei_end; |
577 | + tie(ei, ei_end)= out_edges(orig, g); |
578 | + if ((ei= find_if(ei, ei_end, target_equals(dest, g))) != ei_end) |
579 | + return *ei; |
580 | + } |
581 | + else |
582 | + { |
583 | + graph_traits<Graph>::in_edge_iterator ei, ei_end; |
584 | + tie(ei, ei_end)= in_edges(dest, g); |
585 | + if ((ei= find_if(ei, ei_end, source_equals(orig, g))) != ei_end) |
586 | + return *ei; |
587 | + } |
588 | + return optional<Edge>(); |
589 | + } |
590 | + |
591 | + optional<Vertex> |
592 | + oqgraph_share::find_vertex(VertexID id) const |
593 | + { |
594 | + optional<Vertex> result; |
595 | + graph_traits<Graph>::vertex_iterator vi, viend; |
596 | + |
597 | + tie(vi, viend)= vertices(g); |
598 | + if ((vi= std::find_if(vi, viend, id_equals(id, idmap))) != viend) |
599 | + result= *vi; |
600 | + return result; |
601 | + } |
602 | + |
603 | + int oqgraph::delete_all() throw() |
604 | + { |
605 | + share->g.clear(); |
606 | + return 0; |
607 | + } |
608 | + |
609 | + int oqgraph::insert_edge( |
610 | + VertexID orig_id, VertexID dest_id, EdgeWeight weight, bool replace) throw() |
611 | + { |
612 | + optional<Vertex> orig, dest; |
613 | + optional<Edge> edge; |
614 | + bool inserted= 0; |
615 | + |
616 | + if (weight < 0) |
617 | + return INVALID_WEIGHT; |
618 | + if (!(orig= share->find_vertex(orig_id))) |
619 | + { |
620 | + orig= add_vertex(share->g); |
621 | + if (orig == graph_traits<Graph>::null_vertex()) |
622 | + return CANNOT_ADD_VERTEX; |
623 | + share->idmap[*orig]= orig_id; |
624 | + } |
625 | + if (!(dest= share->find_vertex(dest_id))) |
626 | + { |
627 | + dest= add_vertex(share->g); |
628 | + if (dest == graph_traits<Graph>::null_vertex()) |
629 | + return CANNOT_ADD_VERTEX; |
630 | + share->idmap[*dest]= dest_id; |
631 | + } |
632 | + if (!(edge= share->find_edge(*orig, *dest))) |
633 | + { |
634 | + tie(edge, inserted)= add_edge(*orig, *dest, share->g); |
635 | + if (!inserted) |
636 | + return CANNOT_ADD_EDGE; |
637 | + } |
638 | + else |
639 | + { |
640 | + if (!replace) |
641 | + return DUPLICATE_EDGE; |
642 | + } |
643 | + share->weightmap[*edge]= weight; |
644 | + return OK; |
645 | + } |
646 | + |
647 | + int oqgraph::delete_edge(current_row_st) throw() |
648 | + { |
649 | + reference ref; |
650 | + if (cursor) |
651 | + return EDGE_NOT_FOUND; |
652 | + cursor->current(ref); |
653 | + optional<Edge> edge; |
654 | + if (!(edge= ref.edge())) |
655 | + return EDGE_NOT_FOUND; |
656 | + Vertex orig= source(*edge, share->g); |
657 | + Vertex dest= target(*edge, share->g); |
658 | + remove_edge(*edge, share->g); |
659 | + if (!degree(orig, share->g)) |
660 | + remove_vertex(orig, share->g); |
661 | + if (!degree(dest, share->g)) |
662 | + remove_vertex(dest, share->g); |
663 | + return OK; |
664 | + } |
665 | + |
666 | + int oqgraph::modify_edge(current_row_st, |
667 | + VertexID *orig_id, VertexID *dest_id, EdgeWeight *weight, |
668 | + bool replace) throw() |
669 | + { |
670 | + if (!cursor) |
671 | + return EDGE_NOT_FOUND; |
672 | + reference ref; |
673 | + cursor->current(ref); |
674 | + optional<Edge> edge; |
675 | + if (!(edge= ref.edge())) |
676 | + return EDGE_NOT_FOUND; |
677 | + if (weight && *weight < 0) |
678 | + return INVALID_WEIGHT; |
679 | + |
680 | + optional<Vertex> orig= source(*edge, share->g), |
681 | + dest= target(*edge, share->g); |
682 | + |
683 | + bool orig_neq= orig_id ? share->idmap[*orig] != *orig_id : 0; |
684 | + bool dest_neq= dest_id ? share->idmap[*dest] != *dest_id : 0; |
685 | + if (orig_neq || dest_neq) |
686 | + { |
687 | + optional<Edge> new_edge; |
688 | + if (orig_neq && !(orig= share->find_vertex(*orig_id))) |
689 | + { |
690 | + orig= add_vertex(share->g); |
691 | + if (orig == graph_traits<Graph>::null_vertex()) |
692 | + return CANNOT_ADD_VERTEX; |
693 | + share->idmap[*orig]= *orig_id; |
694 | + } |
695 | + if (dest_neq && !(dest= share->find_vertex(*dest_id))) |
696 | + { |
697 | + dest= add_vertex(share->g); |
698 | + if (dest == graph_traits<Graph>::null_vertex()) |
699 | + return CANNOT_ADD_VERTEX; |
700 | + share->idmap[*dest]= *dest_id; |
701 | + } |
702 | + if (!(new_edge= share->find_edge(*orig, *dest))) |
703 | + { |
704 | + bool inserted; |
705 | + tie(new_edge, inserted)= add_edge(*orig, *dest, share->g); |
706 | + if (!inserted) |
707 | + return CANNOT_ADD_EDGE; |
708 | + } |
709 | + else |
710 | + { |
711 | + if (!replace) |
712 | + return DUPLICATE_EDGE; |
713 | + } |
714 | + share->weightmap[*new_edge]= share->weightmap[*edge]; |
715 | + remove_edge(*edge, share->g); |
716 | + edge= new_edge; |
717 | + } |
718 | + if (weight) |
719 | + share->weightmap[*edge]= *weight; |
720 | + return OK; |
721 | + } |
722 | + |
723 | + int oqgraph::modify_edge( |
724 | + VertexID orig_id, VertexID dest_id, EdgeWeight weight) throw() |
725 | + { |
726 | + optional<Vertex> orig, dest; |
727 | + optional<Edge> edge; |
728 | + |
729 | + if (weight < 0) |
730 | + return INVALID_WEIGHT; |
731 | + if (!(orig= share->find_vertex(orig_id))) |
732 | + return EDGE_NOT_FOUND; |
733 | + if (!(dest= share->find_vertex(dest_id))) |
734 | + return EDGE_NOT_FOUND; |
735 | + if (!(edge= share->find_edge(*orig, *dest))) |
736 | + return EDGE_NOT_FOUND; |
737 | + share->weightmap[*edge]= weight; |
738 | + return OK; |
739 | + } |
740 | + |
741 | + |
742 | + int oqgraph::delete_edge(VertexID orig_id, VertexID dest_id) throw() |
743 | + { |
744 | + optional<Vertex> orig, dest; |
745 | + optional<Edge> edge; |
746 | + |
747 | + if (!(orig= share->find_vertex(orig_id))) |
748 | + return EDGE_NOT_FOUND; |
749 | + if (!(dest= share->find_vertex(dest_id))) |
750 | + return EDGE_NOT_FOUND; |
751 | + if (!(edge= share->find_edge(*orig, *dest))); |
752 | + return EDGE_NOT_FOUND; |
753 | + remove_edge(*edge, share->g); |
754 | + if (!degree(*orig, share->g)) |
755 | + remove_vertex(*orig, share->g); |
756 | + if (!degree(*dest, share->g)) |
757 | + remove_vertex(*dest, share->g); |
758 | + return OK; |
759 | + } |
760 | + |
761 | + |
762 | + int oqgraph::search(int *latch, VertexID *orig_id, VertexID *dest_id) throw() |
763 | + { |
764 | + optional<Vertex> orig, dest; |
765 | + int op= 0, seq= 0; |
766 | + enum { |
767 | + NO_SEARCH = 0, |
768 | + DIJKSTRAS = 1, |
769 | + BREADTH_FIRST = 2, |
770 | + |
771 | + ALGORITHM = 0x0ffff, |
772 | + HAVE_ORIG = 0x10000, |
773 | + HAVE_DEST = 0x20000, |
774 | + }; |
775 | + |
776 | + delete cursor; cursor= 0; |
777 | + row_info= empty_row; |
778 | + if ((row_info.latch_indicator= latch)) |
779 | + op= ALGORITHM & (row_info.latch= *latch); |
780 | + if ((row_info.orig_indicator= orig_id) && (op|= HAVE_ORIG)) |
781 | + orig= share->find_vertex((row_info.orig= *orig_id)); |
782 | + if ((row_info.dest_indicator= dest_id) && (op|= HAVE_DEST)) |
783 | + dest= share->find_vertex((row_info.dest= *dest_id)); |
784 | + //try |
785 | + //{ |
786 | + switch (op) |
787 | + { |
788 | + case NO_SEARCH | HAVE_ORIG | HAVE_DEST: |
789 | + case NO_SEARCH | HAVE_ORIG: |
790 | + if ((cursor= new stack_cursor(share)) && orig) |
791 | + { |
792 | + graph_traits<Graph>::out_edge_iterator ei, ei_end; |
793 | + for (tie(ei, ei_end)= out_edges(*orig, share->g); ei != ei_end; ++ei) |
794 | + { |
795 | + Vertex v= target(*ei, share->g); |
796 | + static_cast<stack_cursor*>(cursor)-> |
797 | + results.push(reference(++seq, v, *ei, share->weightmap[*ei])); |
798 | + } |
799 | + } |
800 | + /* fall through */ |
801 | + case NO_SEARCH | HAVE_DEST: |
802 | + if ((op & HAVE_DEST) && |
803 | + (cursor || (cursor= new stack_cursor(share))) && dest) |
804 | + { |
805 | + graph_traits<Graph>::in_edge_iterator ei, ei_end; |
806 | + for (tie(ei, ei_end)= in_edges(*dest, share->g); ei != ei_end; ++ei) |
807 | + { |
808 | + Vertex v= source(*ei, share->g); |
809 | + static_cast<stack_cursor*>(cursor)-> |
810 | + results.push(reference(++seq, v, *ei, share->weightmap[*ei])); |
811 | + } |
812 | + } |
813 | + break; |
814 | + |
815 | + case NO_SEARCH: |
816 | + cursor= new vertices_cursor(share); |
817 | + break; |
818 | + |
819 | + case DIJKSTRAS | HAVE_ORIG | HAVE_DEST: |
820 | + if ((cursor= new stack_cursor(share)) && orig && dest) |
821 | + { |
822 | + std::vector<Vertex> p(num_vertices(share->g)); |
823 | + std::vector<EdgeWeight> d(num_vertices(share->g)); |
824 | + oqgraph_goal<true, on_finish_vertex> |
825 | + vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor)); |
826 | + p[share->indexmap[*orig]]= *orig; |
827 | + try |
828 | + { |
829 | + dijkstra_shortest_paths(share->g, *orig, |
830 | + weight_map( |
831 | + share->weightmap |
832 | + ). |
833 | + distance_map( |
834 | + make_iterator_property_map(d.begin(), share->indexmap) |
835 | + ). |
836 | + predecessor_map( |
837 | + make_iterator_property_map(p.begin(), share->indexmap) |
838 | + ). |
839 | + visitor( |
840 | + make_dijkstra_visitor(vis) |
841 | + ) |
842 | + ); |
843 | + } |
844 | + catch (...) |
845 | + { /* printf("found\n"); */ } |
846 | + } |
847 | + break; |
848 | + |
849 | + case BREADTH_FIRST | HAVE_ORIG | HAVE_DEST: |
850 | + if ((cursor= new stack_cursor(share)) && orig && dest) |
851 | + { |
852 | + std::vector<Vertex> p(num_vertices(share->g)); |
853 | + oqgraph_goal<false, on_discover_vertex> |
854 | + vis(*dest, p.begin(), static_cast<stack_cursor*>(cursor)); |
855 | + p[share->indexmap[*orig]]= *orig; |
856 | + try |
857 | + { |
858 | + breadth_first_search(share->g, *orig, |
859 | + visitor(make_bfs_visitor( |
860 | + std::make_pair( |
861 | + record_predecessors( |
862 | + make_iterator_property_map(p.begin(), share->indexmap), |
863 | + on_tree_edge() |
864 | + ), |
865 | + vis) |
866 | + ) |
867 | + ) |
868 | + ); |
869 | + } |
870 | + catch (...) |
871 | + { /* printf("found\n"); */ } |
872 | + } |
873 | + break; |
874 | + |
875 | + case DIJKSTRAS | HAVE_ORIG: |
876 | + case BREADTH_FIRST | HAVE_ORIG: |
877 | + if ((cursor= new stack_cursor(share)) && (orig || dest)) |
878 | + { |
879 | + std::vector<Vertex> p(num_vertices(share->g)); |
880 | + std::vector<EdgeWeight> d(num_vertices(share->g)); |
881 | + oqgraph_visit_dist vis(p.begin(), d.begin(), |
882 | + static_cast<stack_cursor*>(cursor)); |
883 | + p[share->indexmap[*orig]]= *orig; |
884 | + switch (ALGORITHM & op) |
885 | + { |
886 | + case DIJKSTRAS: |
887 | + dijkstra_shortest_paths(share->g, *orig, |
888 | + weight_map( |
889 | + share->weightmap |
890 | + ). |
891 | + distance_map( |
892 | + make_iterator_property_map(d.begin(), share->indexmap) |
893 | + ). |
894 | + predecessor_map( |
895 | + make_iterator_property_map(p.begin(), share->indexmap) |
896 | + ). |
897 | + visitor( |
898 | + make_dijkstra_visitor(vis) |
899 | + ) |
900 | + ); |
901 | + break; |
902 | + case BREADTH_FIRST: |
903 | + breadth_first_search(share->g, *orig, |
904 | + visitor(make_bfs_visitor( |
905 | + std::make_pair( |
906 | + record_predecessors( |
907 | + make_iterator_property_map(p.begin(), |
908 | + share->indexmap), |
909 | + on_tree_edge() |
910 | + ), |
911 | + std::make_pair( |
912 | + record_distances( |
913 | + make_iterator_property_map(d.begin(), |
914 | + share->indexmap), |
915 | + on_tree_edge() |
916 | + ), |
917 | + vis |
918 | + )) |
919 | + )) |
920 | + ); |
921 | + break; |
922 | + default: |
923 | + abort(); |
924 | + } |
925 | + } |
926 | + break; |
927 | + |
928 | + case BREADTH_FIRST | HAVE_DEST: |
929 | + case DIJKSTRAS | HAVE_DEST: |
930 | + if ((cursor= new stack_cursor(share)) && (orig || dest)) |
931 | + { |
932 | + std::vector<Vertex> p(num_vertices(share->g)); |
933 | + std::vector<EdgeWeight> d(num_vertices(share->g)); |
934 | + oqgraph_visit_dist vis(p.begin(), d.begin(), |
935 | + static_cast<stack_cursor*>(cursor)); |
936 | + reverse_graph<Graph> r(share->g); |
937 | + p[share->indexmap[*dest]]= *dest; |
938 | + switch (ALGORITHM & op) |
939 | + { |
940 | + case DIJKSTRAS: |
941 | + dijkstra_shortest_paths(r, *dest, |
942 | + weight_map( |
943 | + share->weightmap |
944 | + ). |
945 | + distance_map( |
946 | + make_iterator_property_map(d.begin(), share->indexmap) |
947 | + ). |
948 | + predecessor_map( |
949 | + make_iterator_property_map(p.begin(), share->indexmap) |
950 | + ). |
951 | + visitor( |
952 | + make_dijkstra_visitor(vis) |
953 | + ) |
954 | + ); |
955 | + break; |
956 | + case BREADTH_FIRST: |
957 | + breadth_first_search(r, *dest, |
958 | + visitor(make_bfs_visitor( |
959 | + std::make_pair( |
960 | + record_predecessors( |
961 | + make_iterator_property_map(p.begin(), |
962 | + share->indexmap), |
963 | + on_tree_edge() |
964 | + ), |
965 | + std::make_pair( |
966 | + record_distances( |
967 | + make_iterator_property_map(d.begin(), |
968 | + share->indexmap), |
969 | + on_tree_edge() |
970 | + ), |
971 | + vis |
972 | + )) |
973 | + )) |
974 | + ); |
975 | + break; |
976 | + default: |
977 | + abort(); |
978 | + } |
979 | + } |
980 | + break; |
981 | + |
982 | + default: |
983 | + break; |
984 | + } |
985 | + return 0; |
986 | + //} |
987 | + //catch (...) |
988 | + //{ |
989 | + // return MISC_FAIL; |
990 | + //} |
991 | + } |
992 | + |
993 | + int oqgraph::fetch_row(row& result) throw() |
994 | + { |
995 | + if (!cursor) |
996 | + return NO_MORE_DATA; |
997 | + return cursor->fetch_row(row_info, result); |
998 | + } |
999 | + |
1000 | + int oqgraph::fetch_row(row& result, const void* ref_ptr) throw() |
1001 | + { |
1002 | + const reference &ref= *(const reference*) ref_ptr; |
1003 | + if (!cursor) |
1004 | + return NO_MORE_DATA; |
1005 | + return cursor->fetch_row(row_info, result, ref); |
1006 | + } |
1007 | + |
1008 | + void oqgraph::row_ref(void *ref_ptr) throw() |
1009 | + { |
1010 | + reference &ref= *(reference*) ref_ptr; |
1011 | + if (cursor) |
1012 | + cursor->current(ref); |
1013 | + else |
1014 | + ref= reference(); |
1015 | + } |
1016 | + |
1017 | + int oqgraph::random(bool scan) throw() |
1018 | + { |
1019 | + if (scan || !cursor) |
1020 | + { |
1021 | + delete cursor; cursor= 0; |
1022 | + if (!(cursor= new edges_cursor(share))) |
1023 | + return MISC_FAIL; |
1024 | + } |
1025 | + row_info= empty_row; |
1026 | + return OK; |
1027 | + } |
1028 | + |
1029 | + void oqgraph::free(oqgraph *graph) throw() |
1030 | + { |
1031 | + delete graph; |
1032 | + } |
1033 | + |
1034 | + void oqgraph::free(oqgraph_share *graph) throw() |
1035 | + { |
1036 | + delete graph; |
1037 | + } |
1038 | + |
1039 | + const size_t oqgraph::sizeof_ref= sizeof(reference); |
1040 | +} |
1041 | + |
1042 | +int stack_cursor::fetch_row(const row &row_info, row &result) |
1043 | +{ |
1044 | + if (!results.empty()) |
1045 | + { |
1046 | + if (int res= fetch_row(row_info, result, results.top())) |
1047 | + return res; |
1048 | + results.pop(); |
1049 | + return oqgraph::OK; |
1050 | + } |
1051 | + else |
1052 | + { |
1053 | + last= reference(); |
1054 | + return oqgraph::NO_MORE_DATA; |
1055 | + } |
1056 | +} |
1057 | + |
1058 | +int stack_cursor::fetch_row(const row &row_info, row &result, |
1059 | + const reference &ref) |
1060 | +{ |
1061 | + last= ref; |
1062 | + if (optional<Vertex> v= last.vertex()) |
1063 | + { |
1064 | + optional<int> seq; |
1065 | + optional<EdgeWeight> w; |
1066 | + optional<Vertex> v; |
1067 | + result= row_info; |
1068 | + if ((result.seq_indicator= seq= last.sequence())) |
1069 | + result.seq= *seq; |
1070 | + if ((result.link_indicator= v= last.vertex())) |
1071 | + result.link= share->idmap[*v]; |
1072 | + if ((result.weight_indicator= w= last.weight())) |
1073 | + result.weight= *w; |
1074 | + return oqgraph::OK; |
1075 | + } |
1076 | + else |
1077 | + return oqgraph::NO_MORE_DATA; |
1078 | +} |
1079 | + |
1080 | + |
1081 | +int vertices_cursor::fetch_row(const row &row_info, row &result) |
1082 | +{ |
1083 | + vertex_iterator it, end; |
1084 | + reference ref; |
1085 | + size_t count= position; |
1086 | + for (tie(it, end)= vertices(share->g); count && it != end; ++it, --count); |
1087 | + if (it != end) |
1088 | + ref= reference(position+1, *it); |
1089 | + if (int res= fetch_row(row_info, result, ref)) |
1090 | + return res; |
1091 | + position++; |
1092 | + return oqgraph::OK; |
1093 | +} |
1094 | + |
1095 | +int vertices_cursor::fetch_row(const row &row_info, row &result, |
1096 | + const reference &ref) |
1097 | +{ |
1098 | + last= ref; |
1099 | + optional<Vertex> v= last.vertex(); |
1100 | + result= row_info; |
1101 | + if (v) |
1102 | + { |
1103 | + result.link_indicator= 1; |
1104 | + result.link= share->idmap[*v]; |
1105 | +#ifdef DISPLAY_VERTEX_INFO |
1106 | + result.seq_indicator= 1; |
1107 | + if ((result.seq= degree(*v, share->g))) |
1108 | + { |
1109 | + EdgeWeight weight= 0; |
1110 | + graph_traits<Graph>::in_edge_iterator iei, iei_end; |
1111 | + for (tie(iei, iei_end)= in_edges(*v, share->g); iei != iei_end; ++iei) |
1112 | + weight+= share->weightmap[*iei]; |
1113 | + graph_traits<Graph>::out_edge_iterator oei, oei_end; |
1114 | + for (tie(oei, oei_end)= out_edges(*v, share->g); oei != oei_end; ++oei) |
1115 | + weight+= share->weightmap[*oei]; |
1116 | + result.weight_indicator= 1; |
1117 | + result.weight= weight / result.seq; |
1118 | + } |
1119 | +#endif |
1120 | + return oqgraph::OK; |
1121 | + } |
1122 | + else |
1123 | + return oqgraph::NO_MORE_DATA; |
1124 | +} |
1125 | + |
1126 | +int edges_cursor::fetch_row(const row &row_info, row &result) |
1127 | +{ |
1128 | + edge_iterator it, end; |
1129 | + reference ref; |
1130 | + size_t count= position; |
1131 | + for (tie(it, end)= edges(share->g); count && it != end; ++it, --count); |
1132 | + if (it != end) |
1133 | + ref= reference(position+1, *it); |
1134 | + if (int res= fetch_row(row_info, result, ref)) |
1135 | + return res; |
1136 | + ++position; |
1137 | + return oqgraph::OK; |
1138 | +} |
1139 | + |
1140 | +int edges_cursor::fetch_row(const row &row_info, row &result, |
1141 | + const reference &ref) |
1142 | +{ |
1143 | + optional<Edge> edge; |
1144 | + if ((edge= (last= ref).edge())) |
1145 | + { |
1146 | + result= row_info; |
1147 | + result.orig_indicator= result.dest_indicator= result.weight_indicator= 1; |
1148 | + result.orig= share->idmap[ source( *edge, share->g ) ]; |
1149 | + result.dest= share->idmap[ target( *edge, share->g ) ]; |
1150 | + result.weight= share->weightmap[ *edge ]; |
1151 | + return oqgraph::OK; |
1152 | + } |
1153 | + return oqgraph::NO_MORE_DATA; |
1154 | +} |
1155 | + |
1156 | +namespace boost { |
1157 | + GRAPHCORE_INTERNAL void throw_exception(std::exception const&) |
1158 | + { |
1159 | + abort(); |
1160 | + } |
1161 | +} |
1162 | |
1163 | === added file 'oqgraph/graphcore.h' |
1164 | --- oqgraph/graphcore.h 1970-01-01 00:00:00 +0000 |
1165 | +++ oqgraph/graphcore.h 2009-10-25 07:55:23 +0000 |
1166 | @@ -0,0 +1,116 @@ |
1167 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
1168 | + |
1169 | + This program is free software; you can redistribute it and/or modify |
1170 | + it under the terms of the GNU General Public License as published by |
1171 | + the Free Software Foundation; version 2 of the License, or |
1172 | + (at your option) any later version. |
1173 | + |
1174 | + This program is distributed in the hope that it will be useful, |
1175 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
1176 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1177 | + GNU General Public License for more details. |
1178 | + |
1179 | + You should have received a copy of the GNU General Public License |
1180 | + along with this program; if not, write to the Free Software |
1181 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
1182 | + |
1183 | +/* ====================================================================== |
1184 | + Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
1185 | + Mk.II implementation by Antony Curtis & Arjen Lentz |
1186 | + For more information, documentation, support, enhancement engineering, |
1187 | + and non-GPL licensing, see http://openquery.com/graph |
1188 | + or contact graph@openquery.com |
1189 | + For packaged binaries, see http://ourdelta.org |
1190 | + ====================================================================== |
1191 | +*/ |
1192 | + |
1193 | +#ifndef oq_graphcore_h_ |
1194 | +#define oq_graphcore_h_ |
1195 | + |
1196 | +/* #define GRAPHCORE_INTERNAL __attribute__((visibility("hidden"))) */ |
1197 | +#define GRAPHCORE_INTERNAL |
1198 | + |
1199 | +#include "graphcore-types.h" |
1200 | + |
1201 | +namespace open_query |
1202 | +{ |
1203 | + class oqgraph_share; |
1204 | + class oqgraph_cursor; |
1205 | + |
1206 | + struct row |
1207 | + { |
1208 | + bool latch_indicator; |
1209 | + bool orig_indicator; |
1210 | + bool dest_indicator; |
1211 | + bool weight_indicator; |
1212 | + bool seq_indicator; |
1213 | + bool link_indicator; |
1214 | + |
1215 | + int latch; |
1216 | + VertexID orig; |
1217 | + VertexID dest; |
1218 | + EdgeWeight weight; |
1219 | + unsigned seq; |
1220 | + VertexID link; |
1221 | + }; |
1222 | + |
1223 | + class oqgraph |
1224 | + { |
1225 | + oqgraph_share *const share; |
1226 | + oqgraph_cursor *cursor; |
1227 | + row row_info; |
1228 | + |
1229 | + inline oqgraph(oqgraph_share*) throw(); |
1230 | + inline ~oqgraph() throw(); |
1231 | + public: |
1232 | + |
1233 | + enum error_code |
1234 | + { |
1235 | + OK= 0, |
1236 | + NO_MORE_DATA, |
1237 | + EDGE_NOT_FOUND, |
1238 | + INVALID_WEIGHT, |
1239 | + DUPLICATE_EDGE, |
1240 | + CANNOT_ADD_VERTEX, |
1241 | + CANNOT_ADD_EDGE, |
1242 | + MISC_FAIL |
1243 | + }; |
1244 | + |
1245 | + struct current_row_st {}; |
1246 | + static inline current_row_st current_row() |
1247 | + { return current_row_st(); } |
1248 | + |
1249 | + unsigned vertices_count() const throw(); |
1250 | + unsigned edges_count() const throw(); |
1251 | + |
1252 | + int delete_all(void) throw(); |
1253 | + |
1254 | + int insert_edge(VertexID, VertexID, EdgeWeight, bool=0) throw(); |
1255 | + int modify_edge(VertexID, VertexID, EdgeWeight) throw(); |
1256 | + int delete_edge(VertexID, VertexID) throw(); |
1257 | + |
1258 | + int modify_edge(current_row_st, |
1259 | + VertexID*, VertexID*, EdgeWeight*, bool=0) throw(); |
1260 | + int delete_edge(current_row_st) throw(); |
1261 | + |
1262 | + int replace_edge(VertexID orig, VertexID dest, EdgeWeight weight) throw() |
1263 | + { return insert_edge(orig, dest, weight, true); } |
1264 | + |
1265 | + int search(int*, VertexID*, VertexID*) throw(); |
1266 | + int random(bool) throw(); |
1267 | + |
1268 | + int fetch_row(row&) throw(); |
1269 | + int fetch_row(row&, const void*) throw(); |
1270 | + void row_ref(void*) throw(); |
1271 | + |
1272 | + static oqgraph* create(oqgraph_share*) throw(); |
1273 | + static oqgraph_share *create() throw(); |
1274 | + |
1275 | + static void free(oqgraph*) throw(); |
1276 | + static void free(oqgraph_share*) throw(); |
1277 | + |
1278 | + static const size_t sizeof_ref; |
1279 | + }; |
1280 | + |
1281 | +} |
1282 | +#endif |
1283 | |
1284 | === added file 'oqgraph/graphstore.c' |
1285 | --- oqgraph/graphstore.c 1970-01-01 00:00:00 +0000 |
1286 | +++ oqgraph/graphstore.c 2009-10-25 07:55:23 +0000 |
1287 | @@ -0,0 +1,356 @@ |
1288 | +/* |
1289 | + * Graph Engine - Copyright (C) 2007 by Arjen Lentz (arjen@openquery.com.au) |
1290 | + * graphstore.c internal storage system |
1291 | + */ |
1292 | +#include <stdlib.h> |
1293 | +#include <string.h> |
1294 | +#include <my_global.h> |
1295 | +#include <my_sys.h> |
1296 | +#include "graphstore.h" |
1297 | + |
1298 | + |
1299 | +/* |
1300 | + create a new vertex, and add it to the list (or start a list) |
1301 | + NOTE! gspp is ptr to base ptr |
1302 | + |
1303 | + returns 1 for ok, 0 for error |
1304 | +*/ |
1305 | +static int _add_vertex (GRAPHSTORE **gspp, GRAPH_VERTEXID id) |
1306 | +{ |
1307 | + GRAPHSTORE *newgsp; |
1308 | + GRAPHSTORE *gscurp; |
1309 | + |
1310 | + if (gspp == NULL) |
1311 | + return 0; |
1312 | + |
1313 | + /* not allowing 0 */ |
1314 | + if (!id) |
1315 | + return 0; |
1316 | + |
1317 | + if (*gspp != NULL) { |
1318 | + for (gscurp = *gspp; gscurp != NULL; gscurp = gscurp->next) { |
1319 | + if (gscurp->vertex->id == id) |
1320 | + return 1; /* we can ignore, id already exists */ |
1321 | + } |
1322 | + } |
1323 | + |
1324 | + /* allocate and initialise */ |
1325 | + if ((newgsp = my_malloc(sizeof (GRAPHSTORE),MYF(MY_ZEROFILL))) == NULL) |
1326 | + return 0; |
1327 | + |
1328 | + if ((newgsp->vertex = my_malloc(sizeof (GRAPH_VERTEX),MYF(MY_ZEROFILL))) == NULL) { |
1329 | + my_free(newgsp,MYF(0)); |
1330 | + return 0; |
1331 | + } |
1332 | + |
1333 | + newgsp->vertex->id = id; |
1334 | + /* add new vertex to end of list */ |
1335 | + if (*gspp != NULL) { |
1336 | + for (gscurp = *gspp; gscurp->next != NULL; gscurp = gscurp->next); |
1337 | + gscurp->next = newgsp; |
1338 | + } |
1339 | + else /* new list */ |
1340 | + *gspp = newgsp; |
1341 | + |
1342 | + /* ok */ |
1343 | + return 1; |
1344 | +} |
1345 | + |
1346 | + |
1347 | +/* |
1348 | + find a vertex by id |
1349 | + |
1350 | + returns ptr or NULL |
1351 | +*/ |
1352 | +static GRAPH_VERTEX *_find_vertex (GRAPHSTORE *gsp, GRAPH_VERTEXID id) |
1353 | +{ |
1354 | + /* just loop through the list to find id */ |
1355 | + while (gsp != NULL && gsp->vertex->id != id) |
1356 | + gsp = gsp->next; |
1357 | + |
1358 | + /* return ptr to vertex, or NULL */ |
1359 | + return (gsp != NULL ? gsp->vertex : NULL); |
1360 | +} |
1361 | + |
1362 | + |
1363 | +/* |
1364 | + add edge |
1365 | + both vertices must already exist; graphstore_insert() does this |
1366 | + |
1367 | + return 1 for ok, 0 for error (already exists, alloc error, etc) |
1368 | +*/ |
1369 | +static int _add_edge (GRAPHSTORE *gsp, GRAPH_VERTEXID origid, GRAPH_VERTEXID destid, GRAPH_WEIGHT weight) |
1370 | +{ |
1371 | + GRAPH_VERTEX *origvp, *destvp; |
1372 | + GRAPH_EDGE *ep, *newep; |
1373 | + |
1374 | + /* find both vertices */ |
1375 | + if ((origvp = _find_vertex(gsp,origid)) == NULL || |
1376 | + (destvp = _find_vertex(gsp,destid)) == NULL) |
1377 | + return 0; |
1378 | + |
1379 | + /* check if edge already exists */ |
1380 | + for (ep = origvp->forward_edge; ep != NULL; ep = ep->next_edge) { |
1381 | + if (ep->vertex->id == destid) |
1382 | + return 0; |
1383 | + } |
1384 | + |
1385 | + /* allocate and initialise new edge */ |
1386 | + if ((newep = my_malloc(sizeof (GRAPH_EDGE),MYF(MY_ZEROFILL))) == NULL) |
1387 | + return 0; |
1388 | + |
1389 | + newep->vertex = destvp; |
1390 | + newep->weight = weight; |
1391 | + |
1392 | + /* insert new edge at start of chain, that's easiest */ |
1393 | + ep = origvp->forward_edge; |
1394 | + origvp->forward_edge = newep; |
1395 | + newep->next_edge = ep; |
1396 | + |
1397 | + /* ok */ |
1398 | + return 1; |
1399 | +} |
1400 | + |
1401 | + |
1402 | +/* |
1403 | + create a new row, and add it to the graph set (or start set) |
1404 | + NOTE! gsetpp is ptr to base ptr |
1405 | + |
1406 | + returns 1 for ok, 0 for error |
1407 | +*/ |
1408 | +static int _add_graph_set (GRAPH_SET **gsetpp, GRAPH_TUPLE *gtp) |
1409 | +{ |
1410 | + GRAPH_SET *newgsetp; |
1411 | + GRAPH_SET *gsetcurp; |
1412 | + |
1413 | + if (gsetpp == NULL || gtp == NULL) |
1414 | + return 0; |
1415 | + |
1416 | + /* allocate and initialise */ |
1417 | + if ((newgsetp = my_malloc(sizeof (GRAPH_SET),MYF(MY_ZEROFILL))) == NULL) |
1418 | + return 0; |
1419 | + |
1420 | + /* put in the data */ |
1421 | + memcpy(&newgsetp->tuple,gtp,sizeof (GRAPH_TUPLE)); |
1422 | + |
1423 | + /* add new row to end of set */ |
1424 | + if (*gsetpp != NULL) { |
1425 | + for (gsetcurp = *gsetpp; gsetcurp->next != NULL; gsetcurp = gsetcurp->next); |
1426 | + gsetcurp->next = newgsetp; |
1427 | + } |
1428 | + else { /* new set */ |
1429 | + *gsetpp = newgsetp; |
1430 | + } |
1431 | + |
1432 | + /* ok */ |
1433 | + return 1; |
1434 | +} |
1435 | + |
1436 | + |
1437 | +/* |
1438 | + free a graph set (release memory) |
1439 | + |
1440 | + returns 1 for ok, 0 for error |
1441 | +*/ |
1442 | +int free_graph_set (GRAPH_SET *gsetp) |
1443 | +{ |
1444 | + GRAPH_SET *nextgsetp; |
1445 | + |
1446 | + if (gsetp == NULL) |
1447 | + return 0; |
1448 | + |
1449 | + while (gsetp != NULL) { |
1450 | + nextgsetp = gsetp->next; |
1451 | + /* free() is a void function, nothing to check */ |
1452 | + my_free(gsetp,MYF(0)); |
1453 | + gsetp = nextgsetp; |
1454 | + } |
1455 | + |
1456 | + /* ok */ |
1457 | + return 1; |
1458 | +} |
1459 | + |
1460 | + |
1461 | +/* |
1462 | + insert new data into graphstore |
1463 | + this can be either a vertex or an edge, depending on the params |
1464 | + NOTE! gspp is ptr to base ptr |
1465 | + |
1466 | + returns 1 for ok, 0 for error |
1467 | +*/ |
1468 | +int graphstore_insert (GRAPHSTORE **gspp, GRAPH_TUPLE *gtp) |
1469 | +{ |
1470 | + if (gspp == NULL) |
1471 | + return 0; |
1472 | + |
1473 | + /* if nada or no orig vertex, we can't do anything */ |
1474 | + if (gtp == NULL || !gtp->origid) |
1475 | + return 0; |
1476 | + |
1477 | +#if 0 |
1478 | +printf("inserting: origid=%lu destid=%lu weight=%lu\n",gtp->origid,gtp->destid,gtp->weight); |
1479 | +#endif |
1480 | + |
1481 | + if (!gtp->destid) /* no edge param so just adding vertex */ |
1482 | + return _add_vertex(gspp,gtp->origid); |
1483 | + |
1484 | + /* |
1485 | + add an edge |
1486 | + first add both vertices just in case they didn't yet exist... |
1487 | + not checking result there: if there's a prob, _add_edge() will catch. |
1488 | + */ |
1489 | + _add_vertex(gspp,gtp->origid); |
1490 | + _add_vertex(gspp,gtp->destid); |
1491 | + return _add_edge(*gspp,gtp->origid,gtp->destid,gtp->weight); |
1492 | +} |
1493 | + |
1494 | + |
1495 | +/* |
1496 | + this is an internal function used by graphstore_query() |
1497 | + |
1498 | + find any path from originating vertex to destid |
1499 | + if found, add to the result set on the way back |
1500 | + NOTE: recursive function! |
1501 | + |
1502 | + returns 1 for hit, 0 for nothing, -1 for error |
1503 | +*/ |
1504 | +int _find_any_path(GRAPH_SET **gsetpp, GRAPH_VERTEXID origid, GRAPH_VERTEXID destid, GRAPH_VERTEX *gvp, GRAPH_SEQ depth) |
1505 | +{ |
1506 | + GRAPH_EDGE *gep; |
1507 | + GRAPH_TUPLE tup; |
1508 | + int res; |
1509 | + |
1510 | + if (gvp->id == destid) { |
1511 | + /* found target! */ |
1512 | + bzero(&tup,sizeof (GRAPH_TUPLE)); |
1513 | + tup.origid = origid; |
1514 | + tup.destid = destid; |
1515 | + tup.seq = depth; |
1516 | + tup.linkid = gvp->id; |
1517 | + return (_add_graph_set(gsetpp,&tup) ? 1 : -1); |
1518 | + } |
1519 | + |
1520 | + /* walk through all edges for this vertex */ |
1521 | + for (gep = gvp->forward_edge; gep; gep = gep->next_edge) { |
1522 | + /* recurse */ |
1523 | + res = _find_any_path(gsetpp,origid,destid,gep->vertex,depth+1); |
1524 | + if (res < 0) |
1525 | + return res; |
1526 | + if (res > 0) { |
1527 | + /* found somewhere below this one, insert ourselves and return */ |
1528 | + bzero(&tup,sizeof (GRAPH_TUPLE)); |
1529 | + tup.origid = origid; |
1530 | + tup.destid = destid; |
1531 | + tup.weight = gep->weight; |
1532 | + tup.seq = depth; |
1533 | + tup.linkid = gvp->id; |
1534 | + return (_add_graph_set(gsetpp,&tup) ? 1 : -1); |
1535 | + } |
1536 | + } |
1537 | + |
1538 | + /* nothing found but no error */ |
1539 | + return 0; |
1540 | +} |
1541 | + |
1542 | + |
1543 | +/* |
1544 | + query graphstore |
1545 | + latch specifies what operation to perform |
1546 | + |
1547 | + we need to feed the conditions in... (through engine condition pushdown) |
1548 | + for now we just presume one condition per field so we just feed in a tuple |
1549 | + this also means we can just find constants, not ranges |
1550 | + |
1551 | + return ptr to GRAPH_SET |
1552 | + caller must free with free_graph_set() |
1553 | +*/ |
1554 | +GRAPH_SET *graphstore_query (GRAPHSTORE *gsp, GRAPH_TUPLE *gtp) |
1555 | +{ |
1556 | + GRAPH_SET *gsetp = NULL; |
1557 | + GRAPH_SET *gsetcurp; |
1558 | + GRAPH_SET *newgsetp; |
1559 | + |
1560 | + if (gsp == NULL || gtp == NULL) |
1561 | + return (NULL); |
1562 | + |
1563 | + switch (gtp->latch) { |
1564 | + case 0: /* return all vertices/edges */ |
1565 | + { |
1566 | + GRAPHSTORE *gscurp; |
1567 | + GRAPH_EDGE *gep; |
1568 | + GRAPH_TUPLE tup; |
1569 | + |
1570 | + /* walk through all vertices */ |
1571 | + for (gscurp = gsp; gscurp != NULL; gscurp = gscurp->next) { |
1572 | + /* check for condition */ |
1573 | + if (gtp->origid && gscurp->vertex->id != gtp->origid) |
1574 | + continue; |
1575 | + |
1576 | + bzero(&tup,sizeof (GRAPH_TUPLE)); |
1577 | + tup.origid = gscurp->vertex->id; |
1578 | + |
1579 | + /* no edges? */ |
1580 | + if (gscurp->vertex->forward_edge == NULL) { |
1581 | + /* just add vertex to set */ |
1582 | + if (!_add_graph_set(&gsetp,&tup)) { |
1583 | + if (gsetp != NULL) /* clean up */ |
1584 | + my_free(gsetp,MYF(0)); |
1585 | + return (NULL); |
1586 | + } |
1587 | + } |
1588 | + else { |
1589 | + /* walk through all edges */ |
1590 | + for (gep = gscurp->vertex->forward_edge; gep; gep = gep->next_edge) { |
1591 | + tup.destid = gep->vertex->id; |
1592 | + tup.weight = gep->weight; |
1593 | + |
1594 | + /* just add vertex to set */ |
1595 | + if (!_add_graph_set(&gsetp,&tup)) { |
1596 | + if (gsetp != NULL) /* clean up */ |
1597 | + my_free(gsetp,MYF(0)); |
1598 | + return (NULL); |
1599 | + } |
1600 | + } |
1601 | + } |
1602 | + } |
1603 | + } |
1604 | + break; |
1605 | + |
1606 | + case 1: /* find a path between origid and destid */ |
1607 | + /* yes it'll just go with the first path it finds! */ |
1608 | + { |
1609 | + GRAPHSTORE *gscurp; |
1610 | + GRAPH_VERTEX *origvp; |
1611 | + GRAPH_TUPLE tup; |
1612 | + |
1613 | + if (!gtp->origid || !gtp->destid) |
1614 | + return NULL; |
1615 | + |
1616 | + /* find both vertices */ |
1617 | + if ((origvp = _find_vertex(gsp,gtp->origid)) == NULL || |
1618 | + _find_vertex(gsp,gtp->destid) == NULL) |
1619 | + return NULL; |
1620 | + |
1621 | + if (_find_any_path(&gsetp,gtp->origid,gtp->destid,origvp,0) < 0) { /* error? */ |
1622 | + if (gsetp != NULL) /* clean up */ |
1623 | + my_free(gsetp,MYF(0)); |
1624 | + return NULL; |
1625 | + } |
1626 | + } |
1627 | + break; |
1628 | + |
1629 | + default: |
1630 | + /* this ends up being an empty set */ |
1631 | + break; |
1632 | + } |
1633 | + |
1634 | + /* Fix up latch column with the proper value - to be relationally correct */ |
1635 | + for (gsetcurp = gsetp; gsetcurp != NULL; gsetcurp = gsetcurp->next) |
1636 | + gsetcurp->tuple.latch = gtp->latch; |
1637 | + |
1638 | + return gsetp; |
1639 | +} |
1640 | + |
1641 | + |
1642 | + |
1643 | +/* end of graphstore.c */ |
1644 | \ No newline at end of file |
1645 | |
1646 | === added file 'oqgraph/graphstore.h' |
1647 | --- oqgraph/graphstore.h 1970-01-01 00:00:00 +0000 |
1648 | +++ oqgraph/graphstore.h 2009-10-25 07:55:23 +0000 |
1649 | @@ -0,0 +1,90 @@ |
1650 | +/* |
1651 | + * Graph Engine - Copyright (C) 2007 by Arjen Lentz (arjen@openquery.com.au) |
1652 | + * graphstore.h internal storage system |
1653 | + */ |
1654 | +//typedef unsigned short uint16; |
1655 | +//typedef unsigned long long uint64; |
1656 | + |
1657 | + |
1658 | +/* |
1659 | + This is essentially what a GRAPH engine table looks like on the MySQL end: |
1660 | + CREATE TABLE foo ( |
1661 | + latch SMALLINT UNSIGNED NULL, |
1662 | + origid BIGINT UNSIGNED NULL, |
1663 | + destid BIGINT UNSIGNED NULL, |
1664 | + weight BIGINT UNSIGNED NULL, |
1665 | + seq BIGINT UNSIGNED NULL, |
1666 | + linkid BIGINT UNSIGNED NULL |
1667 | + ) ENGINE=OQGRAPH |
1668 | +*/ |
1669 | + |
1670 | + |
1671 | +/* |
1672 | + We represent the above in C in the following way: |
1673 | +*/ |
1674 | +typedef uint16 GRAPH_LATCH; |
1675 | +typedef uint64 GRAPH_VERTEXID; |
1676 | +typedef uint64 GRAPH_WEIGHT; |
1677 | +typedef uint64 GRAPH_SEQ; |
1678 | + |
1679 | +typedef struct graph_tuple { |
1680 | + GRAPH_LATCH latch; /* function */ |
1681 | + GRAPH_VERTEXID origid; /* vertex (should be != 0) */ |
1682 | + GRAPH_VERTEXID destid; /* edge */ |
1683 | + GRAPH_WEIGHT weight; /* weight */ |
1684 | + GRAPH_SEQ seq; /* seq# within (origid) */ |
1685 | + GRAPH_VERTEXID linkid; /* current step between origid/destid */ |
1686 | +} GRAPH_TUPLE; |
1687 | + |
1688 | +typedef struct graph_set { |
1689 | + GRAPH_TUPLE tuple; |
1690 | + struct graph_set *next; |
1691 | +} GRAPH_SET; |
1692 | + |
1693 | + |
1694 | +/* |
1695 | + Internally, sets look nothing like the above |
1696 | + |
1697 | + - We have vertices, connected by edges. |
1698 | + - Each vertex' edges are maintained in a linked list. |
1699 | + - Edges can be weighted. |
1700 | + |
1701 | + There are some issues with this structure, it'd be a pest to do a delete |
1702 | + So for now, let's just not support deletes! |
1703 | +*/ |
1704 | +/* the below is half-gross and will likely change */ |
1705 | +typedef struct graph_edge { |
1706 | + struct graph_vertex { |
1707 | + GRAPH_VERTEXID id; |
1708 | + struct graph_edge *forward_edge; |
1709 | + } *vertex; |
1710 | + GRAPH_WEIGHT weight; |
1711 | + struct graph_edge *next_edge; |
1712 | +} GRAPH_EDGE; |
1713 | + |
1714 | +typedef struct graph_vertex GRAPH_VERTEX; |
1715 | + |
1716 | + |
1717 | +/* |
1718 | + A rough internal storage system for a set |
1719 | +*/ |
1720 | +/* this below is fully gross and will definitely change */ |
1721 | +typedef struct graphstore { |
1722 | + GRAPH_VERTEX *vertex; /* changed to ptr when integrating into MySQL */ |
1723 | + struct graphstore *next; |
1724 | +} GRAPHSTORE; |
1725 | + |
1726 | +#ifdef __cplusplus |
1727 | +extern "C" { |
1728 | +#endif |
1729 | + |
1730 | +/* public function declarations */ |
1731 | +int graphstore_insert (GRAPHSTORE **gspp, GRAPH_TUPLE *gtp); |
1732 | +GRAPH_SET *graphstore_query (GRAPHSTORE *gsp, GRAPH_TUPLE *gtp); |
1733 | +int free_graph_set (GRAPH_SET *gsetp); |
1734 | + |
1735 | +#ifdef __cplusplus |
1736 | +} |
1737 | +#endif |
1738 | + |
1739 | +/* end of graphstore.h */ |
1740 | \ No newline at end of file |
1741 | |
1742 | === added file 'oqgraph/ha_oqgraph.cc' |
1743 | --- oqgraph/ha_oqgraph.cc 1970-01-01 00:00:00 +0000 |
1744 | +++ oqgraph/ha_oqgraph.cc 2009-10-25 07:55:23 +0000 |
1745 | @@ -0,0 +1,884 @@ |
1746 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
1747 | + Portions of this file copyright (C) 2000-2006 MySQL AB |
1748 | + |
1749 | + This program is free software; you can redistribute it and/or modify |
1750 | + it under the terms of the GNU General Public License as published by |
1751 | + the Free Software Foundation; version 2 of the License. |
1752 | + |
1753 | + This program is distributed in the hope that it will be useful, |
1754 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
1755 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1756 | + GNU General Public License for more details. |
1757 | + |
1758 | + You should have received a copy of the GNU General Public License |
1759 | + along with this program; if not, write to the Free Software |
1760 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
1761 | + |
1762 | +/* ====================================================================== |
1763 | + Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
1764 | + Mk.II implementation by Antony Curtis & Arjen Lentz |
1765 | + For more information, documentation, support, enhancement engineering, |
1766 | + and non-GPL licensing, see http://openquery.com/graph |
1767 | + or contact graph@openquery.com |
1768 | + For packaged binaries, see http://ourdelta.org |
1769 | + ====================================================================== |
1770 | +*/ |
1771 | + |
1772 | +#include "drizzled/server_includes.h" |
1773 | +#include "drizzled/error.h" |
1774 | +#include "drizzled/plugin.h" |
1775 | +#include "drizzled/session.h" |
1776 | +#include "drizzled/table.h" |
1777 | +#include "drizzled/plugin/storage_engine.h" |
1778 | + |
1779 | +#include "ha_oqgraph.h" |
1780 | +#include "open_tables.h" |
1781 | +#include "graphcore.h" |
1782 | + |
1783 | +#define OQGRAPH_STATS_UPDATE_THRESHOLD 10 |
1784 | + |
1785 | +using namespace std; |
1786 | +using namespace open_query; |
1787 | + |
1788 | +static drizzled::plugin::StorageEngine *oqgraph_engine_ptr= NULL; |
1789 | + |
1790 | +static const char *ha_oqgraph_exts[] = |
1791 | +{ |
1792 | + NULL |
1793 | +}; |
1794 | + |
1795 | +class OqgraphEngine : public drizzled::plugin::StorageEngine |
1796 | +{ |
1797 | +public: |
1798 | + OqgraphEngine(string name_arg) |
1799 | + : |
1800 | + drizzled::plugin::StorageEngine(name_arg, |
1801 | + HTON_CAN_RECREATE) |
1802 | + {} |
1803 | + |
1804 | + const char **bas_ext() const |
1805 | + { |
1806 | + return ha_oqgraph_exts; |
1807 | + } |
1808 | + |
1809 | + virtual Cursor *create(TableShare *table, |
1810 | + MEM_ROOT *mem_root) |
1811 | + { |
1812 | + return new(mem_root) ha_oqgraph(this, table); |
1813 | + } |
1814 | + |
1815 | + int createTableImplementation(Session *session, |
1816 | + const char *table_name, |
1817 | + Table *form, |
1818 | + HA_CREATE_INFO *create_info, |
1819 | + drizzled::message::Table *); |
1820 | + |
1821 | + int deleteTableImplementation(Session *session, |
1822 | + const string table_path); |
1823 | + |
1824 | + int renameTableImplementation(Session *session, |
1825 | + const char *from, |
1826 | + const char *to); |
1827 | +}; |
1828 | + |
1829 | +#define STATISTIC_INCREMENT(X) ha_statistic_increment(&SSV::X) |
1830 | +#define MOVE(X) move_field_offset(X) |
1831 | +#define RECORDS stats.records |
1832 | + |
1833 | +static int oqgraph_init(drizzled::plugin::Registry ®istry) |
1834 | +{ |
1835 | + oqgraph_engine_ptr= new(std::nothrow) OqgraphEngine("OQGRAPH"); |
1836 | + if (! oqgraph_engine_ptr) |
1837 | + { |
1838 | + return 1; |
1839 | + } |
1840 | + |
1841 | + registry.add(oqgraph_engine_ptr); |
1842 | + |
1843 | + return 0; |
1844 | +} |
1845 | + |
1846 | +static int oqgraph_fini(drizzled::plugin::Registry ®istry) |
1847 | +{ |
1848 | + registry.remove(oqgraph_engine_ptr); |
1849 | + delete oqgraph_engine_ptr; |
1850 | + |
1851 | + return 0; |
1852 | +} |
1853 | + |
1854 | +static int error_code(int res) |
1855 | +{ |
1856 | + switch (res) |
1857 | + { |
1858 | + case oqgraph::OK: |
1859 | + return 0; |
1860 | + case oqgraph::NO_MORE_DATA: |
1861 | + return HA_ERR_END_OF_FILE; |
1862 | + case oqgraph::EDGE_NOT_FOUND: |
1863 | + return HA_ERR_KEY_NOT_FOUND; |
1864 | + case oqgraph::INVALID_WEIGHT: |
1865 | + return HA_ERR_AUTOINC_ERANGE; |
1866 | + case oqgraph::DUPLICATE_EDGE: |
1867 | + return HA_ERR_FOUND_DUPP_KEY; |
1868 | + case oqgraph::CANNOT_ADD_VERTEX: |
1869 | + case oqgraph::CANNOT_ADD_EDGE: |
1870 | + return HA_ERR_RECORD_FILE_FULL; |
1871 | + case oqgraph::MISC_FAIL: |
1872 | + default: |
1873 | + return HA_ERR_CRASHED_ON_USAGE; |
1874 | + } |
1875 | +} |
1876 | + |
1877 | +/** |
1878 | + * Check if table complies with our designated structure |
1879 | + * |
1880 | + * ColName Type Attributes |
1881 | + * ======= ======== ============= |
1882 | + * latch INT NULL |
1883 | + * origid BIGINT NULL |
1884 | + * destid BIGINT NULL |
1885 | + * weight DOUBLE NULL |
1886 | + * seq BIGINT NULL |
1887 | + * linkid BIGINT NULL |
1888 | + * ================================= |
1889 | + * |
1890 | + CREATE TABLE foo ( |
1891 | + latch INT NULL, |
1892 | + origid BIGINT NULL, |
1893 | + destid BIGINT NULL, |
1894 | + weight DOUBLE NULL, |
1895 | + seq BIGINT NULL, |
1896 | + linkid BIGINT NULL, |
1897 | + KEY (latch, origid, destid) USING HASH, |
1898 | + KEY (latch, destid, origid) USING HASH |
1899 | + ) ENGINE=OQGRAPH |
1900 | + |
1901 | + */ |
1902 | +static int oqgraph_check_table_structure (Table *table_arg) |
1903 | +{ |
1904 | + int i; |
1905 | + struct { const char *colname; int coltype; } skel[] = { |
1906 | + { "latch" , DRIZZLE_TYPE_LONG }, |
1907 | + { "origid", DRIZZLE_TYPE_LONGLONG }, |
1908 | + { "destid", DRIZZLE_TYPE_LONGLONG }, |
1909 | + { "weight", DRIZZLE_TYPE_DOUBLE }, |
1910 | + { "seq" , DRIZZLE_TYPE_LONGLONG }, |
1911 | + { "linkid", DRIZZLE_TYPE_LONGLONG }, |
1912 | + { NULL , 0} |
1913 | + }; |
1914 | + |
1915 | + Field **field= table_arg->field; |
1916 | + for (i= 0; *field && skel[i].colname; i++, field++) { |
1917 | + /* Check Column Type */ |
1918 | + if ((*field)->type() != skel[i].coltype) |
1919 | + return -1; |
1920 | + /* Check THAT NOT NULL isn't set */ |
1921 | + if ((*field)->flags & NOT_NULL_FLAG) |
1922 | + return -1; |
1923 | + /* Check the column name */ |
1924 | + if (strcmp(skel[i].colname,(*field)->field_name)) |
1925 | + return -1; |
1926 | + } |
1927 | + |
1928 | + if (skel[i].colname || *field || !table_arg->key_info || !table_arg->s->keys) |
1929 | + return -1; |
1930 | + |
1931 | + KEY *key= table_arg->key_info; |
1932 | + for (uint32_t x= 0; x < table_arg->s->keys; ++x, ++key) |
1933 | + { |
1934 | + Field **ot_field= table_arg->field; |
1935 | + /* check that the first key part is the latch and it is a hash key */ |
1936 | + if (!(ot_field[0] == key->key_part[0].field && |
1937 | + HA_KEY_ALG_HASH == key->algorithm)) |
1938 | + return -1; |
1939 | + if (key->key_parts == 3) |
1940 | + { |
1941 | + /* KEY (latch, origid, destid) USING HASH */ |
1942 | + /* KEY (latch, destid, origid) USING HASH */ |
1943 | + if (! (ot_field[1] == key->key_part[1].field && |
1944 | + ot_field[2] == key->key_part[2].field) && |
1945 | + ! (ot_field[1] == key->key_part[2].field && |
1946 | + ot_field[2] == key->key_part[1].field)) |
1947 | + return -1; |
1948 | + } |
1949 | + else |
1950 | + return -1; |
1951 | + } |
1952 | + |
1953 | + return 0; |
1954 | +} |
1955 | + |
1956 | +/***************************************************************************** |
1957 | +** OQGRAPH tables |
1958 | +*****************************************************************************/ |
1959 | + |
1960 | +ha_oqgraph::ha_oqgraph(drizzled::plugin::StorageEngine *engine_arg, |
1961 | + TableShare *table_arg) |
1962 | + : |
1963 | + Cursor(engine_arg, table_arg), |
1964 | + share(0), |
1965 | + graph(0), |
1966 | + records_changed(0), |
1967 | + key_stat_version(0) |
1968 | +{ } |
1969 | + |
1970 | + |
1971 | + |
1972 | +uint64_t ha_oqgraph::table_flags() const |
1973 | +{ |
1974 | + return (HA_NO_BLOBS | HA_NULL_IN_KEY | |
1975 | + HA_REC_NOT_IN_SEQ); |
1976 | +} |
1977 | + |
1978 | +uint32_t ha_oqgraph::index_flags(uint32_t, uint32_t, bool) const |
1979 | +{ |
1980 | + return (HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR); |
1981 | +} |
1982 | + |
1983 | +int ha_oqgraph::open(const char *name, int, uint32_t) |
1984 | +{ |
1985 | + OpenTables &open_tables= OpenTables::singleton(); |
1986 | + if ((share = open_tables.getShare(name))) |
1987 | + { |
1988 | + ref_length= oqgraph::sizeof_ref; |
1989 | + } |
1990 | + |
1991 | + if (share) |
1992 | + { |
1993 | + /* Initialize variables for the opened table */ |
1994 | + thr_lock_data_init(&share->lock, &lock, NULL); |
1995 | + |
1996 | + graph= oqgraph::create(share->graph); |
1997 | + |
1998 | + /* |
1999 | + We cannot run update_key_stats() here because we do not have a |
2000 | + lock on the table. The 'records' count might just be changed |
2001 | + temporarily at this moment and we might get wrong statistics (Bug |
2002 | + #10178). Instead we request for update. This will be done in |
2003 | + ha_oqgraph::info(), which is always called before key statistics are |
2004 | + used. |
2005 | + */ |
2006 | + key_stat_version= share->key_stat_version-1; |
2007 | + } |
2008 | + |
2009 | + return (share ? 0 : 1); |
2010 | +} |
2011 | + |
2012 | +int ha_oqgraph::close(void) |
2013 | +{ |
2014 | + oqgraph::free(graph); |
2015 | + graph= 0; |
2016 | + OpenTables &open_tables= OpenTables::singleton(); |
2017 | + open_tables.freeShare(); |
2018 | + return error_code(0); |
2019 | +} |
2020 | + |
2021 | +void ha_oqgraph::update_key_stats() |
2022 | +{ |
2023 | + for (uint i= 0; i < table->s->keys; i++) |
2024 | + { |
2025 | + KEY *key=table->key_info+i; |
2026 | + if (!key->rec_per_key) |
2027 | + continue; |
2028 | + if (key->algorithm != HA_KEY_ALG_BTREE) |
2029 | + { |
2030 | + if (key->flags & HA_NOSAME) |
2031 | + key->rec_per_key[key->key_parts-1]= 1; |
2032 | + else |
2033 | + { |
2034 | + unsigned vertices= graph->vertices_count(); |
2035 | + unsigned edges= graph->edges_count(); |
2036 | + uint no_records= vertices ? 2 * (edges + vertices) / vertices : 2; |
2037 | + if (no_records < 2) |
2038 | + no_records= 2; |
2039 | + key->rec_per_key[key->key_parts-1]= no_records; |
2040 | + } |
2041 | + } |
2042 | + } |
2043 | + records_changed= 0; |
2044 | + /* At the end of update_key_stats() we can proudly claim they are OK. */ |
2045 | + key_stat_version= share->key_stat_version; |
2046 | +} |
2047 | + |
2048 | + |
2049 | +int ha_oqgraph::write_row(unsigned char *buf) |
2050 | +{ |
2051 | + int res= oqgraph::MISC_FAIL; |
2052 | + Field ** const field= table->field; |
2053 | + STATISTIC_INCREMENT(ha_write_count); |
2054 | + |
2055 | + my_bitmap_map *old_map= table->use_all_columns(table->read_set); |
2056 | + ptrdiff_t ptrdiff= buf - table->record[0]; |
2057 | + |
2058 | + if (ptrdiff) |
2059 | + { |
2060 | + field[1]->MOVE(ptrdiff); |
2061 | + field[2]->MOVE(ptrdiff); |
2062 | + field[3]->MOVE(ptrdiff); |
2063 | + } |
2064 | + |
2065 | + if (!field[1]->is_null() && !field[2]->is_null()) |
2066 | + { |
2067 | + VertexID orig_id= (VertexID) field[1]->val_int(); |
2068 | + VertexID dest_id= (VertexID) field[2]->val_int(); |
2069 | + EdgeWeight weight= 1; |
2070 | + |
2071 | + if (!field[3]->is_null()) |
2072 | + weight= (EdgeWeight) field[3]->val_real(); |
2073 | + |
2074 | + if (!(res= graph->insert_edge(orig_id, dest_id, weight, replace_dups))) |
2075 | + { |
2076 | + ++records_changed; |
2077 | + share->records++; |
2078 | + } |
2079 | + if (res == oqgraph::DUPLICATE_EDGE && ignore_dups && !insert_dups) |
2080 | + res= oqgraph::OK; |
2081 | + } |
2082 | + |
2083 | + if (ptrdiff) |
2084 | + { |
2085 | + field[1]->MOVE(-ptrdiff); |
2086 | + field[2]->MOVE(-ptrdiff); |
2087 | + field[3]->MOVE(-ptrdiff); |
2088 | + } |
2089 | + table->restore_column_map(old_map); |
2090 | + |
2091 | + if (!res && records_changed*OQGRAPH_STATS_UPDATE_THRESHOLD > share->records) |
2092 | + { |
2093 | + /* |
2094 | + We can perform this safely since only one writer at the time is |
2095 | + allowed on the table. |
2096 | + */ |
2097 | + share->key_stat_version++; |
2098 | + } |
2099 | + |
2100 | + return error_code(res); |
2101 | +} |
2102 | + |
2103 | +int ha_oqgraph::update_row(const unsigned char * old, unsigned char * buf) |
2104 | +{ |
2105 | + int res= oqgraph::MISC_FAIL; |
2106 | + VertexID orig_id, dest_id; |
2107 | + EdgeWeight weight= 1; |
2108 | + Field **field= table->field; |
2109 | + STATISTIC_INCREMENT(ha_update_count); |
2110 | + |
2111 | + my_bitmap_map *old_map= table->use_all_columns(table->read_set); |
2112 | + ptrdiff_t ptrdiff= buf - table->record[0]; |
2113 | + |
2114 | + if (ptrdiff) |
2115 | + { |
2116 | + field[0]->MOVE(ptrdiff); |
2117 | + field[1]->MOVE(ptrdiff); |
2118 | + field[2]->MOVE(ptrdiff); |
2119 | + field[3]->MOVE(ptrdiff); |
2120 | + } |
2121 | + |
2122 | + if (inited == INDEX || inited == RND) |
2123 | + { |
2124 | + VertexID *origp= 0, *destp= 0; |
2125 | + EdgeWeight *weightp= 0; |
2126 | + if (!field[1]->is_null()) |
2127 | + *(origp= &orig_id)= (VertexID) field[1]->val_int(); |
2128 | + if (!field[2]->is_null()) |
2129 | + *(destp= &dest_id)= (VertexID) field[2]->val_int(); |
2130 | + if (!field[3]->is_null()) |
2131 | + *(weightp= &weight)= (EdgeWeight) field[3]->val_real(); |
2132 | + |
2133 | + ptrdiff_t ptrdiff2= old - buf; |
2134 | + |
2135 | + field[0]->MOVE(ptrdiff2); |
2136 | + field[1]->MOVE(ptrdiff2); |
2137 | + field[2]->MOVE(ptrdiff2); |
2138 | + field[3]->MOVE(ptrdiff2); |
2139 | + |
2140 | + if (field[0]->is_null()) |
2141 | + { |
2142 | + if (!origp == field[1]->is_null() && |
2143 | + *origp == (VertexID) field[1]->val_int()) |
2144 | + origp= 0; |
2145 | + if (!destp == field[2]->is_null() && |
2146 | + *destp == (VertexID) field[2]->val_int()) |
2147 | + origp= 0; |
2148 | + if (!weightp == field[3]->is_null() && |
2149 | + *weightp == (VertexID) field[3]->val_real()) |
2150 | + weightp= 0; |
2151 | + |
2152 | + if (!(res= graph->modify_edge(oqgraph::current_row(), |
2153 | + origp, destp, weightp, replace_dups))) |
2154 | + ++records_changed; |
2155 | + else if (ignore_dups && res == oqgraph::DUPLICATE_EDGE) |
2156 | + res= oqgraph::OK; |
2157 | + } |
2158 | + |
2159 | + field[0]->MOVE(-ptrdiff2); |
2160 | + field[1]->MOVE(-ptrdiff2); |
2161 | + field[2]->MOVE(-ptrdiff2); |
2162 | + field[3]->MOVE(-ptrdiff2); |
2163 | + } |
2164 | + |
2165 | + if (ptrdiff) |
2166 | + { |
2167 | + field[0]->MOVE(-ptrdiff); |
2168 | + field[1]->MOVE(-ptrdiff); |
2169 | + field[2]->MOVE(-ptrdiff); |
2170 | + field[3]->MOVE(-ptrdiff); |
2171 | + } |
2172 | + table->restore_column_map(old_map); |
2173 | + |
2174 | + if (!res && records_changed*OQGRAPH_STATS_UPDATE_THRESHOLD > share->records) |
2175 | + { |
2176 | + /* |
2177 | + We can perform this safely since only one writer at the time is |
2178 | + allowed on the table. |
2179 | + */ |
2180 | + share->key_stat_version++; |
2181 | + } |
2182 | + return error_code(res); |
2183 | +} |
2184 | + |
2185 | +int ha_oqgraph::delete_row(const unsigned char * buf) |
2186 | +{ |
2187 | + int res= oqgraph::EDGE_NOT_FOUND; |
2188 | + Field **field= table->field; |
2189 | + STATISTIC_INCREMENT(ha_delete_count); |
2190 | + |
2191 | + if (inited == INDEX || inited == RND) |
2192 | + { |
2193 | + if ((res= graph->delete_edge(oqgraph::current_row())) == oqgraph::OK) |
2194 | + { |
2195 | + ++records_changed; |
2196 | + share->records--; |
2197 | + } |
2198 | + } |
2199 | + if (res != oqgraph::OK) |
2200 | + { |
2201 | + my_bitmap_map *old_map= table->use_all_columns(table->read_set); |
2202 | + ptrdiff_t ptrdiff= buf - table->record[0]; |
2203 | + |
2204 | + if (ptrdiff) |
2205 | + { |
2206 | + field[0]->MOVE(ptrdiff); |
2207 | + field[1]->MOVE(ptrdiff); |
2208 | + field[2]->MOVE(ptrdiff); |
2209 | + } |
2210 | + |
2211 | + if (field[0]->is_null() && !field[1]->is_null() && !field[2]->is_null()) |
2212 | + { |
2213 | + VertexID orig_id= (VertexID) field[1]->val_int(); |
2214 | + VertexID dest_id= (VertexID) field[2]->val_int(); |
2215 | + |
2216 | + if ((res= graph->delete_edge(orig_id, dest_id)) == oqgraph::OK) |
2217 | + { |
2218 | + ++records_changed; |
2219 | + share->records--; |
2220 | + } |
2221 | + } |
2222 | + |
2223 | + if (ptrdiff) |
2224 | + { |
2225 | + field[0]->MOVE(-ptrdiff); |
2226 | + field[1]->MOVE(-ptrdiff); |
2227 | + field[2]->MOVE(-ptrdiff); |
2228 | + } |
2229 | + table->restore_column_map(old_map); |
2230 | + } |
2231 | + |
2232 | + if (!res && table->s->tmp_table == NO_TMP_TABLE && |
2233 | + records_changed*OQGRAPH_STATS_UPDATE_THRESHOLD > share->records) |
2234 | + { |
2235 | + /* |
2236 | + We can perform this safely since only one writer at the time is |
2237 | + allowed on the table. |
2238 | + */ |
2239 | + share->key_stat_version++; |
2240 | + } |
2241 | + return error_code(res); |
2242 | +} |
2243 | + |
2244 | +int ha_oqgraph::index_read(unsigned char * buf, const unsigned char * key, uint key_len, |
2245 | + enum ha_rkey_function find_flag) |
2246 | +{ |
2247 | + assert(inited==INDEX); |
2248 | + return index_read_idx(buf, active_index, key, key_len, find_flag); |
2249 | +} |
2250 | + |
2251 | +int ha_oqgraph::index_next_same(unsigned char *buf, const unsigned char *, uint) |
2252 | +{ |
2253 | + int res; |
2254 | + open_query::row row; |
2255 | + assert(inited==INDEX); |
2256 | + STATISTIC_INCREMENT(ha_read_key_count); |
2257 | + if (!(res= graph->fetch_row(row))) |
2258 | + res= fill_record(buf, row); |
2259 | + table->status= res ? STATUS_NOT_FOUND : 0; |
2260 | + return error_code(res); |
2261 | +} |
2262 | + |
2263 | +int ha_oqgraph::index_read_idx(unsigned char * buf, |
2264 | + uint32_t index, |
2265 | + const unsigned char * key, |
2266 | + uint32_t key_len, enum ha_rkey_function) |
2267 | +{ |
2268 | + Field **field= table->field; |
2269 | + KEY *key_info= table->key_info + index; |
2270 | + int res; |
2271 | + VertexID orig_id, dest_id; |
2272 | + int latch; |
2273 | + VertexID *orig_idp=0, *dest_idp=0; |
2274 | + int *latchp=0; |
2275 | + open_query::row row; |
2276 | + STATISTIC_INCREMENT(ha_read_key_count); |
2277 | + |
2278 | + memcpy(buf, table->s->default_values, table->s->reclength); |
2279 | + key_restore(buf, (unsigned char*) key, key_info, key_len); |
2280 | + |
2281 | + my_bitmap_map *old_map= table->use_all_columns(table->read_set); |
2282 | + ptrdiff_t ptrdiff= buf - table->record[0]; |
2283 | + |
2284 | + if (ptrdiff) |
2285 | + { |
2286 | + field[0]->MOVE(ptrdiff); |
2287 | + field[1]->MOVE(ptrdiff); |
2288 | + field[2]->MOVE(ptrdiff); |
2289 | + } |
2290 | + |
2291 | + if (!field[0]->is_null()) |
2292 | + { |
2293 | + latch= (int) field[0]->val_int(); |
2294 | + latchp= &latch; |
2295 | + } |
2296 | + |
2297 | + if (!field[1]->is_null()) |
2298 | + { |
2299 | + orig_id= (VertexID) field[1]->val_int(); |
2300 | + orig_idp= &orig_id; |
2301 | + } |
2302 | + |
2303 | + if (!field[2]->is_null()) |
2304 | + { |
2305 | + dest_id= (VertexID) field[2]->val_int(); |
2306 | + dest_idp= &dest_id; |
2307 | + } |
2308 | + |
2309 | + if (ptrdiff) |
2310 | + { |
2311 | + field[0]->MOVE(-ptrdiff); |
2312 | + field[1]->MOVE(-ptrdiff); |
2313 | + field[2]->MOVE(-ptrdiff); |
2314 | + } |
2315 | + table->restore_column_map(old_map); |
2316 | + |
2317 | + res= graph->search(latchp, orig_idp, dest_idp); |
2318 | + |
2319 | + if (!res && !(res= graph->fetch_row(row))) |
2320 | + res= fill_record(buf, row); |
2321 | + table->status = res ? STATUS_NOT_FOUND : 0; |
2322 | + return error_code(res); |
2323 | +} |
2324 | + |
2325 | +int ha_oqgraph::fill_record(unsigned char *record, const open_query::row &row) |
2326 | +{ |
2327 | + Field **field= table->field; |
2328 | + |
2329 | + memcpy(record, table->s->default_values, table->s->reclength); |
2330 | + |
2331 | + my_bitmap_map *old_map= table->use_all_columns(table->write_set); |
2332 | + ptrdiff_t ptrdiff= record - table->record[0]; |
2333 | + |
2334 | + if (ptrdiff) |
2335 | + { |
2336 | + field[0]->MOVE(ptrdiff); |
2337 | + field[1]->MOVE(ptrdiff); |
2338 | + field[2]->MOVE(ptrdiff); |
2339 | + field[3]->MOVE(ptrdiff); |
2340 | + field[4]->MOVE(ptrdiff); |
2341 | + field[5]->MOVE(ptrdiff); |
2342 | + } |
2343 | + |
2344 | + table->restoreRecordAsDefault(); |
2345 | + table->setWriteSet(0); |
2346 | + table->setWriteSet(1); |
2347 | + table->setWriteSet(2); |
2348 | + table->setWriteSet(3); |
2349 | + table->setWriteSet(4); |
2350 | + table->setWriteSet(5); |
2351 | + |
2352 | + // just each field specifically, no sense iterating |
2353 | + if (row.latch_indicator) |
2354 | + { |
2355 | + field[0]->set_notnull(); |
2356 | + field[0]->store((uint64_t) row.latch); |
2357 | + } |
2358 | + |
2359 | + if (row.orig_indicator) |
2360 | + { |
2361 | + field[1]->set_notnull(); |
2362 | + field[1]->store((uint64_t) row.orig); |
2363 | + } |
2364 | + |
2365 | + if (row.dest_indicator) |
2366 | + { |
2367 | + field[2]->set_notnull(); |
2368 | + field[2]->store((uint64_t) row.dest); |
2369 | + } |
2370 | + |
2371 | + if (row.weight_indicator) |
2372 | + { |
2373 | + field[3]->set_notnull(); |
2374 | + field[3]->store((double) row.weight); |
2375 | + } |
2376 | + |
2377 | + if (row.seq_indicator) |
2378 | + { |
2379 | + field[4]->set_notnull(); |
2380 | + field[4]->store((uint64_t) row.seq); |
2381 | + } |
2382 | + |
2383 | + if (row.link_indicator) |
2384 | + { |
2385 | + field[5]->set_notnull(); |
2386 | + field[5]->store((uint64_t) row.link); |
2387 | + } |
2388 | + |
2389 | + if (ptrdiff) |
2390 | + { |
2391 | + field[0]->MOVE(-ptrdiff); |
2392 | + field[1]->MOVE(-ptrdiff); |
2393 | + field[2]->MOVE(-ptrdiff); |
2394 | + field[3]->MOVE(-ptrdiff); |
2395 | + field[4]->MOVE(-ptrdiff); |
2396 | + field[5]->MOVE(-ptrdiff); |
2397 | + } |
2398 | + table->restore_column_map(old_map); |
2399 | + |
2400 | + return 0; |
2401 | +} |
2402 | + |
2403 | +int ha_oqgraph::rnd_init(bool scan) |
2404 | +{ |
2405 | + return error_code(graph->random(scan)); |
2406 | +} |
2407 | + |
2408 | +int ha_oqgraph::rnd_next(unsigned char *buf) |
2409 | +{ |
2410 | + int res; |
2411 | + open_query::row row; |
2412 | + STATISTIC_INCREMENT(ha_read_rnd_next_count); |
2413 | + if (!(res= graph->fetch_row(row))) |
2414 | + res= fill_record(buf, row); |
2415 | + table->status= res ? STATUS_NOT_FOUND: 0; |
2416 | + return error_code(res); |
2417 | +} |
2418 | + |
2419 | +int ha_oqgraph::rnd_pos(unsigned char * buf, unsigned char *pos) |
2420 | +{ |
2421 | + int res; |
2422 | + open_query::row row; |
2423 | + STATISTIC_INCREMENT(ha_read_rnd_count); |
2424 | + if (!(res= graph->fetch_row(row, pos))) |
2425 | + res= fill_record(buf, row); |
2426 | + table->status=res ? STATUS_NOT_FOUND: 0; |
2427 | + return error_code(res); |
2428 | +} |
2429 | + |
2430 | +void ha_oqgraph::position(const unsigned char *) |
2431 | +{ |
2432 | + graph->row_ref((void*) ref); // Ref is aligned |
2433 | +} |
2434 | + |
2435 | +int ha_oqgraph::cmp_ref(const unsigned char *ref1, const unsigned char *ref2) |
2436 | +{ |
2437 | + return memcmp(ref1, ref2, oqgraph::sizeof_ref); |
2438 | +} |
2439 | + |
2440 | +int ha_oqgraph::info(uint32_t) |
2441 | +{ |
2442 | + RECORDS= graph->vertices_count() + graph->edges_count(); |
2443 | +#if 0 |
2444 | + records= hp_info.records; |
2445 | + deleted= hp_info.deleted; |
2446 | + errkey= hp_info.errkey; |
2447 | + mean_rec_length= hp_info.reclength; |
2448 | + data_file_length= hp_info.data_length; |
2449 | + index_file_length= hp_info.index_length; |
2450 | + max_data_file_length= hp_info.max_records* hp_info.reclength; |
2451 | + delete_length= hp_info.deleted * hp_info.reclength; |
2452 | +#endif |
2453 | + /* |
2454 | + If info() is called for the first time after open(), we will still |
2455 | + have to update the key statistics. Hoping that a table lock is now |
2456 | + in place. |
2457 | + */ |
2458 | + if (key_stat_version != share->key_stat_version) |
2459 | + update_key_stats(); |
2460 | + return 0; |
2461 | +} |
2462 | + |
2463 | +int ha_oqgraph::extra(enum ha_extra_function operation) |
2464 | +{ |
2465 | + switch (operation) |
2466 | + { |
2467 | + case HA_EXTRA_IGNORE_DUP_KEY: |
2468 | + ignore_dups= true; |
2469 | + break; |
2470 | + case HA_EXTRA_NO_IGNORE_DUP_KEY: |
2471 | + ignore_dups= false; |
2472 | + insert_dups= false; |
2473 | + break; |
2474 | + case HA_EXTRA_WRITE_CAN_REPLACE: |
2475 | + replace_dups= true; |
2476 | + break; |
2477 | + case HA_EXTRA_WRITE_CANNOT_REPLACE: |
2478 | + replace_dups= false; |
2479 | + break; |
2480 | + case HA_EXTRA_INSERT_WITH_UPDATE: |
2481 | + insert_dups= true; |
2482 | + break; |
2483 | + default: |
2484 | + break; |
2485 | + } |
2486 | + return 0; |
2487 | +} |
2488 | + |
2489 | +int ha_oqgraph::delete_all_rows() |
2490 | +{ |
2491 | + int res; |
2492 | + if (!(res= graph->delete_all())) |
2493 | + { |
2494 | + share->records= 0; |
2495 | + } |
2496 | + |
2497 | + if (!res && table->s->tmp_table == NO_TMP_TABLE) |
2498 | + { |
2499 | + /* |
2500 | + We can perform this safely since only one writer at the time is |
2501 | + allowed on the table. |
2502 | + */ |
2503 | + share->key_stat_version++; |
2504 | + } |
2505 | + return error_code(res); |
2506 | +} |
2507 | + |
2508 | +int ha_oqgraph::external_lock(Session *, int) |
2509 | +{ |
2510 | + return 0; // No external locking |
2511 | +} |
2512 | + |
2513 | + |
2514 | +THR_LOCK_DATA **ha_oqgraph::store_lock(Session *, |
2515 | + THR_LOCK_DATA **to, |
2516 | + enum thr_lock_type lock_type) |
2517 | +{ |
2518 | + if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) |
2519 | + lock.type=lock_type; |
2520 | + *to++= &lock; |
2521 | + return to; |
2522 | +} |
2523 | + |
2524 | +/* |
2525 | + We have to ignore ENOENT entries as the HEAP table is created on open and |
2526 | + not when doing a CREATE on the table. |
2527 | +*/ |
2528 | + |
2529 | +int OqgraphEngine::deleteTableImplementation(Session *, |
2530 | + const string table_name) |
2531 | +{ |
2532 | + int res= 0; |
2533 | + OQGRAPH_INFO *share= NULL; |
2534 | + OpenTables &open_tables= OpenTables::singleton(); |
2535 | + if ((share= open_tables.getShare(table_name.c_str()))) |
2536 | + { |
2537 | + open_tables.freeShare(); |
2538 | + } |
2539 | + return error_code(res); |
2540 | +} |
2541 | + |
2542 | +int OqgraphEngine::renameTableImplementation(Session *, |
2543 | + const char * from, |
2544 | + const char * to) |
2545 | +{ |
2546 | + OpenTables &open_tables= OpenTables::singleton(); |
2547 | + if (OQGRAPH_INFO *share= open_tables.getShare(from)) |
2548 | + { |
2549 | + strcpy(share->name, to); |
2550 | + open_tables.updateShare(share); |
2551 | + } |
2552 | + return 0; |
2553 | +} |
2554 | + |
2555 | + |
2556 | +ha_rows ha_oqgraph::records_in_range(uint32_t inx, |
2557 | + key_range *min_key, |
2558 | + key_range *max_key) |
2559 | +{ |
2560 | + KEY *key=table->key_info+inx; |
2561 | + //if (key->algorithm == HA_KEY_ALG_BTREE) |
2562 | + // return btree_records_in_range(file, inx, min_key, max_key); |
2563 | + |
2564 | + if (!min_key || !max_key || |
2565 | + min_key->length != max_key->length || |
2566 | + min_key->length < key->key_length - key->key_part[2].store_length || |
2567 | + min_key->flag != HA_READ_KEY_EXACT || |
2568 | + max_key->flag != HA_READ_AFTER_KEY) |
2569 | + { |
2570 | + if (min_key->length == key->key_part[0].store_length) |
2571 | + { |
2572 | + // If latch is not null and equals 0, return # nodes |
2573 | + //assert(key->key_part[0].store_length == 3); |
2574 | + if (key->key_part[0].null_bit && !min_key->key[0] && |
2575 | + !min_key->key[1] && !min_key->key[2]) |
2576 | + return graph->vertices_count(); |
2577 | + } |
2578 | + return HA_POS_ERROR; // Can only use exact keys |
2579 | + } |
2580 | + |
2581 | + if (RECORDS <= 1) |
2582 | + return RECORDS; |
2583 | + |
2584 | + /* Assert that info() did run. We need current statistics here. */ |
2585 | + assert(key_stat_version == share->key_stat_version); |
2586 | + ha_rows result= key->rec_per_key[key->key_parts-1]; |
2587 | + |
2588 | + return result; |
2589 | +} |
2590 | + |
2591 | + |
2592 | +int OqgraphEngine::createTableImplementation(Session *, |
2593 | + const char *table_name, |
2594 | + Table *in_table, |
2595 | + HA_CREATE_INFO *, |
2596 | + drizzled::message::Table *) |
2597 | +{ |
2598 | + int res = -1; |
2599 | + OQGRAPH_INFO *share= NULL; |
2600 | + |
2601 | + OpenTables &open_tables= OpenTables::singleton(); |
2602 | + share= open_tables.getShare(table_name); |
2603 | + if (! share) |
2604 | + { |
2605 | + res= -1; |
2606 | + } |
2607 | + else |
2608 | + { |
2609 | + res= oqgraph_check_table_structure(in_table); |
2610 | + } |
2611 | + |
2612 | + return error_code(res); |
2613 | +} |
2614 | + |
2615 | + |
2616 | +drizzle_declare_plugin(oqgraph) |
2617 | +{ |
2618 | + "OQGRAPH", |
2619 | + "2.0", /* Version: 2.0 */ |
2620 | + "Arjen Lentz & Antony T Curtis, Open Query", |
2621 | + "Open Query Graph Computation Engine, stored in memory (http://openquery.com/graph)", |
2622 | + PLUGIN_LICENSE_GPL, |
2623 | + oqgraph_init, /* Plugin Init */ |
2624 | + oqgraph_fini, /* Plugin Deinit */ |
2625 | + NULL, /* status variables */ |
2626 | + NULL, /* system variables */ |
2627 | + NULL /* config options */ |
2628 | +} |
2629 | +drizzle_declare_plugin_end; |
2630 | |
2631 | === added file 'oqgraph/ha_oqgraph.h' |
2632 | --- oqgraph/ha_oqgraph.h 1970-01-01 00:00:00 +0000 |
2633 | +++ oqgraph/ha_oqgraph.h 2009-10-25 07:55:23 +0000 |
2634 | @@ -0,0 +1,118 @@ |
2635 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
2636 | + Portions of this file copyright (C) 2000-2006 MySQL AB |
2637 | + |
2638 | + This program is free software; you can redistribute it and/or modify |
2639 | + it under the terms of the GNU General Public License as published by |
2640 | + the Free Software Foundation; version 2 of the License. |
2641 | + |
2642 | + This program is distributed in the hope that it will be useful, |
2643 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
2644 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2645 | + GNU General Public License for more details. |
2646 | + |
2647 | + You should have received a copy of the GNU General Public License |
2648 | + along with this program; if not, write to the Free Software |
2649 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
2650 | + |
2651 | +/* ====================================================================== |
2652 | + Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
2653 | + Mk.II implementation by Antony Curtis & Arjen Lentz |
2654 | + For more information, documentation, support, enhancement engineering, |
2655 | + and non-GPL licensing, see http://openquery.com/graph |
2656 | + or contact graph@openquery.com |
2657 | + For packaged binaries, see http://ourdelta.org |
2658 | + ====================================================================== |
2659 | +*/ |
2660 | + |
2661 | +#ifndef OQGRAPH_HANDLER_HA_OQGRAPH_H |
2662 | +#define OQGRAPH_HANDLER_HA_OQGRAPH_H |
2663 | + |
2664 | +#include "drizzled/cursor.h" |
2665 | +#include "graphcore.h" |
2666 | + |
2667 | +struct oqgraph_info_st |
2668 | +{ |
2669 | + THR_LOCK lock; |
2670 | + open_query::oqgraph_share *graph; |
2671 | + uint use_count; |
2672 | + uint key_stat_version; |
2673 | + uint records; |
2674 | + bool dropped; |
2675 | + char name[FN_REFLEN+1]; |
2676 | +}; |
2677 | + |
2678 | +typedef struct oqgraph_info_st OQGRAPH_INFO; |
2679 | + |
2680 | +namespace open_query |
2681 | +{ |
2682 | + struct row; |
2683 | + class oqgraph; |
2684 | +} |
2685 | + |
2686 | +/* class for the the Open Query Graph cursor */ |
2687 | + |
2688 | +class ha_oqgraph: public Cursor |
2689 | +{ |
2690 | + OQGRAPH_INFO *share; |
2691 | + open_query::oqgraph *graph; |
2692 | + THR_LOCK_DATA lock; |
2693 | + /* number of records changed since last statistics update */ |
2694 | + uint records_changed; |
2695 | + uint key_stat_version; |
2696 | + bool replace_dups, ignore_dups, insert_dups; |
2697 | + |
2698 | + int fill_record(unsigned char*, const open_query::row&); |
2699 | + |
2700 | +public: |
2701 | + ha_oqgraph(drizzled::plugin::StorageEngine *engine, |
2702 | + TableShare *table_arg); |
2703 | + ~ha_oqgraph() {} |
2704 | + |
2705 | + uint64_t table_flags() const; |
2706 | + const char *table_type() const |
2707 | + { |
2708 | + return "OQGRAPH"; |
2709 | + } |
2710 | + const char *index_type(uint32_t) |
2711 | + { |
2712 | + return "HASH"; |
2713 | + } |
2714 | + /* Rows also use a fixed-size format */ |
2715 | + enum row_type get_row_type() const { return ROW_TYPE_FIXED; } |
2716 | + uint32_t index_flags(uint32_t inx, uint32_t part, bool all_parts) const; |
2717 | + uint32_t max_supported_keys() const { return MAX_KEY; } |
2718 | + uint32_t max_supported_key_part_length() const { return MAX_KEY_LENGTH; } |
2719 | + double scan_time() { return (double) 1000000000; } |
2720 | + double read_time(uint32_t, uint32_t, ha_rows) |
2721 | + { return 1; } |
2722 | + |
2723 | + int open(const char *name, int mode, uint32_t test_if_locked); |
2724 | + int close(void); |
2725 | + int write_row(unsigned char * buf); |
2726 | + int update_row(const unsigned char * old_data, unsigned char * new_data); |
2727 | + int delete_row(const unsigned char * buf); |
2728 | + int index_read(unsigned char * buf, const unsigned char * key, |
2729 | + uint32_t key_len, enum ha_rkey_function find_flag); |
2730 | + int index_read_idx(unsigned char * buf, uint32_t idx, const unsigned char * key, |
2731 | + uint32_t key_len, enum ha_rkey_function find_flag); |
2732 | + int index_next_same(unsigned char * buf, |
2733 | + const unsigned char * key, |
2734 | + uint32_t key_len); |
2735 | + int rnd_init(bool scan); |
2736 | + int rnd_next(unsigned char *buf); |
2737 | + int rnd_pos(unsigned char * buf, unsigned char *pos); |
2738 | + void position(const unsigned char *record); |
2739 | + int info(uint32_t); |
2740 | + int extra(enum ha_extra_function operation); |
2741 | + int external_lock(Session *session, int lock_type); |
2742 | + int delete_all_rows(void); |
2743 | + ha_rows records_in_range(uint32_t inx, key_range *min_key, key_range *max_key); |
2744 | + |
2745 | + THR_LOCK_DATA **store_lock(Session *session, THR_LOCK_DATA **to, |
2746 | + enum thr_lock_type lock_type); |
2747 | + int cmp_ref(const unsigned char *ref1, const unsigned char *ref2); |
2748 | +private: |
2749 | + void update_key_stats(); |
2750 | +}; |
2751 | + |
2752 | +#endif /* OQGRAPH_HANDLER_OQGRAPH_H */ |
2753 | |
2754 | === added file 'oqgraph/open_tables.cc' |
2755 | --- oqgraph/open_tables.cc 1970-01-01 00:00:00 +0000 |
2756 | +++ oqgraph/open_tables.cc 2009-10-25 07:55:23 +0000 |
2757 | @@ -0,0 +1,91 @@ |
2758 | +/* |
2759 | + * Copyright (C) 2009 Sun Microsystems |
2760 | + * |
2761 | + * This program is free software; you can redistribute it and/or modify |
2762 | + * it under the terms of the GNU General Public License as published by |
2763 | + * the Free Software Foundation; either version 2 of the License, or |
2764 | + * (at your option) any later version. |
2765 | + * |
2766 | + * This program is distributed in the hope that it will be useful, |
2767 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2768 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2769 | + * GNU General Public License for more details. |
2770 | + * |
2771 | + * You should have received a copy of the GNU General Public License |
2772 | + * along with this program; if not, write to the Free Software |
2773 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
2774 | + */ |
2775 | + |
2776 | +#include <drizzled/server_includes.h> |
2777 | +#include "ha_oqgraph.h" |
2778 | +#include "graphcore.h" |
2779 | +#include "open_tables.h" |
2780 | + |
2781 | +#include <string> |
2782 | +#include <map> |
2783 | + |
2784 | +using namespace std; |
2785 | +using namespace open_query; |
2786 | +using namespace drizzled; |
2787 | + |
2788 | +OQGRAPH_INFO *OpenTables::getShare(const char *name) |
2789 | +{ |
2790 | + map<const string, OQGRAPH_INFO *>::iterator it; |
2791 | + pthread_mutex_lock(&mutex); |
2792 | + |
2793 | + it= open_tables.find(name); |
2794 | + if (it != open_tables.end()) |
2795 | + { |
2796 | + share= (*it).second; |
2797 | + } |
2798 | + else |
2799 | + { |
2800 | + share= NULL; |
2801 | + } |
2802 | + |
2803 | + if (! share) |
2804 | + { |
2805 | + share= new(std::nothrow) OQGRAPH_INFO(); |
2806 | + if (! share) |
2807 | + { |
2808 | + pthread_mutex_unlock(&mutex); |
2809 | + return NULL; |
2810 | + } |
2811 | + share->use_count= 0; |
2812 | + share->key_stat_version= 0; |
2813 | + share->records= 0; |
2814 | + share->dropped= 0; |
2815 | + strcpy(share->name, name); |
2816 | + share->graph= oqgraph::create(); |
2817 | + if (! share->graph) |
2818 | + { |
2819 | + pthread_mutex_unlock(&mutex); |
2820 | + return NULL; |
2821 | + } |
2822 | + open_tables[name]= share; |
2823 | + thr_lock_init(&share->lock); |
2824 | + } |
2825 | + share->use_count++; |
2826 | + pthread_mutex_unlock(&mutex); |
2827 | + |
2828 | + return share; |
2829 | +} |
2830 | + |
2831 | +void OpenTables::freeShare() |
2832 | +{ |
2833 | + pthread_mutex_lock(&mutex); |
2834 | + share->use_count--; |
2835 | + if (share->use_count == 0) |
2836 | + { |
2837 | + open_tables.erase(share->name); |
2838 | + oqgraph::free(share->graph); |
2839 | + } |
2840 | + pthread_mutex_unlock(&mutex); |
2841 | +} |
2842 | + |
2843 | +void OpenTables::updateShare(OQGRAPH_INFO *in_share) |
2844 | +{ |
2845 | + pthread_mutex_lock(&mutex); |
2846 | + open_tables[in_share->name]= in_share; |
2847 | + pthread_mutex_unlock(&mutex); |
2848 | +} |
2849 | |
2850 | === added file 'oqgraph/open_tables.h' |
2851 | --- oqgraph/open_tables.h 1970-01-01 00:00:00 +0000 |
2852 | +++ oqgraph/open_tables.h 2009-10-25 07:55:23 +0000 |
2853 | @@ -0,0 +1,93 @@ |
2854 | +/* Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
2855 | + Portions of this file copyright (C) 2000-2006 MySQL AB |
2856 | + |
2857 | + This program is free software; you can redistribute it and/or modify |
2858 | + it under the terms of the GNU General Public License as published by |
2859 | + the Free Software Foundation; version 2 of the License. |
2860 | + |
2861 | + This program is distributed in the hope that it will be useful, |
2862 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
2863 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2864 | + GNU General Public License for more details. |
2865 | + |
2866 | + You should have received a copy of the GNU General Public License |
2867 | + along with this program; if not, write to the Free Software |
2868 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
2869 | + |
2870 | +/** |
2871 | + * @file |
2872 | + * |
2873 | + * Defines a class for how open tables will be tracked for this storage |
2874 | + * engine. |
2875 | + */ |
2876 | + |
2877 | +#ifndef OQGRAPH_OPEN_TABLES_H |
2878 | +#define OQGRAPH_OPEN_TABLES_H |
2879 | + |
2880 | +#include "ha_oqgraph.h" |
2881 | + |
2882 | +#include <string> |
2883 | +#include <vector> |
2884 | +#include <map> |
2885 | + |
2886 | +/** |
2887 | + * @class OpenTables |
2888 | + * |
2889 | + * Class which tracks all the open tables for the storage engine. |
2890 | + * Encapsulating this functionality in a class will make it easier for us to |
2891 | + * change things such as whether a std::map or HASH is used to lookup the |
2892 | + * open tables. |
2893 | + */ |
2894 | +class OpenTables |
2895 | +{ |
2896 | +public: |
2897 | + |
2898 | + static OpenTables &singleton() |
2899 | + { |
2900 | + static OpenTables open_tabs; |
2901 | + return open_tabs; |
2902 | + } |
2903 | + |
2904 | + /** |
2905 | + * Retrieve a share from the structure used to main the list of open |
2906 | + * shares. We do a lookup by name. |
2907 | + * |
2908 | + * @param[in] name name to lookup |
2909 | + * @return pointer to the appropriate table share |
2910 | + */ |
2911 | + OQGRAPH_INFO *getShare(const char *name); |
2912 | + |
2913 | + /** |
2914 | + * Erase a share from the structure used to maintain the list of shares. |
2915 | + */ |
2916 | + void freeShare(); |
2917 | + |
2918 | + void updateShare(OQGRAPH_INFO *share); |
2919 | + |
2920 | +private: |
2921 | + |
2922 | + pthread_mutex_t mutex; |
2923 | + |
2924 | + std::map<const std::string, OQGRAPH_INFO *> |
2925 | + open_tables; |
2926 | + |
2927 | + OQGRAPH_INFO *share; |
2928 | + |
2929 | + OpenTables() |
2930 | + : |
2931 | + mutex(), |
2932 | + open_tables(), |
2933 | + share(NULL) |
2934 | + { |
2935 | + pthread_mutex_init(&mutex, MY_MUTEX_INIT_FAST); |
2936 | + } |
2937 | + |
2938 | + ~OpenTables() |
2939 | + { |
2940 | + pthread_mutex_destroy(&mutex); |
2941 | + } |
2942 | + |
2943 | + OpenTables(const OpenTables&); |
2944 | +}; |
2945 | + |
2946 | +#endif /* OQGRAPH_OPEN_TABLES_H */ |
2947 | |
2948 | === added file 'oqgraph/oqgraph_config.h.in' |
2949 | --- oqgraph/oqgraph_config.h.in 1970-01-01 00:00:00 +0000 |
2950 | +++ oqgraph/oqgraph_config.h.in 2009-10-25 07:55:23 +0000 |
2951 | @@ -0,0 +1,73 @@ |
2952 | +/* src/oqgraph_config.h.in. Generated from configure.in by autoheader. */ |
2953 | + |
2954 | +/* Define to 1 if you have the <dlfcn.h> header file. */ |
2955 | +#undef HAVE_DLFCN_H |
2956 | + |
2957 | +/* Enables DTRACE Support */ |
2958 | +#undef HAVE_DTRACE |
2959 | + |
2960 | +/* Define to 1 if you have the <inttypes.h> header file. */ |
2961 | +#undef HAVE_INTTYPES_H |
2962 | + |
2963 | +/* Define to 1 if you have the <limits.h> header file. */ |
2964 | +#undef HAVE_LIMITS_H |
2965 | + |
2966 | +/* Define to 1 if you have the <memory.h> header file. */ |
2967 | +#undef HAVE_MEMORY_H |
2968 | + |
2969 | +/* Define to 1 if you have the <stdint.h> header file. */ |
2970 | +#undef HAVE_STDINT_H |
2971 | + |
2972 | +/* Define to 1 if you have the <stdlib.h> header file. */ |
2973 | +#undef HAVE_STDLIB_H |
2974 | + |
2975 | +/* Define to 1 if you have the <strings.h> header file. */ |
2976 | +#undef HAVE_STRINGS_H |
2977 | + |
2978 | +/* Define to 1 if you have the <string.h> header file. */ |
2979 | +#undef HAVE_STRING_H |
2980 | + |
2981 | +/* Define to 1 if you have the <syslimits.h> header file. */ |
2982 | +#undef HAVE_SYSLIMITS_H |
2983 | + |
2984 | +/* Define to 1 if you have the <sys/stat.h> header file. */ |
2985 | +#undef HAVE_SYS_STAT_H |
2986 | + |
2987 | +/* Define to 1 if you have the <sys/types.h> header file. */ |
2988 | +#undef HAVE_SYS_TYPES_H |
2989 | + |
2990 | +/* Define to 1 if you have the <unistd.h> header file. */ |
2991 | +#undef HAVE_UNISTD_H |
2992 | + |
2993 | +/* Source directory for MySQL */ |
2994 | +#undef MYSQL_SRC |
2995 | + |
2996 | +/* Name of package */ |
2997 | +#undef PACKAGE |
2998 | + |
2999 | +/* Define to the address where bug reports for this package should be sent. */ |
3000 | +#undef PACKAGE_BUGREPORT |
3001 | + |
3002 | +/* Define to the full name of this package. */ |
3003 | +#undef PACKAGE_NAME |
3004 | + |
3005 | +/* Define to the full name and version of this package. */ |
3006 | +#undef PACKAGE_STRING |
3007 | + |
3008 | +/* Define to the one symbol short name of this package. */ |
3009 | +#undef PACKAGE_TARNAME |
3010 | + |
3011 | +/* Define to the version of this package. */ |
3012 | +#undef PACKAGE_VERSION |
3013 | + |
3014 | +/* Define to 1 if you have the ANSI C header files. */ |
3015 | +#undef STDC_HEADERS |
3016 | + |
3017 | +/* Version number of package */ |
3018 | +#undef VERSION |
3019 | + |
3020 | +/* Define to empty if `const' does not conform to ANSI C. */ |
3021 | +#undef const |
3022 | + |
3023 | +/* Define to `unsigned int' if <sys/types.h> does not define. */ |
3024 | +#undef size_t |
3025 | |
3026 | === added file 'oqgraph/oqgraph_probes.d' |
3027 | --- oqgraph/oqgraph_probes.d 1970-01-01 00:00:00 +0000 |
3028 | +++ oqgraph/oqgraph_probes.d 2009-10-25 07:55:23 +0000 |
3029 | @@ -0,0 +1,19 @@ |
3030 | +/* Copyright (C) 2004-2005 MySQL AB |
3031 | + |
3032 | + This program is free software; you can redistribute it and/or modify |
3033 | + it under the terms of the GNU General Public License as published by |
3034 | + the Free Software Foundation; version 2 of the License. |
3035 | + |
3036 | + This program is distributed in the hope that it will be useful, |
3037 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
3038 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3039 | + GNU General Public License for more details. |
3040 | + |
3041 | + You should have received a copy of the GNU General Public License |
3042 | + along with this program; if not, write to the Free Software |
3043 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
3044 | + |
3045 | +provider oqgraph { |
3046 | + probe open(); |
3047 | + probe close(); |
3048 | +}; |
3049 | |
3050 | === added file 'oqgraph/oqgraph_probes.h' |
3051 | --- oqgraph/oqgraph_probes.h 1970-01-01 00:00:00 +0000 |
3052 | +++ oqgraph/oqgraph_probes.h 2009-10-25 07:55:23 +0000 |
3053 | @@ -0,0 +1,45 @@ |
3054 | +/* |
3055 | + * Generated by dtrace(1M). |
3056 | + */ |
3057 | + |
3058 | +#ifndef _OQGRAPH_PROBES_H |
3059 | +#define _OQGRAPH_PROBES_H |
3060 | + |
3061 | + |
3062 | + |
3063 | +#ifdef __cplusplus |
3064 | +extern "C" { |
3065 | +#endif |
3066 | + |
3067 | +#if _DTRACE_VERSION |
3068 | + |
3069 | +#define OQGRAPH_CLOSE() \ |
3070 | + __dtrace_oqgraph___close() |
3071 | +#define OQGRAPH_CLOSE_ENABLED() \ |
3072 | + __dtraceenabled_oqgraph___close() |
3073 | +#define OQGRAPH_OPEN() \ |
3074 | + __dtrace_oqgraph___open() |
3075 | +#define OQGRAPH_OPEN_ENABLED() \ |
3076 | + __dtraceenabled_oqgraph___open() |
3077 | + |
3078 | + |
3079 | +extern void __dtrace_oqgraph___close(void); |
3080 | +extern int __dtraceenabled_oqgraph___close(void); |
3081 | +extern void __dtrace_oqgraph___open(void); |
3082 | +extern int __dtraceenabled_oqgraph___open(void); |
3083 | + |
3084 | +#else |
3085 | + |
3086 | +#define OQGRAPH_CLOSE() |
3087 | +#define OQGRAPH_CLOSE_ENABLED() (0) |
3088 | +#define OQGRAPH_OPEN() |
3089 | +#define OQGRAPH_OPEN_ENABLED() (0) |
3090 | + |
3091 | +#endif |
3092 | + |
3093 | + |
3094 | +#ifdef __cplusplus |
3095 | +} |
3096 | +#endif |
3097 | + |
3098 | +#endif /* _OQGRAPH_PROBES_H */ |
3099 | |
3100 | === added file 'oqgraph/plugin.am' |
3101 | --- oqgraph/plugin.am 1970-01-01 00:00:00 +0000 |
3102 | +++ oqgraph/plugin.am 2009-10-25 07:55:23 +0000 |
3103 | @@ -0,0 +1,67 @@ |
3104 | +# Copyright (C) 2007-2009 Arjen G Lentz & Antony T Curtis for Open Query |
3105 | +# |
3106 | +# This program is free software; you can redistribute it and/or modify |
3107 | +# it under the terms of the GNU General Public License as published by |
3108 | +# the Free Software Foundation; version 2 of the License, or |
3109 | +# (at your option) any later version. |
3110 | +# |
3111 | +# This program is distributed in the hope that it will be useful, |
3112 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3113 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3114 | +# GNU General Public License for more details. |
3115 | +# |
3116 | +# You should have received a copy of the GNU General Public License |
3117 | +# along with this program; if not, write to the Free Software |
3118 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
3119 | + |
3120 | +# ====================================================================== |
3121 | +# Open Query Graph Computation Engine, based on a concept by Arjen Lentz |
3122 | +# Mk.II implementation by Antony Curtis & Arjen Lentz |
3123 | +# For more information, documentation, support, enhancement engineering, |
3124 | +# and non-GPL licensing, see http://openquery.com/graph |
3125 | +# or contact graph@openquery.com |
3126 | +# For packaged binaries, see http://ourdelta.org |
3127 | +# ====================================================================== |
3128 | + |
3129 | +BOOST_CXXFLAGS = -fexceptions -fimplicit-templates |
3130 | +#BOOST_CXXFLAGS+= -g |
3131 | +BOOST_CXXFLAGS+= -O3 -fomit-frame-pointer -fstrict-aliasing |
3132 | +BOOST_CXXFLAGS+= -momit-leaf-frame-pointer -falign-loops |
3133 | +BOOST_CXXFLAGS+= -fvisibility-inlines-hidden |
3134 | +BOOST_CXXFLAGS+= -funroll-loops -fno-trapping-math |
3135 | + |
3136 | +DTRACE = @DTRACE@ |
3137 | +DTRACEFLAGS = @DTRACEFLAGS@ |
3138 | +DTRACEFILES = .libs/liboqgraph_engine_la-ha_oqgraph.o |
3139 | + |
3140 | +#noinst_PROGRAMS = graphcore_test |
3141 | + |
3142 | +noinst_LTLIBRARIES+= plugin/oqgraph/liboqgraph.la |
3143 | +noinst_HEADERS+= plugin/oqgraph/graphcore.h \ |
3144 | + plugin/oqgraph/graphcore-graph.h \ |
3145 | + plugin/oqgraph/graphcore-types.h \ |
3146 | + plugin/oqgraph/graphstore.h |
3147 | + |
3148 | +#graphcore_test_SOURCES = graphcore-test.cc |
3149 | +#graphcore_test_LDADD = libgraphcore.la |
3150 | + |
3151 | +plugin_oqgraph_liboqgraph_la_SOURCES = plugin/oqgraph/graphcore.cc |
3152 | +plugin_oqgraph_liboqgraph_la_CXXFLAGS = @CXXFLAGS@ |
3153 | +#plugin_oqgraph_liboqgraph_la_CXXFLAGS = @CXXFLAGS@ ${BOOST_CXXFLAGS) |
3154 | +plugin_oqgraph_liboqgraph_la_LDFLAGS = -module |
3155 | +# -fimplicit-templates |
3156 | + |
3157 | +if HAVE_DTRACE |
3158 | + plugin_oqgraph_liboqgraph_la_LIBADD = oqgraph_probes.o |
3159 | +endif |
3160 | + |
3161 | +oqgraph_probes.h: oqgraph_probes.d |
3162 | + $(DTRACE) $(DTRACEFLAGS) -h -s oqgraph_probes.d |
3163 | + mv oqgraph_probes.h oqgraph_probes.h.bak |
3164 | + sed "s/#include <unistd.h>//g" oqgraph_probes.h.bak > oqgraph_probes.h |
3165 | + rm oqgraph_probes.h.bak |
3166 | + |
3167 | +oqgraph_probes.o: |
3168 | + $(DTRACE) $(DTRACEFLAGS) -G -s oqgraph_probes.d $(DTRACEFILES) |
3169 | + |
3170 | +# End |
3171 | |
3172 | === added file 'oqgraph/plugin.ini' |
3173 | --- oqgraph/plugin.ini 1970-01-01 00:00:00 +0000 |
3174 | +++ oqgraph/plugin.ini 2009-10-25 07:55:23 +0000 |
3175 | @@ -0,0 +1,9 @@ |
3176 | +[plugin] |
3177 | +name=oqgraph |
3178 | +title=Graph Storage Engine |
3179 | +description=Computation engine for handling hierarchies and graphs |
3180 | +load_by_default=yes |
3181 | +sources=ha_oqgraph.cc open_tables.cc |
3182 | +headers=ha_oqgraph.h open_tables.h |
3183 | +libs=plugin/oqgraph/liboqgraph.la |
3184 | +#testsuite=oqgraph |
3185 | |
3186 | === added directory 'oqgraph/tests' |
3187 | === added directory 'oqgraph/tests/r' |
3188 | === added file 'oqgraph/tests/r/basic.result' |
3189 | --- oqgraph/tests/r/basic.result 1970-01-01 00:00:00 +0000 |
3190 | +++ oqgraph/tests/r/basic.result 2009-10-25 07:55:23 +0000 |
3191 | @@ -0,0 +1,33 @@ |
3192 | +drop table if exists graph; |
3193 | +Warnings: |
3194 | +Note 1051 Unknown table 'graph' |
3195 | +create table graph ( |
3196 | +latch int null, |
3197 | +origid bigint null, |
3198 | +destid bigint null, |
3199 | +weight double null, |
3200 | +seq bigint null, |
3201 | +linkid bigint null, |
3202 | +key (latch, origid, destid) using hash, |
3203 | +key (latch, destid, origid) using hash |
3204 | +) engine=oqgraph; |
3205 | +insert into graph(origid, destid) values (1,2), (2,1); |
3206 | +insert into graph(origid, destid) values (1,3), (3,1); |
3207 | +insert into graph(origid, destid) values (3,4), (4,3); |
3208 | +insert into graph(origid, destid) values (3,5), (5,3); |
3209 | +insert into graph(origid, destid) values (5,6), (6,5); |
3210 | +select linkid from graph where latch = 2 and origid = 1 and weight = 1; |
3211 | +linkid |
3212 | +3 |
3213 | +2 |
3214 | +select linkid from graph where latch = 2 and origid = 1 and weight = 2; |
3215 | +linkid |
3216 | +5 |
3217 | +4 |
3218 | +select linkid from graph |
3219 | +where latch = 2 and origid = 1 and (weight = 1 or weight = 2); |
3220 | +linkid |
3221 | +5 |
3222 | +4 |
3223 | +3 |
3224 | +2 |
3225 | |
3226 | === added directory 'oqgraph/tests/t' |
3227 | === added file 'oqgraph/tests/t/basic.test' |
3228 | --- oqgraph/tests/t/basic.test 1970-01-01 00:00:00 +0000 |
3229 | +++ oqgraph/tests/t/basic.test 2009-10-25 07:55:23 +0000 |
3230 | @@ -0,0 +1,25 @@ |
3231 | +drop table if exists graph; |
3232 | + |
3233 | +create table graph ( |
3234 | +latch int null, |
3235 | +origid bigint null, |
3236 | +destid bigint null, |
3237 | +weight double null, |
3238 | +seq bigint null, |
3239 | +linkid bigint null, |
3240 | +key (latch, origid, destid) using hash, |
3241 | +key (latch, destid, origid) using hash |
3242 | +) engine=oqgraph; |
3243 | + |
3244 | +insert into graph(origid, destid) values (1,2), (2,1); |
3245 | +insert into graph(origid, destid) values (1,3), (3,1); |
3246 | +insert into graph(origid, destid) values (3,4), (4,3); |
3247 | +insert into graph(origid, destid) values (3,5), (5,3); |
3248 | +insert into graph(origid, destid) values (5,6), (6,5); |
3249 | + |
3250 | +select linkid from graph where latch = 2 and origid = 1 and weight = 1; |
3251 | + |
3252 | +select linkid from graph where latch = 2 and origid = 1 and weight = 2; |
3253 | + |
3254 | +select linkid from graph |
3255 | +where latch = 2 and origid = 1 and (weight = 1 or weight = 2); |
Hi!
I wanted to mess around with this engine and since I work mostly with drizzle, I ported it to drizzle. I thought I would submit the work I did to port the engine in case it is of any use to you guys.
I created a subdir named oqgraph that can simply be dropped in to the drizzle plugin directory. I also created a simple test suite for the engine that can be added under tests/suite in the drizzle tree.
I have a branch in drizzle for this work also:
lp:~posulliv/drizzle/oqgraph
If you check that branch out, you can just do the following:
$ ./config/autorun.sh && ./configure && make && make test
and it will automatically run the oqgraph test suite.
I removed the boost subdir for the drizzle port. For future work, I would need to make boost a compile time dependency for the plugin. I also need to investigate the usage of the bitmaps with this engine and make sure everything is ok here. Some weird assertion failures pop up with the read/write sets from time to time.
Anyway, feel free to use what I have done if you like. Let me know if it is of any use to you.
-Padraig