diff --git a/NEWS.md b/NEWS.md index bf696b19be7..e59c0c9a463 100644 --- a/NEWS.md +++ b/NEWS.md @@ -26,6 +26,7 @@ * pgr_degree * Error messages adjustment. + * New signature with only Edges SQL. ## pgRouting 3.7 diff --git a/doc/metrics/pgr_degree.rst b/doc/metrics/pgr_degree.rst index bdfa31e0067..b525bac6a4b 100644 --- a/doc/metrics/pgr_degree.rst +++ b/doc/metrics/pgr_degree.rst @@ -16,7 +16,8 @@ ``pgr_degree`` -- Proposed =============================================================================== -``pgr_degree`` — For each vertex in an undirected graph, return the count of edges incident to the vertex. +``pgr_degree`` — For each vertex in an undirected graph, return the count of +edges incident to the vertex. .. include:: proposed.rst @@ -28,6 +29,7 @@ .. rubric:: Version 3.8.0 * Error messages adjustment. +* New signature with only Edges SQL. .. rubric:: Version 3.4.0 @@ -37,10 +39,25 @@ Description ------------------------------------------------------------------------------- -Calculates the degree of the vertices of an **undirected** graph +Calculates the degree of the vertices of an undirected graph +The degree (or valency) of a vertex of a graph is the number of edges that are +incident to the vertex. -|Boost| Boost Graph Inside +- Works for **undirected** graphs. +- A loop contributes 2 to a vertex's degree. +- A vertex with degree 0 is called an isolated vertex. + + - Isolated vertex is not part of the result + +- Vertex not participating on the subgraph is considered and isolated vertex. +- There can be a ``dryrun`` execution and the code used to get the answer will + be shown in a PostgreSQL ``NOTICE``. + + - The code can be used as base code for the particular application + requirements. + +- No ordering is performed. Signatures ------------------------------------------------------------------------------- @@ -48,21 +65,59 @@ Signatures .. admonition:: \ \ :class: signatures + | pgr_degree(`Edges SQL`_ , [``dryrun``]) | pgr_degree(`Edges SQL`_ , `Vertex SQL`_, [``dryrun``]) | RETURNS SETOF |result-degree| | OR EMPTY SET -:Example: Extracting the vertex information +.. index:: + single: degree - Proposed ; Edges and Vertices - Proposed on v3.4 + +Edges +............................................................................... + +.. admonition:: \ \ + :class: signatures -pgr_degree can utilize output from `pgr_extractVertices` or can have `pgr_extractVertices` embedded in the call. -For decent size networks, it is best to prep your vertices table before hand and use that vertices table -for pgr_degree calls. + | pgr_degree(`Edges SQL`_ , [``dryrun``]) + + | RETURNS SETOF |result-degree| + | OR EMPTY SET + +:example: Get the degree of the vertices defined on the edges table .. literalinclude:: degree.queries :start-after: -- q1 :end-before: -- q2 +Edges and Vertices +............................................................................... + +.. admonition:: \ \ + :class: signatures + + | pgr_degree(`Edges SQL`_ , `Vertex SQL`_, [``dryrun``]) + + | RETURNS SETOF |result-degree| + | OR EMPTY SET + +:Example: Extracting the vertex information + +``pgr_degree`` can use :doc:`pgr_extractVertices` embedded in the call. + +For decent size networks, it is best to prepare your vertices table before hand +and use it on ``pgr_degree`` calls. (See `Using a vertex table`_) + +Calculate the degree of the nodes: + +.. literalinclude:: degree.queries + :start-after: -- q2 + :end-before: -- q3 + +.. index:: + single: degree- Proposed ; Edges - Proposed on v3.4 + Parameters ------------------------------------------------------------------------------- @@ -77,8 +132,6 @@ Parameter Type Description Optional parameters ------------------------------------------------------------------------------- -.. TODO move to pgRouting concepts - =========== ============= ========== ======================================= Parameter Type Default Description =========== ============= ========== ======================================= @@ -95,18 +148,44 @@ Inner Queries Edges SQL ............................................................................... -.. TODO move to pgRouting concepts +.. rubric:: For the `Edges and Vertices`_ signature: + +.. list-table:: + :width: 81 + :widths: auto + :header-rows: 1 + + * - Column + - Type + - Description + * - ``id`` + - ``BIGINT`` + - Identifier of the edge. + +For the `Edges`_ signature: + +.. list-table:: + :width: 81 + :widths: auto + :header-rows: 1 -================= =================== =================================== -Column Type Description -================= =================== =================================== -``id`` ``BIGINT`` Identifier of the edge. -================= =================== =================================== + * - Column + - Type + - Description + * - ``id`` + - ``BIGINT`` + - Identifier of the edge. + * - ``source`` + - ``BIGINT`` + - Identifier of the first end point vertex of the edge. + * - ``target`` + - ``BIGINT`` + - Identifier of the second end point vertex of the edge. Vertex SQL ............................................................................... -.. TODO move to pgRouting concepts +.. rubric:: For the `Edges and Vertices`_ signature: .. list-table:: :width: 81 @@ -136,8 +215,6 @@ Vertex SQL Result columns ------------------------------------------------------------------------------- -.. TODO move to pgRouting concepts - .. list-table:: :width: 81 :widths: auto @@ -159,12 +236,101 @@ Additional Examples .. contents:: :local: +Degree of a loop +............................................................................... + +A loop contributes 2 to a vertex's degree. + +.. graphviz:: + + graph G { + 2 [shape=circle;style=filled;color=green;fontsize=8;width=0.3;fixedsize=true]; + 2 -- 2 [label="1",fontsize=8]; + } + +.. rubric:: Using the `Edges`_ signature. + +.. literalinclude:: degree.queries + :start-after: -- q3 + :end-before: -- q4 + +.. rubric:: Using the `Edges and Vertices`_ signature. + +.. literalinclude:: degree.queries + :start-after: -- q4 + :end-before: -- q5 + Degree of a sub graph ............................................................................... +For the following is a subgraph of the :doc:`sampledata`: + +- :math:`E = \{(1, 5 \leftrightarrow 6), (1, 6 \leftrightarrow 10)\}` +- :math:`V = \{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17\}` + + +.. graphviz:: + + graph G { + 5,6,10 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true]; + 1,2,3,4,7,8,9,11,12,13,14,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true]; + + 5 -- 6 [label="1",fontsize=8]; + 10 -- 6 [label="2",fontsize=8]; + + 1 [pos="0,2!"]; + 2 [pos="0.5,3.5!"]; + 3 [pos="1,2!"]; + 4 [pos="2,3.5!"]; + 5 [pos="2,0!"]; + 6 [pos="2,1!"]; + 7 [pos="2,2!"]; + 8 [pos="2,3!"]; + 9 [pos="2,4!"]; + 10 [pos="3,1!"]; + 11 [pos="3,2!"]; + 12 [pos="3,3!"]; + 13 [pos="3.5,2.3!"]; + 14 [pos="3.5,4!"]; + 15 [pos="4,1!"]; + 16 [pos="4,2!"]; + 17 [pos="4,3!"]; + } + +The vertices not participating on the edge are considered isolated + +- their degree is 0 in the subgraph and +- their degree is not shown in the output. + +.. rubric:: Using the `Edges`_ signature. + +.. literalinclude:: degree.queries + :start-after: -- q5 + :end-before: -- q6 + +.. rubric:: Using the `Edges and Vertices`_ signature. + +.. literalinclude:: degree.queries + :start-after: -- q6 + :end-before: -- q7 + +Using a vertex table +............................................................................... + +For decent size networks, it is best to prepare your vertices table before hand +and use it on ``pgr_degree`` calls. + +Extract the vertex information and save into a table: + .. literalinclude:: degree.queries - :start-after: -- q2 - :end-before: -- q3 + :start-after: -- q8 + :end-before: -- q9 + +Calculate the degree of the nodes: + +.. literalinclude:: degree.queries + :start-after: -- q9 + :end-before: -- q10 Dry run execution ............................................................................... @@ -176,20 +342,33 @@ The results can be used as base code to make a refinement based on the backend development needs. .. literalinclude:: degree.queries - :start-after: -- q3 - :end-before: -- q4 + :start-after: -- q10 + :end-before: -- q11 -Degree from an existing table +Finding dead ends ............................................................................... -If you have a vertices table already built using ``pgr_extractVertices`` -and want the degree of the whole graph rather than a subset, you can forgo using pgr_degree -and work with the ``in_edges`` and ``out_edges`` columns directly. +If there is a vertices table already built using ``pgr_extractVertices`` +and want the degree of the whole graph rather than a subset, it can be forgo using +``pgr_degree`` and work with the ``in_edges`` and ``out_edges`` columns +directly. + +The degree of a dead end is 1. .. include:: pgRouting-concepts.rst :start-after: degree_from_table_start :end-before: degree_from_table_end +Finding linear vertices +............................................................................... + +The degree of a linear vertex is 2. + +If there is a vertices table already built using the ``pgr_extractVertices`` + +.. include:: pgRouting-concepts.rst + :start-after: linear_degree_from_table_start + :end-before: linear_degree_from_table_end See Also ------------------------------------------------------------------------------- diff --git a/doc/src/pgRouting-concepts.rst b/doc/src/pgRouting-concepts.rst index f81cc4df3ff..6ae64e634d6 100644 --- a/doc/src/pgRouting-concepts.rst +++ b/doc/src/pgRouting-concepts.rst @@ -830,23 +830,62 @@ the number of dead ends and/or the number of linear edges. A complete method on how to contract and how to use the contracted graph is described on :doc:`contraction-family` -.. degree_from_table_start Dead ends +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +.. degree_from_table_start + To get the dead ends: .. literalinclude:: concepts.queries :start-after: -- contract1 :end-before: -- contract2 -That information is correct, for example, when the dead end is on the limit of -the imported graph. +A dead end happens when + +- The vertex is the limit of a cul-de-sac, a no-through road or a no-exit + road. +- The vertex is on the limit of the imported graph. + + - If a larger graph is imported then the vertex might not be a dead end + +Node :math:`4`, is a dead end on the query, even that it visually looks like an +end point of 3 edges. -Visually node :math:`4` looks to be as start/ending of 3 edges, but it is not. +.. image:: images/Fig1-originalData.png + :scale: 20% + +.. rubric:: Is node :math:`4` a dead end or not? + +.. graphviz:: + + graph G { + 1,2,4,5,9,13,14 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true]; + 3,6,7,8,10,11,12,15,16,17 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true]; + + 5 -- 6 [label="1",fontsize=8]; 6 -- 10 [label="2",fontsize=8]; + 10 -- 15 [label="3",fontsize=8]; 6 -- 7 [label="4",fontsize=8]; + 10 -- 11 [label="5",fontsize=8]; 1 -- 3 [label="6",fontsize=8]; + 3 -- 7 [label="7",fontsize=8]; 7 -- 11 [label="8",fontsize=8]; + 11 -- 16 [label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8]; + 11 -- 12 [label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8]; + 12 -- 17 [label="13",fontsize=8]; 8 -- 9 [label="",fontsize=8]; + 16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8]; + 2 -- 4 [label="17",fontsize=8]; 13 -- 14 [label="18",fontsize=8]; + + 1 [pos="0,2!"]; 2 [pos="0.5,3.5!"]; + 3 [pos="1,2!"]; 4 [pos="2,3.5!"]; + 5 [pos="2,0!"]; 6 [pos="2,1!"]; + 7 [pos="2,2!"]; 8 [pos="2,3!"]; + 9 [pos="2,4!"]; 10 [pos="3,1!"]; + 11 [pos="3,2!"]; 12 [pos="3,3!"]; + 13 [pos="3.5,2.3!"]; 14 [pos="3.5,4!"]; + 15 [pos="4,1!"]; 16 [pos="4,2!"]; + 17 [pos="4,3!"]; + } -Is that correct? +The answer to that question will depend on the application. * Is there such a small curb: @@ -859,25 +898,59 @@ Is that correct? * Is there a big cliff and from eagles view look like the dead end is close to the segment? -When there are many dead ends, to speed up, the :doc:`contraction-family` -functions can be used to divide the problem. +Depending on the answer, modification of the data might be needed. + +When there are many dead ends, to speed up processing, the :doc:`contraction-family` +functions can be used to contract the graph. + +.. degree_from_table_end Linear edges +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +.. linear_degree_from_table_start + To get the linear edges: .. literalinclude:: concepts.queries :start-after: -- contract2 :end-before: -- contract3 -This information is correct, for example, when the application is taking into -account speed bumps, stop signals. +.. graphviz:: -When there are many linear edges, to speed up, the :doc:`contraction-family` -functions can be used to divide the problem. + graph G { + 3,15,17 [shape=circle;style=filled;color=lightgreen;fontsize=8;width=0.3;fixedsize=true]; + 1,2,4,5,6,7,8,9,10,11,12,13,14,16 [shape=circle;style=filled;color=cyan;fontsize=8;width=0.3;fixedsize=true]; + + 5 -- 6 [label="1",fontsize=8]; 6 -- 10 [label="2",fontsize=8]; + 10 -- 15 [label="3",fontsize=8]; 6 -- 7 [label="4",fontsize=8]; + 10 -- 11 [label="5",fontsize=8]; 1 -- 3 [label="6",fontsize=8]; + 3 -- 7 [label="7",fontsize=8]; 7 -- 11 [label="8",fontsize=8]; + 11 -- 16 [label="9",fontsize=8]; 7 -- 8 [label="10",fontsize=8]; + 11 -- 12 [label="11",fontsize=8]; 8 -- 12 [label="12",fontsize=8]; + 12 -- 17 [label="13",fontsize=8]; 8 -- 9 [label="",fontsize=8]; + 16 -- 17 [label="15",fontsize=8]; 15 -- 16 [label="16",fontsize=8]; + 2 -- 4 [label="17",fontsize=8]; 13 -- 14 [label="18",fontsize=8]; + + 1 [pos="0,2!"]; 2 [pos="0.5,3.5!"]; + 3 [pos="1,2!"]; 4 [pos="2,3.5!"]; + 5 [pos="2,0!"]; 6 [pos="2,1!"]; + 7 [pos="2,2!"]; 8 [pos="2,3!"]; + 9 [pos="2,4!"]; 10 [pos="3,1!"]; + 11 [pos="3,2!"]; 12 [pos="3,3!"]; + 13 [pos="3.5,2.3!"]; 14 [pos="3.5,4!"]; + 15 [pos="4,1!"]; 16 [pos="4,2!"]; + 17 [pos="4,3!"]; + } -.. degree_from_table_end +These linear vertices are correct, for example, when those the vertices are speed +bumps, stop signals and the application is taking them into account. + +When there are many linear vertices, that need not to be taken into account, to +speed up the processing, the :doc:`contraction-family` functions can be used to +contract the problem. + +.. linear_degree_from_table_end Function's structure ------------------------------------------------------------------------------- @@ -889,7 +962,7 @@ The general form of a pgRouting function call is: .. admonition:: \ \ :class: signatures - pgr_(`Inner queries`_, **parameters**, [ ``Optional parameters``) + pgr_(`Inner queries`_, **parameters**, [ ``Optional parameters``) Where: diff --git a/docqueries/metrics/degree.pg b/docqueries/metrics/degree.pg index 95b2e3ba056..1fb89b6e555 100644 --- a/docqueries/metrics/degree.pg +++ b/docqueries/metrics/degree.pg @@ -1,23 +1,39 @@ -- CopyRight(c) pgRouting developers -- Creative Commons Attribution-Share Alike 3.0 License : https://creativecommons.org/licenses/by-sa/3.0/ /* -- q1 */ -DROP TABLE IF EXISTS tmp_edges_vertices_pgr; -CREATE TEMP TABLE tmp_edges_vertices_pgr AS -SELECT id, in_edges, out_edges - FROM pgr_extractVertices('SELECT id, geom FROM edges'); -SELECT * FROM pgr_degree( - $$SELECT id FROM edges$$, - $$SELECT id, in_edges, out_edges - FROM tmp_edges_vertices_pgr$$); +SELECT * FROM pgr_degree($$SELECT id, source, target FROM edges$$) +ORDER BY node; /* -- q2 */ SELECT * FROM pgr_degree( - $$SELECT id FROM edges WHERE id < 17$$, + $$SELECT id FROM edges$$, $$SELECT id, in_edges, out_edges FROM pgr_extractVertices('SELECT id, geom FROM edges')$$); /* -- q3 */ +SELECT * from pgr_degree('SELECT 1 as id, 2 as source, 2 as target'); +/* -- q4 */ SELECT * FROM pgr_degree( - $$SELECT id FROM edges WHERE id < 17$$, + $$SELECT 1 AS id$$, $$SELECT id, in_edges, out_edges - FROM pgr_extractVertices('SELECT id, geom FROM edges')$$, + FROM pgr_extractVertices('SELECT 1 as id, 2 as source, 2 as target')$$); +/* -- q5 */ +SELECT * FROM pgr_degree($$SELECT * FROM edges WHERE id IN (1, 2)$$); +/* -- q6 */ +SELECT * FROM pgr_degree( + $$SELECT * FROM edges WHERE id IN (1, 2)$$, + $$SELECT id, in_edges, out_edges FROM vertices$$); +/* -- q7 */ +DROP TABLE IF EXISTS vertices; +/* -- q8*/ +CREATE TABLE vertices AS +SELECT id, in_edges, out_edges +FROM pgr_extractVertices('SELECT id, geom FROM edges'); +/* -- q9 */ +SELECT * FROM pgr_degree( + $$SELECT id FROM edges$$, + $$SELECT id, in_edges, out_edges FROM vertices$$); +/* -- q10 */ +SELECT * FROM pgr_degree( + $$SELECT id FROM edges WHERE id < 17$$, + $$SELECT id, in_edges, out_edges FROM vertices$$, dryrun => true); -/* -- q4 */ +/* -- q11 */ diff --git a/docqueries/metrics/degree.result b/docqueries/metrics/degree.result index 340dbf44646..d2e101a4f29 100644 --- a/docqueries/metrics/degree.result +++ b/docqueries/metrics/degree.result @@ -3,17 +3,8 @@ BEGIN SET client_min_messages TO NOTICE; SET /* -- q1 */ -DROP TABLE IF EXISTS tmp_edges_vertices_pgr; -NOTICE: table "tmp_edges_vertices_pgr" does not exist, skipping -DROP TABLE -CREATE TEMP TABLE tmp_edges_vertices_pgr AS -SELECT id, in_edges, out_edges - FROM pgr_extractVertices('SELECT id, geom FROM edges'); -SELECT 17 -SELECT * FROM pgr_degree( - $$SELECT id FROM edges$$, - $$SELECT id, in_edges, out_edges - FROM tmp_edges_vertices_pgr$$); +SELECT * FROM pgr_degree($$SELECT id, source, target FROM edges$$) +ORDER BY node; node | degree ------+-------- 1 | 1 @@ -37,15 +28,15 @@ SELECT * FROM pgr_degree( /* -- q2 */ SELECT * FROM pgr_degree( - $$SELECT id FROM edges WHERE id < 17$$, + $$SELECT id FROM edges$$, $$SELECT id, in_edges, out_edges FROM pgr_extractVertices('SELECT id, geom FROM edges')$$); node | degree ------+-------- 1 | 1 - 2 | 0 + 2 | 1 3 | 2 - 4 | 0 + 4 | 1 5 | 1 6 | 3 7 | 4 @@ -54,18 +45,87 @@ SELECT * FROM pgr_degree( 10 | 3 11 | 4 12 | 3 - 13 | 0 - 14 | 0 + 13 | 1 + 14 | 1 15 | 2 16 | 3 17 | 2 (17 rows) /* -- q3 */ +SELECT * from pgr_degree('SELECT 1 as id, 2 as source, 2 as target'); + node | degree +------+-------- + 2 | 2 +(1 row) + +/* -- q4 */ SELECT * FROM pgr_degree( - $$SELECT id FROM edges WHERE id < 17$$, + $$SELECT 1 AS id$$, $$SELECT id, in_edges, out_edges - FROM pgr_extractVertices('SELECT id, geom FROM edges')$$, + FROM pgr_extractVertices('SELECT 1 as id, 2 as source, 2 as target')$$); + node | degree +------+-------- + 2 | 2 +(1 row) + +/* -- q5 */ +SELECT * FROM pgr_degree($$SELECT * FROM edges WHERE id IN (1, 2)$$); + node | degree +------+-------- + 10 | 1 + 6 | 2 + 5 | 1 +(3 rows) + +/* -- q6 */ +SELECT * FROM pgr_degree( + $$SELECT * FROM edges WHERE id IN (1, 2)$$, + $$SELECT id, in_edges, out_edges FROM vertices$$); + node | degree +------+-------- + 5 | 1 + 6 | 2 + 10 | 1 +(3 rows) + +/* -- q7 */ +DROP TABLE IF EXISTS vertices; +DROP TABLE +/* -- q8*/ +CREATE TABLE vertices AS +SELECT id, in_edges, out_edges +FROM pgr_extractVertices('SELECT id, geom FROM edges'); +SELECT 17 +/* -- q9 */ +SELECT * FROM pgr_degree( + $$SELECT id FROM edges$$, + $$SELECT id, in_edges, out_edges FROM vertices$$); + node | degree +------+-------- + 1 | 1 + 2 | 1 + 3 | 2 + 4 | 1 + 5 | 1 + 6 | 3 + 7 | 4 + 8 | 3 + 9 | 1 + 10 | 3 + 11 | 4 + 12 | 3 + 13 | 1 + 14 | 1 + 15 | 2 + 16 | 3 + 17 | 2 +(17 rows) + +/* -- q10 */ +SELECT * FROM pgr_degree( + $$SELECT id FROM edges WHERE id < 17$$, + $$SELECT id, in_edges, out_edges FROM vertices$$, dryrun => true); NOTICE: WITH @@ -77,8 +137,7 @@ NOTICE: -- sub set of vertices of the graph goes here all_vertices AS ( - SELECT id, in_edges, out_edges - FROM pgr_extractVertices('SELECT id, geom FROM edges') + SELECT id, in_edges, out_edges FROM vertices ), g_vertices AS ( @@ -92,16 +151,16 @@ NOTICE: totals AS ( SELECT v.id, count(*) - FROM g_vertices AS v - JOIN g_edges AS e ON (e.id = eid) GROUP BY v.id + FROM g_vertices v + JOIN g_edges e ON (v.eid = e.id) GROUP BY v.id ) - SELECT id::BIGINT, coalesce(count, 0)::BIGINT FROM all_vertices LEFT JOIN totals USING (id) + SELECT id::BIGINT, count::BIGINT FROM all_vertices JOIN totals USING (id) ; node | degree ------+-------- (0 rows) -/* -- q4 */ +/* -- q11 */ ROLLBACK; ROLLBACK diff --git a/pgtap/metrics/degree/edge_cases.pg b/pgtap/metrics/degree/edge_cases.pg index e7cdd91880f..0f953881cfe 100644 --- a/pgtap/metrics/degree/edge_cases.pg +++ b/pgtap/metrics/degree/edge_cases.pg @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. BEGIN; UPDATE edges SET cost = sign(cost), reverse_cost = sign(reverse_cost); -SELECT CASE WHEN min_version('3.4.0') THEN plan(16) ELSE plan(1) END; +SELECT CASE WHEN min_version('3.8.0') THEN plan(23) WHEN min_version('3.4.0') THEN plan(16) ELSE plan(1) END; CREATE OR REPLACE FUNCTION edge_cases() @@ -114,6 +114,34 @@ BEGIN SELECT results_eq('empty_graph', 'SELECT generate_series (1,17)::BIGINT, 0::BIGINT', 'Empty edges give 0 count'); END IF; + IF NOT min_version('3.8.0') THEN RETURN; END IF; + + -- TESTS FOR edges_sql only + + PREPARE edges_1 AS + SELECT id, source, target FROM edges; + + PREPARE subedges_1 AS + SELECT id, source, target FROM edges WHERE id < 17; + + RETURN QUERY + SELECT set_eq($$SELECT * FROM pgr_degree('edges_1')$$, 'query_1', + 'degree(edges) = degree(edges,vertices)'); + + RETURN QUERY + SELECT set_eq($$SELECT * FROM pgr_degree('subedges_1')$$, 'query_7', + 'degree(subedges) = degree(subedges,vertices)'); + + RETURN QUERY SELECT lives_ok($$SELECT * FROM pgr_degree('SELECT id, source, target FROM edges')$$, 'good execution'); + RETURN QUERY SELECT throws_ok($$SELECT * FROM pgr_degree('SELECT source, target FROM edges')$$, + '42703','column "id" does not exist'); + RETURN QUERY SELECT throws_ok($$SELECT * FROM pgr_degree('SELECT id, target FROM edges')$$, + '42703','column "source" does not exist'); + RETURN QUERY SELECT throws_ok($$SELECT * FROM pgr_degree('SELECT id, source FROM edges')$$, + '42703','column "target" does not exist'); + RETURN QUERY SELECT wrong_relation($$SELECT * FROM pgr_degree('SELECT id FROM foo')$$, 'foo'); + + END; $BODY$ LANGUAGE plpgsql; diff --git a/pgtap/metrics/degree/types_check.pg b/pgtap/metrics/degree/types_check.pg index cd4ad3287b5..7c242e33053 100644 --- a/pgtap/metrics/degree/types_check.pg +++ b/pgtap/metrics/degree/types_check.pg @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ BEGIN; -SELECT CASE WHEN min_version('3.4.0') THEN plan(5) ELSE plan(1) END; +SELECT CASE WHEN min_version('3.8.0') THEN plan(7) WHEN min_version('3.4.0') THEN plan(5) ELSE plan(1) END; CREATE OR REPLACE FUNCTION types_check() RETURNS SETOF TEXT AS $BODY$ @@ -33,13 +33,37 @@ BEGIN RETURN QUERY SELECT has_function('pgr_degree', ARRAY['text', 'text', 'boolean']); RETURN QUERY SELECT function_returns('pgr_degree', ARRAY['text', 'text', 'boolean'], 'setof record'); - RETURN QUERY SELECT set_eq( - $$SELECT proargnames from pg_proc where proname = 'pgr_degree'$$, - $$SELECT '{"","","dryrun","node","degree"}'::TEXT[] $$); + RETURN QUERY + SELECT CASE + WHEN min_version('3.8.0') THEN + collect_tap( - RETURN QUERY SELECT set_eq( - $$SELECT proallargtypes from pg_proc where proname = 'pgr_degree'$$, - $$VALUES ('{25,25,16,20,20}'::OID[])$$); + has_function('pgr_degree', ARRAY['text', 'boolean']), + + function_returns('pgr_degree', ARRAY['text', 'boolean'], 'setof record'), + + set_eq($$SELECT proargnames from pg_proc where proname = 'pgr_degree'$$, + $$VALUES + ('{"","dryrun","node","degree"}'::TEXT[]), + ('{"","","dryrun","node","degree"}'::TEXT[]) + $$, 'proargnames'), + + set_eq($$SELECT proallargtypes from pg_proc where proname = 'pgr_degree'$$, + $$VALUES + ('{25,16,20,20}'::OID[]), + ('{25,25,16,20,20}'::OID[]) + $$, 'proallargtypes') + ) + ELSE + collect_tap( + + set_eq($$SELECT proargnames from pg_proc where proname = 'pgr_degree'$$, + $$VALUES ('{"","","dryrun","node","degree"}'::TEXT[]) $$, 'proargnames'), + + set_eq($$SELECT proallargtypes from pg_proc where proname = 'pgr_degree'$$, + $$VALUES ('{25,25,16,20,20}'::OID[])$$, 'proallargtypes') + ) + END; END $BODY$ diff --git a/sql/metrics/degree.sql b/sql/metrics/degree.sql index 980095aad4a..531326bc371 100644 --- a/sql/metrics/degree.sql +++ b/sql/metrics/degree.sql @@ -42,6 +42,7 @@ DECLARE has_out_edges BOOLEAN := TRUE; eids TEXT; query TEXT; + sqlhint TEXT; BEGIN @@ -65,6 +66,7 @@ BEGIN RAISE EXCEPTION 'column "in_edges" does not exist' USING HINT = vertices_sql, ERRCODE = 42703; END IF; + IF has_in_edges THEN eids = $$coalesce(in_edges::BIGINT[], '{}'::BIGINT[])$$; END IF; @@ -105,11 +107,11 @@ BEGIN totals AS ( SELECT v.id, count(*) - FROM g_vertices AS v - JOIN g_edges AS e ON (e.id = eid) GROUP BY v.id + FROM g_vertices v + JOIN g_edges e ON (v.eid = e.id) GROUP BY v.id ) - SELECT id::BIGINT, coalesce(count, 0)::BIGINT FROM all_vertices LEFT JOIN totals USING (id) + SELECT id::BIGINT, count::BIGINT FROM all_vertices JOIN totals USING (id) $q$, eids); IF dryrun THEN @@ -122,6 +124,8 @@ END; $BODY$ LANGUAGE plpgsql VOLATILE STRICT; + +-- COMMENTS COMMENT ON FUNCTION pgr_degree(TEXT, TEXT, BOOLEAN) IS 'pgr_degree - PROPOSED @@ -131,3 +135,82 @@ IS 'pgr_degree - Documentation: - ${PROJECT_DOC_LINK}/pgr_degree.html '; + +--v3.8 +CREATE FUNCTION pgr_degree( + TEXT, -- Edges SQL + + dryrun BOOLEAN DEFAULT false, + + OUT node BIGINT, + OUT degree BIGINT +) +RETURNS SETOF RECORD AS +$BODY$ +DECLARE + edges_sql TEXT; + has_id BOOLEAN; + has_source BOOLEAN; + has_target BOOLEAN; + eids TEXT; + query TEXT; + + sqlhint TEXT; + +BEGIN + + -- Verify the data is complete + BEGIN + edges_sql := _pgr_checkQuery($1); + has_id := _pgr_checkColumn(edges_sql, 'id', 'ANY-INTEGER', dryrun => $2); + has_source := _pgr_checkColumn(edges_sql, 'source', 'ANY-INTEGER', dryrun => $2); + has_target := _pgr_checkColumn(edges_sql, 'target', 'ANY-INTEGER', dryrun => $2); + EXCEPTION WHEN OTHERS THEN + GET STACKED DIAGNOSTICS sqlhint = PG_EXCEPTION_HINT; + RAISE EXCEPTION '%', SQLERRM USING HINT = sqlhint, ERRCODE = SQLSTATE; + END; + + query := format($q$ + + WITH + + -- a sub set of edges of the graph goes here + g_edges AS ( + %1$s + ), + + -- sub set of vertices of the graph goes here + g_vertices AS ( + SELECT source, id FROM g_edges + UNION ALL + SELECT target, id FROM g_edges + ), + + totals AS ( + SELECT source AS node, count(*) AS degree + FROM g_vertices + GROUP BY node + ) + + SELECT node::BIGINT, degree::BIGINT + FROM totals + $q$, edges_sql); + + IF dryrun THEN + RAISE NOTICE '%', query || ';'; + ELSE + RETURN QUERY EXECUTE query; + END IF; + +END; +$BODY$ +LANGUAGE plpgsql VOLATILE STRICT; + +COMMENT ON FUNCTION pgr_degree(TEXT, BOOLEAN) +IS 'pgr_degree +- PROPOSED +- Parameters +- Edges SQL with columns: id +- Documentation: +- ${PROJECT_DOC_LINK}/pgr_degree.html +'; diff --git a/sql/sigs/pgrouting--3.8.sig b/sql/sigs/pgrouting--3.8.sig index 02537609497..59ae351e060 100644 --- a/sql/sigs/pgrouting--3.8.sig +++ b/sql/sigs/pgrouting--3.8.sig @@ -101,6 +101,7 @@ pgr_dagshortestpath(text,bigint,anyarray) pgr_dagshortestpath(text,bigint,bigint) pgr_dagshortestpath(text,text) _pgr_dagshortestpath(text,text,boolean,boolean) +pgr_degree(text,boolean) pgr_degree(text,text,boolean) _pgr_depthfirstsearch(text,anyarray,boolean,bigint) pgr_depthfirstsearch(text,anyarray,boolean,bigint)