Repository navigation
gpcheckcat: fix the defects the Behave suite exposes #2182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1075,9 +1075,12 @@ def checkOwners(): | |
| a.rolname, m.rolname as coordinator_rolname | ||
| from gp_dist_random('pg_class') r | ||
| join pg_class c on (c.oid = r.oid) | ||
| left join pg_index i on (c.oid = i.indexrelid) | ||
| left join pg_appendonly ao on (c.oid = ao.segrelid or | ||
| c.oid = ao.blkdirrelid or | ||
| c.oid = ao.blkdiridxid) | ||
| c.oid = ao.visimaprelid or | ||
| i.indrelid = ao.blkdirrelid or | ||
| i.indrelid = ao.visimaprelid) | ||
| left join pg_class o on (o.oid = ao.relid or | ||
| o.reltoastrelid = c.oid) | ||
| join pg_authid a on (a.oid = r.relowner) | ||
|
|
@@ -1948,6 +1951,180 @@ def checkOrphanedToastTables(): | |
| issue_type="orphaned_toast_tables", | ||
| description='Repairing orphaned TOAST tables') | ||
|
|
||
| # The mix_distribution_policy check below -- fetch_guc_value(), | ||
| # generateDistPolicyQueryFile(), checkMixDistPolicy() and their helpers -- | ||
| # is cherry-picked from Greenplum Database (greenplum-db/gpdb-archive @ 482967c1b4), | ||
| # gpMgmt/bin/gpcheckcat. | ||
| def fetch_guc_value(guc): | ||
| qry = ''' | ||
| show {} | ||
| '''.format(guc) | ||
| try: | ||
| conn = connect2(GV.cfg[GV.coordinator_dbid]) | ||
| curs = conn.query(qry) | ||
| rows = curs.getresult() | ||
| guc_value = rows[0][0] | ||
| return guc_value | ||
|
|
||
| except Exception as e: | ||
| setError(ERROR_NOREPAIR) | ||
| GV.checkStatus = False | ||
| myprint('[ERROR] executing test: mix_distribution_policy') | ||
| myprint(' Execution error: ' + str(e)) | ||
|
|
||
| # The opclasses of the families that hold a legacy cdbhash function. | ||
| # Greenplum lists those functions by OID, but most of Cloudberry's have | ||
| # other OIDs, so they are found by name, which both ports share. | ||
| LEGACY_OPCLASS_OIDS_CTE = '''legacy_opclass_oids(oid_array) as ( | ||
| select | ||
| array_agg(oid) | ||
| from | ||
| pg_opclass | ||
| where | ||
| opcfamily in ( | ||
| select | ||
| amprocfamily | ||
| from | ||
| pg_amproc | ||
| where | ||
| amproc in ( | ||
| select | ||
| oid | ||
| from | ||
| pg_proc | ||
| where | ||
| pronamespace = 'pg_catalog' :: regnamespace | ||
| and starts_with(proname, 'cdblegacyhash_') | ||
| ) | ||
| ) | ||
| )''' | ||
|
|
||
| def generateDistPolicyQueryFile(): | ||
|
|
||
| query_sql = ''' | ||
| -- all tables that use legacy policy: | ||
| with %(legacy)s | ||
| select | ||
| localoid :: regclass :: text as "Legacy Policy" | ||
| from | ||
| gp_distribution_policy, | ||
| legacy_opclass_oids | ||
| where | ||
| policytype = 'p' | ||
| and distclass :: oid[] && oid_array; | ||
|
|
||
| -- all tables that don't use any legacy policy: | ||
| with %(legacy)s | ||
| select | ||
| localoid :: regclass :: text as "Non Legacy Policy" | ||
| from | ||
| gp_distribution_policy, | ||
| legacy_opclass_oids | ||
| where | ||
| policytype = 'p' | ||
| and not (distclass :: oid[] && oid_array); | ||
| ''' % {'legacy': LEGACY_OPCLASS_OIDS_CTE} | ||
| filename = 'gpcheckcat.distpolicy.sql' | ||
|
|
||
| if not os.path.exists(filename) : | ||
| try: | ||
| with open(filename, 'w') as fp: | ||
| fp.write(query_sql + "\n") | ||
| except Exception as e: | ||
| logger.warning('Unable to generate verify file for {}'.format(filename)) | ||
|
|
||
|
|
||
| # Test to check if there are tables that use both legacy opclass/non legacy opclass | ||
| # in distribution policy | ||
| def checkMixDistPolicy() : | ||
|
|
||
| qry = ''' | ||
| with %(legacy)s, | ||
| all_hash_ops(dc) as ( | ||
| select | ||
| distinct unnest(distclass :: oid[]) | ||
| from | ||
| gp_distribution_policy | ||
| ) | ||
| select | ||
| count(1) filter( | ||
| where | ||
| array[x.dc] && oid_array | ||
| ) as n_legacy_dist_class, | ||
| count(1) as n_total_dist_class | ||
| from | ||
| all_hash_ops x, | ||
| legacy_opclass_oids y; | ||
| ''' % {'legacy': LEGACY_OPCLASS_OIDS_CTE} | ||
|
|
||
| try: | ||
| conn = connect2(GV.cfg[GV.coordinator_dbid]) | ||
| curs = conn.query(qry) | ||
| rows = curs.getresult() | ||
|
|
||
| if rows: | ||
| row = rows[0] | ||
| n_legacy_dist_class = row[0] | ||
| n_total_dist_class = row[1] | ||
| GV.checkStatus = False | ||
|
|
||
| if n_legacy_dist_class > 0 and n_total_dist_class > n_legacy_dist_class : | ||
| setError(ERROR_NOREPAIR) | ||
| generateDistPolicyQueryFile() | ||
| #if this condition is true then we have mix distribution Policy | ||
| myprint( | ||
| '[ERROR]: Found tables created using both legacy and non legacy hashops' | ||
| ' in distribution policy.' | ||
| 'Please run the gpcheckcat.distpolicy.sql file to list the tables.' | ||
| ) | ||
| else: | ||
| if (n_legacy_dist_class == 0 or n_legacy_dist_class == n_total_dist_class): | ||
| #if this condition is true then we dont have mix distribution policy | ||
| gp_use_legacy_hashops = fetch_guc_value("gp_use_legacy_hashops") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add otherwise, printDistPolicyMsg() overwrites the failure status set by the failed GUC lookup and the summary then incorrectly reports “PASSED” and raises an AssertionError because the error code is still nonzero.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed. With the |
||
| # The lookup failed and has reported it; printDistPolicyMsg | ||
| # would mark the check as passed. | ||
| if gp_use_legacy_hashops is None: | ||
| return | ||
| printDistPolicyMsg(gp_use_legacy_hashops, | ||
| n_legacy_dist_class, | ||
| n_total_dist_class | ||
| ) | ||
|
|
||
| except Exception as e: | ||
| setError(ERROR_NOREPAIR) | ||
| GV.checkStatus = False | ||
| myprint('[ERROR] executing test: mix_distribution_policy') | ||
| myprint(' Execution error: ' + str(e)) | ||
|
|
||
| def printDistPolicyMsg(gp_use_legacy_hashops,n_legacy_dist_class, n_total_dist_class): | ||
|
|
||
| GV.checkStatus = True | ||
|
|
||
| if n_total_dist_class - n_legacy_dist_class > 0 and gp_use_legacy_hashops == "on": | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GUC-mismatch errors reference a nonexistent diagnostic file. the condition instructs users to run gpcheckcat.distpolicy.sql, but only the mixed-policy branch generates it. Generate it for these mismatch cases too.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed. Both GUC-mismatch branches now write gpcheckcat.distpolicy.sql before printing the message that refers to it, and a unit test checks that each one does. On the cluster, the file is now there after both errors. |
||
| myprint( | ||
| '[ERROR]: GUC gp_use_legacy_hashops is on.' | ||
| ' all newly created tables will use legacy hash ops by default for hash distributed table, ' | ||
| 'but there are tables using non-legacy hash ops in the cluster. ' | ||
| 'Please run the gpcheckcat.distpolicy.sql file to list the tables.' | ||
| ) | ||
| setError(ERROR_NOREPAIR) | ||
| generateDistPolicyQueryFile() | ||
| GV.checkStatus = False | ||
|
|
||
| elif n_legacy_dist_class == 0 and gp_use_legacy_hashops == "off": | ||
| GV.checkStatus = True | ||
|
|
||
| elif n_legacy_dist_class > 0 and gp_use_legacy_hashops == "off" : | ||
| myprint( | ||
| '[ERROR]: GUC gp_use_legacy_hashops is off.' | ||
| ' all newly created tables will use non legacy hash ops by default for hash distributed table, ' | ||
| 'but there are tables using legacy hash ops in the cluster. ' | ||
| 'Please run the gpcheckcat.distpolicy.sql file to list the tables.' | ||
| ) | ||
| setError(ERROR_NOREPAIR) | ||
| generateDistPolicyQueryFile() | ||
| GV.checkStatus = False | ||
|
|
||
|
|
||
| ############################################################################ | ||
| # Help populating repair part for all checked types | ||
|
|
@@ -2082,7 +2259,16 @@ all_checks = { | |
| "version": 'main', | ||
| "order": 15, | ||
| "online": False | ||
| }, | ||
| "mix_distribution_policy": | ||
| { | ||
| "description": "Check for tables that use legacy opclass in distribution policy", | ||
| "fn": lambda: checkMixDistPolicy(), | ||
| "version": 'main', | ||
| "order": 17, | ||
| "online": True | ||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
@@ -2366,6 +2552,7 @@ TableMainColumn['pg_type_encoding'] = ['typid', 'pg_type'] | |
| TableMainColumn['pg_window'] = ['winfnoid', 'pg_proc'] | ||
| TableMainColumn['pg_password_history'] = ['passhistroleid', 'pg_authid'] | ||
| TableMainColumn['pg_description'] = ['objoid', 'pg_description'] | ||
| TableMainColumn['pg_shdescription'] = ['objoid', 'pg_shdescription'] | ||
|
|
||
| # Table with OID (special case), these OIDs are known to be inconsistent | ||
| TableMainColumn['pg_attrdef'] = ['adrelid', 'pg_class'] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detected policy errors still return exit status 0. it set GV.checkStatus = False without calling setError(). Both GUC-mismatch branches do the same. With no other errors, gpcheckcat reports failure but exits successfully, so automation misses it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed: each of the three errors exited 0 with
-R mix_distribution_policy. They now call setError(ERROR_NOREPAIR), and gpcheckcat exits 3. Unit tests cover the mixed policy and both GUC mismatches.