Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running sql scripts with interdependencies

I have a project utilizing a postgres database in the backend and I'm working to create a Dockerfile which can automatically set up a local development instance of the db. The database includes a large number of functions, and these have historically been stored in context-appropriate sql files e.g. users.sql, companies.sql. This has been fine as whenever changes are made, I can simply execute the relevant sql files which REPLACE all Views and drop/recreate all of the Functions.

However, when trying to run these scripts on a fresh postgres instance (after CREATEing all tables), they fail because most of the views/functions reference other views functions which are not defined yet.

I have begun investigating if there is a particular order I can run the scripts in to avoid this issue, but since they weren't designed with this purpose in mind it may be impossible, and there is a large number of entities at play so this is most likely a non-trivial task.

Is there any way to achieve this apart from restructuring the scripts?

like image 407
bkane521 Avatar asked Jul 18 '26 22:07

bkane521


1 Answers

You can write a simple recursive query that starts with all views that don't depend on others and recursively adds the views that depend on these. Then output the view definition for these views in the correct order, and you have got your script:

WITH RECURSIVE viewids AS (
   /* all views that don't depend on other views */
   SELECT t.oid, 1 as level
   FROM pg_class t
      JOIN pg_rewrite AS r ON r.ev_class = t.oid
   WHERE r.rulename = '_RETURN'
     AND t.relkind = 'v'
     AND t.relnamespace NOT IN ('pg_catalog'::regnamespace,
                                'information_schema'::regnamespace,
                                'pg_toast'::regnamespace)
     AND NOT EXISTS (
            /* depends on a view */
            SELECT 1
            FROM pg_depend AS d
               JOIN pg_class AS t2 ON d.refobjid = t2.oid
            WHERE d.objid = r.oid
              AND d.classid = 'pg_rewrite'::regclass
              AND d.refclassid = 'pg_class'::regclass
              AND d.deptype = 'n'
              AND d.refobjsubid <> 0
              AND t2.relkind = 'v'
         )
     AND NOT EXISTS (
            /* depends on an extension */
            SELECT 1
            FROM pg_depend
            WHERE objid = t.oid
              AND classid = 'pg_class'::regclass
              AND refclassid = 'pg_extension'::regclass
              AND deptype = 'e'
         )
UNION ALL
   /* all views that depend on these views */
   SELECT t.oid, viewids.level + 1
   FROM pg_class AS t
      JOIN pg_rewrite AS r ON r.ev_class = t.oid
      JOIN pg_depend AS d ON d.objid = r.oid
      JOIN viewids ON viewids.oid = d.refobjid
   WHERE t.relkind = 'v'
     AND r.rulename = '_RETURN'
     AND d.classid = 'pg_rewrite'::regclass                            
     AND d.refclassid = 'pg_class'::regclass
     AND d.deptype = 'n'
     AND d.refobjsubid <> 0
)
/* order the views by level, eliminating duplicates */
SELECT format('CREATE VIEW %s AS%s',
              oid::regclass,
              pg_get_viewdef(oid::regclass))
FROM viewids
GROUP BY oid
ORDER BY max(level);
like image 132
Laurenz Albe Avatar answered Jul 21 '26 15:07

Laurenz Albe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!