static void _sg_dagfrag__add_leaves_callback(SG_context * pCtx, const char * szKey, SG_UNUSED_PARAM(void * pAssocData), void * pVoidAddLeavesData) { struct _add_leaves_data * pAddLeavesData = (struct _add_leaves_data *)pVoidAddLeavesData; SG_UNUSED(pAssocData); SG_ERR_CHECK_RETURN( SG_dagfrag__load_from_repo__one(pCtx, pAddLeavesData->pFrag, pAddLeavesData->pRepo, szKey, pAddLeavesData->nGenerations) ); }
void SG_dagquery__how_are_dagnodes_related(SG_context * pCtx, SG_repo * pRepo, SG_uint64 dagnum, const char * pszHid1, const char * pszHid2, SG_bool bSkipDescendantCheck, SG_bool bSkipAncestorCheck, SG_dagquery_relationship * pdqRel) { SG_dagnode * pdn1 = NULL; SG_dagnode * pdn2 = NULL; SG_dagfrag * pFrag = NULL; SG_dagquery_relationship dqRel = SG_DAGQUERY_RELATIONSHIP__UNKNOWN; SG_int32 gen1, gen2; SG_bool bFound; SG_NULLARGCHECK_RETURN(pRepo); SG_NONEMPTYCHECK_RETURN(pszHid1); SG_NONEMPTYCHECK_RETURN(pszHid2); SG_NULLARGCHECK_RETURN(pdqRel); if (strcmp(pszHid1, pszHid2) == 0) { dqRel = SG_DAGQUERY_RELATIONSHIP__SAME; goto cleanup; } // fetch the dagnode for both HIDs. this throws when the HID is not found. SG_ERR_CHECK( SG_repo__fetch_dagnode(pCtx, pRepo, dagnum, pszHid1, &pdn1) ); SG_ERR_CHECK( SG_repo__fetch_dagnode(pCtx, pRepo, dagnum, pszHid2, &pdn2) ); // we say that 2 nodes are either: // [1] ancestor/descendant of each other; // [2] or that they are peers (cousins) of each other (no matter // how distant in the DAG). (that have an LCA, but we don't // care about it.) // get the generation of both dagnodes. if they are the same, then they // cannot have an ancestor/descendant relationship and therefore must be // peers/cousins (we don't care how close/distant they are). SG_ERR_CHECK( SG_dagnode__get_generation(pCtx, pdn1, &gen1) ); SG_ERR_CHECK( SG_dagnode__get_generation(pCtx, pdn2, &gen2) ); if (gen1 == gen2) { dqRel = SG_DAGQUERY_RELATIONSHIP__PEER; goto cleanup; } // see if one is an ancestor of the other. since we only have PARENT // edges in our DAG, we start with the deeper one and walk backwards // until we've visited all ancestors at the depth of the shallower one. // // i'm going to be lazy here and not reinvent a recursive-ish parent-edge // graph walker. instead, i'm going to create a DAGFRAG using the // deeper one and request the generation difference as the "thickness". // in theory, if we have an ancestor/descendant relationship, the // shallower one should be in the END-FRINGE of the DAGFRAG. // // i'm going to pick an arbitrary direction "cs1 is R of cs2". SG_ERR_CHECK( SG_dagfrag__alloc_transient(pCtx, dagnum, &pFrag) ); if (gen1 > gen2) // cs1 is *DEEPER* than cs2 { if (bSkipDescendantCheck) { dqRel = SG_DAGQUERY_RELATIONSHIP__UNKNOWN; } else { SG_ERR_CHECK( SG_dagfrag__load_from_repo__one(pCtx, pFrag, pRepo, pszHid1, (gen1 - gen2)) ); SG_ERR_CHECK( SG_dagfrag__query(pCtx, pFrag, pszHid2, NULL, NULL, &bFound, NULL) ); if (bFound) // pszHid2 is an ancestor of pszHid1. READ pszHid1 is a descendent of pszHid2. dqRel = SG_DAGQUERY_RELATIONSHIP__DESCENDANT; else // they are *distant* peers. dqRel = SG_DAGQUERY_RELATIONSHIP__PEER; } goto cleanup; } else { if (bSkipAncestorCheck) { dqRel = SG_DAGQUERY_RELATIONSHIP__UNKNOWN; } else { SG_ERR_CHECK( SG_dagfrag__load_from_repo__one(pCtx, pFrag, pRepo, pszHid2, (gen2 - gen1)) ); SG_ERR_CHECK( SG_dagfrag__query(pCtx, pFrag, pszHid1, NULL, NULL, &bFound, NULL) ); if (bFound) // pszHid1 is an ancestor of pszHid2. dqRel = SG_DAGQUERY_RELATIONSHIP__ANCESTOR; else // they are *distant* peers. dqRel = SG_DAGQUERY_RELATIONSHIP__PEER; } goto cleanup; } /*NOTREACHED*/ cleanup: *pdqRel = dqRel; fail: SG_DAGNODE_NULLFREE(pCtx, pdn1); SG_DAGNODE_NULLFREE(pCtx, pdn2); SG_DAGFRAG_NULLFREE(pCtx, pFrag); }