{"id":1121,"date":"2013-07-08T01:00:00","date_gmt":"2013-07-07T23:00:00","guid":{"rendered":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1121"},"modified":"2013-07-10T12:37:31","modified_gmt":"2013-07-10T11:37:31","slug":"recursive-sql-query","status":"publish","type":"post","link":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1121","title":{"rendered":"Recursive SQL Query"},"content":{"rendered":"<p>Imagine these tables (I\u00e2\u20ac\u2122m using PostgreSQL):<\/p>\n<pre class=\"sourceCode SQL\"><code class=\"sourceCode sql\">    <span class=\"kw\">CREATE<\/span> Capabilities (\n        <span class=\"kw\">ID<\/span>     serial <span class=\"kw\">PRIMARY<\/span> <span class=\"kw\">KEY<\/span>,\n        Code   text   <span class=\"kw\">NOT<\/span> <span class=\"kw\">NULL<\/span>\n    );\n\n    <span class=\"kw\">CREATE<\/span> CapabilityPermissions (\n        <span class=\"kw\">ID<\/span>              serial  <span class=\"kw\">PRIMARY<\/span> <span class=\"kw\">KEY<\/span>,\n        CapabilitityID  <span class=\"dt\">integer<\/span> <span class=\"kw\">NOT<\/span> <span class=\"kw\">NULL<\/span> <span class=\"kw\">REFERENCES<\/span> Capabilities(<span class=\"kw\">ID<\/span>),\n        PermissionID    <span class=\"dt\">integer<\/span> <span class=\"kw\">NOT<\/span> <span class=\"kw\">NULL<\/span> <span class=\"kw\">REFERENCES<\/span> Capabilities(<span class=\"kw\">ID<\/span>)\n    );<\/code><\/pre>\n<p>What I\u00e2\u20ac\u2122m aiming for here is the ability to make a permissions hierarchy. This will let us define, say, an ADMIN capability, and have that imply USER capabilities, EDITOR capabilities, etc. In other words, the <code>CapabilityPermissions<\/code> table is recursive. I\u00e2\u20ac\u2122ve done this sort of thing in quite a few projects \u00e2\u20ac\u201c a flexible permissions system is surprisingly useful. Usually in your app, as soon as the user is identified, you look up their permissions from the database and assemble them into an easily-searched structure. With a recursive table like this you might do (in pseudo-python):<\/p>\n<pre class=\"sourceCode python\"><code class=\"sourceCode python\">    <span class=\"kw\">def<\/span> fetchPermissions( startPermissionID, permissionDict = <span class=\"ot\">None<\/span> ):\n        <span class=\"kw\">if<\/span> permissionDict is <span class=\"ot\">None<\/span>:\n            permissionDict = {}\n        cursor = db.execute(<span class=\"st\">&quot;SELECT CP.PermissionID, C.Code<\/span>\n<span class=\"st\">            FROM CapabilityPermissions AS CP<\/span>\n<span class=\"st\">                INNER JOIN Capabilities AS C ON (C.ID=CP.PermissionID)<\/span>\n<span class=\"st\">            WHERE CP.CapabilityID = <\/span><span class=\"ot\">%s<\/span><span class=\"st\">&quot;<\/span>, (startPermissionID,))\n        <span class=\"kw\">for<\/span> row in cursor:\n            <span class=\"kw\">if<\/span> row[<span class=\"dv\">1<\/span>] not in permissionDict:\n                permissionDict[row[<span class=\"dv\">1<\/span>]] = row[<span class=\"dv\">0<\/span>]\n                fetchPermissions( row[<span class=\"dv\">0<\/span>], permissionDict )\n\n        <span class=\"kw\">return<\/span> permissionDict<\/code><\/pre>\n<p>This function recursively queries as it finds each new permission. Nothing in particular wrong with it other than it\u00e2\u20ac\u2122s making lots of queries.<\/p>\n<p>If you\u00e2\u20ac\u2122ve got a compliant database (PostgreSQL for example), then you can get the database to do the work with a recursive query.<\/p>\n<pre class=\"sourceCode SQL\"><code class=\"sourceCode sql\">    <span class=\"kw\">CREATE<\/span> <span class=\"kw\">VIEW<\/span> AllPermissions <span class=\"kw\">AS<\/span>\n    <span class=\"kw\">WITH<\/span> RECURSIVE RecursedCP(CapabilityID,PermissionID) <span class=\"kw\">AS<\/span> (\n            <span class=\"co\">-- This is the non-recursive query, and forms the basis of the<\/span>\n            <span class=\"co\">-- whole sequence, each record in this result is stored into a<\/span>\n            <span class=\"co\">-- temporary working table, which is then available in the<\/span>\n            <span class=\"co\">-- recursive query (named after the recursive output name,<\/span>\n            <span class=\"co\">-- RecursedCP)<\/span>\n            <span class=\"kw\">SELECT<\/span> CapabilityID,CapabilityID <span class=\"kw\">FROM<\/span> CapabilityPermissions\n        <span class=\"kw\">UNION<\/span>\n            <span class=\"co\">-- The recursive query has the temporary table available<\/span>\n            <span class=\"co\">-- and will add its output to the result, and make that output<\/span>\n            <span class=\"co\">-- the new temporary table.  For each capability then, we want to<\/span>\n            <span class=\"co\">-- use its permission as the new capability for the next<\/span>\n            <span class=\"co\">-- iteration<\/span>\n            <span class=\"kw\">SELECT<\/span> AP.CapabilityID,CP.PermissionID\n                <span class=\"kw\">FROM<\/span> RecursedCP <span class=\"kw\">AS<\/span> AP\n                    <span class=\"kw\">INNER<\/span> <span class=\"kw\">JOIN<\/span> CapabilityPermissions <span class=\"kw\">AS<\/span> CP <span class=\"kw\">ON<\/span> (CP.CapabilityID=AP.PermissionID)\n    ) <span class=\"kw\">SELECT<\/span> CapabilityID, PermissionID <span class=\"kw\">FROM<\/span> RecursedCP;<\/code><\/pre>\n<p>The two parts are a query to \u00e2\u20ac\u0153seed\u00e2\u20ac\u009d the recursion, then the query that takes that seed and expands it, which becomes the next seed. The use of <code>UNION<\/code> tells the database that the expansions should all be aggregated into the single query result.<\/p>\n<p>With this <code>VIEW<\/code> in place, you can then easily convert the whole lot into a filtered query: so if you\u00e2\u20ac\u2122re user is assigned capability ID#1, you can get the full list of permissions that that capability implies like this:<\/p>\n<pre class=\"sourceCode SQL\"><code class=\"sourceCode sql\">    <span class=\"kw\">SELECT<\/span> * <span class=\"kw\">FROM<\/span> AllPermissions <span class=\"kw\">WHERE<\/span> CapabilityID = <span class=\"dv\">1<\/span>;<\/code><\/pre>\n<p>I like to treat these top-level capabilities as \u00e2\u20ac\u0153roles\u00e2\u20ac\u009d, and the leaf permissions at the codes that are checked throughout the application. That way you get a role-based access system with fine-grained access.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Imagine these tables (I\u00e2\u20ac\u2122m using PostgreSQL): CREATE Capabilities ( ID serial PRIMARY KEY, Code text NOT NULL ); CREATE CapabilityPermissions ( ID serial PRIMARY KEY, CapabilitityID integer NOT NULL REFERENCES Capabilities(ID), PermissionID integer NOT NULL REFERENCES Capabilities(ID) ); What I\u00e2\u20ac\u2122m aiming for here is the ability to make a permissions hierarchy. This will let us\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.fussylogic.co.uk\/blog\/?p=1121\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[89,88,6],"_links":{"self":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1121"}],"collection":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1121"}],"version-history":[{"count":6,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1121\/revisions"}],"predecessor-version":[{"id":1143,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1121\/revisions\/1143"}],"wp:attachment":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1121"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1121"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1121"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}