@@ -19,18 +19,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
1919-- leaf_migrations (jsonb) = leaf migration names by app from the django code
2020-- Ex: '{<django-app>: <latest-leaf-migration-name>}'
2121-- Set _verbose to true to see notices raised during execution
22- DROP FUNCTION IF EXISTS public .app_needs_migrations (jsonb, boolean );
22+ DROP FUNCTION IF EXISTS public .migrations_complete (jsonb, boolean );
2323
24- CREATE OR REPLACE FUNCTION public .app_needs_migrations (leaf_migrations jsonb, _verbose boolean DEFAULT false)
24+ CREATE OR REPLACE FUNCTION public .migrations_complete (leaf_migrations jsonb, _verbose boolean DEFAULT false)
2525RETURNS boolean AS $BODY$
2626DECLARE
2727 schema_rec record;
2828 leaf_app_key text ;
2929 leaf_app_keys text [];
3030 latest_migrations jsonb;
3131 required_tables int := 0 ;
32- do_migrations boolean := false;
33- objects_exist boolean := false;
32+ completed_migrations boolean := true;
33+ exists_rec record;
34+ chk_res boolean ;
3435BEGIN
3536 /*
3637 * Verify that the necessary tables are present
4849 */
4950 IF required_tables != 2
5051 THEN
51- RETURN true;
52+ IF _verbose
53+ THEN
54+ RAISE WARNING ' Schema "public" not initialized' ;
55+ END IF;
56+ RETURN false;
5257 END IF;
5358
5459 /*
@@ -61,85 +66,114 @@ BEGIN
6166 FOR schema_rec IN
6267 SELECT t .schema_name
6368 FROM public .api_tenant t
64- JOIN pg_namespace n
65- on n .nspname = t .schema_name
6669 ORDER
6770 BY case when schema_name = ' public'
6871 then ' 0public'
6972 else schema_name
7073 END::text
7174 LOOP
72- /* Check for race condition if someone deletes a source, etc during processing */
73- SELECT EXISTS (
74- SELECT c .oid
75- FROM pg_class c
76- JOIN pg_namespace n
77- ON n .oid = c .relnamespace
78- WHERE c .relname = ' django_migrations'
79- AND n .nspname = schema_rec .schema_name
80- )::boolean
81- INTO objects_exist;
82-
83- IF NOT objects_exist
75+ /* Get the latest recorded migrations by app for this tenant schema */
76+ IF _verbose
8477 THEN
85- RAISE WARNING ' Object %.% does not exist. Skipping schema %' ,
86- schema_rec .schema_name ,
87- ' django_migrations' ,
88- schema_rec .schema_name ;
78+ RAISE INFO ' Checking migration state in schema %' , schema_rec .schema_name ;
8979 END IF;
9080
91- CONTINUE WHEN NOT objects_exist;
81+ /* Check for race condition if someone deletes a source, etc during processing */
82+ EXECUTE ' SELECT EXISTS ( ' ||
83+ ' SELECT c.oid ' ||
84+ ' FROM pg_class c ' ||
85+ ' JOIN pg_namespace n ' ||
86+ ' ON n.oid = c.relnamespace ' ||
87+ ' WHERE c.relname = ' ' django_migrations' ' ' ||
88+ ' AND n.nspname = ' || quote_literal(schema_rec .schema_name ) || ' ' ||
89+ ' )::boolean as "objects_exist", ' ||
90+ ' EXISTS ( ' ||
91+ ' SELECT t.id ' ||
92+ ' FROM public.api_tenant t ' ||
93+ ' WHERE schema_name = ' || quote_literal(schema_rec .schema_name ) || ' ' ||
94+ ' )::boolean as "tenant_exists", ' ||
95+ ' EXISTS ( ' ||
96+ ' SELECT n.oid ' ||
97+ ' FROM pg_namespace n ' ||
98+ ' WHERE nspname = ' || quote_literal(schema_rec .schema_name ) || ' ' ||
99+ ' )::boolean as "schema_exists" '
100+ INTO exists_rec;
92101
93- /* Get the latest recorded migrations by app for this tenant schema */
94- IF _verbose
102+ IF exists_rec .tenant_exists AND NOT exists_rec .schema_exists
95103 THEN
96- RAISE NOTICE ' Checking %' , schema_rec .schema_name ;
104+ RAISE EXCEPTION ' MIGRATION CHECK :: Tenant "%" exists, but there is no database schema.' ,
105+ schema_rec .schema_name ;
97106 END IF;
98- EXECUTE ' SELECT jsonb_object_agg(app, migration) ' ||
99- ' FROM ( ' ||
100- ' SELECT app, ' ||
101- ' max(name) as "migration" ' ||
102- ' FROM ' || quote_ident(schema_rec .schema_name ) || ' .django_migrations ' ||
103- ' GROUP BY app ' ||
104- ' ) AS x ;'
105- INTO latest_migrations;
106-
107- /* Loop through leaf apps */
108- FOREACH leaf_app_key IN ARRAY leaf_app_keys
109- LOOP
110- /* test that app exists or not */
111- IF latest_migrations ? leaf_app_key
112- THEN
113- /* App exists! Test if the leaf migration name is greater than the last recorded migration for the app */
114- IF _verbose
115- THEN
116- RAISE NOTICE ' checking %.% > %.%' ,
117- leaf_app_key,
118- leaf_migrations- >> leaf_app_key,
119- leaf_app_key,
120- latest_migrations- >> leaf_app_key;
121- END IF;
122- IF leaf_migrations- >> leaf_app_key > latest_migrations- >> leaf_app_key
123- THEN
124- /* leaf ahead of last recorded. run migrations! */
125- do_migrations = true;
126- EXIT;
127- END IF;
128- ELSE
129- /* App does not exist, run migrations! */
130- IF _verbose
107+ IF NOT exists_rec .tenant_exists AND exists_rec .schema_exists
108+ THEN
109+ RAISE EXCEPTION ' MIGRATION CHECK :: Schema "%" exists, but there is no tenant record.' ,
110+ schema_rec .schema_name ;
111+ END IF;
112+
113+ CONTINUE WHEN (NOT exists_rec .tenant_exists ) OR (NOT exists_rec .schema_exists );
114+
115+ IF NOT exists_rec .objects_exist
116+ THEN
117+ RAISE WARNING ' %.django_migrations does not exist' , schema_rec .schema_name ;
118+ completed_migrations = false;
119+ ELSE
120+ EXECUTE ' SELECT jsonb_object_agg(app, migration) ' ||
121+ ' FROM ( ' ||
122+ ' SELECT app, ' ||
123+ ' max(name) as "migration" ' ||
124+ ' FROM ' || quote_ident(schema_rec .schema_name ) || ' .django_migrations ' ||
125+ ' GROUP BY app ' ||
126+ ' ) AS x ;'
127+ INTO latest_migrations;
128+
129+ /* Loop through leaf apps */
130+ FOREACH leaf_app_key IN ARRAY leaf_app_keys
131+ LOOP
132+ /* test that app exists or not */
133+ IF latest_migrations ? leaf_app_key
131134 THEN
132- RAISE NOTICE ' app % missing from schema' , leaf_app_key;
135+ /* App exists! Test if the leaf migration name is greater than the last recorded migration for the app */
136+ chk_res = (leaf_migrations- >> leaf_app_key > latest_migrations- >> leaf_app_key)::boolean ;
137+ IF _verbose
138+ THEN
139+ RAISE INFO ' checking %.% > %.% (%)' ,
140+ leaf_app_key,
141+ leaf_migrations- >> leaf_app_key,
142+ leaf_app_key,
143+ latest_migrations- >> leaf_app_key,
144+ chk_res::text ;
145+ END IF;
146+ IF chk_res
147+ THEN
148+ /* leaf ahead of last recorded. run migrations! */
149+ completed_migrations = false;
150+ IF _verbose
151+ THEN
152+ RAISE INFO ' Will run migrations.' ;
153+ END IF;
154+ END IF;
155+ ELSE
156+ /* App does not exist, run migrations! */
157+ IF _verbose
158+ THEN
159+ RAISE INFO ' app % missing from schema' , leaf_app_key;
160+ END IF;
161+ completed_migrations = false;
133162 END IF;
134- do_migrations = true;
135- EXIT;
136- END IF ;
137- END LOOP ;
163+
164+ EXIT WHEN not completed_migrations ;
165+ END LOOP ;
166+ END IF ;
138167
139168 /* Stop processing if we are going to run migrations */
140- EXIT WHEN do_migrations ;
169+ EXIT WHEN not completed_migrations ;
141170 END LOOP;
142171
143- RETURN do_migrations;
172+ IF _verbose
173+ THEN
174+ RAISE INFO ' Migration Check: App should%execute migrations.' , case when completed_migrations then ' not ' else ' ' end::text ;
175+ END IF;
176+
177+ RETURN completed_migrations;
144178END;
145179$BODY$ LANGUAGE PLPGSQL;
0 commit comments