CockroachDB의 재귀 CTE

저는 CockroachDB를 사용하여 매우 유연한 인증 시스템을 구현하는 방법에 대해 생각해 왔습니다.

몇 가지 중요한 면에서 관계형 데이터베이스와 다른 Graph 데이터베이스를 사용한 이전 경험이 있습니다. 그러나 한 가지 큰 차이점은 그래프 데이터베이스가 "고도의 관계형 데이터"에 적합하다는 것입니다(RDBMS 의미에서 관계형을 의미하지는 않습니다). 그래프 데이터베이스는 "가계도가 주어졌을 때, 사람 X와 Y가 관련이 있고, 그렇다면 그들의 관계는 무엇입니까?"또는 "내 LinkedIn 전문 네트워크에서 나는 사람 X와 연결되어 있습니까? 그래서, 우리는 몇 '홉' 떨어져 있습니까?"

이와 같은 질문은 사물 간의 "거리"를 알 수 없기 때문에 일반적으로 대답하기 까다롭지만 표준 데이터 모델, 고도로 구조화된 스키마 및 조인 기반 쿼리를 사용하는 관계형 데이터베이스에서는 대답하기가 특히 어려울 수 있습니다.

승인 시스템 요구 사항에 따라 이러한 유형의 구조화되지 않은/알 수 없는 질문을 할 수 있기를 원합니다. 예를 들어: "내가 시스템의 사용자이고 그룹 A, B, C의 구성원인 경우 X 작업을 수행할 수 있는 권한이 있습니까?"그룹이 그룹 트리에 속할 수 있는 다른 그룹에 속할 수 있는 그룹 계층 구조를 허용하고 필요한 권한을 해당 트리의 모든 그룹에 연결할 수 있습니다.

CockroachDB를 포함한 대부분의 관계형 데이터베이스는 이러한 유형의 "나무를 따라가는"문제를 해결하는 데 도움이 되는 구조를 가지고 있습니다. 이 구조를 "재귀적 CTE"라고 합니다.

일반적인 비재귀적CTE(공통 테이블 표현식)은 일련의 쿼리에서 두 번 이상 재사용할 수 있는 쿼리를 생성할 수 있으므로 그 자체로 훌륭합니다. 재사용을 용이하게 하는 것 외에도 보다 쉽게 ​​유지 관리할 수 있는 SQL 코드를 만드는 데 도움이 되는 복잡한 파생 테이블을 제거하여 쿼리를 간단하게 만들 수 있습니다.

재귀적 CTE는 표현식의 재귀적 실행을 허용하기 때문에 또 다른 장점을 추가합니다.

CockroachDB 문서에서 빌린 비재귀적 CTE의 예를 살펴보겠습니다.

WITH r AS
(
  SELECT *
  FROM rides
  WHERE revenue > 98
)
SELECT *
FROM users AS u, r -- here we're joining to the 'r' CTE
WHERE r.rider_id = u.id;


재귀 CTE를 사용하려면 약간 다른 구문이 필요합니다. 예를 살펴보겠습니다.

WITH RECURSIVE cte (n, factorial) AS
(
    VALUES (0, 1) -- initial subquery
    UNION ALL
    SELECT n+1, (n+1)*factorial
    FROM cte
    WHERE n < 9 -- recursive subquery
)
SELECT * FROM cte;


재귀 CTE에는 "RECURSIVE"키워드와 재귀 쿼리와 함께 UNION된 초기 문(시작점)이 있어야 합니다. 선택적으로 CTE 테이블 반환 구조에서 반환된 열의 이름을 명시적으로 지정하는 데 사용되는 필드 목록이 있을 수도 있습니다.

이제 인증 시스템의 예로 돌아가겠습니다. 저는 이러한 권한 부여 시스템이 CockroachDB에서 어떻게 보이는지와 재귀 CTE를 사용하여 해당 데이터를 효과적으로 쿼리할 수 있는 방법에 대한 몇 가지 예제 데이터가 있는 GitHub 저장소를 만들었습니다.
https://github.com/cockroachlabs-field/perms-example

여기서는 GitHub 리포지토리의 내용을 반복하지 않겠지만 여기에서 사용하고 있는 CTE의 예가 있습니다. 우리는 다음과 같은 질문을 합니다. ID 'Andy, GM'이 Customer 테이블에서 데이터를 읽을 수 있는 권한이 있습니까?
쿼리가 결과를 반환하면 권한이 있는 것입니다. 결과가 반환되지 않으면 권한이 없는 것입니다.

WITH RECURSIVE roles_hierarchy_cte AS (

    --query all the roles for the user we care about
    SELECT r.rid AS rid
    FROM perms_example.role_instance r
    INNER JOIN perms_example.identity_role_assignment ira ON r.rid = ira.rid
    WHERE ira.iid = 'aaaaaaaa-1111-1111-1111-111111111111' -- Andy, GM

    UNION

    --walk up the tree and find any other parent roles
    SELECT rh.child_rid AS rid
    FROM perms_example.role_hierarchy rh
    INNER JOIN roles_hierarchy_cte rhc ON rhc.rid = rh.parent_rid
)

/* get all the permissions that this identity is assigned to directly */
SELECT i.iid, i.identity_name, NULL AS role_name, p.permission_name
FROM perms_example.identity_instance i
INNER JOIN perms_example.identity_permission_assignment ipa on i.iid = ipa.iid
INNER JOIN perms_example.permission_instance p on ipa.pid = p.pid
WHERE i.iid = 'aaaaaaaa-1111-1111-1111-111111111111' -- Andy, GM
AND p.pid = 'cccccccc-1111-1111-1111-111111111111' -- 'Read Customer Data

UNION ALL

/* also, get any permissions that this identity picks up due to role permissions
     and also role permissions from the role hierarchy */
SELECT i.iid, i.identity_name, r.role_name, p.permission_name
FROM perms_example.role_instance r
INNER JOIN perms_example.role_permission_assignment rpa on r.rid = rpa.rid
INNER JOIN perms_example.permission_instance p on rpa.pid = p.pid
--get me 1 identity row so I can include that on the output
CROSS JOIN
(
        SELECT i.iid, i.identity_name
        FROM perms_example.identity_instance i
        WHERE i.iid = 'aaaaaaaa-1111-1111-1111-111111111111' --Andy, GM
) i
WHERE r.rid IN (
    SELECT rhc.rid
    FROM roles_hierarchy_cte rhc
)
AND p.pid = 'cccccccc-1111-1111-1111-111111111111' -- 'view customer data
;


재귀 CTE가 유용할 수 있는 다른 예가 있습니까?
댓글에 게시하세요!

즐거운 되풀이!

좋은 웹페이지 즐겨찾기