pax_global_header00006660000000000000000000000064141700547720014521gustar00rootroot0000000000000052 comment=1dff9505f4f072827338dc491ded1f54a3a24c56 pg_catcheck-1.3.0/000077500000000000000000000000001417005477200137555ustar00rootroot00000000000000pg_catcheck-1.3.0/.gitignore000066400000000000000000000000311417005477200157370ustar00rootroot00000000000000/pg_catcheck /*.o /.deps pg_catcheck-1.3.0/LICENSE000066400000000000000000000022271417005477200147650ustar00rootroot00000000000000pg_catcheck Portions Copyright (c) 2013-2014, EnterpriseDB Corporation Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group Portions Copyright (c) 1994, The Regents of the University of California Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pg_catcheck-1.3.0/Makefile000066400000000000000000000007371417005477200154240ustar00rootroot00000000000000PGFILEDESC = "pg_catcheck - system catalog integrity checker" PGAPPICON = win32 PROGRAM = pg_catcheck OBJS = pg_catcheck.o check_attribute.o check_class.o check_depend.o \ check_oids.o compat.o definitions.o log.o pgrhash.o \ select_from_relations.o PG_CPPFLAGS = -I$(libpq_srcdir) PG_LIBS = $(libpq_pgport) $(PTHREAD_LIBS) PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) ifneq ($(PORTNAME), win32) override CFLAGS += $(PTHREAD_CFLAGS) endif pg_catcheck-1.3.0/README.md000066400000000000000000000154711417005477200152440ustar00rootroot00000000000000What is pg_catcheck? ==================== pg_catcheck is a simple tool for diagnosing system catalog corruption. If you suspect that your system catalogs are corrupted, this tool may help you figure out exactly what problems you have and how serious they are. If you are paranoid, you can run it routinely to search for system catalog corruption that might otherwise go undetected. However, pg_catcheck is not a general corruption detector. For that, you should use PostgreSQL's checksum feature (`initdb -k`). PostgreSQL stores the metadata for SQL objects such as tables and functions using special tables called system catalog tables. Users do not normally modify these tables directly, but instead modify them using SQL commands such as CREATE, ALTER, and DROP. If the system catalog tables become corrupted, you may experience errors when attempting to access your data. Sometimes, it can be impossible to back up your data using pg_dump without correcting these errors. pg_catcheck won't tell you how to your database got corrupted in the first place, and it won't tell you how to fix it. But it will usually be able to give you detailed information about what is broken, which may make it easier for you (or your PostgreSQL support provider) to understand what has gone wrong and explain the options for recovery. How Do I Run pg_catcheck? ========================= pg_catcheck takes the same arguments as most other PostgreSQL utilites, such as -h for the host or -p for the port. You can also pass it a connection string or URL, just like psql. For a full list of options, run `pg_catcheck --help`. If pg_catcheck isn't already installed, you might need to build it first. If no pre-compiled binary package is available for you to install, see the instructions below for "Building on UNIX/Linux" and "Building on Windows". When you run pg_catcheck, it will normally print out a line that looks like this: progress: done (0 inconsistencies, 0 warnings, 0 errors) If you see that line, it means pg_catcheck didn't find any problems. Otherwise, pg_catcheck will generally print two lines of output for each problem it finds, like this: notice: pg_class row has invalid relnamespace "24580": no matching entry in pg_namespace row identity: oid="24581" relname="foo" relkind="r" notice: pg_type row has invalid typnamespace "24580": no matching entry in pg_namespace row identity: oid="24583" notice: pg_type row has invalid typnamespace "24580": no matching entry in pg_namespace row identity: oid="24582" notice: pg_depend row has invalid refobjid "24580": no matching entry in pg_namespace row identity: classid="1259" objid="24581" objsubid="0" refclassid="2615" refobjid="24580" refobjsubid="0" deptype="n" progress: done (4 inconsistencies, 0 warnings, 0 errors) If the final output line mentions inconsistencies, that means that it found problems with the logical structure of your system catalogs. Warnings or errors indicate more serious problems, like not being able to read the system catalogs at all. In this particular example, there are four errors: one pg_class row, two pg_type rows, and one pg_depend row all reference an OID 24580 which they expect to find in pg_namespace. In reality, no such row exists. There are several ways to recover from an error of this type. You could modify the OIDs in the referring rows so that they refer to a pg_namespace entry that does exist. This might enable you to recover access to the underlying data. Note that in this case all four references pertain to the same table (pg_class OID 24581, which has pg_type OIDs 24582 and 24583 for its record and array-of-record types) so you would probably want to make all of those references point to the same namespace. Alternatively, if the dangling references are objects you don't care about (e.g. if the backing file for the pg_class entry doesn't even exist on disk), you could simply delete the referring rows also. This is often enough to make pg_dump run successfully, which is often the main goal. Unless you are sure you understand what pg_catcheck is telling you, you may wish to consult with a PostgreSQL expert. Changing the system catalogs manually can make a bad situation worse and lead to data loss, and should not be attempted unless you are knowledgeable about how PostgreSQL uses these catalogs. pg_catcheck also provides an option to run "SELECT * FROM table_name LIMIT 0" on each table in the database, which will detect missing or inaccessible relation files. The --select-from-relations option enables this check. What is the license for pg_catcheck? Can I contribute? ======================================================= pg_catcheck was initially developed by EnterpriseDB and is released under the same license as PostgreSQL itself. Patches are welcome. Please subscribe to our mailing list, pg-catcheck@enterprisedb.com, by visiting: https://groups.google.com/a/enterprisedb.com/forum/#!forum/pg-catcheck How do I get support for pg_catcheck? ===================================== As with any open source project, you may be able to obtain support via the public mailing list, which is pg-catcheck@enterprisedb.com; to subscribe, visit: https://groups.google.com/a/enterprisedb.com/forum/#!forum/pg-catcheck If you need commercial support, please contact the EnterpriseDB sales team, or check whether your existing PostgreSQL support provider can also support pg_catcheck. What versions of PostgreSQL does pg_catcheck support? ===================================================== pg_catcheck should work when run against a server running PostgreSQL 8.4 or higher. It also should also work when run against a server running EnterpriseDB's Advanced Server product, version 8.4 or higher. To compile pg_catcheck, you will need to build against a server source tree version 9.0 or higher, because it relies on the function PQconnectdbParams(), which did not exist in 8.4. Building on UNIX/Linux ====================== * Make sure that you have a working pg_config executable in your path. (If you are using a binary installation of PostgreSQL, you might need to install additional packages, such as postgresql-devel or libpq-dev.) * Run "make" and, if desired, "make install". * To remove generated files, run "make clean". Building on Windows =================== * Build PostgreSQL with MSVC as described in http://www.postgresql.org/docs/devel/static/install-windows-full.html * Start the Visual Studio Command Prompt. If you wish to build a 64-bit version, you must use the 64-bit version of the command. * "cd" to the source directory (e.g. cd c:\pg_catcheck) * Use a command like "msbuild /p:PGPATH=C:\postgresql-9.4.0 /p:DEBUG=0 /p:ARCH=x64" to perform the actual build. PGPATH should be set to the location of the PostgreSQL source code, and DEBUG should be set to 1 for a debug build. * If you wish to remove the generated files, use "msbuild /target:clean". pg_catcheck-1.3.0/check_attribute.c000066400000000000000000000057131417005477200172670ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * check_attribute.c * * Custom checks for pg_attribute fields. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" #include "catalog/pg_attribute.h" typedef struct { pg_catalog_table *pg_class; int attrelid_result_column; int relnatts_result_column; } attnum_cache; /* * Set up to check attnum. */ void prepare_to_check_attnum(pg_catalog_table *tab, pg_catalog_column *tabcol) { add_table_dependency(tab, find_table_by_name("pg_class")); } /* * Sanity-check the relnatts field. */ void check_attnum(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { char *val = PQgetvalue(tab->data, rownum, tabcol->result_column); char *attrelid_val; char *relnatts_val; char *endptr; attnum_cache *cache; int class_rownum; long attnum; long relnatts; long min_attno; /* Convert the value to a number. */ attnum = strtol(val, &endptr, 10); if (*endptr != '\0') { pgcc_report(tab, tabcol, rownum, "must be an integer\n"); return; } /* Our attribute number should not be zero. */ if (attnum == 0) { pgcc_report(tab, tabcol, rownum, "must not be zero\n"); return; } /* And it should be at least -7 for PostgreSQL, -8 for EnterpriseDB. */ min_attno = remote_is_edb ? -8 : -7; if (attnum < min_attno) { pgcc_report(tab, tabcol, rownum, "must be at least %ld\n", min_attno); return; } /* Find the pg_attribute table; cache result in check_private. */ if (tabcol->check_private == NULL) { cache = pg_malloc(sizeof(attnum_cache)); cache->pg_class = find_table_by_name("pg_class"); cache->attrelid_result_column = PQfnumber(tab->data, "attrelid"); cache->relnatts_result_column = PQfnumber(cache->pg_class->data, "relnatts"); tabcol->check_private = cache; } else cache = tabcol->check_private; /* * Skip max-bound checking if the pg_class data is not available, or if * the pg_class.relnatts or pg_attribute.attrelid column is not available. */ if (cache->pg_class->ht == NULL || cache->relnatts_result_column == -1 || cache->attrelid_result_column == -1) return; /* Find row number of this table in pg_class. */ attrelid_val = PQgetvalue(tab->data, rownum, cache->attrelid_result_column); class_rownum = pgrhash_get(cache->pg_class->ht, &attrelid_val); if (class_rownum == -1) return; /* It's not our job to complain about * attrelid. */ /* Get relnatts, as a number. */ relnatts_val = PQgetvalue(cache->pg_class->data, class_rownum, cache->relnatts_result_column); relnatts = strtol(relnatts_val, &endptr, 10); if (*endptr != '\0' || relnatts < 0) return; /* It's not our job to complain about * relnatts. */ /* Our attribute number should be less than relnatts. */ if (attnum > relnatts) pgcc_report(tab, tabcol, rownum, "exceeds relnatts value of %ld\n", relnatts); } pg_catcheck-1.3.0/check_class.c000066400000000000000000000042761417005477200163740ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * check_class.c * * Custom checks for pg_class fields. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" typedef struct { pg_catalog_table *pg_attribute; int oid_result_column; } relnatts_cache; /* * Set up to check relnatts. */ void prepare_to_check_relnatts(pg_catalog_table *tab, pg_catalog_column *tabcol) { add_table_dependency(tab, find_table_by_name("pg_attribute")); } /* * Sanity-check the relnatts field. */ void check_relnatts(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { char *val = PQgetvalue(tab->data, rownum, tabcol->result_column); char *endptr; relnatts_cache *cache; long relnatts; int attno; char buf[512]; char *keys[2]; /* Convert the value to a number. */ relnatts = strtol(val, &endptr, 10); if (*endptr != '\0' || val < 0) pgcc_report(tab, tabcol, rownum, "must be a non-negative integer\n"); /* Find the pg_attribute table; cache result in check_private. */ if (tabcol->check_private == NULL) { cache = pg_malloc(sizeof(relnatts_cache)); cache->pg_attribute = find_table_by_name("pg_attribute"); cache->oid_result_column = PQfnumber(tab->data, "oid"); tabcol->check_private = cache; } else cache = tabcol->check_private; /* * Skip detailed checking if pg_attribute data is not available, or if the * oid column of pg_class is not available. */ if (cache->pg_attribute->ht == NULL || cache->oid_result_column == -1) return; /* Set up for pg_attribute hash table probes. */ keys[0] = PQgetvalue(tab->data, rownum, cache->oid_result_column); keys[1] = buf; /* * Check that all positive-numbered attributes we expect to find are in * fact present. * * TODO: We could check for negative-numbered attributes as well, but * whether or not those are present will depend on relkind inter alia. */ for (attno = 1; attno <= relnatts; ++attno) { snprintf(buf, sizeof buf, "%d", attno); if (pgrhash_get(cache->pg_attribute->ht, keys) == -1) pgcc_report(tab, tabcol, rownum, "attribute %d does not exist in pg_attribute\n", attno); } } pg_catcheck-1.3.0/check_depend.c000066400000000000000000000605031417005477200165210ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * check_depend.c * * A number of PostgreSQL system catalogs store references to SQL objects * of arbitrary type by recording a class ID (the OID of the system * catalog that contains the referenced object) and an object ID (the OID * of the referenced object within that catalog). In cases where the * referenced object may be a table column, there is also a sub-ID; * when the referenced object is a table column, (class ID, sub-ID) should * match the pg_attribute row's (attrelid, attnum). In all other cases, * the sub-ID should be zero. * * The code in this file aims to validate the class ID, object ID, and * sub-ID. There is some duplication in the code structure, because to * check the object ID, we must validate the class ID and look up the * corresponding table. However, we try hard not to complain about what * is in essence the same problem more than once, and to complain about * it with respect to the correct column. * * The name of this file comes from the fact that the classic example of * the class ID/object ID/sub-ID notation is in the pg_depend catalog, * but we actually use this code to validate other tables that use a * similar convention, such as pg_description. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" typedef enum { DEPEND_COLUMN_STYLE_OBJID, /* pg_(sh)depend, referring side */ DEPEND_COLUMN_STYLE_REFOBJID, /* pg_(sh)depend, referenced side */ DEPEND_COLUMN_STYLE_OBJOID, /* pg_(sh)description, pg_(sh)seclabel */ EDB_DDLTIME_COLUMN_STYLE_OBJID /* edb_last_ddl_time(_shared) */ } depend_column_style; typedef struct check_depend_cache { depend_column_style style; bool is_broken; int database_result_column; int class_result_column; int object_result_column; int deptype_result_column; pgrhash *duplicate_owner_ht; } check_depend_cache; typedef struct class_id_mapping_type { char *oid; pg_catalog_table *tab; } class_id_mapping_type; /* * EnterpriseDB versions prior to 9.4 are expected to have a number of * dangling dependency entries, unless initialized with --no-redwood-compat. * We avoid complaining about these because (1) they're known and basically * harmless and (2) we don't want to give the misimpression of real * corruption. */ typedef struct exception_list { char *table_name; char *class; char *object; } exception_list; exception_list edb84_exception_list[] = { {"pg_depend", "1255", "877"}, {"pg_depend", "1255", "883"}, {"pg_depend", "1255", "1777"}, {"pg_depend", "1255", "1780"}, {"pg_depend", "1255", "2049"}, {"pg_depend", "2617", "2779"}, {"pg_depend", "2617", "2780"}, {NULL} }; exception_list edb90_exception_list[] = { {"pg_depend", "1255", "877"}, {"pg_depend", "1255", "883"}, {"pg_depend", "1255", "1777"}, {"pg_depend", "1255", "1780"}, {"pg_depend", "1255", "2049"}, {"pg_depend", "2617", "2779"}, {"pg_depend", "2617", "2780"}, {NULL} }; exception_list edb91_92_exception_list[] = { {"pg_depend", "1255", "877"}, {"pg_depend", "1255", "883"}, {"pg_depend", "1255", "1777"}, {"pg_depend", "1255", "1780"}, {"pg_depend", "1255", "2049"}, {"pg_depend", "2617", "2779"}, {"pg_depend", "2617", "2780"}, {"pg_description", "2617", "2779"}, {"pg_description", "2617", "2780"}, {NULL} }; exception_list edb93_exception_list[] = { {"pg_depend", "1255", "877"}, {"pg_depend", "1255", "883"}, {"pg_depend", "1255", "1777"}, {"pg_depend", "1255", "1780"}, {"pg_depend", "1255", "2049"}, {NULL} }; static bool class_id_mappings_attempted; static int num_class_id_mapping; static class_id_mapping_type *class_id_mapping; static char *pg_class_oid; static pg_catalog_table *pg_attribute_table; static pg_catalog_table *pg_type_table; static pg_catalog_table *lookup_class_id(char *oid); static void build_class_id_mappings(void); static bool table_key_is_oid(pg_catalog_table *tab); static check_depend_cache *build_depend_cache(pg_catalog_table *tab, pg_catalog_column *tabcol); static bool not_for_this_database(check_depend_cache *cache, pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); static depend_column_style get_style(char *table_name, char *column_name); static bool check_for_exception(char *table_name, char *classval, char *objval); /* * Set up to check a class ID. */ void prepare_to_check_dependency_class_id(pg_catalog_table *tab, pg_catalog_column *tabcol) { pg_catalog_table *pg_class = find_table_by_name("pg_class"); pg_catalog_column *pg_class_relname; pg_catalog_column *pg_class_relnamespace; /* * We need pg_class to figure out system catalog table OIDs. */ add_table_dependency(tab, pg_class); pg_class_relname = find_column_by_name(pg_class, "relname"); pg_class_relname->needed = true; pg_class_relnamespace = find_column_by_name(pg_class, "relnamespace"); pg_class_relnamespace->needed = true; /* We need this to determine whether the class ID can legally zero. */ if (get_style(tab->table_name, tabcol->name) == DEPEND_COLUMN_STYLE_OBJID) { pg_catalog_column *deptype = find_column_by_name(tab, "deptype"); deptype->needed = true; } } /* * Set up to check an object ID. */ void prepare_to_check_dependency_id(pg_catalog_table *tab, pg_catalog_column *tabcol) { pg_catalog_table *cattab; pg_catalog_column *classid; /* * Just as when checking a class ID, we need pg_class to map class IDs to * catalog tables. */ prepare_to_check_dependency_class_id(tab, tabcol); /* * All catalog tables that have an OID column must be loaded before we can * check dependency IDs. */ for (cattab = pg_catalog_tables; cattab->table_name != NULL; ++cattab) if (table_key_is_oid(cattab)) add_table_dependency(tab, cattab); /* Force the necessary classid column to be selected. */ switch (get_style(tab->table_name, tabcol->name)) { case DEPEND_COLUMN_STYLE_OBJID: case EDB_DDLTIME_COLUMN_STYLE_OBJID: classid = find_column_by_name(tab, "classid"); break; case DEPEND_COLUMN_STYLE_REFOBJID: classid = find_column_by_name(tab, "refclassid"); break; case DEPEND_COLUMN_STYLE_OBJOID: classid = find_column_by_name(tab, "classoid"); break; default: pgcc_log(PGCC_FATAL, "unexpected depend column style"); return; /* placate compiler */ } classid->needed = true; } /* * Set up to check a sub-ID. */ void prepare_to_check_dependency_subid(pg_catalog_table *tab, pg_catalog_column *tabcol) { pg_catalog_column *classid; pg_catalog_column *objectid; /* * Just as when checking a class ID, we need pg_class to map class IDs to * catalog tables. Specifically, we've got to be able to identify the OID * of pg_class itself, so that we know whether a non-zero sub-ID is legal. * Currently, that OID is the same in all server versions we support, so * we could hard-code it, but there's little harm in doing it this way: if * pg_class is too botched to interpret, the chances of anything else * making much sense are slim to none. */ prepare_to_check_dependency_class_id(tab, tabcol); /* We need the pg_attribute table to check sub-IDs. */ add_table_dependency(tab, find_table_by_name("pg_attribute")); /* Make sure we have the class and object IDs. */ switch (get_style(tab->table_name, tabcol->name)) { case DEPEND_COLUMN_STYLE_OBJID: case EDB_DDLTIME_COLUMN_STYLE_OBJID: classid = find_column_by_name(tab, "classid"); objectid = find_column_by_name(tab, "objid"); break; case DEPEND_COLUMN_STYLE_REFOBJID: classid = find_column_by_name(tab, "refclassid"); objectid = find_column_by_name(tab, "refobjid"); break; case DEPEND_COLUMN_STYLE_OBJOID: classid = find_column_by_name(tab, "classoid"); objectid = find_column_by_name(tab, "objoid"); break; default: pgcc_log(PGCC_FATAL, "unexpected depend column style"); return; /* placate compiler */ } classid->needed = true; objectid->needed = true; } /* * Check a class ID. * * This is basically just testing that the class ID is a system catalog * table that we know about and that's supposed to exists in this server * version, or else 0 if that's a legal value in this context. */ void check_dependency_class_id(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { char *val; check_depend_cache *cache; cache = build_depend_cache(tab, tabcol); if (cache->is_broken) return; if (not_for_this_database(cache, tab, tabcol, rownum)) return; val = PQgetvalue(tab->data, rownum, tabcol->result_column); /* * We normally expect that the class ID is non-zero, but "pin" depedencies * are an exception. */ if (strcmp(val, "0") == 0) { bool complain = true; if (cache->style == DEPEND_COLUMN_STYLE_OBJID) { char *deptype; deptype = PQgetvalue(tab->data, rownum, cache->deptype_result_column); if (strcmp(deptype, "p") == 0) complain = false; } if (complain) pgcc_report(tab, tabcol, rownum, "unexpected zero value\n"); return; } if (lookup_class_id(val) == NULL) { /* * Workaround for an old EnterpriseDB bug: 8.4 installed a bogus * dependency with reclassid 16722. */ if (remote_is_edb && remote_version <= 90000 && strcmp(val, "16722") == 0) { pgcc_log(PGCC_DEBUG, "ignoring reference to class ID 16722\n"); return; } pgcc_report(tab, tabcol, rownum, "not a system catalog OID\n"); } } /* * Check a dependency ID. * * We have to examine the class ID to figure out which table ought to * contain the indicated object. We then look up that table and check * whether the value appears in its OID column. */ void check_dependency_id(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { char *classval; char *val; pg_catalog_table *object_tab; check_depend_cache *cache; cache = build_depend_cache(tab, tabcol); if (cache->is_broken) return; if (not_for_this_database(cache, tab, tabcol, rownum)) return; /* * Check for multiple owner dependencies for the same object. * * FIXME: Current pg_catcheck design don't support table-level checks, * all checks are column-level. We might want to re-architect this at * some point in the future. For now, we check this here. */ if (cache->duplicate_owner_ht != NULL && strcmp(PQgetvalue(tab->data, rownum, PQfnumber(tab->data, "deptype")), "o") == 0 && pgrhash_insert(cache->duplicate_owner_ht, rownum) != -1) pgcc_report(tab, NULL, rownum, "duplicate owner dependency\n"); /* Fetch the class ID and object ID. */ classval = PQgetvalue(tab->data, rownum, cache->class_result_column); val = PQgetvalue(tab->data, rownum, tabcol->result_column); /* If the class ID is zero, the object ID should be zero as well. */ if (strcmp(classval, "0") == 0) { if (strcmp(val, "0") != 0) pgcc_report(tab, tabcol, rownum, "class ID is zero, but object ID is non-zero\n"); return; } /* Find the correct table. */ object_tab = lookup_class_id(classval); if (object_tab == NULL || object_tab->ht == NULL) return; /* * Workaround for EnterpriseDB bug: EnterpriseDB versions prior to 9.4 * would sometimes create bogus dependencies on type ID 0. Since we'll * never create a real type with that OID, this was (as far as we know) * harmless, so just ignore them. */ if (pg_type_table == NULL) pg_type_table = find_table_by_name("pg_type"); if (remote_version < 90400 && remote_is_edb && object_tab == pg_type_table && strcmp(val, "0") == 0) { pgcc_log(PGCC_DEBUG, "ignoring reference to pg_type OID 0\n"); return; } /* * lookup_class_id() will only return tables where the only key column is * the OID column. So this is safe. */ if (pgrhash_get(object_tab->ht, &val) == -1 && !check_for_exception(tab->table_name, classval, val)) pgcc_report(tab, tabcol, rownum, "no matching entry in %s\n", object_tab->table_name); } /* * Check a dependency sub-ID. * * This should always be zero except in the where the class ID points to * pg_class. In that case, should be able to find in * pg_attribute. The object ID will appear in attrelid and the sub-ID in attnum. */ void check_dependency_subid(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { char *classval; char *vals[2]; check_depend_cache *cache; cache = build_depend_cache(tab, tabcol); if (cache->is_broken) return; if (not_for_this_database(cache, tab, tabcol, rownum)) return; /* * We find pg_attribute on our first trip through this function and avoid * repeating the lookup thereafter using a global variable. */ if (pg_attribute_table == NULL) pg_attribute_table = find_table_by_name("pg_attribute"); /* Fetch the class ID, object ID, and sub-ID. */ classval = PQgetvalue(tab->data, rownum, cache->class_result_column); vals[0] = PQgetvalue(tab->data, rownum, cache->object_result_column); vals[1] = PQgetvalue(tab->data, rownum, tabcol->result_column); /* Sub-ID is always permitted to be zero. */ if (strcmp(vals[1], "0") == 0) return; /* * If we get here, the sub-ID is non-zero. Therefore, the class ID should * definitely point to pg_class; if it does not, that's an inconsistency. * If it does point to pg_class, then a matching pg_attribute row should * exist. */ if (strcmp(classval, pg_class_oid) != 0) pgcc_report(tab, tabcol, rownum, "class ID %s is not pg_class, but sub-ID is non-zero\n", classval); else if (pg_attribute_table->ht) /* We might have failed to read it. */ { if (pgrhash_get(pg_attribute_table->ht, vals) == -1) pgcc_report(tab, tabcol, rownum, "no matching entry in %s\n", pg_attribute_table->table_name); } } /* * Given a text-form OID found in an objid or refobjid table, search for a * corresponding catalog table. * * NB: We could make this more efficient by teaching build_class_id_mappings() * to sort the array, and then using binary search. */ static pg_catalog_table * lookup_class_id(char *oid) { int i; Assert(class_id_mappings_attempted && class_id_mapping != NULL); /* For LargeObjectRelationId, substitute LargeObjectMetadataOidIndexId. */ if (strcmp(oid, "2613") == 0) oid = "2995"; for (i = 0; i < num_class_id_mapping; ++i) if (strcmp(class_id_mapping[i].oid, oid) == 0) return class_id_mapping[i].tab; return NULL; } /* * Build a set of mappings from text-form OIDs which PQgetvalue might hand * back for an objid or refobjid column to pg_catalog_table objects. */ static void build_class_id_mappings(void) { int map_used = 0; int map_size = 10; class_id_mapping_type *map; pg_catalog_table *pg_class_tab; int oid_column; int relnamespace_column; int relname_column; int ntups = 0; int i; /* * If we fall out of this function due to some kind of unexpected error, * we do not want to retry, as we'll just hit the same problem the second * time. Set a flag so that callers can detect this case. */ class_id_mappings_attempted = true; /* Find the pg_class table. */ pg_class_tab = find_table_by_name("pg_class"); if (pg_class_tab->data != NULL) ntups = PQntuples(pg_class_tab->data); if (ntups == 0) { pgcc_log(PGCC_WARNING, "can't identify class IDs: no pg_class data\n"); return; } /* Find the pg_class columns we need. */ oid_column = PQfnumber(pg_class_tab->data, "oid"); relnamespace_column = PQfnumber(pg_class_tab->data, "relnamespace"); relname_column = PQfnumber(pg_class_tab->data, "relname"); if (oid_column == -1 || relname_column == -1 || relnamespace_column == -1) { pgcc_log(PGCC_WARNING, "can't identify class IDs: missing pg_class columns\n"); return; } /* Initialize map data structure. */ map = pg_malloc(sizeof(class_id_mapping_type) * map_size); /* Scan pg_class rows to construct mapping table. */ for (i = 0; i < ntups; ++i) { pg_catalog_table *tab; char *relnamespace; char *relname; /* Skip tables that are not part of the pg_catalog namespace. */ relnamespace = PQgetvalue(pg_class_tab->data, i, relnamespace_column); if (strcmp(relnamespace, "11") != 0) continue; /* See if it's a catalog table we know about. */ relname = PQgetvalue(pg_class_tab->data, i, relname_column); for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { /* Skip table if name does not match. */ if (strcmp(tab->table_name, relname) != 0) continue; /* Ignore matching table if it's not available. */ if (!tab->available) break; /* Ignore matching table if not keyed by OID. */ if (!table_key_is_oid(tab)) break; /* Increase map space if required. */ if (map_used >= map_size) { map_size *= 2; map = pg_realloc(map, sizeof(class_id_mapping_type) * map_size); } /* Create map entry. */ map[map_used].oid = PQgetvalue(pg_class_tab->data, i, oid_column); map[map_used].tab = tab; /* * Special bookkeeping for pg_class itself, due to its role in * checking sub-IDs. */ if (pg_class_tab == tab) pg_class_oid = map[map_used].oid; ++map_used; break; } } /* Avoid installing a bogus empty mapping. */ if (map_used == 0) { pgcc_log(PGCC_WARNING, "can't identify class IDs: no catalog tables found in pg_class\n"); return; } /* * Avoid installing mapping that doesn't include pg_class itself. * * This is probably a good sanity check on general principal, but what * truly makes it necessary is that sub-ID verification needs * pg_class_oid. */ if (pg_class_oid == NULL) { pgcc_log(PGCC_WARNING, "can't identify class IDs: pg_class not found in pg_class\n"); return; } /* Install new mapping table. */ class_id_mapping = map; num_class_id_mapping = map_used; } /* * Is oid the only key column for this table? */ static bool table_key_is_oid(pg_catalog_table *tab) { bool has_oid_key = false; bool has_other_key = false; pg_catalog_column *tabcol; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { if (!tabcol->is_key_column) continue; if (strcmp(tabcol->name, "oid") == 0) has_oid_key = true; else has_other_key = true; } return has_oid_key && !has_other_key; } /* * Cache per-column dependency checking information, basically column indexes * into the PGresult structure so that we can quickly find the class ID for * an object ID and the class and object ID for a sub-ID. */ static check_depend_cache * build_depend_cache(pg_catalog_table *tab, pg_catalog_column *tabcol) { check_depend_cache *cache; bool columns_missing = false; /* If we've already built the cache, just return the existing data. */ if (tabcol->check_private != NULL) return tabcol->check_private; /* Create and initialize the cache object. */ cache = pg_malloc0(sizeof(check_depend_cache)); cache->style = get_style(tab->table_name, tabcol->name); cache->is_broken = false; switch (cache->style) { case DEPEND_COLUMN_STYLE_OBJID: /* special case for pg_shdepend */ cache->database_result_column = PQfnumber(tab->data, "dbid"); cache->class_result_column = PQfnumber(tab->data, "classid"); cache->object_result_column = PQfnumber(tab->data, "objid"); break; case EDB_DDLTIME_COLUMN_STYLE_OBJID: cache->database_result_column = -1; cache->class_result_column = PQfnumber(tab->data, "classid"); cache->object_result_column = PQfnumber(tab->data, "objid"); break; case DEPEND_COLUMN_STYLE_REFOBJID: cache->database_result_column = -1; cache->class_result_column = PQfnumber(tab->data, "refclassid"); cache->object_result_column = PQfnumber(tab->data, "refobjid"); break; case DEPEND_COLUMN_STYLE_OBJOID: cache->database_result_column = -1; cache->class_result_column = PQfnumber(tab->data, "classoid"); cache->object_result_column = PQfnumber(tab->data, "objoid"); break; default: pgcc_log(PGCC_FATAL, "unexpected depend column style"); break; } /* Verify that PQfnumber() worked as expected. */ if (cache->class_result_column == -1) columns_missing = true; if (cache->object_result_column == -1) columns_missing = true; /* Look up the column number for the deptype column, if expected. */ if (cache->style == DEPEND_COLUMN_STYLE_OBJID) { cache->deptype_result_column = PQfnumber(tab->data, "deptype"); if (cache->deptype_result_column == -1) columns_missing = true; } /* * If we failed to find the relevant columns, then mark the cache as * broken, which will cause the individual rows not to be checked. * Otherwise, we'd end up having to emit a message for every row, which * would be too much chatter. */ if (columns_missing) { pgcc_log(PGCC_WARNING, "can't identify class IDs: columns missing from %s\n", tab->table_name); cache->is_broken = true; } /* * For the convenience of our callers, we also try to build the global * cache data here, namely the mappings from class IDs to pg_catalog_table * objects. Unlike the data stored in the cache object, these mappings * can be reused across all columns where we check dependencies. */ if (!class_id_mappings_attempted) build_class_id_mappings(); /* * If we failed to build the mappings, either now or previously, mark the * cache object as broken, to avoid log chatter for each separate row. */ if (class_id_mapping == NULL) cache->is_broken = true; /* * If needed, create hash table for duplicate-owner-dependency cheecking. */ if (!cache->is_broken && cache->database_result_column != -1 && cache->deptype_result_column != -1) { int keycols[3]; keycols[0] = cache->database_result_column; keycols[1] = cache->class_result_column; keycols[2] = cache->object_result_column; cache->duplicate_owner_ht = pgrhash_create(tab->data, 3, keycols); } /* We're done. */ tabcol->check_private = cache; return cache; } /* * Determine whether this dependency should be ignored because it's not * part of this database. This will only ever return true when we're checking * a table that has a dbid column, which currently means just pg_shdepend. */ static bool not_for_this_database(check_depend_cache *cache, pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { char *dbval; /* If there's no dbid column, then it's part of this database. */ if (cache->database_result_column == -1) return false; /* Look up the value in that column. */ dbval = PQgetvalue(tab->data, rownum, cache->database_result_column); /* 0 means it's a global object, so it's fine to check it here. */ if (strcmp(dbval, "0") == 0) return false; /* * If we don't know the database OID, skip the check, to avoid bogus * complaints. */ if (database_oid == NULL) return true; /* Straightforward comparison. */ return strcmp(database_oid, dbval) != 0; } /* * Determine which naming style applies to this table and column. * * There are three naming conventions that are used for references to objects * in arbitrary catalogs. pg_depend and pg_shdepend use classid/objid/objsubid * for one side of the dependency and refclassid/refobjid/refobjsubid for the * other. Other tables that contain similar information, such as * pg_description, pg_shdescription, pg_seclabel, and pg_shseclabel, use * objoid/classoid/objsubid. * * edb_last_ddl_time and edb_last_ddl_time_shared use objid/classid/objsubid * like pg_depend but need a separate enum value because they do not have a * deptype column, which prepare_to_check_dependency_class_id() would expect * if we used DEPEND_COLUMN_STYLE_OBJID. */ static depend_column_style get_style(char *table_name, char *column_name) { if (strncmp(column_name, "ref", 3) == 0) return DEPEND_COLUMN_STYLE_REFOBJID; else if (strstr(table_name, "depend")) return DEPEND_COLUMN_STYLE_OBJID; else if (strstr(table_name, "edb_last_ddl_time")) return EDB_DDLTIME_COLUMN_STYLE_OBJID; else return DEPEND_COLUMN_STYLE_OBJOID; } /* * Check whether a detected inconsistency is one that we were expecting. */ static bool check_for_exception(char *table_name, char *classval, char *objval) { exception_list *exc; if (!remote_is_edb || remote_version >= 90400) return false; if (remote_version >= 90300) exc = edb93_exception_list; else if (remote_version >= 90100) exc = edb91_92_exception_list; else if (remote_version >= 90000) exc = edb90_exception_list; else exc = edb84_exception_list; while (exc->table_name != NULL) { if (strcmp(exc->table_name, table_name) == 0 && strcmp(exc->class, classval) == 0 && strcmp(exc->object, objval) == 0) { pgcc_log(PGCC_DEBUG, "ignoring reference to class ID %s object ID %s in %s\n", exc->class, exc->object, exc->table_name); return true; } ++exc; } return false; } pg_catcheck-1.3.0/check_oids.c000066400000000000000000000126301417005477200162160ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * check_oids.c * * Many PostgreSQL system catalogs have OID, OID array, or OID vector * columns where each OID identifies a row in some other catalog table. * Although not marked as such, these are essentially foreign key * relationships. The code in this file aims to validate that every * object referenced in such a column actually exists. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" static void do_oid_check(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum, pg_catalog_check_oid * check_oid, pg_catalog_table *reftab, char *value); /* * Set up for an OID referential integrity check. * * We simply need to make sure that the referenced table will be available. */ void prepare_to_check_oid_reference(pg_catalog_table *tab, pg_catalog_column *tabcol) { pg_catalog_check_oid *check_oid = tabcol->check; pg_catalog_table *reftab; reftab = find_table_by_name(check_oid->oid_references_table); add_table_dependency(tab, reftab); } /* * Perform an OID referential integrity check. */ void check_oid_reference(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum) { pg_catalog_check_oid *check_oid = tabcol->check; char *val = PQgetvalue(tab->data, rownum, tabcol->result_column); pg_catalog_table *reftab; /* * Find the hash table we need in order to perform the check. * * Since find_table_by_name is O(n) in the number of catalog tables being * checked, we cache the result, so that we only need to do that work * once. */ if (tabcol->check_private == NULL) { reftab = find_table_by_name(check_oid->oid_references_table); tabcol->check_private = reftab; } else reftab = tabcol->check_private; /* * The table might not be available in this server version, or we might * have failed to read it. There's actually one real case where the * referenced table was adding later than referring table: pg_largeobject * has existed for a long time, but pg_largeobject_metadata is newer. */ if (!reftab->ht) return; switch (check_oid->type) { case CHECK_OID_REFERENCE: /* * Simple OID reference. Easy! * * We don't use do do_oid_check() here because the error message * is a little different in this case. */ { if (check_oid->zero_oid_ok && strcmp(val, "0") == 0) return; if (pgrhash_get(reftab->ht, &val) == -1) pgcc_report(tab, tabcol, rownum, "no matching entry in %s\n", reftab->table_name); } break; case CHECK_OID_VECTOR_REFERENCE: /* Space-separated list of values. */ { char *s = val; char buf[32]; for (;;) { /* Find next word boundary. */ while (*s != '\0' && *s != ' ') ++s; /* If it's the last word, we're done here! */ if (*s == '\0') { if (s > val) { /* If last entry is non-empty, check it. */ do_oid_check(tab, tabcol, rownum, check_oid, reftab, val); } break; } /* Make a copy of the data we need to check. */ if (s - val >= sizeof buf) { /* OIDs can't be this long. */ pgcc_report(tab, tabcol, rownum, "contains a token of %ld characters\n", s - val); return; } memcpy(buf, val, s - val); buf[s - val] = '\0'; /* Check it and move ahead one character. */ do_oid_check(tab, tabcol, rownum, check_oid, reftab, buf); val = ++s; } } break; case CHECK_OID_ARRAY_REFERENCE: /* Opening curly brace, comma-separated values, closing brace. */ { char *s = val; char buf[32]; bool bad = false; /* Allow a completely empty field. */ if (*s == '\0') break; /* Otherwise, expect the opening delimeter. */ if (*s == '{') val = ++s; else bad = true; while (!bad) { /* Find next delimeter. */ while (*s != '\0' && *s != ',' && *s != '}') ++s; /* * If we hit '\0' before '}', that's bad; and if we hit * two consecutive delimeters, that's also bad. */ if (val == s || *s == '\0') { bad = true; break; } /* Make a copy of the data we need to check. */ if (s - val >= sizeof buf) { /* OIDs can't be this long. */ pgcc_report(tab, tabcol, rownum, "contains a token of %ld characters\n", s - val); return; } memcpy(buf, val, s - val); buf[s - val] = '\0'; /* Check it. */ if (!check_oid->zero_oid_ok || strcmp(buf, "0") != 0) do_oid_check(tab, tabcol, rownum, check_oid, reftab, buf); /* Expect end of string if at '}'. */ if (*s == '}') { val = ++s; if (*s != '\0') bad = true; break; } /* Skip comma and continue. */ val = ++s; } if (bad) pgcc_report(tab, tabcol, rownum, "not a valid 1-D array"); } break; default: Assert(false); break; } } /* * Check one of possibly several OIDs found in a single column. */ static void do_oid_check(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum, pg_catalog_check_oid * check_oid, pg_catalog_table *reftab, char *value) { if (check_oid->zero_oid_ok && strcmp(value, "0") == 0) return; if (pgrhash_get(reftab->ht, &value) == -1) pgcc_report(tab, tabcol, rownum, "\"%s\" not found in %s\n", value, reftab->table_name); } pg_catcheck-1.3.0/compat.c000066400000000000000000000027731417005477200154150ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * compat.c * compatibility definitions for older server versions * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" #if PG_VERSION_NUM < 90300 /* 9.3 and higher have this in fe_memutils.c */ void * pg_malloc(size_t size) { void *tmp; /* Avoid unportable behavior of malloc(0) */ if (size == 0) size = 1; tmp = malloc(size); if (!tmp) { fprintf(stderr, _("out of memory\n")); exit(EXIT_FAILURE); } return tmp; } /* 9.3 and higher have this in fe_memutils.c */ void * pg_malloc0(size_t size) { void *tmp; tmp = pg_malloc(size); MemSet(tmp, 0, size); return tmp; } /* 9.3 and higher have this in fe_memutils.c */ void * pg_realloc(void *ptr, size_t size) { void *tmp; /* Avoid unportable behavior of realloc(NULL, 0) */ if (ptr == NULL && size == 0) size = 1; tmp = realloc(ptr, size); if (!tmp) { fprintf(stderr, _("out of memory\n")); exit(EXIT_FAILURE); } return tmp; } /* 9.3 and higher have this in fe_memutils.c */ char * pg_strdup(const char *in) { char *tmp; if (!in) { fprintf(stderr, _("cannot duplicate null pointer (internal error)\n")); exit(EXIT_FAILURE); } tmp = strdup(in); if (!tmp) { fprintf(stderr, _("out of memory\n")); exit(EXIT_FAILURE); } return tmp; } /* 9.3 and higher have this in fe_memutils.c */ void pg_free(void *ptr) { if (ptr != NULL) free(ptr); } #endif pg_catcheck-1.3.0/compat.h000066400000000000000000000017461417005477200154210ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * compat.h * * Compatibility with older PostgreSQL source trees. * *------------------------------------------------------------------------- */ #ifndef COMPAT_H #define COMPAT_H #if PG_VERSION_NUM < 90300 /* Just disable assertions for builds against older source trees. */ #define Assert(p) /* Convenience routines for frontend memory allocation were added in 9.3. */ extern char *pg_strdup(const char *in); extern void *pg_malloc(size_t size); extern void *pg_malloc0(size_t size); extern void *pg_realloc(void *pointer, size_t size); extern void pg_free(void *pointer); #endif /* PG_VERSION_NUM < 90300 */ #if PG_VERSION_NUM < 90100 /* * PG_PRINTF_ATTRIBUTE is essential for suppressing compiler warning from * printf-like functions, but it wasn't added until 9.1. */ #ifdef WIN32 #define PG_PRINTF_ATTRIBUTE gnu_printf #else #define PG_PRINTF_ATTRIBUTE printf #endif #endif #endif /* COMPAT_H */ pg_catcheck-1.3.0/definitions.c000066400000000000000000001124221417005477200164360ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * definitions.c * * This file defines the data structures that drive our checking * strategies. We define the names of each column, the versions to which * it applies, whether or not it forms part of the table's key, whether it * should be included in diagnostics regarding that table, and, if * applicable, the type of check that should be performed on it. * * Some columns, such as OID columns, are included even though no check * is defined. This is because they're part of the key: some other table * might contain that OID, and we'll need to look it up in the referenced * table. Note that we don't bother defining the key for all tables that * have one; even if a table has a unique key, there's no point in * building a hash table to allow lookups into that table by key unless * we require the ability to perform suchh lookups. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" struct pg_catalog_check_oid check_am_oid = {CHECK_OID_REFERENCE, false, "pg_am"}; struct pg_catalog_check_oid check_am_optional_oid = {CHECK_OID_REFERENCE, true, "pg_am"}; struct pg_catalog_check_oid check_attnum_value = {CHECK_ATTNUM}; struct pg_catalog_check_oid check_authid_oid = {CHECK_OID_REFERENCE, false, "pg_authid"}; struct pg_catalog_check_oid check_authid_oid_array_zero_ok = {CHECK_OID_ARRAY_REFERENCE, true, "pg_authid"}; struct pg_catalog_check_oid check_authid_optional_oid = {CHECK_OID_REFERENCE, true, "pg_authid"}; struct pg_catalog_check_oid check_class_oid = {CHECK_OID_REFERENCE, false, "pg_class"}; struct pg_catalog_check_oid check_class_oid_array = {CHECK_OID_ARRAY_REFERENCE, false, "pg_class"}; struct pg_catalog_check_oid check_class_optional_oid = {CHECK_OID_REFERENCE, true, "pg_class"}; struct pg_catalog_check_oid check_constraint_oid = {CHECK_OID_REFERENCE, false, "pg_constraint"}; struct pg_catalog_check_oid check_collation_optional_oid = {CHECK_OID_REFERENCE, true, "pg_collation"}; struct pg_catalog_check_oid check_collation_optional_oid_vector = {CHECK_OID_VECTOR_REFERENCE, true, "pg_collation"}; struct pg_catalog_check_oid check_constraint_optional_oid = {CHECK_OID_REFERENCE, true, "pg_constraint"}; struct pg_catalog_check_oid check_database_optional_oid = {CHECK_OID_REFERENCE, true, "pg_database"}; struct pg_catalog_check check_dependency_id_value = {CHECK_DEPENDENCY_ID}; struct pg_catalog_check check_dependency_class_id_value = {CHECK_DEPENDENCY_CLASS_ID}; struct pg_catalog_check check_dependency_subid_value = {CHECK_DEPENDENCY_SUBID}; struct pg_catalog_check_oid check_edb_partdef = {CHECK_OID_REFERENCE, false, "edb_partdef"}; struct pg_catalog_check_oid check_edb_partition_optional_oid = {CHECK_OID_REFERENCE, true, "edb_partition"}; struct pg_catalog_check_oid check_foreign_data_wrapper_oid = {CHECK_OID_REFERENCE, false, "pg_foreign_data_wrapper"}; struct pg_catalog_check_oid check_foreign_server_oid = {CHECK_OID_REFERENCE, false, "pg_foreign_server"}; struct pg_catalog_check_oid check_foreign_server_optional_oid = {CHECK_OID_REFERENCE, true, "pg_foreign_server"}; struct pg_catalog_check_oid check_index_optional_oid = {CHECK_OID_REFERENCE, true, "pg_index"}; struct pg_catalog_check_oid check_language_oid = {CHECK_OID_REFERENCE, false, "pg_language"}; struct pg_catalog_check_oid check_largeobject_metadata_oid = {CHECK_OID_REFERENCE, false, "pg_largeobject_metadata"}; struct pg_catalog_check_oid check_namespace_oid = {CHECK_OID_REFERENCE, false, "pg_namespace"}; struct pg_catalog_check_oid check_namespace_optional_oid = {CHECK_OID_REFERENCE, true, "pg_namespace"}; struct pg_catalog_check_oid check_opclass_oid = {CHECK_OID_REFERENCE, false, "pg_opclass"}; struct pg_catalog_check_oid check_opclass_oid_vector = {CHECK_OID_VECTOR_REFERENCE, false, "pg_opclass"}; struct pg_catalog_check_oid check_operator_oid = {CHECK_OID_REFERENCE, false, "pg_operator"}; struct pg_catalog_check_oid check_operator_optional_oid = {CHECK_OID_REFERENCE, true, "pg_operator"}; struct pg_catalog_check_oid check_operator_oid_array = {CHECK_OID_ARRAY_REFERENCE, false, "pg_operator"}; struct pg_catalog_check_oid check_opfamily_oid = {CHECK_OID_REFERENCE, false, "pg_opfamily"}; struct pg_catalog_check_oid check_opfamily_optional_oid = {CHECK_OID_REFERENCE, true, "pg_opfamily"}; struct pg_catalog_check_oid check_proc_oid = {CHECK_OID_REFERENCE, false, "pg_proc"}; struct pg_catalog_check_oid check_proc_optional_oid = {CHECK_OID_REFERENCE, true, "pg_proc"}; struct pg_catalog_check_oid check_profile_oid = {CHECK_OID_REFERENCE, true, "edb_profile"}; struct pg_catalog_check_oid check_relnatts_value = {CHECK_RELNATTS}; struct pg_catalog_check_oid check_tablespace_oid = {CHECK_OID_REFERENCE, false, "pg_tablespace"}; struct pg_catalog_check_oid check_tablespace_optional_oid = {CHECK_OID_REFERENCE, true, "pg_tablespace"}; struct pg_catalog_check_oid check_ts_config_oid = {CHECK_OID_REFERENCE, true, "pg_ts_config"}; struct pg_catalog_check_oid check_ts_dict_oid = {CHECK_OID_REFERENCE, true, "pg_ts_dict"}; struct pg_catalog_check_oid check_ts_parser_oid = {CHECK_OID_REFERENCE, true, "pg_ts_parser"}; struct pg_catalog_check_oid check_ts_template_oid = {CHECK_OID_REFERENCE, true, "pg_ts_template"}; struct pg_catalog_check_oid check_type_oid = {CHECK_OID_REFERENCE, false, "pg_type"}; struct pg_catalog_check_oid check_type_oid_array = {CHECK_OID_ARRAY_REFERENCE, false, "pg_type"}; struct pg_catalog_check_oid check_type_oid_vector = {CHECK_OID_VECTOR_REFERENCE, false, "pg_type"}; struct pg_catalog_check_oid check_type_optional_oid = {CHECK_OID_REFERENCE, true, "pg_type"}; struct pg_catalog_check_oid check_queue_oid = {CHECK_OID_REFERENCE, false, "edb_queue"}; struct pg_catalog_check_oid check_collation_oid = {CHECK_OID_REFERENCE, false, "pg_collation"}; struct pg_catalog_check_oid check_publication_oid = {CHECK_OID_REFERENCE, false, "pg_publication"}; struct pg_catalog_check_oid check_database_oid = {CHECK_OID_REFERENCE, false, "pg_database"}; struct pg_catalog_check_oid check_subscription_oid = {CHECK_OID_REFERENCE, false, "pg_subscription"}; struct pg_catalog_check_oid check_redaction_policy_oid = {CHECK_OID_REFERENCE, false, "edb_redaction_policy"}; struct pg_catalog_check_oid check_statistic_ext_oid = {CHECK_OID_REFERENCE, false, "pg_statistic_ext"}; struct pg_catalog_check_oid check_trigger_optional_oid = {CHECK_OID_REFERENCE, true, "pg_trigger"}; /* pg_catalog_table & pg_catalog_column */ struct pg_catalog_column pg_class_column[] = { /* pg_class */ {"oid", NULL, 0, 0, false, true, true}, {"relowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"relnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"relname", NULL, 0, 0, false, false, true}, {"reltype", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"reloftype", NULL, 90000, 0, false, false, false, &check_type_optional_oid}, {"relkind", NULL, 0, 0, false, false, true}, {"relam", NULL, 0, 0, false, false, false, &check_am_optional_oid}, {"relnatts", NULL, 0, 0, false, false, false, &check_relnatts_value}, {"reltablespace", NULL, 0, 0, false, false, false, &check_tablespace_optional_oid}, {"reltoastrelid", NULL, 0, 0, false, false, false, &check_class_optional_oid}, {NULL} }; struct pg_catalog_column pg_namespace_column[] = { /* pg_namespace */ {"oid", NULL, 0, 0, false, true, true}, {"nspname", NULL, 0, 0, false, false, true}, {"nspowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"nspparent", NULL, 0, 0, true, false, false, &check_namespace_optional_oid}, {"nspobjecttype", NULL, 90200, 0, true, false, false, &check_type_optional_oid}, {"nspforeignserver", NULL, 0, 0, true, false, false, &check_foreign_server_optional_oid}, {NULL} }; struct pg_catalog_column pg_authid_column[] = { /* pg_authid */ {"oid", NULL, 0, 0, false, true, true}, {"rolname", NULL, 0, 0, false, false, true}, {"rolprofile", NULL, 90500, 0, true, false, false, &check_profile_oid}, {NULL} }; struct pg_catalog_column pg_tablespace_column[] = { /* pg_tablespace */ {"oid", NULL, 0, 0, false, true, true}, {"spcname", NULL, 0, 0, false, false, false}, {"spcowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_type_column[] = { /* pg_type */ {"oid", NULL, 0, 0, false, true, true}, {"typname", NULL, 0, 0, false, false, false}, {"typowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"typnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"typrelid", NULL, 0, 0, false, false, false, &check_class_optional_oid}, {"typelem", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"typarray", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"typbasetype", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"typcollation", NULL, 90100, 0, false, false, false, &check_collation_optional_oid}, {NULL} }; struct pg_catalog_column pg_am_column[] = { /* pg_am */ {"oid", NULL, 0, 0, false, true, true}, {"amkeytype", NULL, 0, 90599, false, false, false, &check_type_optional_oid}, {NULL} }; struct pg_catalog_column pg_collation_column[] = { /* pg_collation */ {"oid", NULL, 90100, 0, false, true, true}, {"collnamespace", NULL, 90100, 0, false, false, false, &check_namespace_oid}, {"collowner", NULL, 90100, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_proc_column[] = { /* pg_proc */ {"oid", NULL, 0, 0, false, true, true}, {"pronamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"proowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"prolang", NULL, 0, 0, false, false, false, &check_language_oid}, {"provariadic", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"prorettype", NULL, 0, 0, false, false, false, &check_type_oid}, {"proargtypes", NULL, 0, 0, false, false, false, &check_type_oid_vector}, {"proallargtypes", NULL, 0, 0, false, false, false, &check_type_oid_array}, {NULL} }; struct pg_catalog_column pg_language_column[] = { /* pg_language */ {"oid", NULL, 0, 0, false, true, true}, {"lanowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"lanplcallfoid", NULL, 0, 0, false, false, false}, {"laninline", NULL, 90000, 0, false, false, false}, {"lanvalidator", NULL, 0, 0, false, false, false}, {NULL} }; struct pg_catalog_column pg_index_column[] = { /* pg_index */ {"indexrelid", NULL, 0, 0, false, true, true}, {"indrelid", NULL, 0, 0, false, false, false, &check_class_oid}, {"indcollation", NULL, 90100, 0, false, false, false, &check_collation_optional_oid_vector}, {"indclass", NULL, 0, 0, false, false, false, &check_opclass_oid_vector}, {NULL} }; struct pg_catalog_column pg_constraint_column[] = { /* pg_constraint */ {"oid", NULL, 0, 0, false, true, true}, {"conname", NULL, 0, 0, false, false, false}, {"connamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"conrelid", NULL, 0, 0, false, false, false, &check_class_optional_oid}, {"contypid", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"conindid", NULL, 90000, 0, false, false, false, &check_index_optional_oid}, {"confrelid", NULL, 0, 0, false, false, false, &check_class_optional_oid}, {"conpfeqop", NULL, 0, 0, false, false, false, &check_operator_oid_array}, {"conppeqop", NULL, 0, 0, false, false, false, &check_operator_oid_array}, {"conffeqop", NULL, 0, 0, false, false, false, &check_operator_oid_array}, {"conexclop", NULL, 90000, 0, false, false, false, &check_operator_oid_array}, {"conparentid", NULL, 110000, 0, false, false, false, &check_constraint_optional_oid}, {NULL} }; struct pg_catalog_column pg_database_column[] = { /* pg_database */ {"oid", NULL, 0, 0, false, true, true}, {"datname", NULL, 0, 0, false, false, false}, {"datdba", NULL, 0, 0, false, false, false, &check_authid_oid}, {"dattablespace", NULL, 0, 0, false, false, false, &check_tablespace_oid}, {NULL} }; struct pg_catalog_column pg_cast_column[] = { /* pg_cast */ {"oid", NULL, 0, 0, false, true, true}, {"castsource", NULL, 0, 0, false, false, false, &check_type_oid}, {"casttarget", NULL, 0, 0, false, false, false, &check_type_oid}, {"castfunc", NULL, 0, 0, false, false, false, &check_proc_optional_oid}, {NULL} }; struct pg_catalog_column pg_conversion_column[] = { /* pg_conversion */ {"oid", NULL, 0, 0, false, true, true}, {"connamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"conowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"conproc", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column pg_extension_column[] = { /* pg_extension */ {"oid", NULL, 90100, 0, false, true, true}, {"extowner", NULL, 90100, 0, false, false, false, &check_authid_oid}, {"extnamespace", NULL, 90100, 0, false, false, false, &check_namespace_oid}, {"extconfig", NULL, 90100, 0, false, false, false, &check_class_oid_array}, {NULL} }; struct pg_catalog_column pg_enum_column[] = { /* pg_enum */ {"oid", NULL, 0, 0, false, true, true}, {"enumtypid", NULL, 0, 0, false, false, false, &check_type_oid}, {NULL} }; struct pg_catalog_column pg_trigger_column[] = { /* pg_trigger */ {"oid", NULL, 0, 0, false, true, true}, {"tgpackageoid", NULL, 120000, 0, true, false, false, &check_namespace_optional_oid}, {"tgparentid", NULL, 130000, 0, false, false, false, &check_trigger_optional_oid}, {"tgrelid", NULL, 0, 0, false, false, false, &check_class_oid}, {"tgfoid", NULL, 0, 0, false, false, false, &check_proc_optional_oid}, {"tgconstrrelid", NULL, 0, 0, false, false, false, &check_class_optional_oid}, {"tgconstrindid", NULL, 90000, 0, false, false, false, &check_index_optional_oid}, {"tgconstraint", NULL, 0, 0, false, false, false, &check_constraint_optional_oid}, {NULL} }; struct pg_catalog_column pg_ts_parser_column[] = { /* pg_ts_parser */ {"oid", NULL, 0, 0, false, true, true}, {"prsnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"prsstart", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {"prstoken", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {"prsend", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {"prsheadline", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {"prslextype", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column pg_ts_config_column[] = { /* pg_ts_config */ {"oid", NULL, 0, 0, false, true, true}, {"cfgowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"cfgnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"cfgparser", NULL, 0, 0, false, false, false, &check_ts_parser_oid}, {NULL} }; struct pg_catalog_column pg_ts_template_column[] = { /* pg_ts_template */ {"oid", NULL, 0, 0, false, true, true}, {"tmplnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"tmplinit", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {"tmpllexize", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column pg_ts_dict_column[] = { /* pg_ts_dict */ {"oid", NULL, 0, 0, false, true, true}, {"dictnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"dictowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"dicttemplate", NULL, 0, 0, false, false, false, &check_ts_template_oid}, {NULL} }; struct pg_catalog_column pg_fdw_column[] = { /* pg_foreign_data_wrapper */ {"oid", NULL, 0, 0, false, true, true}, {"fdwowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"fdwhandler", NULL, 90100, 0, false, false, false, &check_proc_optional_oid}, {"fdwvalidator", NULL, 0, 0, false, false, false, &check_proc_optional_oid}, {NULL} }; struct pg_catalog_column pg_foreign_server_column[] = { /* pg_foreign_server */ {"oid", NULL, 0, 0, false, true, true}, {"srvowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"srvfdw", NULL, 0, 0, false, false, false, &check_foreign_data_wrapper_oid}, {NULL} }; struct pg_catalog_column pg_user_mapping_column[] = { /* pg_user_mapping */ {"oid", NULL, 0, 0, false, true, true}, {"umuser", NULL, 0, 0, false, false, false, &check_authid_optional_oid}, {"umserver", NULL, 0, 0, false, false, false, &check_foreign_server_oid}, {NULL} }; struct pg_catalog_column pg_foreign_table_column[] = { /* pg_foreign_table */ {"ftrelid", NULL, 90100, 0, false, true, true, &check_class_oid}, {"ftserver", NULL, 90100, 0, false, false, false, &check_foreign_server_oid}, {NULL} }; struct pg_catalog_column pg_event_trigger_column[] = { /* pg_event_trigger */ {"oid", NULL, 90300, 0, false, true, true}, {"evtowner", NULL, 90300, 0, false, false, false, &check_authid_oid}, {"evtfoid", NULL, 90300, 0, false, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column pg_opfamily_column[] = { /* pg_opfamily */ {"oid", NULL, 0, 0, false, true, true}, {"opfname", NULL, 0, 0, false, false, false}, {"opfmethod", NULL, 0, 0, false, false, false, &check_am_oid}, {"opfnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"opfowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_opclass_column[] = { /* pg_opclass */ {"oid", NULL, 0, 0, false, true, true}, {"opcname", NULL, 0, 0, false, false, false}, {"opcmethod", NULL, 0, 0, false, false, false, &check_am_oid}, {"opcnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"opcowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"opcfamily", NULL, 0, 0, false, false, false, &check_opfamily_oid}, {"opcintype", NULL, 0, 0, false, false, false, &check_type_oid}, {"opckeytype", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {NULL} }; struct pg_catalog_column pg_operator_column[] = { /* pg_operator */ {"oid", NULL, 0, 0, false, true, true}, {"oprname", NULL, 0, 0, false, false, false}, {"oprnamespace", NULL, 0, 0, false, false, false, &check_namespace_oid}, {"oprowner", NULL, 0, 0, false, false, false, &check_authid_oid}, {"oprleft", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"oprright", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"oprresult", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"oprcom", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"oprnegate", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"oprcode", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_optional_oid}, {"oprrest", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_optional_oid}, {"oprjoin", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_optional_oid}, {NULL} }; struct pg_catalog_column pg_amop_column[] = { /* pg_amop */ {"oid", NULL, 0, 0, false, true, true}, {"amopfamily", NULL, 0, 0, false, false, false, &check_opfamily_oid}, {"amoplefttype", NULL, 0, 0, false, false, false, &check_type_oid}, {"amoprighttype", NULL, 0, 0, false, false, false, &check_type_oid}, {"amopopr", NULL, 0, 0, false, false, false, &check_operator_oid}, {"amopmethod", NULL, 0, 0, false, false, false, &check_am_oid}, {"amopsortfamily", NULL, 90100, 0, false, false, false, &check_opfamily_optional_oid}, {NULL} }; struct pg_catalog_column pg_amproc_column[] = { /* pg_amproc */ {"oid", NULL, 0, 0, false, true, true}, {"amprocfamily", NULL, 0, 0, false, false, false, &check_opfamily_oid}, {"amproclefttype", NULL, 0, 0, false, false, false, &check_type_oid}, {"amprocrighttype", NULL, 0, 0, false, false, false, &check_type_oid}, {"amproc", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column pg_default_acl_column[] = { /* pg_default_acl */ {"oid", NULL, 90000, 0, false, true, true}, {"defaclnamespace", NULL, 90000, 0, false, false, false, &check_namespace_optional_oid}, {"defaclrole", NULL, 90000, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_rewrite_column[] = { /* pg_rewrite_column */ {"oid", NULL, 0, 0, false, true, true}, {"rulename", NULL, 0, 0, false, false, false}, {"ev_class", NULL, 0, 0, false, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column edb_variable_column[] = { /* edb_variable_column */ {"oid", NULL, 0, 0, true, true, true}, {"varpackage", NULL, 0, 0, true, false, false, &check_namespace_oid}, {"vartype", NULL, 0, 0, true, false, false, &check_type_optional_oid}, {NULL} }; struct pg_catalog_column pg_inherits_column[] = { /* pg_inherits */ {"inhrelid", NULL, 0, 0, false, true, true, &check_class_oid}, {"inhparent", NULL, 0, 0, false, true, true, &check_class_oid}, {NULL} }; struct pg_catalog_column pg_largeobject_metadata_column[] = { /* pg_largeobject_metadata */ {"oid", NULL, 90000, 0, false, true, true}, {"lomowner", NULL, 90000, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_largeobject_column[] = { /* pg_largeobject */ {"loid", NULL, 0, 0, false, true, true, &check_largeobject_metadata_oid}, {"pageno", NULL, 0, 0, false, true, true}, {NULL} }; struct pg_catalog_column pg_aggregate_column[] = { /* pg_aggregate */ {"aggfnoid", "pg_catalog.oid", 0, 0, false, true, true, &check_proc_oid}, {"aggtransfn", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_oid}, {"aggfinalfn", "pg_catalog.oid", 0, 0, false, false, false, &check_proc_optional_oid}, {"aggsortop", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"aggtranstype", NULL, 0, 0, false, false, false, &check_type_oid}, {NULL} }; struct pg_catalog_column pg_ts_config_map_column[] = { /* pg_ts_config_map */ {"mapcfg", NULL, 0, 0, false, true, true, &check_ts_config_oid}, {"maptokentype", NULL, 0, 0, false, true, true}, {"mapseqno", NULL, 0, 0, false, true, true}, {"mapdict", NULL, 0, 0, false, false, false, &check_ts_dict_oid}, {NULL} }; struct pg_catalog_column pg_range_column[] = { /* pg_range */ {"rngtypid", NULL, 90200, 0, false, true, true, &check_type_oid}, {"rngsubtype", NULL, 90200, 0, false, false, false, &check_type_oid}, {"rngcollation", NULL, 90200, 0, false, false, false, &check_collation_optional_oid}, {"rngsubopc", NULL, 90200, 0, false, false, false, &check_opclass_oid}, {"rngcanonical", "pg_catalog.oid", 90200, 0, false, false, false, &check_proc_optional_oid}, {"rngsubdiff", "pg_catalog.oid", 90200, 0, false, false, false, &check_proc_optional_oid}, {"rngmultitypid", NULL, 140000, 0, false, false, false, &check_type_oid}, {NULL} }; struct pg_catalog_column pg_attribute_column[] = { /* pg_attribute */ {"attrelid", NULL, 0, 0, false, true, true, &check_class_oid}, {"attname", NULL, 0, 0, false, false, true}, {"attnum", NULL, 0, 0, false, true, true, &check_attnum_value}, {"atttypid", NULL, 0, 0, false, false, false, &check_type_optional_oid}, {"attcollation", NULL, 90100, 0, false, false, false, &check_collation_optional_oid}, {NULL} }; struct pg_catalog_column pg_statistic_column[] = { /* pg_statistic */ {"starelid", NULL, 0, 0, false, true, true, &check_class_oid}, {"staattnum", NULL, 0, 0, false, true, true}, {"stainherit", NULL, 90000, 0, false, true, true}, {"staop1", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"staop2", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"staop3", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"staop4", NULL, 0, 0, false, false, false, &check_operator_optional_oid}, {"stacoll1", NULL, 120000, 0, false, false, false, &check_collation_optional_oid}, {"stacoll2", NULL, 120000, 0, false, false, false, &check_collation_optional_oid}, {"stacoll3", NULL, 120000, 0, false, false, false, &check_collation_optional_oid}, {"stacoll4", NULL, 120000, 0, false, false, false, &check_collation_optional_oid}, {NULL} }; struct pg_catalog_column pg_db_role_setting_column[] = { /* pg_db_role_setting */ {"setdatabase", NULL, 90000, 0, false, true, true, &check_database_optional_oid}, {"setrole", NULL, 90000, 0, false, true, true, &check_authid_optional_oid}, {NULL} }; struct pg_catalog_column pg_attrdef_column[] = { /* pg_attrdef */ {"oid", NULL, 0, 0, false, true, true}, {"adrelid", NULL, 0, 0, false, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column edb_dir_column[] = { /* edb_dir */ {"oid", NULL, 0, 0, true, true, true}, {"dirowner", NULL, 0, 0, true, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column edb_partdef_column[] = { /* edb_partdef */ {"oid", NULL, 90100, 90699, true, true, true}, {"pdefrel", NULL, 90100, 90699, true, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column edb_partition_column[] = { /* edb_partition */ {"oid", NULL, 90100, 90699, true, true, true}, {"partpdefid", NULL, 90100, 90699, true, false, false, &check_edb_partdef}, {"partrelid", NULL, 90100, 90699, true, false, false, &check_class_oid}, {"partparent", NULL, 90100, 90699, true, false, false, &check_edb_partition_optional_oid}, {"partcons", NULL, 90100, 90699, true, false, false, &check_constraint_oid}, {NULL} }; /* * policyobject was originally envisioned to point either to a pg_class OID * or a pg_synonym OID depending on policykind, but the pg_synonym support was * never implemented. So for now, we can just check that it's a pg_class OID. */ struct pg_catalog_column edb_policy_column[] = { /* edb_policy */ {"oid", NULL, 90100, 0, true, true, true}, {"policygroup", NULL, 90100, 0, true, false, false}, {"policyobject", NULL, 90100, 0, true, false, false, &check_class_oid}, {"policyproc", NULL, 90100, 0, true, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column pg_synonym_column[] = { /* pg_synonym */ {"oid", NULL, 0, 0, true, true, true}, {"synnamespace", NULL, 0, 0, true, false, false, &check_namespace_optional_oid}, {"synowner", NULL, 0, 0, true, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_depend_column[] = { /* pg_depend */ {"classid", NULL, 0, 0, false, false, true, &check_dependency_class_id_value}, {"objid", NULL, 0, 0, false, false, true, &check_dependency_id_value}, {"objsubid", NULL, 0, 0, false, false, true, &check_dependency_subid_value}, {"refclassid", NULL, 0, 0, false, false, true, &check_dependency_class_id_value}, {"refobjid", NULL, 0, 0, false, false, true, &check_dependency_id_value}, {"refobjsubid", NULL, 0, 0, false, false, true, &check_dependency_subid_value}, {"deptype", NULL, 0, 0, false, false, true}, {NULL} }; struct pg_catalog_column pg_shdepend_column[] = { /* pg_shdepend */ {"dbid", NULL, 0, 0, false, false, true, &check_database_optional_oid}, {"classid", NULL, 0, 0, false, false, true, &check_dependency_class_id_value}, {"objid", NULL, 0, 0, false, false, true, &check_dependency_id_value}, {"objsubid", NULL, 0, 0, false, false, true, &check_dependency_subid_value}, {"refclassid", NULL, 0, 0, false, false, true, &check_dependency_class_id_value}, {"refobjid", NULL, 0, 0, false, false, true, &check_dependency_id_value}, {"deptype", NULL, 0, 0, false, false, true}, {NULL} }; struct pg_catalog_column pg_description_column[] = { /* pg_description */ {"classoid", NULL, 0, 0, false, false, true, &check_dependency_class_id_value}, {"objoid", NULL, 0, 0, false, false, true, &check_dependency_id_value}, {"objsubid", NULL, 0, 0, false, false, true, &check_dependency_subid_value}, {NULL} }; struct pg_catalog_column pg_shdescription_column[] = { /* pg_shdescription */ {"classoid", NULL, 0, 0, false, false, true, &check_dependency_class_id_value}, {"objoid", NULL, 0, 0, false, false, true, &check_dependency_id_value}, {NULL} }; struct pg_catalog_column pg_seclabel_column[] = { /* pg_seclabel */ {"classoid", NULL, 90100, 0, false, false, true, &check_dependency_class_id_value}, {"objoid", NULL, 90100, 0, false, false, true, &check_dependency_id_value}, {"objsubid", NULL, 90100, 0, false, false, true, &check_dependency_subid_value}, {"provider", NULL, 90100, 0, false, false, true}, {NULL} }; struct pg_catalog_column pg_shseclabel_column[] = { /* pg_shseclabel */ {"classoid", NULL, 90200, 0, false, false, true, &check_dependency_class_id_value}, {"objoid", NULL, 90200, 0, false, false, true, &check_dependency_id_value}, {"provider", NULL, 90200, 0, false, false, true, NULL}, {NULL} }; struct pg_catalog_column pg_auth_members_column[] = { {"roleid", NULL, 0, 0, false, false, true, &check_authid_oid}, {"member", NULL, 0, 0, false, false, true, &check_authid_oid}, {"grantor", NULL, 0, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_policy_column[] = { {"oid", NULL, 90500, 0, false, true, true}, {"polname", NULL, 90500, 0, false, false, false}, {"polrelid", NULL, 90500, 0, false, false, false, &check_class_oid}, {"polroles", NULL, 90500, 0, false, false, false, &check_authid_oid_array_zero_ok}, {NULL} }; struct pg_catalog_column edb_profile_column [] = { {"oid", NULL, 90500, 0, true, true, true}, {"prfname", NULL, 90500, 0, true, false, true}, {NULL} }; struct pg_catalog_column edb_queue_table_column[] = { {"oid", NULL, 90600, 0, true, true, true}, {"qtname", NULL, 90600, 0, true, false, true}, {"qtnamespace", NULL, 90600, 0, true, false, false, &check_namespace_oid}, {"qtrelid", NULL, 90600, 0, true, false, false, &check_class_oid}, {"qpayloadtype", NULL, 90600, 0, true, false, false, &check_type_oid}, {NULL} }; struct pg_catalog_column edb_queue_column [] = { {"oid", NULL, 90600, 0, true, true, true}, {"aqname", NULL, 90600, 0, true, false, true}, {"aqrelid", NULL, 90600, 0, true, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column edb_password_history_column[] = { /* edb_password_history */ {"passhistroleid", NULL, 90500, 0, true, true, true, &check_authid_oid}, {"passhistpassword", NULL, 90500, 0, true, true, true}, {"passhistpasswordsetat", NULL, 90500, 0, true, false, true}, {NULL} }; struct pg_catalog_column edb_queue_callback_column[] = { /* edb_queue_callback */ {"oid", NULL, 90600, 0, true, true, true}, {"qcbqueueid", NULL, 90600, 0, true, false, true, &check_queue_oid}, {"qcbowner", NULL, 90600, 0, true, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column edb_resource_group_column[] = { /* edb_resource_group */ {"oid", NULL, 90400, 0, true, true, true}, {"rgrpname", NULL, 90400, 0, true, false, true}, {NULL} }; struct pg_catalog_column pg_init_privs_column[] = { /* pg_init_privs */ {"objoid", NULL, 90600, 0, false, true, true}, {"classoid", NULL, 90600, 0, false, true, true, &check_class_oid}, {"objsubid", NULL, 90600, 0, false, true, true}, {NULL} }; struct pg_catalog_column pg_partitioned_table_column[] = { /* pg_partitioned_table */ {"partrelid", NULL, 100000, 0, false, true, true, &check_class_oid}, {"partdefid", NULL, 110000, 0, false, false, false, &check_class_optional_oid}, {"partclass", NULL, 100000, 0, false, false, false, &check_opclass_oid_vector}, {"partcollation", NULL, 100000, 0, false, false, false, &check_collation_optional_oid_vector}, {NULL} }; struct pg_catalog_column pg_pltemplate_column[] = { /* pg_pltemplate */ {"tmplname", NULL, 0, 0, false, true, true}, {NULL} }; struct pg_catalog_column pg_publication_column[] = { /* pg_publication */ {"oid", NULL, 100000, 0, false, true, true}, {"pubowner", NULL, 100000, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_publication_rel_column[] = { /* pg_publication_rel */ {"oid", NULL, 100000, 0, false, true, true}, {"prpubid", NULL, 100000, 0, false, false, false, &check_publication_oid}, {"prrelid", NULL, 100000, 0, false, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column pg_replication_origin_column[] = { /* pg_replication_origin */ {"roident", NULL, 90500, 0, false, true, true}, {NULL} }; struct pg_catalog_column pg_sequence_column[] = { /* pg_sequence */ {"seqrelid", NULL, 100000, 0, false, true, true, &check_class_oid}, {"seqtypid", NULL, 100000, 0, false, false, false, &check_type_oid}, {NULL} }; struct pg_catalog_column pg_statistic_ext_column[] = { /* pg_statistic_ext */ {"oid", NULL, 100000, 0, false, true, true}, {"stxrelid", NULL, 100000, 0, false, false, false, &check_class_oid}, {"stxnamespace", NULL, 100000, 0, false, false, false, &check_namespace_oid}, {"stxowner", NULL, 100000, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_statistic_ext_data_column[] = { /* pg_statistic_ext_data */ {"stxoid", NULL, 120000, 0, false, true, true, &check_statistic_ext_oid}, {NULL} }; struct pg_catalog_column pg_subscription_column[] = { /* pg_subscription */ {"oid", NULL, 100000, 0, false, true, true}, {"subdbid", NULL, 100000, 0, false, false, false, &check_database_oid}, {"subowner", NULL, 100000, 0, false, false, false, &check_authid_oid}, {NULL} }; struct pg_catalog_column pg_subscription_rel_column[] = { /* pg_subscription_rel */ {"srsubid", NULL, 100000, 0, false, true, true, &check_subscription_oid}, {"srrelid", NULL, 100000, 0, false, true, true, &check_class_oid}, {NULL} }; struct pg_catalog_column pg_transform_column[] = { /* pg_transform */ {"oid", NULL, 90500, 0, false, true, true}, {"trftype", NULL, 90500, 0, false, false, false, &check_type_oid}, {"trflang", NULL, 90500, 0, false, false, false, &check_language_oid}, {"trffromsql", "pg_catalog.oid", 90500, 0, false, false, false, &check_proc_oid}, {"trftosql", "pg_catalog.oid", 90500, 0, false, false, false, &check_proc_oid}, {NULL} }; struct pg_catalog_column edb_redaction_column[] = { /* edb_redaction_column */ {"oid", NULL, 110000, 0, true, true, true}, {"rdpolicyid", NULL, 110000, 0, true, false, false, &check_redaction_policy_oid}, {"rdrelid", NULL, 110000, 0, true, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column edb_redaction_policy_column[] = { /* edb_redaction_policy */ {"oid", NULL, 110000, 0, true, true, true}, {"rdrelid", NULL, 110000, 0, true, false, false, &check_class_oid}, {NULL} }; struct pg_catalog_column edb_last_ddl_time_column[] = { /* edb_last_ddl_time */ {"classid", NULL, 140000, 0, true, false, true, &check_dependency_class_id_value}, {"objid", NULL, 140000, 0, true, false, true, &check_dependency_id_value}, {NULL} }; struct pg_catalog_column edb_last_ddl_time_shared_column[] = { /* edb_last_ddl_time_shared */ {"classid", NULL, 140000, 0, true, false, true, &check_dependency_class_id_value}, {"objid", NULL, 140000, 0, true, false, true, &check_dependency_id_value}, {NULL} }; struct pg_catalog_table pg_catalog_tables[] = { {"pg_class", pg_class_column}, {"pg_namespace", pg_namespace_column}, {"pg_authid", pg_authid_column}, {"pg_tablespace", pg_tablespace_column}, {"pg_type", pg_type_column}, {"pg_am", pg_am_column}, {"pg_collation", pg_collation_column}, {"pg_proc", pg_proc_column}, {"pg_language", pg_language_column}, {"pg_index", pg_index_column}, {"pg_constraint", pg_constraint_column}, {"pg_database", pg_database_column}, {"pg_cast", pg_cast_column}, {"pg_conversion", pg_conversion_column}, {"pg_extension", pg_extension_column}, {"pg_enum", pg_enum_column}, {"pg_trigger", pg_trigger_column}, {"pg_ts_parser", pg_ts_parser_column}, {"pg_ts_config", pg_ts_config_column}, {"pg_ts_template", pg_ts_template_column}, {"pg_ts_dict", pg_ts_dict_column}, {"pg_foreign_data_wrapper", pg_fdw_column}, {"pg_foreign_server", pg_foreign_server_column}, {"pg_user_mapping", pg_user_mapping_column}, {"pg_foreign_table", pg_foreign_table_column}, {"pg_event_trigger", pg_event_trigger_column}, {"pg_opfamily", pg_opfamily_column}, {"pg_opclass", pg_opclass_column}, {"pg_operator", pg_operator_column}, {"pg_amop", pg_amop_column}, {"pg_amproc", pg_amproc_column}, {"pg_default_acl", pg_default_acl_column}, {"pg_rewrite", pg_rewrite_column}, {"pg_inherits", pg_inherits_column}, {"pg_largeobject_metadata", pg_largeobject_metadata_column}, {"pg_largeobject", pg_largeobject_column}, {"pg_aggregate", pg_aggregate_column}, {"pg_ts_config_map", pg_ts_config_map_column}, {"pg_range", pg_range_column}, {"pg_attrdef", pg_attrdef_column}, {"pg_attribute", pg_attribute_column}, {"pg_statistic", pg_statistic_column}, {"pg_db_role_setting", pg_db_role_setting_column}, {"pg_depend", pg_depend_column}, {"pg_shdepend", pg_shdepend_column}, {"edb_dir", edb_dir_column}, {"edb_partdef", edb_partdef_column}, {"edb_partition", edb_partition_column}, {"edb_policy", edb_policy_column}, {"pg_synonym", pg_synonym_column}, {"edb_variable", edb_variable_column}, {"pg_description", pg_description_column}, {"pg_shdescription", pg_shdescription_column}, {"pg_seclabel", pg_seclabel_column}, {"pg_shseclabel", pg_shseclabel_column}, {"pg_auth_members", pg_auth_members_column}, {"pg_policy", pg_policy_column}, {"edb_profile", edb_profile_column}, {"edb_queue_table", edb_queue_table_column}, {"edb_queue", edb_queue_column}, {"edb_password_history", edb_password_history_column}, {"edb_queue_callback", edb_queue_callback_column}, {"edb_resource_group", edb_resource_group_column}, {"pg_init_privs", pg_init_privs_column}, {"pg_partitioned_table", pg_partitioned_table_column}, {"pg_pltemplate", pg_pltemplate_column}, {"pg_publication", pg_publication_column}, {"pg_publication_rel", pg_publication_rel_column}, {"pg_replication_origin", pg_replication_origin_column}, {"pg_sequence", pg_sequence_column}, {"pg_statistic_ext", pg_statistic_ext_column}, {"pg_subscription", pg_subscription_column}, {"pg_subscription_rel", pg_subscription_rel_column}, {"pg_transform", pg_transform_column}, {"edb_redaction_column", edb_redaction_column}, {"edb_redaction_policy", edb_redaction_policy_column}, {"pg_statistic_ext_data", pg_statistic_ext_data_column}, {"edb_last_ddl_time", edb_last_ddl_time_column}, {"edb_last_ddl_time_shared", edb_last_ddl_time_shared_column}, {NULL} }; pg_catcheck-1.3.0/log.c000066400000000000000000000070151417005477200147050ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * log.c * * Logging support for the system catalog integrity checker. This vaguely * parallel's the backend and pg_upgrade, but with some foibles specific * to our particular needs. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" bool quiet = false; /* Don't display progress messages. */ int verbose = 0; /* 1 = verbose messages; 2+ = debug messages */ static int notices = 0; static int warnings = 0; static int errors = 0; static pgcc_severity highest_message_severity = PGCC_DEBUG; static bool pgcc_log_severity(pgcc_severity sev); /* * Log a message. We write messages of level NOTICE and below to standard * output; anything higher goes to standard error. */ void pgcc_log(pgcc_severity sev, char *fmt,...) { va_list args; if (!pgcc_log_severity(sev)) return; va_start(args, fmt); if (sev <= PGCC_NOTICE) vfprintf(stdout, fmt, args); else vfprintf(stderr, fmt, args); va_end(args); if (sev >= PGCC_FATAL) exit(2); } /* * Report a catalog inconsistency; this always uses level PGCC_NOTICE. */ void pgcc_report(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum, char *fmt,...) { va_list args; pg_catalog_column *displaytabcol; bool first = true; if (!pgcc_log_severity(PGCC_NOTICE)) return; if (tabcol != NULL) printf("%s row has invalid %s \"%s\": ", tab->table_name, tabcol->name, PQgetvalue(tab->data, rownum, tabcol->result_column)); va_start(args, fmt); vfprintf(stdout, fmt, args); va_end(args); for (displaytabcol = tab->cols; displaytabcol->name != NULL; ++displaytabcol) { if (displaytabcol->is_display_column) { printf("%s%s=\"%s\"", first ? "row identity: " : " ", displaytabcol->name, PQgetvalue(tab->data, rownum, displaytabcol->result_column)); first = false; } } if (!first) printf("\n"); } /* * Report that we have completed our checks, and exit with an appropriate * status code. */ void pgcc_log_completion(void) { pgcc_log(PGCC_PROGRESS, "done (%d inconsistencies, %d warnings, %d errors)\n", notices, warnings, errors); if (highest_message_severity > PGCC_NOTICE) exit(2); else if (highest_message_severity == PGCC_NOTICE) exit(1); exit(0); } /* * Common code for pgcc_log() and pgcc_report(). * * Determine whether a message of the indicated severity should be logged * given the command-line options specified by the user, and print out the * severity level on standard output or standard error as appropriate, so that * it prefixes the message. * * Also updates statistics on what messages have been logged so that * pgcc_log_completion() can make use of that information. */ static bool pgcc_log_severity(pgcc_severity sev) { switch (sev) { case PGCC_DEBUG: if (verbose < 2) return false; printf("debug: "); break; case PGCC_VERBOSE: if (verbose < 1) return false; printf("verbose: "); break; case PGCC_PROGRESS: if (quiet) return false; printf("progress: "); break; case PGCC_NOTICE: printf("notice: "); break; case PGCC_WARNING: fprintf(stderr, "warning: "); break; case PGCC_ERROR: fprintf(stderr, "error: "); break; case PGCC_FATAL: fprintf(stderr, "fatal: "); break; } if (sev == PGCC_NOTICE) ++notices; if (sev == PGCC_WARNING) ++warnings; if (sev == PGCC_ERROR) ++errors; if (sev >= highest_message_severity) highest_message_severity = sev; return true; } pg_catcheck-1.3.0/pg_catcheck.c000066400000000000000000000652471417005477200163720ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * pg_catcheck.c * * Driver code for the system catalog integrity checker. Options parsing * logic as well as code to connect to the database and build and execute * SQL queries live here, as does other management code that is used to * plan and drive the flow of the checks. The checks themselves, however, * are not defined here. * * This tool only attempts to detect logical errors (like a dependency in * pg_depend that points to a non-existent logic), not lower-level * corruption scenarios (like an index that doesn't match the table). * Nevertheless, we attempt to be resilient against the possible presence * of such scenarios by issuing just one query per table fetching only * the columns we need, and continuing on so far as possible even if some * queries fail. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "getopt_long.h" #include "pqexpbuffer.h" #include "libpq-fe.h" #include "pg_catcheck.h" #include #if PG_VERSION_NUM >= 140000 #include "common/string.h" #endif extern char *optarg; extern int optind; /* variable definitions */ static char *pghost = ""; static char *pgport = ""; static char *login = NULL; static char *dbName; const char *progname; int remote_version; bool remote_is_edb; char *database_oid; bool select_from_relations = false; #define MINIMUM_SUPPORTED_VERSION 80400 /* Static functions */ static int parse_target_version(char *version); static void select_column(char *column_name, enum trivalue whether); static void select_table(char *table_name, enum trivalue whether); static PGconn *do_connect(void); static void decide_what_to_check(bool selected_columns); static void perform_checks(PGconn *conn); static void load_table(PGconn *conn, pg_catalog_table *tab); static void check_table(PGconn *conn, pg_catalog_table *tab); static PQExpBuffer build_query_for_table(pg_catalog_table *tab); static void build_hash_from_query_results(pg_catalog_table *tab); static void usage(void); static char *get_database_oid(PGconn *conn); /* * Main program. */ int main(int argc, char **argv) { static struct option long_options[] = { /* systematic long/short named options */ {"host", required_argument, NULL, 'h'}, {"port", required_argument, NULL, 'p'}, {"username", required_argument, NULL, 'U'}, {"table", required_argument, NULL, 't'}, {"column", required_argument, NULL, 'c'}, {"quiet", no_argument, NULL, 'q'}, {"verbose", no_argument, NULL, 'v'}, {"select-from-relations", no_argument, NULL, 105}, {"target-version", required_argument, NULL, 101}, {"enterprisedb", no_argument, NULL, 102}, {"postgresql", no_argument, NULL, 103}, {NULL, 0, NULL, 0} }; int c; int optindex; char *env; PGconn *conn; int target_version = 0; bool detect_edb = true; bool selected_columns = false; progname = get_progname(argv[0]); if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_catcheck (EnterpriseDB) " PG_VERSION); exit(0); } } #ifdef WIN32 /* stderr is buffered on Win32. */ setvbuf(stderr, NULL, _IONBF, 0); #endif if ((env = getenv("PGHOST")) != NULL && *env != '\0') pghost = env; if ((env = getenv("PGPORT")) != NULL && *env != '\0') pgport = env; else if ((env = getenv("PGUSER")) != NULL && *env != '\0') login = env; while ((c = getopt_long(argc, argv, "h:p:U:t:T:g:c:C:qv", long_options, &optindex)) != -1) { switch (c) { case 'h': pghost = pg_strdup(optarg); break; case 'p': pgport = pg_strdup(optarg); break; case 'c': select_column(optarg, TRI_YES); selected_columns = true; break; case 'C': select_column(optarg, TRI_NO); break; case 't': select_table(optarg, TRI_YES); selected_columns = true; break; case 'T': select_table(optarg, TRI_NO); break; case 'U': login = pg_strdup(optarg); break; case 'q': quiet = true; break; case 'v': ++verbose; break; case 101: target_version = parse_target_version(optarg); break; case 102: remote_is_edb = true; detect_edb = false; break; case 103: remote_is_edb = false; detect_edb = false; break; case 105: select_from_relations = true; break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); break; } } if (argc > optind) dbName = argv[optind++]; else { if ((env = getenv("PGDATABASE")) != NULL && *env != '\0') dbName = env; else if (login != NULL && *login != '\0') dbName = login; else dbName = ""; } /* Complain if any arguments remain */ if (optind < argc) { fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind]); fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); exit(1); } /* opening connection... */ conn = do_connect(); if (conn == NULL) exit(1); if (PQstatus(conn) == CONNECTION_BAD) pgcc_log(PGCC_FATAL, "could not connect to server: %s", PQerrorMessage(conn)); /* Detect remote version, or user user-specified value. */ if (target_version == 0) { remote_version = PQserverVersion(conn); pgcc_log(PGCC_VERBOSE, "detected server version %d\n", remote_version); } else { remote_version = target_version; pgcc_log(PGCC_VERBOSE, "assuming server version %d\n", remote_version); } /* Warn that we don't support checking really old versions. */ if (remote_version < MINIMUM_SUPPORTED_VERSION) pgcc_log(PGCC_WARNING, "server version (%d) is older than the minimum version supported by this tool (%d)\n", remote_version, MINIMUM_SUPPORTED_VERSION); /* * If neither --enterprisedb nor --postgresql was specified, attempt to * detect which type of database we're accessing. */ if (detect_edb) { PGresult *res; res = PQexec(conn, "select strpos(version(), 'EnterpriseDB')"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { pgcc_log(PGCC_ERROR, "query failed: %s", PQerrorMessage(conn)); PQclear(res); pgcc_log(PGCC_FATAL, "Please use --enterprisedb or --postgresql to specify the database type.\n"); } remote_is_edb = atol(PQgetvalue(res, 0, 0)); if (remote_is_edb) pgcc_log(PGCC_VERBOSE, "detected EnterpriseDB server\n"); else pgcc_log(PGCC_VERBOSE, "detected PostgreSQL server\n"); PQclear(res); } else { if (remote_is_edb) pgcc_log(PGCC_VERBOSE, "assuming EnterpriseDB server\n"); else pgcc_log(PGCC_VERBOSE, "assuming PostgreSQL server\n"); } /* * At this point, we know the database version and flavor that we'll be * checking and can fix the list of columns to be checked. */ decide_what_to_check(selected_columns); /* Cache the OID of the current database, if possible. */ database_oid = get_database_oid(conn); /* Run the checks. */ perform_checks(conn); /* Cleanup */ PQfinish(conn); pgcc_log_completion(); return 0; } /* * Parse the target version string. * * We expect either something of the form MAJOR.MINOR or else a single number * in the format used by PQremoteVersion(). */ static int parse_target_version(char *version) { int major = 0; int minor = 0; char *s; /* Try to parse major version number. */ for (s = version; *s >= '0' && *s <= '9'; ++s) major = (major * 10) + (*s - '0'); /* * If we found a 5+ digit number, assume it's in PQremoteVersion() format * and call it good. */ if (*s == '\0' && major >= 10000) return major; /* Expecting a period and then a minor version number. */ if (*s != '.') goto bad; for (++s; *s >= '0' && *s <= '9'; ++s) minor = (minor * 10) + (*s - '0'); /* And now we're expecting end of string. */ if (*s != '\0') goto bad; return (major * 10000) + (minor * 100); bad: fprintf(stderr, _("%s: invalid argument for option --target-version\n"), progname); fprintf(stderr, _("Target version should be formatted as MAJOR.MINOR.\n")); exit(1); } /* * Select or deselect the named table. */ static void select_table(char *table_name, enum trivalue whether) { pg_catalog_table *tab; int nmatched = 0; for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { if (strcmp(table_name, tab->table_name) != 0) continue; ++nmatched; tab->checked = whether; } if (nmatched == 0) pgcc_log(PGCC_FATAL, "table name \"%s\" not recognized\n", table_name); } /* * Select or deselect the named column. * * FIXME: Allow table_name.column_name syntax here. */ static void select_column(char *column_name, enum trivalue whether) { pg_catalog_table *tab; int nmatched = 0; for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { pg_catalog_column *tabcol; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { if (strcmp(column_name, tabcol->name) == 0) { tabcol->checked = whether; ++nmatched; } } } if (nmatched == 0) pgcc_log(PGCC_FATAL, "column name \"%s\" not recognized\n", column_name); } /* * Given a table name, find the corresponding pg_catalog_table structure. */ pg_catalog_table * find_table_by_name(char *table_name) { pg_catalog_table *tab; for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) if (strcmp(tab->table_name, table_name) == 0) return tab; pgcc_log(PGCC_FATAL, "no metadata found for table %s\n", table_name); return NULL; /* placate compiler */ } /* * Given a table and a column name, find the pg_catalog_column structure. */ pg_catalog_column * find_column_by_name(pg_catalog_table *tab, char *name) { pg_catalog_column *tabcol; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) if (strcmp(tabcol->name, name) == 0) return tabcol; pgcc_log(PGCC_FATAL, "no metadata found for column %s.%s\n", tab->table_name, name); return NULL; /* placate compiler */ } /* * Connect to the database. */ static PGconn * do_connect(void) { PGconn *conn; static char *password = NULL; bool new_pass; /* * Start the connection. Loop until we have a password if requested by * backend. */ do { #define PARAMS_ARRAY_SIZE 7 const char *keywords[PARAMS_ARRAY_SIZE]; const char *values[PARAMS_ARRAY_SIZE]; keywords[0] = "host"; values[0] = pghost; keywords[1] = "port"; values[1] = pgport; keywords[2] = "user"; values[2] = login; keywords[3] = "password"; values[3] = password; keywords[4] = "dbname"; values[4] = dbName; keywords[5] = "fallback_application_name"; values[5] = progname; keywords[6] = NULL; values[6] = NULL; new_pass = false; conn = PQconnectdbParams(keywords, values, true); if (!conn) { pgcc_log(PGCC_FATAL, "could not connect to server: %s", PQerrorMessage(conn)); return NULL; } if (PQstatus(conn) == CONNECTION_BAD && PQconnectionNeedsPassword(conn) && password == NULL) { #if (PG_VERSION_NUM >= 100000 && PG_VERSION_NUM < 140000) char passbuf[100]; #endif PQfinish(conn); #if PG_VERSION_NUM >= 140000 password = simple_prompt("Password: ", false); #elif PG_VERSION_NUM >= 100000 simple_prompt("Password: ", passbuf, sizeof(passbuf), false); password = passbuf; #else password = simple_prompt("Password: ", 100, false); #endif new_pass = true; } } while (new_pass); /* check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD) pgcc_log(PGCC_FATAL, "could not connect to server: %s", PQerrorMessage(conn)); return conn; } /* * Attempt to obtain the OID of the database being checked. */ char * get_database_oid(PGconn *conn) { PGresult *res; char *val; res = PQexec(conn, "SELECT oid FROM pg_database WHERE datname = current_database()"); if (PQresultStatus(res) != PGRES_TUPLES_OK) { char *message = PQresultErrorMessage(res); if (message != NULL && message[0] != '\0') pgcc_log(PGCC_ERROR, "could not determine database OID: %s", message); else pgcc_log(PGCC_ERROR, "could not determine database OID: unexpected status %s\n", PQresStatus(PQresultStatus(res))); return NULL; } if (PQntuples(res) != 1) { pgcc_log(PGCC_ERROR, "query for database OID returned %d values\n", PQntuples(res)); return NULL; } val = pg_strdup(PQgetvalue(res, 0, 0)); PQclear(res); pgcc_log(PGCC_DEBUG, "database OID is %s\n", val); return val; } /* * Decide which columns to check. */ static void decide_what_to_check(bool selected_columns) { pg_catalog_table *tab; /* * First pass: set "checked" flags, and tentatively set "needed" flags. */ for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { pg_catalog_column *tabcol; tab->available = false; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { /* * If the user explicitly asked us to check a column we don't know * how to check, that's a usage error, so bail out. */ if (tabcol->checked == TRI_YES && tabcol->check == NULL) pgcc_log(PGCC_FATAL, "no check defined for column %s.%s\n", tab->table_name, tabcol->name); /* Decide whether this column is available. */ if (tabcol->is_edb_only && !remote_is_edb) tabcol->available = false; /* EDB column on non-EDB * database */ else if (tabcol->minimum_version && remote_version < tabcol->minimum_version) tabcol->available = false; /* DB version too old */ else if (tabcol->maximum_version && remote_version > tabcol->maximum_version) tabcol->available = false; /* DB version too new */ else tabcol->available = true; if (tabcol->available) tab->available = true; /* * If the column looks like it is not available in this version * but the user asked explicitly for that particular column, warn * them that things might not work out well. */ if (tabcol->available == false && tabcol->checked == TRI_YES) { tabcol->available = true; pgcc_log(PGCC_WARNING, "column %s.%s is not supported by this server version\n", tab->table_name, tabcol->name); } /* * If the user didn't specify whether to check the column, decide * whether or not to do so. */ if (tabcol->checked == TRI_DEFAULT) { if (tabcol->check == NULL) tabcol->checked = TRI_NO; /* no check defined */ else if (!tabcol->available) tabcol->checked = TRI_NO; /* not in this version */ else if (tab->checked != TRI_DEFAULT) tabcol->checked = tab->checked; /* use table setting */ else if (selected_columns) tabcol->checked = TRI_NO; /* only specified columns */ else tabcol->checked = TRI_YES; /* otherwise, check it */ } /* * Decide whether the column is needed, indicating whether it will * be selected when we retrieve data from the table. We exclude * columns not available in this server version, but include other * columns if they are to be checked, if they are part of the key, * or if we display them for purposes of row identification. */ if (!tabcol->available) tabcol->needed = false; else tabcol->needed = (tabcol->checked == TRI_YES) || tabcol->is_key_column || tabcol->is_display_column; } } /* Prepare for select_from_relations, if option been provided. */ if (select_from_relations) prepare_to_select_from_relations(); /* * Second pass: allow individual checks to mark additional columns as * needed, and set ordering dependencies. */ for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { pg_catalog_column *tabcol; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { pg_catalog_check *check; check = tabcol->check; if (check == NULL || tabcol->checked != TRI_YES) continue; switch (check->type) { case CHECK_ATTNUM: prepare_to_check_relnatts(tab, tabcol); break; case CHECK_OID_REFERENCE: case CHECK_OID_VECTOR_REFERENCE: case CHECK_OID_ARRAY_REFERENCE: prepare_to_check_oid_reference(tab, tabcol); break; case CHECK_DEPENDENCY_CLASS_ID: prepare_to_check_dependency_class_id(tab, tabcol); break; case CHECK_DEPENDENCY_ID: prepare_to_check_dependency_id(tab, tabcol); break; case CHECK_DEPENDENCY_SUBID: prepare_to_check_dependency_subid(tab, tabcol); break; case CHECK_RELNATTS: prepare_to_check_relnatts(tab, tabcol); break; } } } } /* * Set up metadata that will be needed to choose an order in which to check * the tables. */ static void perform_checks(PGconn *conn) { pg_catalog_table *tab; /* Initialize the table check states. */ for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { pg_catalog_column *tabcol; if (tab->num_needed_by != 0) tab->needs_load = true; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { if (tabcol->needed) tab->needs_load = true; if (tabcol->checked == TRI_YES) { Assert(tab->needs_load); tab->needs_check = true; break; } } } /* Loop until all checks are complete. */ for (;;) { pg_catalog_table *best = NULL; pg_catalog_table *tab; int remaining = 0; /* * Search for tables that can be checked without loading any more data * from the database. If we find any, check them. Along the way, * keep a count of the number of tables remaining to be checked. */ for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { if (tab->needs_check && !tab->needs_load && tab->num_needs == 0) check_table(conn, tab); if (tab->needs_check) ++remaining; } /* If no tables remain to be checked, we're done. */ if (remaining == 0) break; /* * There are tables that remain to be checked, but none of them can be * checked without reading data from the database. Choose one which * requires preloading the fewest tables; in case of a tie, prefer the * one required by the most yet-to-be-checked tables, in the hopes of * unblocking as many other checks as possible. */ for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab) { if (!tab->needs_check) continue; if (best == NULL || tab->num_needs < best->num_needs || (tab->num_needs == best->num_needs && tab->num_needed_by > best->num_needed_by)) best = tab; } Assert(best != NULL); /* If the selected candidate needs other tables preloaded, do that. */ while (best->num_needs > 0) { pg_catalog_table *reftab = best->needs[best->num_needs - 1]; int old_num_needs PG_USED_FOR_ASSERTS_ONLY = best->num_needs; if (!reftab->needs_load) continue; pgcc_log(PGCC_VERBOSE, "preloading table %s because it is required in order to check %s\n", reftab->table_name, best->table_name); load_table(conn, reftab); Assert(old_num_needs > best->num_needs); } /* Load the table itself, if it isn't already. */ if (best->needs_load) { pgcc_log(PGCC_VERBOSE, "loading table %s\n", best->table_name); load_table(conn, best); } /* Check the table. */ check_table(conn, best); } /* Check select-from-relations */ if (select_from_relations) perform_select_from_relations(conn); } /* * Load a table into memory. */ static void load_table(PGconn *conn, pg_catalog_table *tab) { PQExpBuffer query; int i; Assert(tab->needs_load); /* Load the table data. */ query = build_query_for_table(tab); pgcc_log(PGCC_DEBUG, "executing query: %s\n", query->data); tab->data = PQexec(conn, query->data); if (PQresultStatus(tab->data) != PGRES_TUPLES_OK) { char *message = PQresultErrorMessage(tab->data); if (message != NULL && message[0] != '\0') pgcc_log(PGCC_ERROR, "could not load table %s: %s", tab->table_name, message); else pgcc_log(PGCC_ERROR, "could not load table %s: unexpected status %s\n", tab->table_name, PQresStatus(PQresultStatus(tab->data))); } else build_hash_from_query_results(tab); destroyPQExpBuffer(query); /* This table is now loaded. */ tab->needs_load = false; /* Any other tables that neeed this table no longer do. */ for (i = 0; i < tab->num_needed_by; ++i) { pg_catalog_table *reftab = tab->needed_by[i]; int j, k; for (j = 0, k = 0; j < reftab->num_needs; ++j) { reftab->needs[k] = reftab->needs[j]; if (tab != reftab->needs[j]) ++k; } Assert(k == reftab->num_needs - 1); reftab->num_needs = k; } /* We can throw away our side of the dependency information as well. */ tab->num_needed_by = 0; if (tab->num_needed_by_allocated > 0) { pg_free(tab->needed_by); tab->num_needed_by_allocated = 0; } } /* * Perform integrity checks on a table. */ static void check_table(PGconn *conn, pg_catalog_table *tab) { int i; int ntups; /* Once we've tried to check the table, we shouldn't try again. */ tab->needs_check = false; Assert(tab->data != NULL); /* * If we weren't able to retrieve the table data, then we can't check the * table. But there's no real need to log the error message, becaues * load_table() will have already done so. */ if (PQresultStatus(tab->data) != PGRES_TUPLES_OK) return; /* Log a message, if verbose mode is enabled. */ ntups = PQntuples(tab->data); pgcc_log(PGCC_VERBOSE, "checking table %s (%d rows)\n", tab->table_name, ntups); /* Loop over the rows and check them. */ for (i = 0; i < ntups; ++i) { pg_catalog_column *tabcol; for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { pg_catalog_check *check; if (tabcol->checked != TRI_YES || tabcol->check == NULL) continue; check = tabcol->check; switch (check->type) { case CHECK_ATTNUM: check_attnum(tab, tabcol, i); break; case CHECK_OID_REFERENCE: case CHECK_OID_VECTOR_REFERENCE: case CHECK_OID_ARRAY_REFERENCE: check_oid_reference(tab, tabcol, i); break; case CHECK_DEPENDENCY_CLASS_ID: check_dependency_class_id(tab, tabcol, i); break; case CHECK_DEPENDENCY_ID: check_dependency_id(tab, tabcol, i); break; case CHECK_DEPENDENCY_SUBID: check_dependency_subid(tab, tabcol, i); break; case CHECK_RELNATTS: check_relnatts(tab, tabcol, i); break; } } } } /* * Build a hash table on the key columns of the catalog table contents. */ static void build_hash_from_query_results(pg_catalog_table *tab) { int i; pg_catalog_column *tabcol; pgrhash *ht; int keycols[MAX_KEY_COLS]; int nkeycols = 0; int ntups = PQntuples(tab->data); for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) if (tabcol->available && tabcol->is_key_column) keycols[nkeycols++] = PQfnumber(tab->data, tabcol->name); /* * Tables like pg_depend get loaded so that we can check them, but they * don't have a primary key, so we don't build a hash table. */ if (nkeycols == 0) return; /* Create the hash table. */ ht = tab->ht = pgrhash_create(tab->data, nkeycols, keycols); for (i = 0; i < ntups; i++) if (pgrhash_insert(ht, i) != -1) pgcc_report(tab, NULL, i, "%s row duplicates existing key\n", tab->table_name); } /* * Build a query to read the needed columns from a table. */ static PQExpBuffer build_query_for_table(pg_catalog_table *tab) { PQExpBuffer query; pg_catalog_column *tabcol; int index = 0; query = createPQExpBuffer(); appendPQExpBuffer(query, "SELECT"); for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol) { if (!tabcol->needed) continue; if (index == 0) appendPQExpBuffer(query, " %s", tabcol->name); else appendPQExpBuffer(query, ", %s", tabcol->name); if (tabcol->cast) appendPQExpBuffer(query, "::%s", tabcol->cast); /* Remember where this column is supposed to be in the output. */ tabcol->result_column = index; index++; } Assert(index > 0); appendPQExpBuffer(query, " FROM pg_catalog.%s", tab->table_name); return query; } /* * Indicate that one table ("needs") requires that another table ("needed_by") * be loaded before it is checked. */ void add_table_dependency(pg_catalog_table *needs, pg_catalog_table *needed_by) { int i; if (!needs->available || !needed_by->available) return; /* * We necessarily load tables before checking them, so there's no point in * a circular dependency. */ if (needs == needed_by) return; pgcc_log(PGCC_DEBUG, "table %s depends on table %s\n", needs->table_name, needed_by->table_name); /* Check whether the dependency is already present; if so, do nothing. */ for (i = 0; i < needs->num_needs; ++i) if (needs->needs[i] == needed_by) return; /* Make sure there's enough space to store the new dependency. */ if (needs->num_needs >= needs->num_needs_allocated) { if (needs->num_needs_allocated == 0) { needs->num_needs_allocated = 4; needs->needs = pg_malloc(needs->num_needs_allocated * sizeof(pg_catalog_table *)); } else { needs->num_needs_allocated *= 2; needs->needs = pg_realloc(needs->needs, needs->num_needs_allocated * sizeof(pg_catalog_table *)); } } if (needed_by->num_needed_by >= needed_by->num_needed_by_allocated) { if (needed_by->num_needed_by_allocated == 0) { needed_by->num_needed_by_allocated = 4; needed_by->needed_by = pg_malloc(needed_by->num_needed_by_allocated * sizeof(pg_catalog_table *)); } else { needed_by->num_needed_by_allocated *= 2; needed_by->needed_by = pg_realloc(needed_by->needed_by, needed_by->num_needed_by_allocated * sizeof(pg_catalog_table *)); } } /* Add the dependency. */ needs->needs[needs->num_needs] = needed_by; ++needs->num_needs; needed_by->needed_by[needed_by->num_needed_by] = needs; ++needed_by->num_needed_by; } /* * Print a usage message and exit. */ static void usage(void) { printf("%s is catalog table validation tool for PostgreSQL.\n\n", progname); printf("Usage:\n %s [OPTION]... [DBNAME]\n\n", progname); printf("Options:\n"); printf(" -c, --column check only the named columns\n"); printf(" -t, --table check only columns in the named tables\n"); printf(" -T, --exclude-table do NOT check the named tables\n"); printf(" -C, --exclude-column do NOT check the named columns\n"); printf(" --select-from-relations execute the SELECT on relations in the database\n"); printf(" --target-version=VERSION assume specified target version\n"); printf(" --enterprisedb assume EnterpriseDB database\n"); printf(" --postgresql assume PostgreSQL database\n"); printf(" -h, --host=HOSTNAME database server host or socket directory\n"); printf(" -p, --port=PORT database server port number\n"); printf(" -q, --quiet do not display progress messages\n"); printf(" -U, --username=USERNAME connect as specified database user\n"); printf(" -v, --verbose enable verbose internal logging\n"); printf(" -V, --version output version information, then exit\n"); printf(" -?, --help show this help, then exit\n"); printf("\nReport bugs to .\n"); } pg_catcheck-1.3.0/pg_catcheck.h000066400000000000000000000135521417005477200163670ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * pg_catcheck.h * * Functions and data structures for catalog integrity checking. * *------------------------------------------------------------------------- */ #ifndef PGCATCHECK_H #define PGCATCHECK_H #include "libpq-fe.h" /* for PGresult */ #include "compat.h" /* Forward declarations. */ struct pgrhash; typedef struct pgrhash pgrhash; struct pg_catalog_table; typedef struct pg_catalog_table pg_catalog_table; /* Tri-value logic for handling table and column selection. */ enum trivalue { TRI_DEFAULT, TRI_NO, TRI_YES }; /* Defined check types. */ typedef enum checktype { CHECK_ATTNUM, CHECK_OID_REFERENCE, CHECK_OID_VECTOR_REFERENCE, CHECK_OID_ARRAY_REFERENCE, CHECK_DEPENDENCY_CLASS_ID, CHECK_DEPENDENCY_ID, CHECK_DEPENDENCY_SUBID, CHECK_RELNATTS, } checktype; /* Generic catalog check structure. */ typedef struct pg_catalog_check { checktype type; } pg_catalog_check; /* Specialization of pg_catalog_check for the various types of OID checks. */ typedef struct pg_catalog_check_oid { checktype type; bool zero_oid_ok; char *oid_references_table; } pg_catalog_check_oid; /* Everything we need to check a catalog column. */ typedef struct pg_catalog_column { /* These columns are listed in definitions.c */ char *name; char *cast; int minimum_version; int maximum_version; bool is_edb_only; bool is_key_column; bool is_display_column; void *check; /* some kind of pg_catalog_check object */ /* These columns are populated at runtime. */ bool available; enum trivalue checked; bool needed; void *check_private; /* workspace for individual checks */ int result_column; /* result column number */ } pg_catalog_column; /* Everything we need to check an entire catalog table. */ struct pg_catalog_table { /* These columns are listed in definitions.c */ char *table_name; pg_catalog_column *cols; /* These columns are populated at runtime. */ bool available; /* OK for this version? */ enum trivalue checked; bool needs_load; /* Still needs to be loaded? */ bool needs_check; /* Still needs to be checked? */ PGresult *data; /* Table data. */ pgrhash *ht; /* Hash of table data. */ int num_needs; /* # of tables we depend on. */ int num_needs_allocated; /* Allocated slots for same. */ pg_catalog_table **needs; /* Array of tables we depend on. */ int num_needed_by; /* # of tables depending on us. */ int num_needed_by_allocated; /* Allocated slots for same. */ pg_catalog_table **needed_by; /* Array of tables depending on us. */ }; /* Array of tables known to this tool. */ extern struct pg_catalog_table pg_catalog_tables[]; /* Identifying characteristics of the database to be checked. */ extern int remote_version; /* Version number. */ extern bool remote_is_edb; /* Is it an EDB database? */ extern char *database_oid; /* Database OID, if known. */ /* pg_catcheck.c */ extern pg_catalog_table *find_table_by_name(char *table_name); extern pg_catalog_column *find_column_by_name(pg_catalog_table *, char *); extern void add_table_dependency(pg_catalog_table *needs, pg_catalog_table *needed_by); /* check_attribute.c */ extern void prepare_to_check_attnum(pg_catalog_table *tab, pg_catalog_column *tabcol); extern void check_attnum(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); /* check_class.c */ extern void prepare_to_check_relnatts(pg_catalog_table *tab, pg_catalog_column *tabcol); extern void check_relnatts(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); /* check_depend.c */ extern void prepare_to_check_dependency_class_id(pg_catalog_table *tab, pg_catalog_column *tabcol); extern void prepare_to_check_dependency_id(pg_catalog_table *tab, pg_catalog_column *tabcol); extern void prepare_to_check_dependency_subid(pg_catalog_table *tab, pg_catalog_column *tabcol); extern void check_dependency_class_id(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); extern void check_dependency_id(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); extern void check_dependency_subid(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); /* check_oids.c */ extern void prepare_to_check_oid_reference(pg_catalog_table *tab, pg_catalog_column *tabcol); extern void check_oid_reference(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum); /* select_from_relations.c */ extern void prepare_to_select_from_relations(void); extern void perform_select_from_relations(PGconn *conn); /* log.c */ typedef enum pgcc_severity { PGCC_DEBUG, /* Debugging messages for developers. */ PGCC_VERBOSE, /* Verbose messages. */ PGCC_PROGRESS, /* Progress messages. */ PGCC_NOTICE, /* Database inconsistencies. */ PGCC_WARNING, /* Warnings other than inconsistencies. */ PGCC_ERROR, /* Serious but not fatal errors. */ PGCC_FATAL /* Fatal errors. */ } pgcc_severity; extern bool quiet; extern int verbose; #if PG_VERSION_NUM < 90500 extern void pgcc_log(pgcc_severity sev, char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3))); extern void pgcc_report(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum, char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 4, 5))); #else extern void pgcc_log(pgcc_severity sev, char *fmt,...) pg_attribute_printf(2, 3); extern void pgcc_report(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum, char *fmt,...) pg_attribute_printf(4, 5); #endif extern void pgcc_log_completion(void); #ifndef PG_USED_FOR_ASSERTS_ONLY #define PG_USED_FOR_ASSERTS_ONLY #endif /* pgrhash.c */ #define MAX_KEY_COLS 10 extern pgrhash *pgrhash_create(PGresult *result, int nkeycols, int *keycols); extern int pgrhash_get(pgrhash *ht, char **keyvals); extern int pgrhash_insert(pgrhash *ht, int rownum); #endif /* PGCATCHECK_H */ pg_catcheck-1.3.0/pg_catcheck.proj000066400000000000000000000066451417005477200171170ustar00rootroot00000000000000 /Od /MDd /Zi /D "DEBUG=1" /D "_DEBUG" Debug /Ox /MD /GF Release /MACHINE:X64 /D "_USE_32BIT_TIME_T" /nologo /wd4273 /TC /LD $(XTRA_CFLAGS) /GS /fp:precise /Zc:wchar_t /D "WIN32" /D "__WIN32__" $(XTRA_ARCH_CFLAGS) /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "_MBCS" $(XTRA_ARCH_LDFLAGS) /DEBUG /PDB:"pg_catcheck.pdb" /defaultlib:user32 /defaultlib:netapi32 /defaultlib:advapi32 /defaultlib:shell32 /defaultlib:ws2_32 /defaultlib:Secur32.lib /defaultlib:$(PGPATH)\$(BUILD_SUBDIR)\libpq\libpq.lib /defaultlib:$(PGPATH)\$(BUILD_SUBDIR)\libpgport\libpgport.lib /defaultlib:$(PGPATH)\$(BUILD_SUBDIR)\libpgcommon\libpgcommon.lib" pg_catcheck.exe pg_catcheck-1.3.0/pgrhash.c000066400000000000000000000110641417005477200155570ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * pgrhash.c * * Simple hash table implementation for text data stored in a PGresult. * The user can specify which columns are to serve as keys. The code * is loosely based on the backend's dynahash.c, but is dramatically * simpler since we need only a small subset of the functionality offered * by that module. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" typedef struct pgrhash_entry { struct pgrhash_entry *next; /* link to next entry in same bucket */ uint32 hashvalue; /* hash function result for this entry */ int rownum; /* row number of data in PGresult */ } pgrhash_entry; struct pgrhash { PGresult *res; /* pointer to PGresult data */ int nkeycols; /* number of key columns */ int keycols[MAX_KEY_COLS]; /* array of key column indices */ unsigned nbuckets; /* number of buckets */ pgrhash_entry **bucket; /* pointer to hash entries */ }; static uint32 string_hash_sdbm(const char *key); static bool pgrhash_compare(pgrhash *ht, int rownum, char **keyvals); /* * Create a new hash table for given result set, keyed by the indicate * column indexes, but do not populate it. pgrhash_insert() should * be called separately for each row of the result set to actually * insert the rows. */ pgrhash * pgrhash_create(PGresult *result, int nkeycols, int *keycols) { unsigned bucket_shift; pgrhash *ht; Assert(nkeycols >= 1 && nkeycols <= MAX_KEY_COLS); bucket_shift = fls(PQntuples(result)); if (bucket_shift >= sizeof(unsigned) * BITS_PER_BYTE) pgcc_log(PGCC_FATAL, "too many tuples"); ht = (pgrhash *) pg_malloc(sizeof(pgrhash)); ht->res = result; ht->nbuckets = ((unsigned) 1) << bucket_shift; ht->bucket = (pgrhash_entry **) pg_malloc0(ht->nbuckets * sizeof(pgrhash_entry *)); ht->nkeycols = nkeycols; memcpy(ht->keycols, keycols, sizeof(int) * nkeycols); return ht; } /* * Search a result-set hash table for a row matching a given set of key values. * * The return value is the matching row number, or -1 if none. */ int pgrhash_get(pgrhash *ht, char **keyvals) { int i; uint32 hashvalue = 0; pgrhash_entry *bucket; for (i = 0; i < ht->nkeycols; i++) hashvalue ^= string_hash_sdbm(keyvals[i]); for (bucket = ht->bucket[hashvalue & (ht->nbuckets - 1)]; bucket != NULL; bucket = bucket->next) if (pgrhash_compare(ht, bucket->rownum, keyvals)) return bucket->rownum; return -1; } /* * Insert a row into a result-set hash table, provided no such row is already * present. * * The return value is -1 on success, or the row number of an existing row * with the same key. * * The only reason we expose this as a separate function, rather than making * it part of pgrhash_create, is that it allows callers to insert rows one * at a time and detect unexpected duplicate key violations. */ int pgrhash_insert(pgrhash *ht, int rownum) { unsigned bucket_number; int i; unsigned hashvalue = 0; char *keyvals[MAX_KEY_COLS]; pgrhash_entry *bucket; pgrhash_entry *entry; for (i = 0; i < ht->nkeycols; i++) { keyvals[i] = PQgetvalue(ht->res, rownum, ht->keycols[i]); hashvalue ^= string_hash_sdbm(keyvals[i]); } /* Check for a conflicting entry already present in the table. */ bucket_number = hashvalue & (ht->nbuckets - 1); for (bucket = ht->bucket[bucket_number]; bucket != NULL; bucket = bucket->next) if (pgrhash_compare(ht, bucket->rownum, keyvals)) return bucket->rownum; /* Insert the new entry. */ entry = pg_malloc(sizeof(pgrhash_entry)); entry->next = ht->bucket[bucket_number]; entry->rownum = rownum; ht->bucket[bucket_number] = entry; return -1; } /* * Simple string hash function from http://www.cse.yorku.ca/~oz/hash.html * * The backend uses a more sophisticated function for hashing strings, * but we don't really need that complexity here. Most of the values * that we're hashing are short integers formatted as text, so there * shouldn't be much room for pathological input. */ static uint32 string_hash_sdbm(const char *key) { uint32 hash = 0; int c; while ((c = *key++)) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } /* * Test whether the given row number is match for the supplied keys. */ static bool pgrhash_compare(pgrhash *ht, int rownum, char **keyvals) { int i; char *keycol; char *keyval; for (i = 0; i < ht->nkeycols; i++) { keycol = PQgetvalue(ht->res, rownum, ht->keycols[i]); keyval = keyvals[i]; if (strcmp(keycol, keyval) != 0) return false; } return true; } pg_catcheck-1.3.0/select_from_relations.c000066400000000000000000000102221417005477200205000ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * select_from_relations.c * * Try to select from relations with storage. This will fail if the * underlying files are absent or inaccessible. This is a little outside * the general remit of this tool, which is to check the integrity of * the system catalogs, but it seems like a useful addition. * *------------------------------------------------------------------------- */ #include "postgres_fe.h" #include "pg_catcheck.h" #include "pqexpbuffer.h" /* * Set up to check SELECT from relations. */ void prepare_to_select_from_relations(void) { pg_catalog_table *pg_class = find_table_by_name("pg_class"); pg_catalog_table *pg_namespace = find_table_by_name("pg_namespace"); /* Flag tables that must be loaded for this check. */ pg_class->needs_check = true; pg_class->needs_load = true; pg_namespace->needs_load = true; pg_namespace->needs_check = true; /* Flag columns that must be loaded for this check. */ find_column_by_name(pg_namespace, "nspname")->needed = true; find_column_by_name(pg_class, "relname")->needed = true; find_column_by_name(pg_class, "relnamespace")->needed = true; find_column_by_name(pg_class, "relkind")->needed = true; } /* * Try a SELECT from each relation. * * We use SELECT 0 here to make it fast; we're just trying to verify * that selecting data from the relation doesn't fail outright. */ void perform_select_from_relations(PGconn *conn) { PQExpBuffer query; char *tablename, *nspname, *nspoid; int rownum; int ntups; int oid_result_column; int relname_result_column; int relnamespace_result_column; int relkind_result_column; int nspname_result_column; pg_catalog_table *pg_class = find_table_by_name("pg_class"); pg_catalog_table *pg_namespace = find_table_by_name("pg_namespace"); /* * If we weren't able to retrieve the table data for either table, then * we can't run these checks. */ if (PQresultStatus(pg_class->data) != PGRES_TUPLES_OK || PQresultStatus(pg_namespace->data) != PGRES_TUPLES_OK) return; /* Locate the data we need. */ ntups = PQntuples(pg_class->data); oid_result_column = PQfnumber(pg_class->data, "oid"); relname_result_column = PQfnumber(pg_class->data, "relname"); relnamespace_result_column = PQfnumber(pg_class->data, "relnamespace"); relkind_result_column = PQfnumber(pg_class->data, "relkind"); nspname_result_column = PQfnumber(pg_namespace->data, "nspname"); query = createPQExpBuffer(); /* Loop over the rows and check them. */ for (rownum = 0; rownum < ntups; ++rownum) { char relkind; int nsp_rownum; PGresult *qryres; /* Check plain tables, toast tables, and materialized views. */ relkind = *(PQgetvalue(pg_class->data, rownum, relkind_result_column)); if (relkind != 'r' && relkind != 't' && relkind != 'm') continue; /* Get the table name and namespace OID from the pg_class */ tablename = PQgetvalue(pg_class->data, rownum, relname_result_column); nspoid = PQgetvalue(pg_class->data, rownum, relnamespace_result_column); /* * Get the namespace name for the given namespace OID. Any errors here * have already been reported, so we just emit a debug message here. */ nsp_rownum = pgrhash_get(pg_namespace->ht, &nspoid); if (nsp_rownum == -1) { pgcc_log(PGCC_DEBUG, "can't find schema name for select query for table with OID %s\n", PQgetvalue(pg_class->data, rownum, oid_result_column)); continue; } nspname = PQgetvalue(pg_namespace->data, nsp_rownum, nspname_result_column); /* Debug message. */ pgcc_log(PGCC_DEBUG, "selecting from \"%s\".\"%s\"\n", nspname, tablename); /* Build up a query. */ resetPQExpBuffer(query); appendPQExpBuffer(query, "SELECT 1 FROM %s.%s LIMIT 0", PQescapeIdentifier(conn, nspname, strlen(nspname)), PQescapeIdentifier(conn, tablename, strlen(tablename))); /* Run the query. */ qryres = PQexec(conn, query->data); if (PQresultStatus(qryres) != PGRES_TUPLES_OK) pgcc_log(PGCC_NOTICE, "unable to query relation \"%s\".\"%s\": %s", nspname, tablename, PQerrorMessage(conn)); /* Clean up. */ PQclear(qryres); } destroyPQExpBuffer(query); } pg_catcheck-1.3.0/settings.projinc000066400000000000000000000007121417005477200172030ustar00rootroot00000000000000 0 C:\postgresql-9.4.0 pg_catcheck-1.3.0/typedefs.list000066400000000000000000000002611417005477200164740ustar00rootroot00000000000000attnum_cache check_depend_cache class_id_mapping_type depend_column_style exception_list pg_catalog_column pg_catalog_table PGconn PGresult pgrhash pgrhash_entry relnatts_cache