> I'm looking for a tool that (should) be relatively simple to build with jOOQ
> (and maybe someone already did it): I'd like to pass it a list of records
> (or tables + PKs) and it should dump the convex hull of all FK relations to
> an XML file.
>
> Usage: Extract test data from a testing/production system.
>
> Anyone?

That's an interesting problem. How about this canonical, yet a bit
inefficient algorithm:

-------------------------------------------------
public class Hull {
    public static Set<Table<?>> hull(Table<?>... tables) {
        Set<Table<?>> result = new HashSet<Table<?>>(Arrays.asList(tables));

        // Loop as long as there are no new result tables
        int size = 0;
        while (result.size() > size) {
            size = result.size();

            for (Table<?> table : new ArrayList<Table<?>>(result)) {
                // Follow all outbound foreign keys
                for (ForeignKey<?, ?> fk : table.getReferences()) {
                    result.add(fk.getKey().getTable());
                }

                // Follow all inbound foreign keys from tables within
the same schema
                for (Table<?> other : table.getSchema().getTables()) {
                    if (other.getReferencesTo(table).size() > 0) {
                        result.add(other);
                    }
                }
            }
        }


        return result;
    }

    public static void main(String[] args) {
        System.out.println(hull(T_AUTHOR));
    }
}
-------------------------------------------------

Note that this algorithm relies on a set of generated tables from a
single schema and doesn't follow foreign keys, if they lead to enum
types generated from "master data tables". A more reliable solution
would be to query the Oracle dictionary tables as such:

-------------------------------------------------
-- "graph" denotes an undirected foreign key reference graph
-- for schema "TEST"
with graph as (
        select c1.table_name t1, c2.table_name t2
        from all_constraints c1
                join all_constraints c2
                        on c1.owner = c2.r_owner
                        and c1.constraint_name = c2.r_constraint_name
        where c1.owner = 'TEST'
        union all
        select c2.table_name t1, c1.table_name t2
        from all_constraints c1
                join all_constraints c2
                        on c1.owner = c2.r_owner
                        and c1.constraint_name = c2.r_constraint_name
        where c1.owner = 'TEST'
),
-- "paths" are all directed paths within that schema as a #-delimited string
paths as (
        select sys_connect_by_path(t1, '#') || '#' path
        from graph
        connect by nocycle prior t1 = t2
),
-- "subgraph" are all those directed paths that go trough a given table T_AUTHOR
subgraph as (
        select distinct regexp_replace(path, '^#(.*)#$', '\1') path
        from paths
        where path like '%#T_AUTHOR#%'
)
-- This XML-trick splits paths and generates rows for every distinct
select distinct cast(t.column_value.extract('//text()') as
varchar2(4000)) table_name
from subgraph,
         table(xmlsequence(xmltype(
           '<x><x>' || replace(path, '#', '</x><x>') ||
'</x></x>').extract('//x/*'))) t
order by table_name
-------------------------------------------------

Happy deciphering this beast! I'll pay you a beer if you manage to
phrase this query with jOOQ (contributing the required extensions, of
course) ;-)

Cheers
Lukas

Reply via email to