execsql2 1.130.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2792 @@
1
+ -- pg_upsert.sql
2
+ --
3
+ -- PURPOSE
4
+ -- A set of execsql scripts to check data in a staging table, or
5
+ -- a set of staging tables, and then update and insert rows of a base table
6
+ -- or base tables from the staging table(s) of the same name.
7
+ --
8
+ -- HOW TO USE THESE SCRIPTS
9
+ -- In the following steps, "call" means to use an execsql "execute script"
10
+ -- metacommand. Script names are displayed in uppercase to distinguish
11
+ -- them below, but execsql is not case-sensitive.
12
+ --
13
+ -- The simplest usage is:
14
+ -- 1. Call STAGED_TO_LOAD to create and initially populate a table
15
+ -- with the names of staging tables to be loaded, and initial
16
+ -- control variables used by other scripts.
17
+ -- 2. Call LOAD_STAGING to perform QA checks for erroneous nulls,
18
+ -- duplicated primary keys, and missing foreign keys, and to
19
+ -- load the data using update and insert statements if there were
20
+ -- no QA errors.
21
+ --
22
+ -- The control table that is produced in step 1 above can be edited to
23
+ -- add a list of columns to exclude from the update, or to change the
24
+ -- defaults for display of changed data. See the header notes for the
25
+ -- STAGED_TO_LOAD script.
26
+ --
27
+ -- The processes of performing QA checks and performing the upsert operations
28
+ -- can be further broken down into individual steps. See Note #1 below
29
+ -- for the other scripts that can be used to carry out these steps.
30
+ --
31
+ -- NOTES
32
+ -- 1. The scripts contained in this file that are intended to be called
33
+ -- directly by the user are:
34
+ -- STAGED_TO_LOAD : Initialize a control table to load multiple tables.
35
+ -- LOAD_STAGING : Perform all QA checks and load data from all staging tables.
36
+ -- QA_ALL : Perform null and foreign key checks on all staging tables.
37
+ -- UPSERT_ALL : Load data from all staging tables.
38
+ -- NULLQA_ONE : Perform null column checks on one staging table.
39
+ -- PKQA_ONE : Perform primary key check on one staging table.
40
+ -- FKQA_ONE : Perform foreign key checks on one staging table.
41
+ -- UPSERT_ONE : Load data from one staging table.
42
+ -- UPDTPK_ONE : Perform PK updates for one table.
43
+ -- UPDTPKQA_ONE : Perform QA checks related to PK updates, for one table.
44
+ -- This file contains other scripts that are intended to be used
45
+ -- only by one of the scripts listed above, and not called
46
+ -- directly by the user.
47
+ -- 2. These scripts query the information schema to obtain
48
+ -- the information needed to perform the checks and changes.
49
+ -- 3. These scripts were developed for PostgreSQL, and are likely to
50
+ -- need modification to run on other DBMSs.
51
+ -- 4. These scripts take arguments that control their functions. They
52
+ -- also use global variables pertinent to logging, if they are
53
+ -- defined. The logging-control variables are global because
54
+ -- they may also be used by other code that uses these scripts,
55
+ -- and some of that code may be older and only recognize global
56
+ -- variables rather than script arguments; logging is intended
57
+ -- to be compatible with them.
58
+ -- 5. The control table that is used to drive the loading of multiple
59
+ -- staging tables will be updated by the QA scripts with information
60
+ -- about any QA failures. This information consists of a list of
61
+ -- the names of the columns or constraints that failed the check,
62
+ -- with the number of failing rows in parentheses following the name.
63
+ -- 6. The control table that is used to drive the loading of multiple
64
+ -- staging tables will be updated by the upsert operation with
65
+ -- a count of the number of rows of the base table that are updated,
66
+ -- and a count of the number of rows that were inserted into the
67
+ -- base table.
68
+ -- 7. All of these scripts assume that schema, table, and column
69
+ -- names need not be quoted.
70
+ -- 8. These scripts create temporary tables and views. All of these
71
+ -- have prefixes of "ups_". Scripts that include this file
72
+ -- should not use this prefix to avoid possible conflicts.
73
+ --
74
+ -- COPYRIGHT AND LICENSE
75
+ -- Copyright (c) 2019, R. Dreas Nielsen
76
+ -- This program is free software: you can redistribute it and/or modify it
77
+ -- under the terms of the GNU General Public License as published by the
78
+ -- Free Software Foundation, either version 3 of the License, or (at your
79
+ -- option) any later version. This program is distributed in the hope that
80
+ -- it will be useful, but WITHOUT ANY WARRANTY; without even the implied
81
+ -- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
82
+ -- GNU General Public License for more details. The GNU General Public
83
+ -- License is available at http://www.gnu.org/licenses/.
84
+
85
+ -- AUTHORS
86
+ -- Dreas Nielsen (RDN)
87
+ -- Elizabeth Shea (ES)
88
+ -- Tom Schulz (TS)
89
+ --
90
+ -- VERSION
91
+ -- 2.0.0
92
+ --
93
+ -- HISTORY
94
+ -- Date Remarks
95
+ -- ---------- -----------------------------------------------------
96
+ -- 2019-01-15 Began first component scripts. RDN.
97
+ -- 2019-01-30 Integrated separate scripts into this one file and
98
+ -- began modifications to set parameters and arguments. RDN.
99
+ -- 2019-01-31 Editing. RDN.
100
+ -- 2019-02-01 Completed revisions to set parameters and arguments.
101
+ -- Added scripts "staged_to_load", "upsert_all", and
102
+ -- "load_staging".
103
+ -- Added and modified documentation. RDN.
104
+ -- 2019-02-02 Debugging edits. RDN.
105
+ -- 2019-02-03 Added to script documentation. Modified NULLQA_ONE
106
+ -- to always drop and recreate tt_nonnull_columns.
107
+ -- Corrected calls to foreign key check scripts. RDN.
108
+ -- 2019-02-06 Fixed some erroneous argument variable references.
109
+ -- Modified to initially nullify the columns with return
110
+ -- values in the control table. RDN.
111
+ -- 2019-02-09 Modified to update the status bar if the console is
112
+ -- being used. RDN.
113
+ -- 2019-02-15 Modified all internal table and view names to use a
114
+ -- prefix of "ups_". RDN.
115
+ -- 2019-02-16 Modified to include in the list of ordered tables all
116
+ -- tables that are not part of any foreign key relationship.
117
+ -- Modified to use an 'exclude_null_checks' setting. RDN.
118
+ -- 2019-02-17 Corrected the check to display changes in UPSERT_ONE. RDN.
119
+ -- 2019-02-21 Modified QA_ALL_NULLLOOP and QA_ALL_FKLOOP to use
120
+ -- local variables and the new outer scope referent prefix
121
+ -- for the return values. RDN.
122
+ -- 2019-02-28 Changed to use system catalog to get foreign key columns.
123
+ -- Changed to use 'autocommit with commit'. RDN.
124
+ -- 2019-03-02 Modified recursive CTE to order dependencies to protect
125
+ -- against multi-table cycles. RDN.
126
+ -- 2019-03-02 Modified to show the number of rows with each invalid
127
+ -- foreign key value, in both the GUI display and the
128
+ -- logfile. RDN.
129
+ -- 2019-03-03 Added the 'do_commit' argument to LOAD_STAGING. RDN.
130
+ -- 2019-03-08 Made the following changes. ES.
131
+ -- o In UPSERT_ALL, changed ups_proctables from a permanent
132
+ -- to a temp table.
133
+ -- o Edits to header notes to include PK check.
134
+ -- o Added logging of commit status to LOAD_STAGING.
135
+ -- o Modified UPSERT_ONE to create update statement only
136
+ -- if there are any non-key columns in the base table.
137
+ -- 2019-03-09 Made the following changes. ES.
138
+ -- o In UPSERT_ALL, removed duplication of parents with
139
+ -- no children in ups_ordered_tables (lvls 1 and 0).
140
+ -- o In QA_ALL made progress bar progress specific to
141
+ -- check type.
142
+ -- o Added primary key check.
143
+ -- 2019-03-14 Modified regexp_split_to_table when populating control_table.
144
+ -- "Escape's leading \ will need to be doubled when entering the
145
+ -- pattern as an SQL string constant." TS.
146
+ -- 2019-03-15 Added definition and execution of validation scripts to
147
+ -- validate base and staging schemas and tables. ES.
148
+ -- 2019-03-15 Modified ups_validate_control to cast base_schema and
149
+ -- staging_schema as text. TS.
150
+ -- 2019-05-28 Corrected selection for ups_cols in script UPSERT_ONE to
151
+ -- include only columns that are also in base table (i.e.,
152
+ -- exclude any columns that are only in staging table, such
153
+ -- as any "new_" PK columns to be used for PK updates). ES.
154
+ -- 2019-05-29 Added script UPDTPK_ONE. ES.
155
+ -- 2019-05-30 Added UPDTPKQA_ONE through check #8. ES.
156
+ -- 2019-06-03 Completed UPDTPKQA_ONE and added UPDTPKQA_ONE_INNERLOOP. ES.
157
+ -- 2020-04-28 Explicitly rolled back the changes if 'do_commit' = "No". RDN.
158
+ -- 2020-05-09 Moved the assignment of 'upsert_progress_denom' out of the
159
+ -- 'update_console_qa' script. RDN.
160
+ -- 2021-04-27 Added a semicolon to the end of all SQL statements that are
161
+ -- written to the logfile. RDN.
162
+ -- 2021-04-29 Modified to log the SQL for null checks as well, and so
163
+ -- that the "select" keyword of all checks is the first word
164
+ -- on a line of the logfile, for easier extraction. RDN.
165
+ -- ==================================================================
166
+
167
+
168
+ -- ################################################################
169
+ -- Script VALIDATE_SCHEMAS
170
+ -- ===============================================================
171
+ --
172
+ -- Utility script to validate base and staging schema
173
+ --
174
+ -- Required input arguments:
175
+ -- base_schema : The name of the base table schema.
176
+ -- staging : The name of the staging schema.
177
+ --
178
+ -- Required output arguments:
179
+ -- error_list : The name of the variable to receive a comma-
180
+ -- delimited list of the names of invalid
181
+ -- schema names.
182
+ -- ===============================================================
183
+ -- !x! BEGIN SCRIPT VALIDATE_SCHEMAS with parameters (base_schema, staging, error_list)
184
+
185
+ drop table if exists ups_ctrl_invl_schema cascade;
186
+ select
187
+ string_agg(schemas.schema_name || ' (' || schema_type || ')', '; ' order by schema_type) as schema_string
188
+ into temporary table ups_ctrl_invl_schema
189
+ from
190
+ (
191
+ select
192
+ '!!#base_schema!!' as schema_name,
193
+ 'base' as schema_type
194
+ union
195
+ select
196
+
197
+ '!!#staging!!' as schema_name,
198
+ 'staging' as schema_type
199
+ ) as schemas
200
+ left join information_schema.schemata as iss on schemas.schema_name=iss.schema_name
201
+ where
202
+ iss.schema_name is null
203
+ --This is required in Postgres, otherwise, string_agg will return one row with NULL
204
+ having count(*)>0
205
+ ;
206
+
207
+ -- !x! if(hasrows(ups_ctrl_invl_schema))
208
+ -- !x! subdata !!#error_list!! ups_ctrl_invl_schema
209
+ -- !x! endif
210
+
211
+ -- !x! END SCRIPT
212
+ -- #################### End of VALIDATE_SCHEMAS #################
213
+ -- ################################################################
214
+
215
+
216
+
217
+ -- ################################################################
218
+ -- Script VALIDATE_ONE
219
+ -- ===============================================================
220
+ --
221
+ -- Utility script to validate one table in both base and staging schema.
222
+ -- Halts script processing if any either of the schemas are non-existent,
223
+ -- or if either of the tables are not present within those schemas.
224
+ --
225
+ -- Input parameters:
226
+ -- base_schema : The name of the base table schema.
227
+ -- staging : The name of the staging schema.
228
+ -- table_name : The name of the table.
229
+ -- script : The name of the script in which the
230
+ -- schemas and table were referenced
231
+ -- (for error reporting).
232
+ -- script_line : The script line in which they were referenced
233
+ -- (for error reporting).
234
+ -- ===============================================================
235
+ -- !x! BEGIN SCRIPT VALIDATE_ONE with parameters (base_schema, staging, table, script, script_line)
236
+
237
+ -- Initialize the strings used to compile error information
238
+ -- !x! sub_empty ~err_info
239
+ -- !x! sub_empty ~error_list
240
+
241
+
242
+ -- Validate schemas
243
+ -- !x! execute script validate_schemas with args (base_schema=!!#base_schema!!, staging=!!#staging!!, error_list=+err_info)
244
+
245
+
246
+ -- If no schemas are invalid, check tables
247
+ -- !x! if(is_null("!!~err_info!!"))
248
+ drop table if exists ups_invl_table cascade;
249
+ select
250
+ string_agg(tt.schema_name || '.' || tt.table_name || ' (' || tt.schema_type || ')', '; ' order by tt.schema_name, tt.table_name) as schema_table
251
+ into temporary table ups_invl_table
252
+ from
253
+ (
254
+ select
255
+ '!!#base_schema!!' as schema_name,
256
+ 'base' as schema_type,
257
+ '!!#table!!' as table_name
258
+ union
259
+ select
260
+
261
+ '!!#staging!!' as schema_name,
262
+ 'staging' as schema_type,
263
+ '!!#table!!' as table_name
264
+ ) as tt
265
+ left join information_schema.tables as iss on tt.schema_name=iss.table_schema and tt.table_name=iss.table_name
266
+ where
267
+ iss.table_name is null
268
+ --This is required in Postgres, otherwise, string_agg will return one row with NULL
269
+ having count(*)>0
270
+ ;
271
+
272
+ -- !x! if(hasrows(ups_invl_table))
273
+ -- !x! subdata ~err_info ups_invl_table
274
+ -- !x! sub ~error_list Non-existent table: !!~err_info!!
275
+ -- !x! endif
276
+
277
+ -- !x! else
278
+ -- !x! sub ~error_list Non-existent schema(s): !!~err_info!!
279
+ -- !x! endif
280
+
281
+
282
+ -- Halt script if any schemas or tables were found to be invalid
283
+ -- !x! if(not is_null("!!~error_list!!"))
284
+
285
+ -- !x! sub ~error_message ERROR - INVALID OBJECT IN SCRIPT ARGUMENT
286
+
287
+ -- !x! if(sub_defined(log_changes))
288
+ -- !x! andif(is_true(!!log_changes!!))
289
+ -- !x! write "==================================================================" to !!logfile!!
290
+ -- !x! write "!!$current_time!! -- !!~error_message!!" to !!logfile!!
291
+ -- !x! write "Script: !!#script!!; Line: !!#script_line!!" to !!logfile!!
292
+ -- !x! write "!!~error_list!!" to !!logfile!!
293
+ -- !x! endif
294
+
295
+ -- !x! sub_append ~error_message Script: !!#script!!; Line: !!#script_line!!
296
+ -- !x! sub_append ~error_message !!~error_list!!
297
+ -- !x! halt message "!!~error_message!!"
298
+
299
+ -- !x! endif
300
+
301
+
302
+ -- !x! END SCRIPT
303
+ -- #################### End of VALIDATE_ONE #####################
304
+ -- ################################################################
305
+
306
+
307
+
308
+ -- ################################################################
309
+ -- Script VALIDATE_CONTROL
310
+ -- ===============================================================
311
+ --
312
+ -- Utility script to validate contents of control table against
313
+ -- base and staging schema
314
+ --
315
+ -- Required input arguments:
316
+ -- base_schema : The name of the base table schema.
317
+ -- staging : The name of the staging schema.
318
+ -- control_table : The name of a temporary table as created by the
319
+ -- script STAGED_TO_LOAD.
320
+ -- script : The name of the script in which the
321
+ -- schemas and control table were referenced
322
+ -- (for error reporting).
323
+ -- script_line : The script line in which they were referenced
324
+ -- (for error reporting).
325
+ -- ===============================================================
326
+ -- !x! BEGIN SCRIPT VALIDATE_CONTROL with parameters (base_schema, staging, control_table, script, script_line)
327
+
328
+ -- Initialize the strings used to compile error information
329
+ -- !x! sub_empty ~err_info
330
+ -- !x! sub_empty ~error_list
331
+
332
+ -- !x! execute script validate_schemas with args (base_schema=!!#base_schema!!, staging=!!#staging!!, error_list=+err_info)
333
+
334
+
335
+ -- If no schemas are invalid, check tables
336
+ -- !x! if(is_null("!!~err_info!!"))
337
+ drop table if exists ups_validate_control cascade;
338
+ select
339
+ cast('!!#base_schema!!' as text) as base_schema,
340
+ cast('!!#staging!!' as text) as staging_schema,
341
+ table_name,
342
+ False as base_exists,
343
+ False as staging_exists
344
+ into temporary table ups_validate_control
345
+ from !!#control_table!!
346
+ ;
347
+
348
+ -- Update the control table
349
+ update ups_validate_control as vc
350
+ set base_exists = True
351
+ from information_schema.tables as bt
352
+ where
353
+ vc.base_schema=bt.table_schema and vc.table_name=bt.table_name
354
+ and bt.table_type= cast('BASE TABLE' as text)
355
+ ;
356
+
357
+ update ups_validate_control as vc
358
+ set staging_exists = True
359
+ from information_schema.tables as st
360
+ where
361
+ vc.staging_schema=st.table_schema and vc.table_name=st.table_name
362
+ and st.table_type= cast('BASE TABLE' as text)
363
+ ;
364
+
365
+ drop table if exists ups_ctrl_invl_table cascade;
366
+ select
367
+ string_agg(schema_table, '; ' order by it.schema_table) as schema_table
368
+ into temporary table ups_ctrl_invl_table
369
+ from
370
+ (
371
+ select base_schema || '.' || table_name as schema_table
372
+ from ups_validate_control
373
+ where not base_exists
374
+ union
375
+ select staging_schema || '.' || table_name as schema_table
376
+ from ups_validate_control
377
+ where not staging_exists
378
+ ) as it
379
+ --This is required in Postgres, otherwise, string_agg will return one row with NULL
380
+ having count(*)>0
381
+ ;
382
+
383
+ -- Any table is invalid
384
+ -- !x! if(hasrows(ups_ctrl_invl_table))
385
+ -- !x! subdata ~err_info ups_validate_control
386
+ -- !x! sub ~error_list Non-existent table(s): !!~err_info!!
387
+ -- !x! endif
388
+
389
+ -- !x! else
390
+ -- !x! sub ~error_list Non-existent schema(s): !!~err_info!!
391
+ -- !x! endif
392
+
393
+
394
+ -- Halt script if any invalid objects found in control table
395
+ -- !x! if(not is_null("!!~error_list!!"))
396
+
397
+ -- !x! sub ~error_message ERROR - INVALID OBJECTS IN CONTROL TABLE
398
+
399
+ -- !x! if(sub_defined(log_changes))
400
+ -- !x! andif(is_true(!!log_changes!!))
401
+ -- !x! write "==================================================================" to !!logfile!!
402
+ -- !x! write "!!$current_time!! -- !!~error_message!!" to !!logfile!!
403
+ -- !x! write "Script: !!#script!!; Line: !!#script_line!!" to !!logfile!!
404
+ -- !x! write "!!~error_list!!" to !!logfile!!
405
+ -- !x! endif
406
+
407
+ -- !x! sub_append ~error_message Script: !!#script!!; Line: !!#script_line!!
408
+ -- !x! sub_append ~error_message !!~error_list!!
409
+ -- !x! halt message "!!~error_message!!"
410
+
411
+ -- !x! endif
412
+
413
+
414
+ -- !x! END SCRIPT
415
+ -- #################### End of VALIDATE_CONTROL #################
416
+ -- ################################################################
417
+
418
+
419
+ -- ################################################################
420
+ -- Script STAGED_TO_LOAD
421
+ -- ===============================================================
422
+ --
423
+ -- Creates a table having the structure that is used to drive other
424
+ -- scripts that perform QA checks and the upsert operation on multiple
425
+ -- staging tables.
426
+ --
427
+ -- Input parameters:
428
+ -- control_table : The name of the temporary table to be created.
429
+ -- table_list : A string of comma-separated table names,
430
+ -- identifying the staging tables to be
431
+ -- checked or loaded.
432
+ --
433
+ -- Columns in the table created:
434
+ -- table_name : The name of a base table that has a
435
+ -- corresponding table in a staging schema
436
+ -- containing data to be used to modify
437
+ -- the base table.
438
+ -- exclude_cols : Contains a comma-separated list of single-quoted
439
+ -- column names in the base table that are not to
440
+ -- be updated from the staging table. This is
441
+ -- uninitialized.
442
+ -- exclude_null_checks :
443
+ -- Contains a comma-separated list of single-quoted
444
+ -- column names identifying column in the
445
+ -- staging table for which null checks
446
+ -- should not be performed. This is
447
+ -- uninitialized.
448
+ -- display_changes : A Boolean indicating whether the 'upsert'
449
+ -- operation should display the changes to
450
+ -- be made to the base table. A separate
451
+ -- GUI display is used for updates and inserts.
452
+ -- Initialized to True.
453
+ -- display_final : A Boolean indicating whether the 'upsert'
454
+ -- operation should display the entire base
455
+ -- table after updates and inserts have been
456
+ -- made. Initialized to False.
457
+ -- null_errors : Will contain a comma-separated list of
458
+ -- columns that are non-nullable in the base
459
+ -- table but that have nulls in the staging
460
+ -- table. Initialized to null; may be filled
461
+ -- by the QA routines.
462
+ -- pk_errors : Will contain a count of the number of distinct
463
+ -- primary key values having duplicate rows,
464
+ -- followed by the total row count for the
465
+ -- duplicated keys. Initialized to null; may
466
+ -- be filled by the QA routines.
467
+ -- fk_errors : Will contain a comma-separated list of
468
+ -- foreign-key constraint names that are
469
+ -- not met by data in the staging table.
470
+ -- Initialized to null; may be filled by the
471
+ -- QA routines.
472
+ -- rows_updated : Will contain a count of the rows updated
473
+ -- in the table by the upsert operation.
474
+ -- rows_inserted : Will contain a count of the rows inserted
475
+ -- in the table by the upsert operation.
476
+ --
477
+ -- Example:
478
+ -- -- !x! execute script staged_to_load with (control_table=stagingtables, table_list="tbla, tblb, tblc")
479
+ -- ===============================================================
480
+
481
+ -- !x! BEGIN SCRIPT STAGED_TO_LOAD with parameters (control_table, table_list)
482
+
483
+ drop table if exists !!#control_table!! cascade;
484
+
485
+ create temporary table !!#control_table!! (
486
+ table_name text not null unique,
487
+ exclude_cols text,
488
+ exclude_null_checks text,
489
+ display_changes text not null default 'Yes',
490
+ display_final text not null default 'No',
491
+ null_errors text,
492
+ pk_errors text,
493
+ fk_errors text,
494
+ rows_updated integer,
495
+ rows_inserted integer,
496
+ constraint ck_chgyn check (display_changes in ('Yes', 'No')),
497
+ constraint ck_finalyn check (display_final in ('Yes', 'No'))
498
+ );
499
+
500
+ insert into !!#control_table!!
501
+ (table_name)
502
+ select
503
+ trim(regexp_split_to_table('!!#table_list!!', E'\\s*,\\s*')) as table_name;
504
+
505
+
506
+ -- !x! END SCRIPT
507
+ -- ################## End of STAGED_TO_LOAD #####################
508
+ -- ################################################################
509
+
510
+
511
+
512
+ -- ################################################################
513
+ -- Script LOAD_STAGING
514
+ -- ===============================================================
515
+ --
516
+ -- Performs QA checks for nulls in non-null columns, for duplicated
517
+ -- primary key values, and for invalid foreign keys in a set of staging
518
+ -- tables to be loaded into base tables. If there are failures in the
519
+ -- QA checks, loading is not attempted. If the loading step is carried
520
+ -- out, it is done within a transaction.
521
+ --
522
+ -- The "null_errors", "pk_errors", and "fk_errors" columns of the
523
+ -- control table will be updated to identify any errors that occur,
524
+ -- so that this information is available to the caller.
525
+ --
526
+ -- The "rows_updated" and "rows_inserted" columns of the control table
527
+ -- will be updated with counts of the number of rows affected by the
528
+ -- upsert operation for each table.
529
+ --
530
+ -- When the upsert operation updates the base table, all columns of the
531
+ -- base table that are also in the staging table are updated. The
532
+ -- update operation does not test to see if column contents are different,
533
+ -- and so does not update only those values that are different.
534
+ --
535
+ -- Input parameters:
536
+ -- base_schema : The name of the base table schema.
537
+ -- staging : The name of the staging schema.
538
+ -- control_table : The name of a table as created by the
539
+ -- script STAGED_TO_LOAD.
540
+ -- do_commit : Whether or not the script should commit
541
+ -- the changes; should be 'Yes' or 'No'.
542
+ -- Global variables:
543
+ -- logfile : The name of a log file to which error
544
+ -- messages will be written. Optional.
545
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
546
+ -- the SQL that is generated for each foreign
547
+ -- key check, and for each update and insert
548
+ -- statement, is written to the logfile. Optional.
549
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
550
+ -- foreign key errors are written to the logfile.
551
+ -- Optional.
552
+ -- log_changes : A value of 'Yes' or 'No' indicating whether
553
+ -- the updated and inserted data should be
554
+ -- written to the logfile. Optional.
555
+ --
556
+ -- Tables and views created or modified:
557
+ -- ups_qa_fails : temporary view
558
+ -- ===============================================================
559
+
560
+ -- !x! BEGIN SCRIPT LOAD_STAGING with parameters (base_schema, staging, control_table, do_commit)
561
+
562
+ -- Clear the columns of return values from the control table, in case this control
563
+ -- table has been used previously.
564
+ update !!#control_table!!
565
+ set
566
+ null_errors = null,
567
+ pk_errors = null,
568
+ fk_errors = null,
569
+ rows_updated = null,
570
+ rows_inserted = null
571
+ ;
572
+
573
+ -- Run QA checks.
574
+ -- !x! execute script QA_ALL with arguments (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
575
+ drop view if exists ups_qa_fails cascade;
576
+ create temporary view ups_qa_fails as
577
+ select *
578
+ from !!#control_table!!
579
+ where null_errors is not null or pk_errors is not null or fk_errors is not null;
580
+ -- !x! if(not hasrows(ups_qa_fails))
581
+ -- !x! sub ~preautocommit !!$autocommit_state!!
582
+ -- !x! autocommit off
583
+ -- Run the UPSERT operation.
584
+ -- !x! execute script UPSERT_ALL with arguments (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
585
+ -- Commit the changes and then restore the previous autocommit state.
586
+ -- !x! if(is_true(!!#do_commit!!))
587
+ -- !x! autocommit on with commit
588
+ -- !x! write "CHANGES COMMITTED."
589
+ -- !x! if(sub_defined(log_changes))
590
+ -- !x! andif(is_true(!!log_changes!!))
591
+ -- !x! write "" to !!logfile!!
592
+ -- !x! write "==================================================================" to !!logfile!!
593
+ -- !x! write "!!$current_time!! -- CHANGES COMMITTED." to !!logfile!!
594
+ -- !x! endif
595
+ -- !x! else
596
+ -- !x! autocommit on with rollback
597
+ -- !x! write "CHANGES NOT COMMITTED ('do_commit' argument = !!#do_commit!!)"
598
+ -- !x! if(sub_defined(log_changes))
599
+ -- !x! andif(is_true(!!log_changes!!))
600
+ -- !x! write "" to !!logfile!!
601
+ -- !x! write "==================================================================" to !!logfile!!
602
+ -- !x! write "!!$current_time!! -- CHANGES NOT COMMITTED ('do_commit' argument = !!#do_commit!!)" to !!logfile!!
603
+ -- !x! endif
604
+ -- !x! endif
605
+ -- !x! autocommit !!~preautocommit!!
606
+ -- !x! endif
607
+
608
+ -- !x! END SCRIPT
609
+ -- ################### End of LOAD_STAGING ######################
610
+ -- ################################################################
611
+
612
+
613
+
614
+
615
+ -- ################################################################
616
+ -- Script NULLQA_ONE
617
+ -- ===============================================================
618
+ --
619
+ -- Checks that non-nullable columns are fully populated in a
620
+ -- staging table that is an image of the base table.
621
+ -- Reports any non-conforming columns to the console and optionally
622
+ -- to a log file.
623
+ --
624
+ -- Required input arguments:
625
+ -- base_schema : The name of the base table schema.
626
+ -- staging : The name of the staging schema.
627
+ -- table : The table name--same for base and staging.
628
+ -- Optional input arguments:
629
+ -- exclude_null_checks : A comma-separated list of singly-quoted
630
+ -- column names to be excluded from null checks.
631
+ --
632
+ -- Required output arguments:
633
+ -- error_list : The name of the variable to receive a comma-
634
+ -- delimited list of the names of non-null
635
+ -- columns that contain nulls; each column name
636
+ -- will be followed by the number of rows with
637
+ -- nulls, in parentheses.
638
+ --
639
+ -- Global variables:
640
+ -- logfile : The name of a log file to which error
641
+ -- messages will be written. Optional.
642
+ --
643
+ -- Tables and views created or modified:
644
+ -- ups_nonnull_cols : temporary table
645
+ -- ups_next_column : temporary view
646
+ -- ups_null_error_list : temporary view
647
+ -- ups_qa_nonnull_col : temporary view
648
+ -- ===============================================================
649
+
650
+ -- !x! BEGIN SCRIPT NULLQA_ONE with parameters (base_schema, staging, table, error_list)
651
+
652
+ -- Write an initial header to the logfile.
653
+ -- !x! if(sub_defined(logfile))
654
+ -- !x! write "" to !!logfile!!
655
+ -- !x! write "==================================================================" to !!logfile!!
656
+ -- !x! write "!!$current_time!! -- Non-null QA checks on table !!#staging!!.!!#table!!" to !!logfile!!
657
+ -- !x! endif
658
+
659
+ -- !x! write "Conducting non-null QA checks on table !!#staging!!.!!#table!!"
660
+
661
+ -- Validate inputs: base/staging schemas and table
662
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
663
+
664
+
665
+ -- Initialize the return value to empty (no null errors)
666
+ -- !x! sub_empty !!#error_list!!
667
+
668
+ -- Create a table listing the columns of the base table that must
669
+ -- be non-null and that do not have a default expression.
670
+ -- Include a column for the number of rows with nulls in the staging table.
671
+ -- Include a 'processed' column for loop control.
672
+ -- !x! if(sub_defined(#exclude_null_checks))
673
+ -- !x! sub ~omitnull and column_name not in (!!#exclude_null_checks!!)
674
+ -- !x! else
675
+ -- !x! sub_empty ~omitnull
676
+ -- !x! endif
677
+ drop table if exists ups_nonnull_cols cascade;
678
+ select
679
+ column_name,
680
+ 0::integer as null_rows,
681
+ False as processed
682
+ into
683
+ temporary table ups_nonnull_cols
684
+ from
685
+ information_schema.columns
686
+ where
687
+ table_schema = '!!#base_schema!!'
688
+ and table_name = '!!#table!!'
689
+ and is_nullable = 'NO'
690
+ and column_default is null
691
+ !!~omitnull!!
692
+ ;
693
+
694
+ -- Create a view to select one column to process.
695
+ create or replace temporary view ups_next_column as
696
+ select column_name
697
+ from ups_nonnull_cols
698
+ where not processed
699
+ limit 1;
700
+
701
+ -- Process all non-nullable columns.
702
+ -- !x! execute script nullqa_one_innerloop with (staging=!!#staging!!, table=!!#table!!)
703
+
704
+ -- Create the return value.
705
+ create or replace temporary view ups_null_error_list as
706
+ select
707
+ string_agg(column_name || ' (' || null_rows || ')', ', ') as null_errors
708
+ from
709
+ ups_nonnull_cols
710
+ where
711
+ coalesce(null_rows, 0) > 0;
712
+ -- !x! if(hasrows(ups_null_error_list))
713
+ -- !x! subdata !!#error_list!! ups_null_error_list
714
+ -- !x! endif
715
+
716
+
717
+ -- !x! END SCRIPT
718
+ -- End of NULLQA_ONE
719
+ -- ****************************************************************
720
+ -- ****************************************************************
721
+ -- Script NULLQA_ONE_INNERLOOP
722
+ -- ---------------------------------------------------------------
723
+ -- !x! BEGIN SCRIPT NULLQA_ONE_INNERLOOP with parameters (staging, table)
724
+
725
+ -- !x! if(hasrows(ups_next_column))
726
+ -- !x! subdata ~column_name ups_next_column
727
+
728
+ -- !x! if(sub_defined(logfile))
729
+ -- !x! write "Checking column !!~column_name!!." to !!logfile!!
730
+ -- !x! endif
731
+
732
+ -- !x! sub ~null_check select nrows
733
+ -- !x! sub_append ~null_check from (
734
+ -- !x! sub_append ~null_check select count(*) as nrows
735
+ -- !x! sub_append ~null_check from !!#staging!!.!!#table!!
736
+ -- !x! sub_append ~null_check where !!~column_name!! is null
737
+ -- !x! sub_append ~null_check ) as nullcount
738
+ -- !x! sub_append ~null_check where nrows > 0
739
+ -- !x! sub_append ~null_check limit 1
740
+
741
+ create or replace temporary view ups_qa_nonnull_col as
742
+ !!~null_check!!;
743
+ /*
744
+ select nrows
745
+ from (
746
+ select count(*) as nrows
747
+ from !!#staging!!.!!#table!!
748
+ where !!~column_name!! is null
749
+ ) as nullcount
750
+ where nrows > 0
751
+ limit 1;
752
+ */
753
+ -- Write the SQL to the log file if requested.
754
+ -- !x! if(sub_defined(logfile))
755
+ -- !x! andif(sub_defined(log_sql))
756
+ -- !x! andif(is_true(!!log_sql!!))
757
+ -- !x! write "SQL for null check of !!~column_name!!:" to !!logfile!!
758
+ -- !x! write [!!~null_check!!;] to !!logfile!!
759
+ -- !x! endif
760
+
761
+
762
+ -- !x! if(hasrows(ups_qa_nonnull_col))
763
+ -- !x! subdata ~nullrows ups_qa_nonnull_col
764
+ -- !x! write " Column !!~column_name!! has !!~nullrows!! nulls."
765
+ -- !x! if(sub_defined(logfile))
766
+ -- !x! write " Column !!~column_name!! has !!~nullrows!! nulls." to !!logfile!!
767
+ -- !x! endif
768
+ update ups_nonnull_cols
769
+ set null_rows = (select nrows from ups_qa_nonnull_col limit 1)
770
+ where column_name = '!!~column_name!!';
771
+ -- !x! endif
772
+
773
+ -- Mark this constraint as processed.
774
+ update ups_nonnull_cols
775
+ set processed = True
776
+ where column_name = '!!~column_name!!';
777
+
778
+ -- Loop.
779
+ -- !x! execute script nullqa_one_innerloop with (staging=!!#staging!!, table=!!#table!!)
780
+
781
+ -- !x! endif
782
+
783
+ -- !x! END SCRIPT
784
+ -- ################### End of NULL_QA_ONE #######################
785
+ -- ################################################################
786
+
787
+
788
+
789
+ -- ################################################################
790
+ -- Script PKQA_ONE
791
+ --
792
+ -- Check data a staging table for violations of the primary key
793
+ -- of the corresponding base table.
794
+ -- Reports any PK violations found to the console and optionally
795
+ -- to a log file.
796
+ --
797
+ -- Input parameters:
798
+ -- base_schema : The name of the base table schema.
799
+ -- staging : The name of the staging schema.
800
+ -- table : The table name--same for base and staging.
801
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
802
+ -- unrecognized values should be displayed
803
+ -- in a GUI.
804
+ -- Output parameters:
805
+ -- error_list : The name of the variable to receive a count
806
+ -- of the total number of distinct PK values
807
+ -- having violations, followed by a count of
808
+ -- the total rows associated with each.
809
+ --
810
+ -- Global variables:
811
+ -- logfile : The name of a log file to which update
812
+ -- messages will be written. Optional.
813
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
814
+ -- the SQL that is generated for each foreign
815
+ -- key check is written to the logfile. Optional.
816
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
817
+ -- foreign key errors are written to the logfile.
818
+ -- Optional.
819
+ --
820
+ -- Tables and views created or modified:
821
+ -- ups_primary_key_columns : temporary table
822
+ -- ups_pk_list : temporary view
823
+ -- ups_pk_check : temporary view
824
+ -- ups_ercnt : temporary view
825
+ -- ===============================================================
826
+
827
+ -- !x! BEGIN SCRIPT PKQA_ONE with parameters (base_schema, staging, table, display_errors, error_list)
828
+
829
+ -- Write an initial header to the logfile.
830
+ -- !x! if(sub_defined(logfile))
831
+ -- !x! write "" to !!logfile!!
832
+ -- !x! write "==================================================================" to !!logfile!!
833
+ -- !x! write "!!$current_time!! -- Primary key QA checks on table !!#staging!!.!!#table!!" to !!logfile!!
834
+ -- !x! endif
835
+
836
+ -- !x! write "Conducting primary key QA checks on table !!#staging!!.!!#table!!"
837
+
838
+ -- Validate inputs: base/staging schemas and table
839
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
840
+
841
+ -- Initialize the return value to False (no primary key errors)
842
+ -- !x! sub_empty !!#error_list!!
843
+
844
+ -- Create a table of primary key columns on this table
845
+ drop table if exists ups_primary_key_columns cascade;
846
+ select k.constraint_name, k.column_name, k.ordinal_position
847
+ into temporary table ups_primary_key_columns
848
+ from information_schema.table_constraints as tc
849
+ inner join information_schema.key_column_usage as k
850
+ on tc.constraint_type = 'PRIMARY KEY'
851
+ and tc.constraint_name = k.constraint_name
852
+ and tc.constraint_catalog = k.constraint_catalog
853
+ and tc.constraint_schema = k.constraint_schema
854
+ and tc.table_schema = k.table_schema
855
+ and tc.table_name = k.table_name
856
+ and tc.constraint_name = k.constraint_name
857
+ where
858
+ k.table_name = '!!#table!!'
859
+ and k.table_schema = '!!#base_schema!!'
860
+ order by k.ordinal_position
861
+ ;
862
+
863
+
864
+ -- !x! if(hasrows(ups_primary_key_columns))
865
+ -- !x! subdata ~constraint_name ups_primary_key_columns
866
+
867
+ -- !x! if(sub_defined(logfile))
868
+ -- !x! write "Checking constraint !!~constraint_name!!." to !!logfile!!
869
+ -- !x! endif
870
+
871
+ -- Get a comma-delimited list of primary key columns to build SQL selection for duplicate keys
872
+ create or replace temporary view ups_pk_list as
873
+ select
874
+ string_agg(column_name, ', ' order by ordinal_position) as pkcollist
875
+ from ups_primary_key_columns
876
+ ;
877
+ --!x! subdata ~pkcollist ups_pk_list
878
+
879
+ -- Construct a query to test for duplicate values for pk columns.
880
+ -- !x! sub ~pk_check select !!~pkcollist!!, count(*) as row_count
881
+ -- !x! sub_append ~pk_check from !!#staging!!.!!#table!! as s
882
+ -- !x! sub_append ~pk_check group by !!~pkcollist!!
883
+ -- !x! sub_append ~pk_check having count(*) > 1
884
+
885
+ -- Write the SQL to the log file if requested.
886
+ -- !x! if(sub_defined(logfile))
887
+ -- !x! andif(sub_defined(log_sql))
888
+ -- !x! andif(is_true(!!log_sql!!))
889
+ -- !x! write "SQL for primary key check:" to !!logfile!!
890
+ -- !x! write [!!~pk_check!!;] to !!logfile!!
891
+ -- !x! endif
892
+
893
+ -- Run the check.
894
+ drop view if exists ups_pk_check cascade;
895
+ create or replace temporary view ups_pk_check as
896
+ !!~pk_check!!;
897
+ -- !x! if(hasrows(ups_pk_check))
898
+ -- !x! write " Duplicate key error on columns: !!~pkcollist!!."
899
+ drop view if exists ups_ercnt cascade;
900
+ create temporary view ups_ercnt as
901
+ select
902
+ count(*) as errcnt, sum(row_count) as total_rows
903
+ from ups_pk_check;
904
+ -- !x! select_sub ups_ercnt
905
+ -- !x! sub !!#error_list!! !!@errcnt!! duplicated key(s) (!!@total_rows!! rows)
906
+ -- !x! if(sub_defined(logfile))
907
+ -- !x! write "Duplicate primary key values in !!#staging!!.!!#table!!" to !!logfile!!
908
+ -- !x! if(sub_defined(log_errors))
909
+ -- !x! andif(is_true(!!log_errors!!))
910
+ -- !x! export ups_pk_check append to !!logfile!! as txt
911
+ -- !x! endif
912
+ -- !x! endif
913
+ -- !x! if(is_true(!!#display_errors!!))
914
+ -- !x! prompt message "Primary key violations in !!#table!!" display ups_pk_check
915
+ -- !x! endif
916
+ -- !x! endif
917
+ -- !x! endif
918
+
919
+
920
+ -- !x! END SCRIPT
921
+ -- #################### End of PKQA_ONE ########################
922
+ -- ################################################################
923
+
924
+
925
+
926
+ -- ################################################################
927
+ -- Script FKQA_ONE
928
+ --
929
+ -- Checks foreign keys from a staging table against a base table
930
+ -- and, if it exists, another staging table that is an image of the
931
+ -- base table.
932
+ -- Reports any bad references found to the console and optionally
933
+ -- to a log file.
934
+ --
935
+ -- Input parameters:
936
+ -- base_schema : The name of the base table schema.
937
+ -- staging : The name of the staging schema.
938
+ -- table : The table name--same for base and staging.
939
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
940
+ -- unrecognized values should be displayed
941
+ -- in a GUI.
942
+ -- Output parameters:
943
+ -- error_list : The name of the variable to receive a comma-
944
+ -- delimited list of the names of foreign key
945
+ -- constraints that are not met.
946
+ --
947
+ -- Global variables:
948
+ -- logfile : The name of a log file to which update
949
+ -- messages will be written. Optional.
950
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
951
+ -- the SQL that is generated for each foreign
952
+ -- key check is written to the logfile. Optional.
953
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
954
+ -- foreign key errors are written to the logfile.
955
+ -- Optional.
956
+ --
957
+ -- Tables and views created or modified:
958
+ -- ups_foreign_key_columns : temporary table
959
+ -- ups_sel_fks : temporary table
960
+ -- ups_fk_constraints : temporary table
961
+ -- ups_next_constraint : temporary view
962
+ -- ups_error_list : temporary view
963
+ -- ups_one_fk : temporary table
964
+ -- ups_fk_joins : temporary view
965
+ -- ups_fk_check : temporary view
966
+ -- ups_ercnt : temporary view
967
+ -- ===============================================================
968
+
969
+ -- !x! BEGIN SCRIPT FKQA_ONE with parameters (base_schema, staging, table, display_errors, error_list)
970
+
971
+ -- Write an initial header to the logfile.
972
+ -- !x! if(sub_defined(logfile))
973
+ -- !x! write "" to !!logfile!!
974
+ -- !x! write "==================================================================" to !!logfile!!
975
+ -- !x! write "!!$current_time!! -- Foreign key QA checks on table !!#staging!!.!!#table!!" to !!logfile!!
976
+ -- !x! endif
977
+
978
+ -- !x! write "Conducting foreign key QA checks on table !!#staging!!.!!#table!!"
979
+
980
+ -- Validate inputs: base/staging schemas and table
981
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
982
+
983
+
984
+ -- Initialize the return value to False (no foreign key errors)
985
+ -- !x! sub_empty !!#error_list!!
986
+
987
+
988
+ -- Create a table of *all* foreign key dependencies in this database.
989
+ -- Because this may be an expensive operation (in terms of time), the
990
+ -- table is not re-created if it already exists. "Already exists"
991
+ -- means that a table with the expected name exists. No check is
992
+ -- done to ensure that this table has the correct structure. The
993
+ -- goal is to create the table of all foreign keys only once to
994
+ -- minimize the time required if QA checks are to be run on multiple
995
+ -- staging tables.
996
+ -- !x! if(not table_exists(ups_foreign_key_columns))
997
+ select
998
+ fkinf.constraint_name,
999
+ fkinf.table_schema,
1000
+ fkinf.table_name,
1001
+ att1.attname as column_name,
1002
+ fkinf.uq_schema,
1003
+ cls.relname as uq_table,
1004
+ att2.attname as uq_column
1005
+ into
1006
+ temporary table ups_foreign_key_columns
1007
+ from
1008
+ (select
1009
+ ns1.nspname as table_schema,
1010
+ cls.relname as table_name,
1011
+ unnest(cons.conkey) as uq_table_id,
1012
+ unnest(cons.confkey) as table_id,
1013
+ cons.conname as constraint_name,
1014
+ ns2.nspname as uq_schema,
1015
+ cons.confrelid,
1016
+ cons.conrelid
1017
+ from
1018
+ pg_constraint as cons
1019
+ inner join pg_class as cls on cls.oid = cons.conrelid
1020
+ inner join pg_namespace ns1 on ns1.oid = cls.relnamespace
1021
+ inner join pg_namespace ns2 on ns2.oid = cons.connamespace
1022
+ where
1023
+ cons.contype = 'f'
1024
+ ) as fkinf
1025
+ inner join pg_attribute att1 on
1026
+ att1.attrelid = fkinf.conrelid and att1.attnum = fkinf.uq_table_id
1027
+ inner join pg_attribute att2 on
1028
+ att2.attrelid = fkinf.confrelid and att2.attnum = fkinf.table_id
1029
+ inner join pg_class cls on cls.oid = fkinf.confrelid;
1030
+ -- !x! endif
1031
+
1032
+ -- Create a temporary table of just the foreign key relationships for the base
1033
+ -- table corresponding to the staging table to check.
1034
+ drop table if exists ups_sel_fks cascade;
1035
+ select
1036
+ constraint_name, table_schema, table_name, column_name,
1037
+ --ordinal_position,
1038
+ uq_schema, uq_table, uq_column
1039
+ into
1040
+ temporary table ups_sel_fks
1041
+ from
1042
+ ups_foreign_key_columns
1043
+ where
1044
+ table_schema = '!!#base_schema!!'
1045
+ and table_name = '!!#table!!';
1046
+
1047
+ -- Create a temporary table of all unique constraint names for
1048
+ -- this table, with an integer column to be populated with the
1049
+ -- number of rows failing the foreign key check, and a 'processed'
1050
+ -- flag to control looping.
1051
+ drop table if exists ups_fk_constraints cascade;
1052
+ select distinct
1053
+ constraint_name, table_schema, table_name,
1054
+ 0::integer as fkerror_values,
1055
+ False as processed
1056
+ into temporary table ups_fk_constraints
1057
+ from ups_sel_fks;
1058
+
1059
+ -- Create a view to select one constraint to process.
1060
+ create or replace temporary view ups_next_constraint as
1061
+ select constraint_name, table_schema, table_name
1062
+ from ups_fk_constraints
1063
+ where not processed
1064
+ limit 1;
1065
+
1066
+ -- Process all constraints: check every foreign key.
1067
+ -- !x! execute script fk_qa_one_innerloop with (staging=!!#staging!!, table=!!#table!!, display_errors=!!#display_errors!!)
1068
+
1069
+ -- Create the return value.
1070
+ create or replace temporary view ups_error_list as
1071
+ select
1072
+ string_agg(constraint_name || ' (' || fkerror_values || ')', ', ') as fk_errors
1073
+ from
1074
+ ups_fk_constraints
1075
+ where
1076
+ coalesce(fkerror_values, 0) > 0;
1077
+ -- !x! if(hasrows(ups_error_list))
1078
+ -- !x! subdata !!#error_list!! ups_error_list
1079
+ -- !x! endif
1080
+
1081
+
1082
+ -- !x! END SCRIPT
1083
+ -- End of FKQA_ONE
1084
+ -- ****************************************************************
1085
+ -- ****************************************************************
1086
+ -- Script FK_QA_ONE_INNERLOOP
1087
+ -- ----------------------------------------------------------------
1088
+ -- !x! BEGIN SCRIPT FK_QA_ONE_INNERLOOP with parameters (staging, table, display_errors)
1089
+
1090
+ -- !x! if(hasrows(ups_next_constraint))
1091
+ -- !x! select_sub ups_next_constraint
1092
+
1093
+ -- !x! if(sub_defined(logfile))
1094
+ -- !x! write "Checking constraint !!@constraint_name!! for table !!@table_schema!!.!!@table_name!!." to !!logfile!!
1095
+ -- !x! endif
1096
+
1097
+ drop table if exists ups_one_fk cascade;
1098
+ select column_name, uq_schema, uq_table, uq_column
1099
+ into temporary table ups_one_fk
1100
+ from ups_sel_fks
1101
+ where
1102
+ constraint_name = '!!@constraint_name!!'
1103
+ and table_schema = '!!@table_schema!!'
1104
+ and table_name = '!!@table_name!!';
1105
+
1106
+ -- Get the unique table schema and name into data variables.
1107
+ -- !x! select_sub ups_one_fk
1108
+
1109
+ -- Create join expressions from staging table (s) to unique table (u)
1110
+ -- and to staging table equivalent to unique table (su) (though we
1111
+ -- don't know yet if the latter exists). Also create a 'where'
1112
+ -- condition to ensure that all columns being matched are non-null.
1113
+ -- Also create a comma-separated list of the columns being checked.
1114
+ create or replace temporary view ups_fk_joins as
1115
+ select
1116
+ string_agg('s.' || column_name || ' = u.' || uq_column, ' and ') as u_join,
1117
+ string_agg('s.' || column_name || ' = su.' || uq_column, ' and ') as su_join,
1118
+ string_agg('s.' || column_name || ' is not null', ' and ') as s_not_null,
1119
+ string_agg('s.' || column_name, ', ') as s_checked
1120
+ from
1121
+ (select * from ups_one_fk) as fkcols;
1122
+ -- !x! select_sub ups_fk_joins
1123
+
1124
+ -- Determine whether a staging-table equivalent of the unique table exists.
1125
+ -- !x! sub su_exists No
1126
+ -- !x! if(table_exists(!!#staging!!.!!@uq_table!!))
1127
+ -- !x! sub su_exists Yes
1128
+ -- !x! endif
1129
+
1130
+ -- Construct a query to test for missing unique values for fk columns.
1131
+ -- !x! sub ~fk_check select !!@s_checked!!, count(*) as row_count
1132
+ -- !x! sub_append ~fk_check from !!#staging!!.!!#table!! as s
1133
+ -- !x! sub_append ~fk_check left join !!@uq_schema!!.!!@uq_table!! as u on !!@u_join!!
1134
+ -- !x! if(is_true(!!su_exists!!))
1135
+ -- !x! sub_append ~fk_check left join !!#staging!!.!!@uq_table!! as su on !!@su_join!!
1136
+ -- !x! endif
1137
+ -- !x! sub_append ~fk_check where u.!!@uq_column!! is null
1138
+ -- !x! if(is_true(!!su_exists!!))
1139
+ -- !x! sub_append ~fk_check and su.!!@uq_column!! is null
1140
+ -- !x! endif
1141
+ -- !x! sub_append ~fk_check and !!@s_not_null!!
1142
+ -- !x! sub_append ~fk_check group by !!@s_checked!!
1143
+
1144
+ -- Write the SQL to the log file if requested.
1145
+ -- !x! if(sub_defined(logfile))
1146
+ -- !x! andif(sub_defined(log_sql))
1147
+ -- !x! andif(is_true(!!log_sql!!))
1148
+ -- !x! write "SQL for foreign key check:" to !!logfile!!
1149
+ -- !x! write [!!~fk_check!!;] to !!logfile!!
1150
+ -- !x! endif
1151
+
1152
+ -- Run the check.
1153
+ drop view if exists ups_fk_check cascade;
1154
+ create or replace temporary view ups_fk_check as
1155
+ !!~fk_check!!;
1156
+ -- !x! if(hasrows(ups_fk_check))
1157
+ -- !x! write " Foreign key error referencing !!@uq_table!!."
1158
+ drop view if exists ups_ercnt cascade;
1159
+ create temporary view ups_ercnt as
1160
+ select count(*) from ups_fk_check;
1161
+ -- !x! subdata ~errcnt ups_ercnt
1162
+ update ups_fk_constraints
1163
+ set fkerror_values = !!~errcnt!!
1164
+ where
1165
+ constraint_name = '!!@constraint_name!!'
1166
+ and table_schema = '!!@table_schema!!'
1167
+ and table_name = '!!@table_name!!';
1168
+ -- !x! if(sub_defined(logfile))
1169
+ -- !x! write " Foreign key errors in !!#table!! referencing !!@uq_table!!" to !!logfile!!
1170
+ -- !x! if(sub_defined(log_errors))
1171
+ -- !x! andif(is_true(!!log_errors!!))
1172
+ -- !x! export ups_fk_check append to !!logfile!! as txt
1173
+ -- !x! endif
1174
+ -- !x! endif
1175
+ -- !x! if(is_true(!!#display_errors!!))
1176
+ -- !x! prompt message "Foreign key errors in !!#table!! referencing !!@uq_table!!" display ups_fk_check
1177
+ -- !x! endif
1178
+ -- !x! endif
1179
+
1180
+
1181
+ -- Mark this constraint as processed.
1182
+ update ups_fk_constraints
1183
+ set processed = True
1184
+ where
1185
+ constraint_name = '!!@constraint_name!!'
1186
+ and table_schema = '!!@table_schema!!'
1187
+ and table_name = '!!@table_name!!';
1188
+
1189
+ -- Loop.
1190
+ -- !x! execute script fk_qa_one_innerloop with (staging=!!#staging!!, table=!!#table!!, display_errors=!!#display_errors!!)
1191
+
1192
+ -- !x! endif
1193
+
1194
+ -- !x! END SCRIPT
1195
+ -- #################### End of FK_QA_ONE ########################
1196
+ -- ################################################################
1197
+
1198
+
1199
+
1200
+
1201
+ -- ################################################################
1202
+ -- Script UPSERT_ONE
1203
+ --
1204
+ -- Adds data from a staging table to a base table, using UPDATE
1205
+ -- and INSERT statements. Displays data to be modified to the
1206
+ -- user before any modifications are done. Reports the changes
1207
+ -- made to the console and optionally to a log file.
1208
+ --
1209
+ -- Input parameters:
1210
+ -- base_schema : The name of the base table schema.
1211
+ -- staging : The name of the staging schema.
1212
+ -- table : The table name--same for base and staging.
1213
+ -- exclude_cols : A comma-delimited list of single-quoted
1214
+ -- column names identifying the columns
1215
+ -- of the base table that are not to be
1216
+ -- modified. These may be autonumber
1217
+ -- columns or columns filled by triggers.
1218
+ -- display_changes : A boolean variable indicating whether
1219
+ -- or not the changes to be made to the
1220
+ -- base table should be displayed in a GUI.
1221
+ -- display_final : A boolean variable indicating whether or
1222
+ -- not the base table should be displayed
1223
+ -- after updates and inserts are completed.
1224
+ -- updcntvar : The name of a substitution variable that
1225
+ -- will be set to the number of rows updated.
1226
+ -- inscntvar : The name of a substitution variable that
1227
+ -- will be set to the number of rows inserted.
1228
+ --
1229
+ -- Global variables:
1230
+ -- logfile : The name of a log file to which update
1231
+ -- messages will be written. Optional.
1232
+ -- log_sql : A value of 'Yes' or 'No' indicating whether
1233
+ -- the update and insert statements should
1234
+ -- also be written to the logfile. Optional.
1235
+ -- log_changes : A value of 'Yes' or 'No' indicating whether
1236
+ -- the updated and inserted data should be
1237
+ -- written to the logfile. Optional.
1238
+ --
1239
+ -- Tables and views created or modified:
1240
+ -- ups_cols : temporary table
1241
+ -- ups_pks : temporary table
1242
+ -- ups_allcollist : temporary view
1243
+ -- ups_allbasecollist : temporary view
1244
+ -- ups_allstgcollist : temporary view
1245
+ -- ups_pkcollist : temporary view
1246
+ -- ups_joinexpr : temporary view
1247
+ -- ups_basematches : temporary view
1248
+ -- ups_stgmatches : temporary view
1249
+ -- ups_nk : temporary view
1250
+ -- ups_assexpr : temporary view
1251
+ -- ups_newrows : temporary view
1252
+ -- ===============================================================
1253
+
1254
+ -- !x! BEGIN SCRIPT UPSERT_ONE with parameters (base_schema, staging, table, exclude_cols, display_changes, display_final, updcntvar, inscntvar)
1255
+
1256
+ -- Remove substitution variables that will contain the generated
1257
+ -- update and insert statements so that the existence of valid
1258
+ -- statements can be later tested based on the existence of these variables.
1259
+ -- !x! rm_sub ~updatestmt
1260
+ -- !x! rm_sub ~insertstmt
1261
+
1262
+ -- !x! sub ~do_updates Yes
1263
+ -- !x! sub ~do_inserts Yes
1264
+
1265
+ -- !x! if(sub_defined(logfile))
1266
+ -- !x! write "" to !!logfile!!
1267
+ -- !x! write "==================================================================" to !!logfile!!
1268
+ -- !x! write "!!$current_time!! -- Performing upsert on table !!#base_schema!!.!!#table!!" to !!logfile!!
1269
+ -- !x! endif
1270
+
1271
+
1272
+ -- !x! write "Performing upsert on table !!#base_schema!!.!!#table!!"
1273
+
1274
+
1275
+ -- Validate inputs: base/staging schemas and table
1276
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
1277
+
1278
+
1279
+ -- Populate a (temporary) table with the names of the columns
1280
+ -- in the base table that are to be updated from the staging table.
1281
+ -- Include only those columns from staging table that are also in base table.
1282
+ -- !x! if(is_null("!!#exclude_cols!!"))
1283
+ -- !x! sub_empty ~col_excl
1284
+ -- !x! else
1285
+ -- !x! sub ~col_excl and column_name not in (!!#exclude_cols!!)
1286
+ -- !x! endif
1287
+ drop table if exists ups_cols cascade;
1288
+ select s.column_name
1289
+ into temporary table ups_cols
1290
+ from information_schema.columns as s
1291
+ inner join information_schema.columns as b on s.column_name=b.column_name
1292
+ where
1293
+ s.table_schema = '!!#staging!!'
1294
+ and s.table_name = '!!#table!!'
1295
+ and b.table_schema = '!!#base_schema!!'
1296
+ and b.table_name = '!!#table!!'
1297
+ !!~col_excl!!
1298
+ order by s.ordinal_position;
1299
+
1300
+
1301
+ -- Populate a (temporary) table with the names of the primary key
1302
+ -- columns of the base table.
1303
+ drop table if exists ups_pks cascade;
1304
+ select k.column_name
1305
+ into temporary table ups_pks
1306
+ from information_schema.table_constraints as tc
1307
+ inner join information_schema.key_column_usage as k
1308
+ on tc.constraint_type = 'PRIMARY KEY'
1309
+ and tc.constraint_name = k.constraint_name
1310
+ and tc.constraint_catalog = k.constraint_catalog
1311
+ and tc.constraint_schema = k.constraint_schema
1312
+ and tc.table_schema = k.table_schema
1313
+ and tc.table_name = k.table_name
1314
+ and tc.constraint_name = k.constraint_name
1315
+ where
1316
+ k.table_name = '!!#table!!'
1317
+ and k.table_schema = '!!#base_schema!!'
1318
+ order by k.ordinal_position;
1319
+
1320
+ -- Get all base table columns that are to be updated into a comma-delimited list.
1321
+ drop view if exists ups_allcollist cascade;
1322
+ create temporary view ups_allcollist as
1323
+ select string_agg(column_name, ', ')
1324
+ from ups_cols;
1325
+ -- !x! subdata ~allcollist ups_allcollist;
1326
+
1327
+
1328
+ -- Get all base table columns that are to be updated into a comma-delimited list
1329
+ -- with a "b." prefix.
1330
+ drop view if exists ups_allbasecollist cascade;
1331
+ create temporary view ups_allbasecollist as
1332
+ select string_agg('b.' || column_name, ', ')
1333
+ from ups_cols;
1334
+ -- !x! subdata ~allbasecollist ups_allbasecollist;
1335
+
1336
+ -- Get all staging table column names for columns that are to be updated
1337
+ -- into a comma-delimited list with an "s." prefix.
1338
+ drop view if exists ups_allstgcollist cascade;
1339
+ create temporary view ups_allstgcollist as
1340
+ select string_agg('s.' || column_name, ', ')
1341
+ from ups_cols;
1342
+ -- !x! subdata ~allstgcollist ups_allstgcollist;
1343
+
1344
+
1345
+ -- Get the primary key columns in a comma-delimited list.
1346
+ drop view if exists ups_pkcollist cascade;
1347
+ create temporary view ups_pkcollist as
1348
+ select string_agg(column_name, ', ')
1349
+ from ups_pks;
1350
+ -- !x! subdata ~pkcollist ups_pkcollist;
1351
+
1352
+
1353
+ -- Create a join expression for key columns of the base (b) and
1354
+ -- staging (s) tables.
1355
+ drop view if exists ups_joinexpr cascade;
1356
+ create temporary view ups_joinexpr as
1357
+ select
1358
+ string_agg('b.' || column_name || ' = s.' || column_name, ' and ')
1359
+ from
1360
+ ups_pks;
1361
+ -- !x! subdata ~joinexpr ups_joinexpr
1362
+
1363
+
1364
+ -- Create a FROM clause for an inner join between base and staging
1365
+ -- tables on the primary key column(s).
1366
+ -- !x! sub ~fromclause FROM !!#base_schema!!.!!#table!! as b INNER JOIN !!#staging!!.!!#table!! as s ON !!~joinexpr!!
1367
+
1368
+
1369
+ -- Create SELECT queries to pull all columns with matching keys from both
1370
+ -- base and staging tables.
1371
+ drop view if exists ups_basematches cascade;
1372
+ create temporary view ups_basematches as select !!~allbasecollist!! !!~fromclause!!;
1373
+
1374
+ drop view if exists ups_stgmatches cascade;
1375
+ create temporary view ups_stgmatches as select !!~allstgcollist!! !!~fromclause!!;
1376
+
1377
+
1378
+ --Get non-key columns to be updated
1379
+ drop view if exists ups_nk cascade;
1380
+ create temporary view ups_nk as
1381
+ select column_name from ups_cols
1382
+ except
1383
+ select column_name from ups_pks
1384
+ ;
1385
+
1386
+ -- Prompt user to examine matching data and commit, don't commit, or quit.
1387
+ -- !x! if(hasrows(ups_stgmatches))
1388
+ -- !x! andif(hasrows(ups_nk))
1389
+ -- Prompt user to examine matching data and commit, don't commit, or quit.
1390
+ -- !x! if(is_true(!!#display_changes!!))
1391
+ -- !x! prompt ask "Do you want to make these changes? For table !!#table!!, new data are shown in the top table below; existing data are in the lower table." sub ~do_updates compare ups_stgmatches and ups_basematches key (!!~pkcollist!!)
1392
+ -- !x! endif
1393
+ -- !x! if(is_true(!!~do_updates!!))
1394
+ -- Create an assignment expression to update non-key columns of the
1395
+ -- base table (un-aliased) from columns of the staging table (as s).
1396
+ drop view if exists ups_assexpr cascade;
1397
+ create temporary view ups_assexpr as
1398
+ select
1399
+ string_agg(column_name || ' = s.' || column_name, ', ') as col
1400
+ from ups_nk;
1401
+ -- !x! subdata ~assexpr ups_assexpr
1402
+ -- Create an UPDATE statement to update the base table with
1403
+ -- non-key columns from the staging table. No semicolon terminating generated SQL.
1404
+ -- !x! sub ~updatestmt UPDATE !!#base_schema!!.!!#table!! as b SET !!~assexpr!! FROM !!#staging!!.!!#table!! as s WHERE !!~joinexpr!!
1405
+ -- !x! endif
1406
+ -- !x! endif
1407
+
1408
+
1409
+ -- Create a select statement to find all rows of the staging table
1410
+ -- that are not in the base table.
1411
+ drop view if exists ups_newrows cascade;
1412
+ create temporary view ups_newrows as
1413
+ with newpks as (
1414
+ select !!~pkcollist!! from !!#staging!!.!!#table!!
1415
+ except
1416
+ select !!~pkcollist!! from !!#base_schema!!.!!#table!!
1417
+ )
1418
+ select
1419
+ s.*
1420
+ from
1421
+ !!#staging!!.!!#table!! as s
1422
+ inner join newpks using (!!~pkcollist!!);
1423
+
1424
+
1425
+ -- Prompt user to examine new data and continue or quit.
1426
+ -- !x! if(hasrows(ups_newrows))
1427
+ -- !x! if(is_true(!!#display_changes!!))
1428
+ -- !x! prompt ask "Do you want to add these new data to the !!#base_schema!!.!!#table!! table?" sub ~do_inserts display ups_newrows
1429
+ -- !x! endif
1430
+
1431
+ -- !x! if(is_true(!!~do_inserts!!))
1432
+ -- Create an insert statement. No semicolon terminating generated SQL.
1433
+ -- !x! sub ~insertstmt INSERT INTO !!#base_schema!!.!!#table!! (!!~allcollist!!) SELECT !!~allcollist!! FROM ups_newrows
1434
+ -- !x! endif
1435
+ -- !x! endif
1436
+
1437
+
1438
+ -- Run the update and insert statements.
1439
+
1440
+ -- !x! if(sub_defined(~updatestmt))
1441
+ -- !x! andif(is_true(!!~do_updates!!))
1442
+ -- !x! write "Updating !!#base_schema!!.!!#table!!"
1443
+ -- !x! if(sub_defined(logfile))
1444
+ -- !x! write "" to !!logfile!!
1445
+ -- !x! if(sub_defined(log_sql))
1446
+ -- !x! andif(is_true(!!log_sql!!))
1447
+ -- !x! write "UPDATE statement for !!#base_schema!!.!!#table!!:" to !!logfile!!
1448
+ -- !x! write [!!~updatestmt!!;] to !!logfile!!
1449
+ -- !x! endif
1450
+ -- !x! if(sub_defined(log_changes))
1451
+ -- !x! andif(is_true(!!log_changes!!))
1452
+ -- !x! write "Updates:" to !!logfile!!
1453
+ -- !x! export ups_stgmatches append to !!logfile!! as txt
1454
+ -- !x! endif
1455
+ -- !x! write "" to !!logfile!!
1456
+ -- !x! endif
1457
+ !!~updatestmt!!;
1458
+ -- !x! sub !!#updcntvar!! !!$last_rowcount!!
1459
+ -- !x! if(sub_defined(logfile))
1460
+ -- !x! write "!!$last_rowcount!! rows of !!#base_schema!!.!!#table!! updated." to !!logfile!!
1461
+ -- !x! endif
1462
+ -- !x! write " !!$last_rowcount!! rows updated."
1463
+ -- !x! endif
1464
+
1465
+
1466
+ -- !x! if(sub_defined(~insertstmt))
1467
+ -- !x! andif(is_true(!!~do_inserts!!))
1468
+ -- !x! write "Adding data to !!#base_schema!!.!!#table!!"
1469
+ -- !x! if(sub_defined(logfile))
1470
+ -- !x! write "" to !!logfile!!
1471
+ -- !x! if(sub_defined(log_sql))
1472
+ -- !x! andif(is_true(!!log_sql!!))
1473
+ -- !x! write "INSERT statement for !!#base_schema!!.!!#table!!:" to !!logfile!!
1474
+ -- !x! write [!!~insertstmt!!;] to !!logfile!!
1475
+ -- !x! endif
1476
+ -- !x! if(sub_defined(log_changes))
1477
+ -- !x! andif(is_true(!!log_changes!!))
1478
+ -- !x! write "New data:" to !!logfile!!
1479
+ -- !x! export ups_newrows append to !!logfile!! as txt
1480
+ -- !x! endif
1481
+ -- !x! write "" to !!logfile!!
1482
+ -- !x! endif
1483
+ !!~insertstmt!!;
1484
+ -- !x! sub !!#inscntvar!! !!$last_rowcount!!
1485
+ -- !x! if(sub_defined(logfile))
1486
+ -- !x! write "!!$last_rowcount!! rows added to !!#base_schema!!.!!#table!!." to !!logfile!!
1487
+ -- !x! endif
1488
+ -- !x! write " !!$last_rowcount!! rows added."
1489
+ -- !x! endif
1490
+
1491
+
1492
+ -- !x! if(is_true(!!#display_final!!))
1493
+ -- !x! prompt message "Table !!#base_schema!!.!!#table!! after updates and inserts." display !!#base_schema!!.!!#table!!
1494
+ -- !x! endif
1495
+
1496
+
1497
+ -- !x! END SCRIPT
1498
+ -- ################### End of UPSERT_ONE ########################
1499
+ -- ################################################################
1500
+
1501
+
1502
+
1503
+
1504
+ -- ################################################################
1505
+ -- Script UPSERT_ALL
1506
+ --
1507
+ -- Updates multiple base tables with new or revised data from
1508
+ -- staging tables, using the UPSERT_ONE script.
1509
+ --
1510
+ -- Input parameters:
1511
+ -- base_schema : The name of the base table schema.
1512
+ -- staging : The name of the staging schema.
1513
+ -- control_table : The name of a table containing at least the
1514
+ -- following four columns:
1515
+ -- table_name : The name of a table
1516
+ -- to be updated.
1517
+ -- exclude_cols : A comma-delimited
1518
+ -- list of single-
1519
+ -- quoted column
1520
+ -- names, as required
1521
+ -- by UPDATE_ANY.
1522
+ -- display_changes : A value of "Yes" or
1523
+ -- "No" indicating
1524
+ -- whether the changes
1525
+ -- for the table should
1526
+ -- be displayed.
1527
+ -- display_final : A value of "Yes" or
1528
+ -- "No" indicating
1529
+ -- whether the final
1530
+ -- state of the table
1531
+ -- should be displayed.
1532
+ -- A table with these columns will be created
1533
+ -- by the script STAGED_TO_LOAD.
1534
+ --
1535
+ -- Global variables:
1536
+ -- logfile : The name of a log file to which update
1537
+ -- messages will be written. Optional.
1538
+ -- log_sql : A boolean variable indicating whether
1539
+ -- the update and insert statements should
1540
+ -- also be written to the logfile.
1541
+ -- upsert_progress_denom : Created or modified.
1542
+ --
1543
+ -- Tables and views used:
1544
+ -- ups_dependencies : temporary table
1545
+ -- ups_ordered_tables : temporary table
1546
+ -- ups_upsert_rows : temporary view
1547
+ --
1548
+ -- Counter variables:
1549
+ -- 221585944 : Progress indicator.
1550
+ --
1551
+ -- ===============================================================
1552
+
1553
+ -- !x! BEGIN SCRIPT UPSERT_ALL with parameters (base_schema, staging, control_table)
1554
+
1555
+
1556
+ -- Validate contents of control table
1557
+ -- !x! execute script validate_control with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!, script=!!$CURRENT_SCRIPT_NAME!!, script_line=!!$SCRIPT_LINE!!)
1558
+
1559
+
1560
+ -- Initialize the status and progress bars if the console is running.
1561
+ -- !x! if(console_on)
1562
+ -- !x! reset counter 221585944
1563
+ -- !x! console status "Merging data"
1564
+ -- !x! console progress 0
1565
+ drop view if exists ups_upsert_rows;
1566
+ create temporary view ups_upsert_rows as
1567
+ select count(*) + 1 as upsert_rows
1568
+ from !!#control_table!!;
1569
+ -- !x! subdata upsert_progress_denom ups_upsert_rows
1570
+ -- !x! endif
1571
+
1572
+
1573
+ -- Get a table of all dependencies for the base schema.
1574
+ drop table if exists ups_dependencies cascade;
1575
+ create temporary table ups_dependencies as
1576
+ select
1577
+ tc.table_name as child,
1578
+ tu.table_name as parent
1579
+ from
1580
+ information_schema.table_constraints as tc
1581
+ inner join information_schema.constraint_table_usage as tu
1582
+ on tu.constraint_name = tc.constraint_name
1583
+ where
1584
+ tc.constraint_type = 'FOREIGN KEY'
1585
+ and tc.table_name <> tu.table_name
1586
+ and tc.table_schema = '!!#base_schema!!';
1587
+
1588
+
1589
+ -- Create a list of tables in the base schema ordered by dependency.
1590
+ drop table if exists ups_ordered_tables cascade;
1591
+ with recursive dep_depth as (
1592
+ select
1593
+ dep.child as first_child,
1594
+ dep.child,
1595
+ dep.parent,
1596
+ 1 as lvl
1597
+ from
1598
+ ups_dependencies as dep
1599
+ union all
1600
+ select
1601
+ dd.first_child,
1602
+ dep.child,
1603
+ dep.parent,
1604
+ dd.lvl + 1 as lvl
1605
+ from
1606
+ dep_depth as dd
1607
+ inner join ups_dependencies as dep on dep.parent = dd.child
1608
+ and dep.child <> dd.parent
1609
+ and not (dep.parent = dd.first_child and dd.lvl > 2)
1610
+ )
1611
+ select
1612
+ table_name,
1613
+ table_order
1614
+ into
1615
+ temporary table ups_ordered_tables
1616
+ from (
1617
+ --All parents
1618
+ select
1619
+ dd.parent as table_name,
1620
+ max(lvl) as table_order
1621
+ from
1622
+ dep_depth as dd
1623
+ group by
1624
+ table_name
1625
+ union
1626
+ --Children that are not parents
1627
+ select
1628
+ dd.child as table_name,
1629
+ max(lvl) + 1 as level
1630
+ from
1631
+ dep_depth as dd
1632
+ left join ups_dependencies as dp on dp.parent = dd.child
1633
+ where
1634
+ dp.parent is null
1635
+ group by
1636
+ dd.child
1637
+ union
1638
+ --Neither parents nor children
1639
+ select distinct
1640
+ t.table_name,
1641
+ 0 as level
1642
+ from
1643
+ information_schema.tables as t
1644
+ left join ups_dependencies as p on t.table_name=p.parent
1645
+ left join ups_dependencies as c on t.table_name=c.child
1646
+ where
1647
+ t.table_schema = '!!#base_schema!!'
1648
+ and t.table_type = 'BASE TABLE'
1649
+ and p.parent is null
1650
+ and c.child is null
1651
+ ) as all_levels;
1652
+
1653
+
1654
+ -- Create a list of the selected tables with ordering information.
1655
+ drop table if exists ups_proctables cascade;
1656
+ select
1657
+ ot.table_order,
1658
+ tl.table_name,
1659
+ tl.exclude_cols,
1660
+ tl.display_changes,
1661
+ tl.display_final,
1662
+ tl.rows_updated,
1663
+ tl.rows_inserted,
1664
+ False::boolean as processed
1665
+ into
1666
+ temporary table ups_proctables
1667
+ from
1668
+ !!#control_table!! as tl
1669
+ inner join ups_ordered_tables as ot on ot.table_name = tl.table_name
1670
+ ;
1671
+
1672
+ -- Create a view returning a single unprocessed table, in order.
1673
+ drop view if exists ups_toprocess cascade;
1674
+ create temporary view ups_toprocess as
1675
+ select
1676
+ table_name, exclude_cols,
1677
+ display_changes, display_final,
1678
+ rows_updated, rows_inserted
1679
+ from ups_proctables
1680
+ where not processed
1681
+ order by table_order
1682
+ limit 1;
1683
+
1684
+ -- Process all tables in order.
1685
+ -- !x! execute script upsert_all_innerloop with (base_schema=!!#base_schema!!, staging=!!#staging!!)
1686
+
1687
+ -- Move the update/insert counts back into the control table.
1688
+ update !!#control_table!! as ct
1689
+ set
1690
+ rows_updated = pt.rows_updated,
1691
+ rows_inserted = pt.rows_inserted
1692
+ from
1693
+ ups_proctables as pt
1694
+ where
1695
+ pt.table_name = ct.table_name;
1696
+
1697
+
1698
+ -- Update the status bar if the console is running.
1699
+ -- !x! if(console_on)
1700
+ -- !x! console status "Data merge complete"
1701
+ -- !x! console progress 0
1702
+ -- !x! endif
1703
+
1704
+
1705
+ -- !x! END SCRIPT
1706
+ -- UPSERT_ALL
1707
+ -- ****************************************************************
1708
+ -- ****************************************************************
1709
+ -- Script UPSERT_ALL_INNERLOOP
1710
+ -- ---------------------------------------------------------------
1711
+
1712
+ -- !x! BEGIN SCRIPT UPSERT_ALL_INNERLOOP with parameters (base_schema, staging)
1713
+
1714
+ -- Update the status bar if the console is running.
1715
+ -- !x! if(console_on)
1716
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
1717
+ -- !x! endif
1718
+
1719
+ -- !x! if(hasrows(ups_toprocess))
1720
+ -- Create variables to store the row counts from updates and inserts.
1721
+ -- (These should be changed to local variables after an outer-scope-referent prefix is implemented.)
1722
+ -- !x! sub ~rows_updated 0
1723
+ -- !x! sub ~rows_inserted 0
1724
+
1725
+ -- !x! select_sub ups_toprocess
1726
+ -- !x! execute script upsert_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, exclude_cols=[!!@exclude_cols!!], display_changes=!!@display_changes!!, display_final=!!@display_final!!, updcntvar=+rows_updated, inscntvar=+rows_inserted)
1727
+
1728
+ update ups_proctables
1729
+ set rows_updated = !!~rows_updated!!,
1730
+ rows_inserted = !!~rows_inserted!!
1731
+ where table_name = '!!@table_name!!';
1732
+
1733
+ update ups_proctables
1734
+ set processed = True
1735
+ where table_name = '!!@table_name!!';
1736
+ -- !x! execute script upsert_all_innerloop with (base_schema=!!#base_schema!!, staging=!!#staging!!)
1737
+ -- !x! endif
1738
+
1739
+ -- !x! END SCRIPT
1740
+ -- ############### End of UPSERT_ALL_INNERLOOP ##################
1741
+ -- ################################################################
1742
+
1743
+
1744
+
1745
+
1746
+ -- ################################################################
1747
+ -- Script QA_ALL
1748
+ --
1749
+ -- Conducts null and foreign key checks on multiple staging tables
1750
+ -- containing new or revised data for staging tables, using the
1751
+ -- NULLQA_ONE, PKQA_ONE, and FKQA_ONE scripts.
1752
+ --
1753
+ -- Input parameters:
1754
+ -- base_schema : The name of the base table schema.
1755
+ -- staging : The name of the staging schema.
1756
+ -- control_table : The name of a table containing at least the
1757
+ -- following three columns:
1758
+ -- table_name : The name of a table
1759
+ -- to be updated.
1760
+ -- null_errors : For a comma-separated
1761
+ -- list of columns that
1762
+ -- are non-nullable in
1763
+ -- the base table but
1764
+ -- null in the staging
1765
+ -- table.
1766
+ -- pk_errors : For a count of the number
1767
+ -- of distinct primary key
1768
+ -- values that are duplicated,
1769
+ -- followed by the total row
1770
+ -- count for the duplicated keys.
1771
+ -- fk_errors : For a comma-separated
1772
+ -- list of foreign-key
1773
+ -- constraint names that
1774
+ -- are not met by the
1775
+ -- staging table.
1776
+ -- A table with these columns will be created
1777
+ -- by the script STAGED_TO_LOAD.
1778
+ --
1779
+ -- Global variables:
1780
+ -- logfile : The name of a log file to which error
1781
+ -- messages will be written. Optional.
1782
+ -- log_sql : A value of 'Yes' or 'No' to indicate whether
1783
+ -- the SQL that is generated for each foreign
1784
+ -- key check is written to the logfile. Optional.
1785
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
1786
+ -- foreign key errors are written to the logfile.
1787
+ -- Optional.
1788
+ --
1789
+ -- Tables and views used:
1790
+ -- ups_proctables : table
1791
+ -- ups_toprocess : temporary view
1792
+ -- ups_upsert_rows : temporary view
1793
+ --
1794
+ -- Counters used:
1795
+ -- 221585944 : Progress bar position
1796
+ --
1797
+ -- Global variables modified:
1798
+ -- upsert_progress_denom : Created or redefined.
1799
+ --
1800
+ -- ===============================================================
1801
+
1802
+ -- !x! BEGIN SCRIPT QA_ALL with parameters (base_schema, staging, control_table)
1803
+
1804
+ -- Get denominator for progress bar if console is on.
1805
+ -- !x! if(console_on)
1806
+ drop view if exists ups_upsert_rows;
1807
+ create temporary view ups_upsert_rows as
1808
+ select count(*) + 1 as upsert_rows
1809
+ from !!#control_table!!;
1810
+ -- !x! subdata upsert_progress_denom ups_upsert_rows
1811
+ -- !x! endif
1812
+
1813
+ -- Initialize the status and progress bars if the console is running.
1814
+ -- !x! begin script update_console_qa with parameters (check_type)
1815
+ -- !x! if(console_on)
1816
+ -- !x! reset counter 221585944
1817
+ -- !x! console status "Performing !!#check_type!! QA checks"
1818
+ -- !x! console progress 0
1819
+ -- !x! endif
1820
+ -- !x! end script
1821
+
1822
+ -- Create a list of the selected tables with a loop control flag.
1823
+ drop table if exists ups_proctables cascade;
1824
+ select
1825
+ tl.table_name,
1826
+ tl.exclude_null_checks,
1827
+ tl.display_changes,
1828
+ False::boolean as processed
1829
+ into
1830
+ temporary table ups_proctables
1831
+ from
1832
+ !!#control_table!! as tl
1833
+ ;
1834
+
1835
+ -- Create a view returning a single unprocessed table, in order.
1836
+ drop view if exists ups_toprocess cascade;
1837
+ create temporary view ups_toprocess as
1838
+ select table_name, exclude_null_checks, display_changes
1839
+ from ups_proctables
1840
+ where not processed
1841
+ limit 1;
1842
+
1843
+ -- Perform null QA checks on all tables.
1844
+ -- !x! execute script update_console_qa with args (check_type=NULL)
1845
+ -- !x! execute script qa_all_nullloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1846
+
1847
+
1848
+ -- Perform primary QA checks on all tables.
1849
+ update ups_proctables set processed = False;
1850
+ -- !x! execute script update_console_qa with args (check_type=primary key)
1851
+ -- !x! execute script qa_all_pkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1852
+
1853
+
1854
+ -- Perform foreign key QA checks on all tables.
1855
+ update ups_proctables set processed = False;
1856
+ -- !x! execute script update_console_qa with args (check_type=foreign key)
1857
+ -- !x! execute script qa_all_fkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1858
+
1859
+ -- Update the status bar if the console is running.
1860
+ -- !x! if(console_on)
1861
+ -- !x! console status "Data QA checks complete"
1862
+ -- !x! console progress 0
1863
+ -- !x! endif
1864
+
1865
+ -- !x! END SCRIPT
1866
+ -- QA_ALL
1867
+ -- ****************************************************************
1868
+ -- ****************************************************************
1869
+ -- Script QA_ALL_NULLLOOP
1870
+ -- ---------------------------------------------------------------
1871
+
1872
+ -- !x! BEGIN SCRIPT QA_ALL_NULLLOOP with parameters (base_schema, staging, control_table)
1873
+
1874
+ -- !x! sub_empty ~ups_null_error_list
1875
+
1876
+ -- Update the status bar if the console is running.
1877
+ -- !x! if(console_on)
1878
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
1879
+ -- !x! endif
1880
+
1881
+ -- !x! if(hasrows(ups_toprocess))
1882
+ -- !x! select_sub ups_toprocess
1883
+ -- !x! if(is_null("!!@exclude_null_checks!!"))
1884
+ -- !x! execute script nullqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, error_list=+ups_null_error_list)
1885
+ -- !x! else
1886
+ -- !x! execute script nullqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, error_list=+ups_null_error_list, exclude_null_checks=[!!@exclude_null_checks!!])
1887
+ -- !x! endif
1888
+ -- !x! if(not is_null("!!~ups_null_error_list!!"))
1889
+ update !!#control_table!!
1890
+ set null_errors = '!!~ups_null_error_list!!'
1891
+ where table_name = '!!@table_name!!';
1892
+ -- !x! endif
1893
+
1894
+ update ups_proctables
1895
+ set processed = True
1896
+ where table_name = '!!@table_name!!';
1897
+ -- !x! execute script qa_all_nullloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1898
+ -- !x! endif
1899
+
1900
+ -- !x! END SCRIPT
1901
+ -- QA_ALL_NULLLOOP
1902
+ -- ****************************************************************
1903
+ -- ****************************************************************
1904
+ -- Script QA_ALL_PKLOOP
1905
+ -- ---------------------------------------------------------------
1906
+
1907
+ -- !x! BEGIN SCRIPT QA_ALL_PKLOOP with parameters (base_schema, staging, control_table)
1908
+
1909
+ -- !x! sub_empty ~ups_pk_error_list
1910
+ -- Update the status bar if the console is running.
1911
+ -- !x! if(console_on)
1912
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
1913
+ -- !x! endif
1914
+
1915
+ -- !x! if(hasrows(ups_toprocess))
1916
+ -- !x! select_sub ups_toprocess
1917
+ -- !x! execute script pkqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, display_errors=!!@display_changes!!, error_list=+ups_pk_error_list)
1918
+ -- !x! if(not is_null("!!~ups_pk_error_list!!"))
1919
+ update !!#control_table!!
1920
+ set pk_errors = '!!~ups_pk_error_list!!'
1921
+ where table_name = '!!@table_name!!';
1922
+ -- !x! endif
1923
+
1924
+ update ups_proctables
1925
+ set processed = True
1926
+ where table_name = '!!@table_name!!';
1927
+
1928
+ -- !x! execute script qa_all_pkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1929
+ -- !x! endif
1930
+
1931
+
1932
+ -- !x! END SCRIPT
1933
+ -- QA_ALL_PKLOOP
1934
+ -- ****************************************************************
1935
+ -- ****************************************************************
1936
+ -- Script QA_ALL_FKLOOP
1937
+ -- ---------------------------------------------------------------
1938
+
1939
+ -- !x! BEGIN SCRIPT QA_ALL_FKLOOP with parameters (base_schema, staging, control_table)
1940
+
1941
+ -- !x! sub_empty ~ups_error_list
1942
+
1943
+ -- Update the status bar if the console is running.
1944
+ -- !x! if(console_on)
1945
+ -- !x! console progress !!$counter_221585944!! / !!upsert_progress_denom!!
1946
+ -- !x! endif
1947
+
1948
+ -- !x! if(hasrows(ups_toprocess))
1949
+ -- !x! select_sub ups_toprocess
1950
+ -- !x! execute script fkqa_one with (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!@table_name!!, display_errors=!!@display_changes!!, error_list=+ups_error_list)
1951
+ -- !x! if(not is_null("!!~ups_error_list!!"))
1952
+ update !!#control_table!!
1953
+ set fk_errors = '!!~ups_error_list!!'
1954
+ where table_name = '!!@table_name!!';
1955
+ -- !x! endif
1956
+
1957
+ update ups_proctables
1958
+ set processed = True
1959
+ where table_name = '!!@table_name!!';
1960
+ -- !x! execute script qa_all_fkloop with (base_schema=!!#base_schema!!, staging=!!#staging!!, control_table=!!#control_table!!)
1961
+ -- !x! endif
1962
+
1963
+ -- !x! END SCRIPT
1964
+ -- ##################### End of QA_ALL ###########################
1965
+ -- #################################################################
1966
+
1967
+
1968
+
1969
+ -- ################################################################
1970
+ -- Script UPDTPK_ONE
1971
+ --
1972
+ -- Updates primary keys in base table, based on new and older
1973
+ -- values of PK columns in a staging table, using UPDATE
1974
+ -- statements. Displays data to be modified to the
1975
+ -- user before any modifications are done. Reports the changes
1976
+ -- made to the console and optionally to a log file.
1977
+ --
1978
+ -- Input parameters:
1979
+ -- base_schema : The name of the base table schema.
1980
+ -- staging : The name of the staging schema.
1981
+ -- table : The table name--same for base and staging.
1982
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
1983
+ -- any errors should be displayed in a GUI.
1984
+ -- display_changes : A value of 'Yes' or 'No' to indicate whether
1985
+ -- or not the changes to be made to the
1986
+ -- base table should be displayed in a GUI.
1987
+ --
1988
+ -- Global variables:
1989
+ -- logfile : The name of a log file to which update
1990
+ -- messages will be written. Optional.
1991
+ -- log_sql : A value of 'Yes' or 'No' indicating whether
1992
+ -- the update and insert statements should
1993
+ -- also be written to the logfile. Optional.
1994
+ -- log_changes : A value of 'Yes' or 'No' indicating whether
1995
+ -- the updated and inserted data should be
1996
+ -- written to the logfile. Optional.
1997
+ --
1998
+ -- Tables and views created or modified:
1999
+ -- ups_pkqa_errors : temporary table
2000
+ -- ups_pkcollinfo : temporary table
2001
+ -- ups_pkupdates : temporary table
2002
+ -- ups_pkupdate_strings : temporary view
2003
+ -- ups_pkupdates : temporary table
2004
+ -- ===============================================================
2005
+
2006
+ -- !x! BEGIN SCRIPT UPDTPK_ONE with parameters (base_schema, staging, table, display_errors, display_changes)
2007
+
2008
+ -- !x! if(console_on)
2009
+ -- !x! console status "Primary key updates"
2010
+ -- !x! endif
2011
+
2012
+
2013
+ -- Validate inputs: base/staging schemas and table
2014
+ -- !x! execute script validate_one with args (base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, script=!!$CURRENT_SCRIPT!!, script_line=!!$SCRIPT_LINE!!)
2015
+
2016
+ -- Write an initial header to the logfile.
2017
+ -- !x! if(sub_defined(logfile))
2018
+ -- !x! write "" to !!logfile!!
2019
+ -- !x! write "==================================================================" to !!logfile!!
2020
+ -- !x! write "!!$current_time!! -- Performing primary key updates on table !!#base_schema!!.!!#table!! from !!#staging!!.!!#table!!" to !!logfile!!
2021
+ -- !x! endif
2022
+
2023
+ -- !x! write "Performing primary key updates on table !!#base_schema!!.!!#table!! from !!#staging!!.!!#table!!"
2024
+
2025
+ -- Create a temp table to store the results of the PK update QA checks
2026
+ drop table if exists ups_pkqa_errors cascade;
2027
+ create temporary table ups_pkqa_errors (
2028
+ error_code varchar(40),
2029
+ error_description varchar(500)
2030
+ );
2031
+
2032
+
2033
+ -- Populate a (temporary) table with the names of the primary key columns of the base table.
2034
+ -- Get the old and new primary key columns from staging table into various formats
2035
+ -- to use later to construct SQL statement to select records in various ways for both updates and QA checks.
2036
+ -- Include column lists, join expression, and where clause
2037
+ drop table if exists ups_pkcol_info cascade;
2038
+ select
2039
+ k.table_schema,
2040
+ k.table_name,
2041
+ k.column_name,
2042
+ cast('b.' || column_name as text) as base_aliased,
2043
+ cast('s.' || column_name as text) as staging_aliased,
2044
+ cast('s.' || column_name || ' as staging_' || column_name as text) as staging_aliased_prefix,
2045
+ cast('b.' || column_name || ' = s.' || column_name as text) as join_expr,
2046
+ cast('new_' || column_name as text) as newpk_col,
2047
+ cast('s.new_' || column_name as text) as newpk_col_aliased,
2048
+ cast('new_' || column_name || ' is null' as text) as newpk_col_empty,
2049
+ cast('new_' || column_name || ' is not null' as text) as newpk_col_not_empty,
2050
+ cast(column_name || ' = s.new_' || column_name as text) as assmt_expr,
2051
+ cast('b.' || column_name || ' = s.new_' || column_name as text) as join_expr_oldnew,
2052
+ cast('s.new_' || column_name || ' = b.new_' || column_name as text) as join_expr_new,
2053
+ k.ordinal_position
2054
+ into temporary table ups_pkcol_info
2055
+ from information_schema.table_constraints as tc
2056
+ inner join information_schema.key_column_usage as k
2057
+ on tc.constraint_type = 'PRIMARY KEY'
2058
+ and tc.constraint_name = k.constraint_name
2059
+ and tc.constraint_catalog = k.constraint_catalog
2060
+ and tc.constraint_schema = k.constraint_schema
2061
+ and tc.table_schema = k.table_schema
2062
+ and tc.table_name = k.table_name
2063
+ and tc.constraint_name = k.constraint_name
2064
+ where
2065
+ k.table_name = '!!#table!!'
2066
+ and k.table_schema = '!!#base_schema!!'
2067
+ ;
2068
+
2069
+
2070
+ -- Run QA checks
2071
+ -- !x! execute script UPDTPKQA_ONE with arguments(base_schema=!!#base_schema!!, staging=!!#staging!!, table=!!#table!!, pkinfo_table=ups_pkcol_info, qaerror_table=ups_pkqa_errors, display_errors=!!#display_errors!!)
2072
+
2073
+
2074
+ -- Run the PK update ONLY if QA check script returned no errors
2075
+ -- !x! if(not hasrows(ups_pkqa_errors))
2076
+ -- !x! rm_sub ~updatestmt
2077
+
2078
+ -- !x! sub ~do_updates Yes
2079
+
2080
+ -- !x! if(sub_defined(logfile))
2081
+ -- !x! write "" to !!logfile!!
2082
+ -- !x! write "==================================================================" to !!logfile!!
2083
+ -- !x! write "!!$current_time!! -- Performing primary key update on table !!#base_schema!!.!!#table!!" to !!logfile!!
2084
+ -- !x! endif
2085
+
2086
+ -- !x! if(console_on)
2087
+ -- !x! console status "Performing PK updates"
2088
+ -- !x! console progress 0
2089
+ -- !x! endif
2090
+
2091
+ -- !x! write "Performing primary key update on table !!#base_schema!!.!!#table!!"
2092
+
2093
+ -- Create strings necessary to construct SQL to perform the updates
2094
+ drop view if exists ups_pkupdate_strings cascade;
2095
+ create temporary view ups_pkupdate_strings as
2096
+ select
2097
+ string_agg(base_aliased, ', ' order by ordinal_position) as oldpk_cols,
2098
+ string_agg(newpk_col, ', ' order by ordinal_position) as newpk_cols,
2099
+ string_agg(join_expr, ' and ' order by ordinal_position) as joinexpr,
2100
+ string_agg(newpk_col_not_empty, ' and ' order by ordinal_position) as all_newpk_col_not_empty,
2101
+ string_agg(assmt_expr, ', ' order by ordinal_position) as assmt_expr
2102
+ from ups_pkcol_info
2103
+ group by table_schema, table_name
2104
+ ;
2105
+ -- !x! select_sub ups_pkupdate_strings
2106
+
2107
+ -- Create a FROM clause for an inner join between base and staging
2108
+ -- tables on the primary key column(s).
2109
+ -- !x! sub ~fromclause FROM !!#base_schema!!.!!#table!! as b INNER JOIN !!#staging!!.!!#table!! as s ON !!@joinexpr!!
2110
+
2111
+ -- Create a WHERE clause for the rows to include in the selection (only those having new PK columns populated in the staging table)
2112
+ -- !x! sub ~whereclause WHERE !!@all_newpk_col_not_empty!!
2113
+
2114
+ -- Select all matches for PK update into temp table
2115
+ drop table if exists ups_pkupdates cascade;
2116
+ select
2117
+ !!@oldpk_cols!!,
2118
+ !!@newpk_cols!!
2119
+ into temporary table ups_pkupdates
2120
+ !!~fromclause!!
2121
+ !!~whereclause!!
2122
+ ;
2123
+
2124
+ -- Prompt user to examine matching data and commit, don't commit, or quit.
2125
+ -- !x! if(hasrows(ups_pkupdates))
2126
+ -- !x! if(is_true(!!#display_changes!!))
2127
+ -- !x! prompt ask "Do you want to make these changes to primary key values for table !!#table!!?" sub ~do_updates display ups_pkupdates
2128
+ -- !x! endif
2129
+ -- !x! if(is_true(!!~do_updates!!))
2130
+
2131
+ -- Create an UPDATE statement to update PK columns of the base table with
2132
+ -- "new" PK columns from the staging table. No semicolon terminating generated SQL.
2133
+ -- !x! sub ~updatestmt UPDATE !!#base_schema!!.!!#table!! as b SET !!@assmt_expr!! FROM !!#staging!!.!!#table!! as s WHERE !!@joinexpr!! and !!@all_newpk_col_not_empty!!
2134
+
2135
+ -- !x! write "Updating !!#base_schema!!.!!#table!!"
2136
+ -- !x! if(sub_defined(logfile))
2137
+ -- !x! write "" to !!logfile!!
2138
+ -- !x! if(sub_defined(log_sql))
2139
+ -- !x! andif(is_true(!!log_sql!!))
2140
+ -- !x! write "UPDATE statement for !!#base_schema!!.!!#table!!:" to !!logfile!!
2141
+ -- !x! write [!!~updatestmt!!;] to !!logfile!!
2142
+ -- !x! endif
2143
+ -- !x! if(sub_defined(log_changes))
2144
+ -- !x! andif(is_true(!!log_changes!!))
2145
+ -- !x! write "Updates:" to !!logfile!!
2146
+ -- !x! export ups_pkupdates append to !!logfile!! as txt
2147
+ -- !x! endif
2148
+ -- !x! write "" to !!logfile!!
2149
+ -- !x! endif
2150
+ !!~updatestmt!!;
2151
+ -- !x! if(sub_defined(logfile))
2152
+ -- !x! write "!!$last_rowcount!! rows of !!#base_schema!!.!!#table!! updated." to !!logfile!!
2153
+ -- !x! endif
2154
+ -- !x! write " !!$last_rowcount!! rows updated."
2155
+ -- !x! endif
2156
+ -- !x! else
2157
+ --!x! write "No primary key updates specified for existing records in !!#base_schema!!.!!#table!!"
2158
+ -- !x! endif
2159
+ -- !x! endif
2160
+
2161
+
2162
+ -- !x! END SCRIPT
2163
+ -- ################### End of UPDTPK_ONE ########################
2164
+ -- ################################################################
2165
+
2166
+
2167
+ -- ################################################################
2168
+ -- Script UPDTPKQA_ONE
2169
+ --
2170
+ -- Performs QA checks on requested primary key updates to a table,
2171
+ -- based on old and new values of the table's primary key columns
2172
+ -- in a staging table.
2173
+ --
2174
+ -- Input parameters:
2175
+ -- base_schema : The name of the base table schema.
2176
+ -- staging : The name of the staging schema.
2177
+ -- table : The table name--same for base and staging.
2178
+ -- pkinfo_table : The name of a temporary table to be passed by
2179
+ -- the caller that contains information about the table PK,
2180
+ -- including strings to be used in constructing
2181
+ -- SQL for checks
2182
+ -- qaerror_table : The name of a temporary table to
2183
+ -- store any errors found by QA checks.
2184
+ -- display_errors : A value of 'Yes' or 'No' to indicate whether
2185
+ -- any errors should be displayed in a GUI.
2186
+ -- Output parameters:
2187
+ -- error_list : The name of the variable to receive FILL IN.
2188
+ --
2189
+ -- Global variables:
2190
+ -- logfile : The name of a log file to which update
2191
+ -- messages will be written. Optional.
2192
+ -- log_sql : A value of 'Yes' or 'No' indicating whether
2193
+ -- the update and insert statements should
2194
+ -- also be written to the logfile. Optional.
2195
+ -- Currently only writes SQL for foreign key checks
2196
+ -- (final check) to log.
2197
+ -- log_errors : A value of 'Yes' or 'No' to indicate whether
2198
+ -- errors are written to the logfile. Optional.
2199
+ --
2200
+ -- Tables and views created or modified:
2201
+ -- ups_missing_pk_cols : temporary table
2202
+ -- ups_pkqa_str_lib : tempoarary table
2203
+ -- ups_any_pk_cols : temporary table
2204
+ -- ups_empty_pk_cols : temporary table
2205
+ -- ups_empty_pk_cols_rwcnt : temporary view
2206
+ -- ups_old_pks_wc : temporary table
2207
+ -- ups_invalid_old_pks : temporary table
2208
+ -- ups_invld_pk_rwcnt : temporary view
2209
+ -- ups_existing_new_pks : temporary table
2210
+ -- ups_exst_nwpk_rwcnt : temporary view
2211
+ -- ups_pk_mapping_conflict : temporary table
2212
+ -- ups_map_conf_rwcnt : temporary view
2213
+ -- ups_pk_duplicate_keys : temporary table
2214
+ -- ups_dup_key_rwcnt : temporary view
2215
+ -- ups_fkcol_refs : temporary table
2216
+ -- ups_pkcol_deps : temporary table
2217
+ -- ups_pkfk_ctrl : temporary table
2218
+ -- ===============================================================
2219
+
2220
+ -- !x! BEGIN SCRIPT UPDTPKQA_ONE with parameters (base_schema, staging, table, pkinfo_table, qaerror_table, display_errors)
2221
+
2222
+ -- Write an initial header to the logfile.
2223
+ -- !x! if(sub_defined(logfile))
2224
+ -- !x! write "" to !!logfile!!
2225
+ -- !x! write "==================================================================" to !!logfile!!
2226
+ -- !x! write "!!$current_time!! -- QA checks for primary key updates on table !!#table!!" to !!logfile!!
2227
+ -- !x! endif
2228
+
2229
+ -- !x! write "Conducting QA checks on table !!#staging!!.!!#table!! for primary key updates to table !!#base_schema!!.!!#table!!"
2230
+
2231
+ -- Initialize the status and progress bars if the console is running.
2232
+ -- !x! if(console_on)
2233
+ -- !x! console status "QA checks for PK updates on !!#base_schema!!.!!#table!!"
2234
+ -- !x! endif
2235
+
2236
+
2237
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2238
+ -- Check 1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2239
+ -- No primary key constraint on base table
2240
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2241
+ -- !x! if(not hasrows(!!#pkinfo_table!!))
2242
+
2243
+ -- !x! sub ~error_description No primary key constraint on base table !!#base_schema!!.!!#table!!
2244
+ -- !x! write " !!~error_description!!"
2245
+ -- !x! if(sub_defined(logfile))
2246
+ -- !x! write "" to !!logfile!!
2247
+ -- !x! write "!!~error_description!!" to !!logfile!!
2248
+ -- !x! endif
2249
+ insert into !!#qaerror_table!! (error_code, error_description)
2250
+ values ('No PK on base table', '!!~error_description!!')
2251
+ ;
2252
+
2253
+ -- No other QA checks are conducted if this check fails:
2254
+ -- Remaining QA checks are conducted ONLY if base table has PK
2255
+ -- !x! else
2256
+
2257
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2258
+ -- Check 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2259
+ -- A "new" PK column exists in staging table for every PK column of base table
2260
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2261
+
2262
+ -- Find any MISSING PK columns in staging table
2263
+ drop table if exists ups_missing_pk_cols cascade;
2264
+ select
2265
+ string_agg(newpk_col, ', ' order by ordinal_position) as missing_newpk_cols
2266
+ into temporary table ups_missing_pk_cols
2267
+ from
2268
+ --Base table PK columns, with expected name in staging table ("new_" prepended to column name)
2269
+ !!#pkinfo_table!! as pk
2270
+ --Staging table columns
2271
+ left join
2272
+ (
2273
+ select table_name, column_name
2274
+ from information_schema.columns
2275
+ where
2276
+ table_schema = '!!#staging!!'
2277
+ ) as stag on pk.table_name=stag.table_name and pk.newpk_col=stag.column_name
2278
+ where
2279
+ stag.column_name is null
2280
+ having count(*)>0
2281
+ ;
2282
+
2283
+ -- !x! if(hasrows(ups_missing_pk_cols))
2284
+
2285
+ -- !x! subdata ~error_info ups_missing_pk_cols
2286
+
2287
+ -- !x! sub ~error_description New primary key column(s) missing from staging table: !!~error_info!!
2288
+
2289
+ -- !x! write " !!~error_description!!"
2290
+ -- !x! if(sub_defined(logfile))
2291
+ -- !x! write "" to !!logfile!!
2292
+ -- !x! write "!!~error_description!!" to !!logfile!!
2293
+ -- !x! endif
2294
+ insert into !!#qaerror_table!! (error_code, error_description)
2295
+ values ('Missing new PK column(s)', '!!~error_description!!')
2296
+ ;
2297
+
2298
+ -- No other QA checks are conducted if this check fails:
2299
+ -- Remaining QA checks are all conducted ONLY if all expected "new PK" columns exist in staging table
2300
+ -- !x! else
2301
+
2302
+ -- Library of aggregated strings used to construct SQL for the remaining checks
2303
+
2304
+ -- Just base table
2305
+ -- !x! sub ~base_table !!#base_schema!!.!!#table!!
2306
+
2307
+ -- Just staging table
2308
+ -- !x! sub ~staging_table !!#staging!!.!!#table!!
2309
+
2310
+ drop table if exists ups_pkqa_str_lib;
2311
+ select
2312
+ string_agg(column_name, ', ' order by ordinal_position) as old_pkcol,
2313
+ string_agg(staging_aliased, ', ' order by ordinal_position) as old_pkcol_aliased,
2314
+ string_agg(staging_aliased_prefix, ', ' order by ordinal_position) as old_pkcol_aliased_prefix,
2315
+ string_agg(newpk_col, ', ' order by ordinal_position) as new_pkcol,
2316
+ string_agg(newpk_col_aliased, ', ' order by ordinal_position) as new_pkcol_aliased,
2317
+ string_agg(join_expr, ' and ' order by ordinal_position) as joincond_origorig,
2318
+ string_agg(join_expr_oldnew, ' and ' order by ordinal_position) as joincond_oldnew,
2319
+ string_agg(join_expr_new, ' and ' order by ordinal_position) as joincond_newnew,
2320
+ string_agg(newpk_col_not_empty, ' or ' order by ordinal_position) as any_newpk_col_not_empty,
2321
+ string_agg(newpk_col_not_empty, ' and ' order by ordinal_position) as all_newpk_col_not_empty,
2322
+ string_agg(newpk_col_empty, ' or ' order by ordinal_position) as any_newpk_col_empty,
2323
+ string_agg(newpk_col_empty, ' and ' order by ordinal_position) as all_newpk_col_empty
2324
+ into temporary table ups_pkqa_str_lib
2325
+ from !!#pkinfo_table!!
2326
+ ;
2327
+ -- !x! select_sub ups_pkqa_str_lib
2328
+
2329
+
2330
+
2331
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2332
+ -- Check 3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2333
+ -- There are any rows with PK updates specified.
2334
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2335
+
2336
+ -- Find any populated new PK columns in staging table
2337
+ drop table if exists ups_any_pk_cols cascade;
2338
+ select * into temporary table ups_any_pk_cols
2339
+ from !!~staging_table!!
2340
+ where !!@any_newpk_col_not_empty!!
2341
+ ;
2342
+ -- !x! if(not hasrows(ups_any_pk_cols))
2343
+ -- !x! sub ~error_description No primary key updates specified in !!#staging!!.!!#table!!
2344
+ -- !x! write " !!~error_description!!"
2345
+ -- !x! if(sub_defined(logfile))
2346
+ -- !x! write "" to !!logfile!!
2347
+ -- !x! write "!!~error_description!!" to !!logfile!!
2348
+ -- !x! endif
2349
+ insert into !!#qaerror_table!! (error_code, error_description)
2350
+ values ('No PK updates specified in staging table', '!!~error_description!!')
2351
+ ;
2352
+ -- No other QA checks are conducted if this check fails
2353
+ -- !x! else
2354
+
2355
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2356
+ -- Check 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2357
+ -- Where any "new" PK column is populated in the staging table, they are all populated.
2358
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2359
+
2360
+ -- Construct SQL statement looking for any NULLs in "new" PK columns in rows where any PK columns are populated
2361
+ -- Find any EMPTY PK columns in staging table
2362
+ drop table if exists ups_empty_pk_cols cascade;
2363
+ select
2364
+ !!@old_pkcol!!,
2365
+ !!@new_pkcol!!
2366
+ into temporary table ups_empty_pk_cols
2367
+ from
2368
+ !!~staging_table!!
2369
+ where
2370
+ not (!!@all_newpk_col_empty!!)
2371
+ and (!!@any_newpk_col_empty!!)
2372
+ ;
2373
+
2374
+ -- !x! if(hasrows(ups_empty_pk_cols))
2375
+ drop view if exists ups_empty_pk_cols_rwcnt cascade;
2376
+ create temporary view ups_empty_pk_cols_rwcnt as
2377
+ select count(*) as rwcnt
2378
+ from ups_empty_pk_cols
2379
+ ;
2380
+ -- !x! subdata ~rowcount ups_empty_pk_cols_rwcnt
2381
+ -- !x! sub ~error_description Missing values in new PK columns in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2382
+ -- !x! write " !!~error_description!!"
2383
+ insert into !!#qaerror_table!! (error_code, error_description)
2384
+ values ('Incomplete mapping', '!!~error_description!!')
2385
+ ;
2386
+ -- !x! if(sub_defined(logfile))
2387
+ -- !x! write "" to !!logfile!!
2388
+ -- !x! write "!!~error_description!!" to !!logfile!!
2389
+ -- !x! if(sub_defined(log_errors))
2390
+ -- !x! andif(is_true(!!log_errors!!))
2391
+ -- !x! export ups_empty_pk_cols append to !!logfile!! as txt
2392
+ -- !x! endif
2393
+ -- !x! endif
2394
+ -- !x! if(is_true(!!#display_errors!!))
2395
+ -- !x! prompt message "Missing values in new PK columns in !!#staging!!.!!#table!!" display ups_empty_pk_cols
2396
+ -- !x! endif
2397
+ -- !x! endif
2398
+
2399
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2400
+ -- Check 5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2401
+ -- Where any "new" PK column is populated in the staging table, the value of the original PK for that row is valid
2402
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2403
+
2404
+ -- New PK col in staging table are not empty
2405
+ drop table if exists ups_old_pks_wc cascade;
2406
+ select base_aliased
2407
+ into temporary table ups_old_pks_wc
2408
+ from !!#pkinfo_table!!
2409
+ order by ordinal_position
2410
+ limit 1;
2411
+ -- !x! subdata ~old_pk_firstcol ups_old_pks_wc
2412
+
2413
+ drop table if exists ups_invalid_old_pks cascade;
2414
+ select
2415
+ !!@old_pkcol_aliased!!,
2416
+ !!@new_pkcol!!
2417
+ into temporary table ups_invalid_old_pks
2418
+ from !!~staging_table!! as s
2419
+ left join !!~base_table!! as b on !!@joincond_origorig!!
2420
+ where !!@all_newpk_col_not_empty!! and !!~old_pk_firstcol!! is null
2421
+ ;
2422
+
2423
+ -- !x! if(hasrows(ups_invalid_old_pks))
2424
+ drop view if exists ups_invld_pk_rwcnt cascade;
2425
+ create temporary view ups_invld_pk_rwcnt as
2426
+ select count(*) as rwcnt
2427
+ from ups_invalid_old_pks
2428
+ ;
2429
+ -- !x! subdata ~rowcount ups_invld_pk_rwcnt
2430
+ -- !x! sub ~error_description Invalid original PK in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2431
+ -- !x! write " !!~error_description!!"
2432
+ insert into !!#qaerror_table!! (error_code, error_description)
2433
+ values ('Invalid old PK value', '!!~error_description!!')
2434
+ ;
2435
+ -- !x! if(sub_defined(logfile))
2436
+ -- !x! write "" to !!logfile!!
2437
+ -- !x! write "!!~error_description!!" to !!logfile!!
2438
+ -- !x! if(sub_defined(log_errors))
2439
+ -- !x! andif(is_true(!!log_errors!!))
2440
+ -- !x! export ups_invalid_old_pks append to !!logfile!! as txt
2441
+ -- !x! endif
2442
+ -- !x! endif
2443
+ -- !x! if(is_true(!!#display_errors!!))
2444
+ -- !x! prompt message "Invalid original PK in !!#staging!!.!!#table!!" display ups_invalid_old_pks
2445
+ -- !x! endif
2446
+ -- !x! endif
2447
+
2448
+
2449
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2450
+ -- Check 6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2451
+ -- None of the "new" PK values already exist in the base table
2452
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2453
+ drop table if exists ups_existing_new_pks cascade;
2454
+ select
2455
+ !!@old_pkcol_aliased_prefix!!,
2456
+ !!@new_pkcol!!,
2457
+ b.*
2458
+ into temporary table ups_existing_new_pks
2459
+ from !!~staging_table!! as s
2460
+ inner join !!~base_table!! as b on !!@joincond_oldnew!!
2461
+ ;
2462
+
2463
+ -- !x! if(hasrows(ups_existing_new_pks))
2464
+ drop view if exists ups_exst_nwpk_rwcnt cascade;
2465
+ create temporary view ups_exst_nwpk_rwcnt as
2466
+ select count(*) as rwcnt
2467
+ from ups_existing_new_pks
2468
+ ;
2469
+ -- !x! subdata ~rowcount ups_exst_nwpk_rwcnt
2470
+ -- !x! sub ~error_description New PK already exists in !!#base_schema!!.!!#table!!: !!~rowcount!! row(s)
2471
+ -- !x! write " !!~error_description!!"
2472
+ insert into !!#qaerror_table!! (error_code, error_description)
2473
+ values ('Existing new PK value', '!!~error_description!!')
2474
+ ;
2475
+ -- !x! if(sub_defined(logfile))
2476
+ -- !x! write "" to !!logfile!!
2477
+ -- !x! write "!!~error_description!!" to !!logfile!!
2478
+ -- !x! if(sub_defined(log_errors))
2479
+ -- !x! andif(is_true(!!log_errors!!))
2480
+ -- !x! export ups_existing_new_pks append to !!logfile!! as txt
2481
+ -- !x! endif
2482
+ -- !x! endif
2483
+ -- !x! if(is_true(!!#display_errors!!))
2484
+ -- !x! prompt message "New PK already exists in !!#base_schema!!.!!#table!!" display ups_existing_new_pks
2485
+ -- !x! endif
2486
+ -- !x! endif
2487
+
2488
+
2489
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2490
+ -- Check 7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2491
+ -- No two (or more) original PK values map to same new PK value
2492
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2493
+ drop table if exists ups_pk_mapping_conflict cascade;
2494
+ select
2495
+ !!@old_pkcol_aliased!!,
2496
+ !!@new_pkcol_aliased!!
2497
+ into temporary table ups_pk_mapping_conflict
2498
+ from !!~staging_table!! as s
2499
+ inner join
2500
+ (
2501
+ select
2502
+ !!@new_pkcol!!
2503
+ from
2504
+ (select distinct !!@old_pkcol!!, !!@new_pkcol!! from !!~staging_table!! where !!@all_newpk_col_not_empty!!) as a
2505
+ group by
2506
+ !!@new_pkcol!!
2507
+ having count(*) >1
2508
+ ) as b on !!@joincond_newnew!!
2509
+ ;
2510
+
2511
+ -- !x! if(hasrows(ups_pk_mapping_conflict))
2512
+ drop view if exists ups_map_conf_rwcnt cascade;
2513
+ create temporary view ups_map_conf_rwcnt as
2514
+ select count(*) as rwcnt
2515
+ from ups_pk_mapping_conflict
2516
+ ;
2517
+ -- !x! subdata ~rowcount ups_map_conf_rwcnt
2518
+ -- !x! sub ~error_description Multiple original PKs mapped to same new PK in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2519
+ -- !x! write " !!~error_description!!"
2520
+ insert into !!#qaerror_table!! (error_code, error_description)
2521
+ values ('Mapping conflict', '!!~error_description!!')
2522
+ ;
2523
+ -- !x! if(sub_defined(logfile))
2524
+ -- !x! write "" to !!logfile!!
2525
+ -- !x! write "!!~error_description!!" to !!logfile!!
2526
+ -- !x! if(sub_defined(log_errors))
2527
+ -- !x! andif(is_true(!!log_errors!!))
2528
+ -- !x! export ups_pk_mapping_conflict append to !!logfile!! as txt
2529
+ -- !x! endif
2530
+ -- !x! endif
2531
+ -- !x! if(is_true(!!#display_errors!!))
2532
+ -- !x! prompt message "Multiple original PKs mapped to same new PK in !!#staging!!.!!#table!!" display ups_pk_mapping_conflict
2533
+ -- !x! endif
2534
+ -- !x! endif
2535
+
2536
+
2537
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2538
+ -- Check 8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2539
+ -- No single original PK value maps to multiple new PK values
2540
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2541
+
2542
+ drop table if exists ups_pk_duplicate_keys cascade;
2543
+ select
2544
+ !!@old_pkcol_aliased!!,
2545
+ !!@new_pkcol_aliased!!
2546
+ into temporary table ups_pk_duplicate_keys
2547
+ from !!~staging_table!! as s
2548
+ inner join
2549
+ (
2550
+ select
2551
+ !!@old_pkcol!!
2552
+ from
2553
+ (select distinct !!@old_pkcol!!, !!@new_pkcol!! from !!~staging_table!! where !!@all_newpk_col_not_empty!!) as a
2554
+ group by
2555
+ !!@old_pkcol!!
2556
+ having count(*)>1
2557
+ ) as b on !!@joincond_origorig!!
2558
+ ;
2559
+
2560
+ -- !x! if(hasrows(ups_pk_duplicate_keys))
2561
+ drop view if exists ups_dup_key_rwcnt cascade;
2562
+ create temporary view ups_dup_key_rwcnt as
2563
+ select count(*) as rwcnt
2564
+ from ups_pk_duplicate_keys
2565
+ ;
2566
+ -- !x! subdata ~rowcount ups_dup_key_rwcnt
2567
+ -- !x! sub ~error_description Original PK mapped to multiple new PKs in !!#staging!!.!!#table!!: !!~rowcount!! row(s)
2568
+ -- !x! write " !!~error_description!!"
2569
+ insert into !!#qaerror_table!! (error_code, error_description)
2570
+ values ('Duplicate keys', '!!~error_description!!')
2571
+ ;
2572
+ -- !x! if(sub_defined(logfile))
2573
+ -- !x! write "" to !!logfile!!
2574
+ -- !x! write "!!~error_description!!" to !!logfile!!
2575
+ -- !x! if(sub_defined(log_errors))
2576
+ -- !x! andif(is_true(!!log_errors!!))
2577
+ -- !x! export ups_pk_duplicate_keys append to !!logfile!! as txt
2578
+ -- !x! endif
2579
+ -- !x! endif
2580
+ -- !x! if(is_true(!!#display_errors!!))
2581
+ -- !x! prompt message "Original PK mapped to multiple new PKs in !!#staging!!.!!#table!!" display ups_pk_duplicate_keys
2582
+ -- !x! endif
2583
+ -- !x! endif
2584
+
2585
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2586
+ -- Check 9 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2587
+ -- If any of the PK columns reference a parent table, all the "new" values of that column are valid
2588
+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2589
+
2590
+ -- Get ALL column references for the base table
2591
+ drop table if exists ups_fkcol_refs cascade;
2592
+ select
2593
+ fk_constraint,
2594
+ staging_schema,
2595
+ fkinf.table_schema,
2596
+ fkinf.table_name,
2597
+ att1.attname as column_name,
2598
+ fkinf.column_id,
2599
+ ns2.nspname as parent_schema,
2600
+ cls2.relname as parent_table,
2601
+ att2.attname as parent_column,
2602
+ fkinf.parent_column_id
2603
+ into temporary table ups_fkcol_refs
2604
+ from
2605
+ (
2606
+ select
2607
+ cons.conname as fk_constraint,
2608
+ '!!#staging!!' as staging_schema,
2609
+ ns1.nspname as table_schema,
2610
+ cls1.relname as table_name,
2611
+ cons.conrelid as table_id,
2612
+ unnest(cons.conkey) as column_id,
2613
+ cons.confrelid as parent_table_id,
2614
+ unnest(cons.confkey) as parent_column_id
2615
+ from
2616
+ pg_constraint as cons
2617
+ inner join pg_class as cls1 on cls1.oid=cons.conrelid
2618
+ inner join pg_namespace ns1 on ns1.oid = cls1.relnamespace
2619
+
2620
+ where
2621
+ cons.contype='f'
2622
+ and ns1.nspname = '!!#base_schema!!'
2623
+ and cls1.relname = '!!#table!!'
2624
+ ) as fkinf
2625
+ inner join pg_class as cls2 on cls2.oid=fkinf.parent_table_id
2626
+ inner join pg_namespace ns2 on ns2.oid = cls2.relnamespace
2627
+ inner join pg_attribute att1 on att1.attrelid = fkinf.table_id and att1.attnum = fkinf.column_id
2628
+ inner join pg_attribute att2 on att2.attrelid = fkinf.parent_table_id and att2.attnum = fkinf.parent_column_id
2629
+ ;
2630
+
2631
+ -- Narrow the list down to ONLY dependencies that affect PK columns
2632
+ -- Include not just the PK columns themselves, but ALL columns included in FKs
2633
+ -- that include ANY PK columns (probably rare/unlikely that a non-PK column would be
2634
+ -- part of the same foreign key as a PK column, but this ensures that ALL columns of the FK
2635
+ -- are included, whether or not the column is part of the PK)
2636
+ drop table if exists ups_pkcol_deps cascade;
2637
+ select
2638
+ refs.*
2639
+ into temporary table ups_pkcol_deps
2640
+ from ups_fkcol_refs as refs
2641
+ inner join
2642
+ --Distinct list of FK constraints on the table that include ANY PK columns
2643
+ (
2644
+ select distinct
2645
+ fk_constraint, r.table_schema, r.table_name
2646
+ from
2647
+ ups_fkcol_refs as r
2648
+ inner join ups_pkcol_info as p on r.table_schema=p.table_schema and r.table_name=p.table_name and r.column_name=p.column_name
2649
+ ) as const on refs.fk_constraint=const.fk_constraint and refs.table_schema=const.table_schema and refs.table_name=const.table_name
2650
+ ;
2651
+
2652
+ -- Create a control table for looping to check each fk
2653
+ -- Include (for later use) some of the constructed strings that apply to the entire PK (not
2654
+ -- just the FK being checked)
2655
+ drop table if exists ups_pkfk_ctrl cascade;
2656
+ select
2657
+ fk_constraint,
2658
+ staging_schema, table_schema, table_name, parent_schema, parent_table,
2659
+ min(parent_column) as any_referenced_column,
2660
+ '!!@old_pkcol_aliased!!' as old_pkcol_aliased,
2661
+ '!!@new_pkcol!!' as new_pkcol,
2662
+ '!!@all_newpk_col_not_empty!!' as all_newpk_col_not_empty,
2663
+ False::boolean as processed
2664
+ into temporary table ups_pkfk_ctrl
2665
+ from ups_pkcol_deps
2666
+ group by
2667
+ fk_constraint,
2668
+ staging_schema, table_schema, table_name, parent_schema, parent_table
2669
+ ;
2670
+
2671
+ -- Create a view to select one constraint to process.
2672
+ drop view if exists ups_next_fk cascade;
2673
+ create temporary view ups_next_fk as
2674
+ select *
2675
+ from ups_pkfk_ctrl
2676
+ where not processed
2677
+ limit 1
2678
+ ;
2679
+
2680
+ --Process all constraints: check every foreign key
2681
+ --!x! execute script updtpkqa_one_innerloop with (qaerror_table=!!#qaerror_table!!, display_errors=!!#display_errors!!)
2682
+ -- !x! endif
2683
+ -- !x! endif
2684
+ -- !x! endif
2685
+ -- !x! END SCRIPT
2686
+ -- ##################### UPDTPKQA_ONE ###########################
2687
+ -- ################################################################
2688
+ -- Script UPDTPKQA_ONE_INNERLOOP
2689
+ -- ----------------------------------------------------------------
2690
+ -- !x! BEGIN SCRIPT UPDTPKQA_ONE_INNERLOOP with parameters(qaerror_table, display_errors)
2691
+ -- !x! if(hasrows(ups_next_fk))
2692
+
2693
+ -- !x! select_sub ups_next_fk
2694
+
2695
+ -- Compile FK info for the selected constraint
2696
+ drop table if exists ups_sel_fk_cols cascade;
2697
+ select
2698
+ staging_schema, fk_constraint, table_schema, table_name, parent_schema,
2699
+ parent_table,
2700
+ string_agg(parent_column, ', ' order by column_id) as referenced_cols,
2701
+ string_agg('s.new_' || column_name || '=' || 'b.' || parent_column, ' and ' order by column_id) as join_condition
2702
+ into temporary table ups_sel_fk_cols
2703
+ from ups_pkcol_deps
2704
+ where fk_constraint='!!@fk_constraint!!'
2705
+ group by
2706
+ staging_schema, fk_constraint, table_schema, table_name, parent_schema,
2707
+ parent_table
2708
+ ;
2709
+ -- !x! select_sub ups_sel_fk_cols
2710
+
2711
+ -- Construct SQL to check the selected FK
2712
+ -- !x! sub ~select_stmt select !!@old_pkcol_aliased!!, !!@new_pkcol!! into temporary table ups_pk_fk_check from !!@staging_schema!!.!!@table_name!! as s
2713
+ -- !x! sub ~join_stmt left join !!@parent_schema!!.!!@parent_table!! as b on !!@join_condition!!
2714
+ -- !x! sub ~where_clause where !!@all_newpk_col_not_empty!! and b.!!@any_referenced_column!! is null
2715
+
2716
+ -- !x! sub ~fk_check drop table if exists ups_pk_fk_check cascade;
2717
+ -- !x! sub_append ~fk_check !!~select_stmt!!
2718
+ -- !x! sub_append ~fk_check !!~join_stmt!!
2719
+ -- !x! sub_append ~fk_check !!~where_clause!!
2720
+
2721
+ -- Write the SQL to the log file if requested.
2722
+ -- !x! if(sub_defined(logfile))
2723
+ -- !x! andif(sub_defined(log_sql))
2724
+ -- !x! andif(is_true(!!log_sql!!))
2725
+ -- !x! write "" to !!logfile!!
2726
+ -- !x! write "SQL for checking foreign key !!@fk_constraint!! for PK update to !!@table_schema!!.!!@table_name!!:" to !!logfile!!
2727
+ -- !x! write [!!~fk_check!!;] to !!logfile!!
2728
+ -- !x! endif
2729
+
2730
+ -- Run the check
2731
+ !!~fk_check!!;
2732
+
2733
+
2734
+ -- !x! if(hasrows(ups_pk_fk_check))
2735
+
2736
+ drop view if exists ups_pk_fk_check_rwcnt cascade;
2737
+ create temporary view ups_pk_fk_check_rwcnt as
2738
+ select count(*) as rwcnt
2739
+ from ups_pk_fk_check
2740
+ ;
2741
+
2742
+ -- !x! subdata ~rowcount ups_pk_fk_check_rwcnt
2743
+ -- !x! sub ~error_description !!@parent_schema!!.!!@parent_table!! (!!@referenced_cols!!): !!~rowcount!! row(s)
2744
+
2745
+ -- !x! write " Violation of foreign key !!@fk_constraint!! in new primary key columns in !!@staging_schema!!.!!@table_name!! referencing !!@parent_schema!!.!!@parent_table!!: !!~rowcount!! row(s)"
2746
+
2747
+ drop view if exists ups_pk_fk_qa_error cascade;
2748
+ create temporary view ups_pk_fk_qa_error as
2749
+ select
2750
+ error_code, error_description
2751
+ from !!#qaerror_table!!
2752
+ where error_code='Invalid reference to parent table(s)';
2753
+ -- !x! if(hasrows(ups_pk_fk_qa_error))
2754
+ update !!#qaerror_table!!
2755
+ set error_description=error_description || '; !!~error_description!!'
2756
+ where error_code='Invalid reference to parent table(s)';
2757
+
2758
+ -- !x! else
2759
+ insert into !!#qaerror_table!! (error_code, error_description)
2760
+ values ('Invalid reference to parent table(s)', '!!~error_description!!')
2761
+ ;
2762
+ -- !x! endif
2763
+
2764
+
2765
+ -- !x! if(sub_defined(logfile))
2766
+ -- !x! write "" to !!logfile!!
2767
+ -- !x! write "Violation of foreign key !!@fk_constraint!! in new primary key columns in !!@staging_schema!!.!!@table_name!! referencing !!@parent_schema!!.!!@parent_table!!: !!~rowcount!! row(s)" to !!logfile!!
2768
+ -- !x! if(sub_defined(log_errors))
2769
+ -- !x! andif(is_true(!!log_errors!!))
2770
+ -- !x! export ups_pk_fk_check append to !!logfile!! as txt
2771
+ -- !x! endif
2772
+ -- !x! endif
2773
+ -- !x! if(is_true(!!#display_errors!!))
2774
+ -- !x! prompt message "Violation of foreign key !!@fk_constraint!! in new primary key columns in !!@staging_schema!!.!!@table_name!! referencing !!@parent_schema!!.!!@parent_table!!" display ups_pk_fk_check
2775
+ -- !x! endif
2776
+
2777
+ -- !x! endif
2778
+
2779
+ -- Mark constraint as processed
2780
+ update ups_pkfk_ctrl
2781
+ set processed=True
2782
+ where fk_constraint='!!@fk_constraint!!';
2783
+
2784
+ --LOOP
2785
+ -- !x! execute script updtpkqa_one_innerloop with (qaerror_table=!!#qaerror_table!!, display_errors=!!#display_errors!!)
2786
+
2787
+ -- !x! endif
2788
+
2789
+ -- !x! END SCRIPT
2790
+ -- #################### End of UPDTPKQA_ONE ########################
2791
+ -- ###################################################################
2792
+