From ff2ef28a0eb2adca9cc36d103aa3fb48695ce51a Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 1 Nov 2023 16:56:50 -0400 Subject: [PATCH v4 2/2] Generate ObjectProperty from catalog files Most of the information can be extracted from catalog files and generated automatically. TODO: Currently missing: object type and class description. There are some workarounds in place. We also now replace the linear search with a hash function generated using PerfectHash. This would allow this lookup table to be used in more performance-sensitive situations. Discussion: https://www.postgresql.org/message-id/flat/75ae5875-3abc-dafc-8aec-73247ed41cde@eisentraut.org --- src/backend/catalog/.gitignore | 1 + src/backend/catalog/Makefile | 2 +- src/backend/catalog/genbki.pl | 167 ++++++++++++++++++++++++++++ src/backend/catalog/objectaddress.c | 22 ++-- src/include/catalog/meson.build | 2 + src/tools/pginclude/cpluspluscheck | 3 + src/tools/pginclude/headerscheck | 3 + 7 files changed, 188 insertions(+), 12 deletions(-) diff --git a/src/backend/catalog/.gitignore b/src/backend/catalog/.gitignore index ff655603792..d817798dc0e 100644 --- a/src/backend/catalog/.gitignore +++ b/src/backend/catalog/.gitignore @@ -1,3 +1,4 @@ +/objectproperty_info.c.h /postgres.bki /schemapg.h /syscache_info.c.h diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 2c94126ec5c..e6c5878e99c 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -120,7 +120,7 @@ CATALOG_HEADERS := \ pg_subscription.h \ pg_subscription_rel.h -GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) schemapg.h syscache_info.h syscache_info.c.h system_fk_info.h +GENERATED_HEADERS := $(CATALOG_HEADERS:%.h=%_d.h) objectproperty_info.c.h schemapg.h syscache_info.h syscache_info.c.h system_fk_info.h POSTGRES_BKI_SRCS := $(addprefix $(top_srcdir)/src/include/catalog/, $(CATALOG_HEADERS)) diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index 78f2da690ec..199091305c3 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -19,8 +19,10 @@ use FindBin; use lib $FindBin::RealBin; +use lib "$FindBin::RealBin/../../tools"; use Catalog; +use PerfectHash; my $output_path = ''; my $major_version; @@ -59,6 +61,7 @@ my %syscaches; my %oidcounts; my @system_constraints; +my @object_properties; foreach my $header (@ARGV) { @@ -133,6 +136,7 @@ $oidcounts{ $toast->{toast_oid} }++; $oidcounts{ $toast->{toast_index_oid} }++; } + my ($oid_index, $oid_syscache, $name_syscache, $is_nsp_name_unique); foreach my $index (@{ $catalog->{indexing} }) { push @index_decls, @@ -166,7 +170,136 @@ nbuckets => $index->{syscache_nbuckets}, }; } + + if ($index->{index_decl} eq 'btree(oid oid_ops)') + { + $oid_index = $index->{index_oid_macro}; + $oid_syscache = $index->{syscache_name}; + } + if ($index->{index_decl} =~ /\(\w+name name_ops(, \w+namespace oid_ops)?\)/) + { + $name_syscache = $index->{syscache_name}; + $is_nsp_name_unique = 1 if $index->{is_unique}; + } + } + + my ($attnum_oid, $attnum_name, $attnum_namespace, $attnum_owner, $attnum_acl); + foreach my $att (@$schema) + { + if ($att->{name} eq 'oid' && $att->{type} eq 'oid') + { + $attnum_oid = "Anum_${catname}_" . $att->{name}; + } + elsif ($att->{name} =~ /^\w{2,4}name$/ && $att->{type} eq 'name') + { + $attnum_name = "Anum_${catname}_" . $att->{name}; + } + elsif ($att->{name} =~ /^\w{2,4}namespace$/ && $att->{type} eq 'oid' && $att->{lookup} eq 'pg_namespace') + { + $attnum_namespace = "Anum_${catname}_" . $att->{name}; + } + elsif ($att->{name} =~ /^\w{2,4}owner$/ && $att->{type} eq 'oid' && $att->{lookup} eq 'pg_authid') + { + $attnum_owner = "Anum_${catname}_" . $att->{name}; + } + elsif ($att->{name} =~ /^\w{2,4}acl$/ && $att->{type} eq '_aclitem') + { + $attnum_acl = "Anum_${catname}_" . $att->{name}; + } + } + + # XXX hardcoded exceptions + # extension doesn't belong to extnamespace + $attnum_namespace = undef if $catname eq 'pg_extension'; + # pg_database owner is spelled datdba + $attnum_owner = "Anum_pg_database_datdba" if $catname eq 'pg_database'; + # XXX? + $name_syscache = "SUBSCRIPTIONNAME" if $catname eq 'pg_subscription'; + # XXX? + $is_nsp_name_unique = 1 if $catname eq 'pg_collation'; + $is_nsp_name_unique = 1 if $catname eq 'pg_opclass'; + $is_nsp_name_unique = 1 if $catname eq 'pg_opfamily'; + $is_nsp_name_unique = 1 if $catname eq 'pg_subscription'; + + # XXX These catalogs were not covered by the previous hand-maintained table. + my @skip = qw( + AttrDefaultRelationId EnumRelationId IndexRelationId + LargeObjectRelationId ParameterAclRelationId + PublicationNamespaceRelationId PublicationRelRelationId + InheritsRelationId AggregateRelationId StatisticRelationId + StatisticExtDataRelationId DescriptionRelationId + DependRelationId DbRoleSettingRelationId + SharedDependRelationId SharedDescriptionRelationId + TSConfigMapRelationId ForeignTableRelationId + ReplicationOriginRelationId InitPrivsRelationId + SecLabelRelationId SharedSecLabelRelationId + PartitionedRelationId RangeRelationId SequenceRelationId + SubscriptionRelRelationId); + # XXX This one neither, but if I add it to @skip, PerfectHash will fail. (???) + #FIXME: AttributeRelationId + + # XXX hardcoded ObjectType mapping -- where to put this? + my %objtypes = ( + 'AccessMethodRelationId' => 'OBJECT_ACCESS_METHOD', + 'AccessMethodOperatorRelationId' => 'OBJECT_AMOP', + 'AccessMethodProcedureRelationId' => 'OBJECT_AMPROC', + 'CastRelationId' => 'OBJECT_CAST', + 'CollationRelationId' => 'OBJECT_COLLATION', + 'ConversionRelationId' => 'OBJECT_CONVERSION', + 'DatabaseRelationId' => 'OBJECT_DATABASE', + 'DefaultAclRelationId' => 'OBJECT_DEFACL', + 'ExtensionRelationId' => 'OBJECT_EXTENSION', + 'ForeignDataWrapperRelationId' => 'OBJECT_FDW', + 'ForeignServerRelationId' => 'OBJECT_FOREIGN_SERVER', + 'ProcedureRelationId' => 'OBJECT_FUNCTION', + 'LanguageRelationId' => 'OBJECT_LANGUAGE', + 'LargeObjectMetadataRelationId' => 'OBJECT_LARGEOBJECT', + 'OperatorClassRelationId' => 'OBJECT_OPCLASS', + 'OperatorRelationId' => 'OBJECT_OPERATOR', + 'OperatorFamilyRelationId' => 'OBJECT_OPFAMILY', + 'AuthIdRelationId' => 'OBJECT_ROLE', + 'RewriteRelationId' => 'OBJECT_RULE', + 'NamespaceRelationId' => 'OBJECT_SCHEMA', + 'RelationRelationId' => 'OBJECT_TABLE', + 'TableSpaceRelationId' => 'OBJECT_TABLESPACE', + 'TransformRelationId' => 'OBJECT_TRANSFORM', + 'TriggerRelationId' => 'OBJECT_TRIGGER', + 'PolicyRelationId' => 'OBJECT_POLICY', + 'EventTriggerRelationId' => 'OBJECT_EVENT_TRIGGER', + 'TSConfigRelationId' => 'OBJECT_TSCONFIGURATION', + 'TSDictionaryRelationId' => 'OBJECT_TSDICTIONARY', + 'TSParserRelationId' => 'OBJECT_TSPARSER', + 'TSTemplateRelationId' => 'OBJECT_TSTEMPLATE', + 'TypeRelationId' => 'OBJECT_TYPE', + 'PublicationRelationId' => 'OBJECT_PUBLICATION', + 'SubscriptionRelationId' => 'OBJECT_SUBSCRIPTION', + 'StatisticExtRelationId' => 'OBJECT_STATISTIC_EXT', + 'UserMappingRelationId' => 'OBJECT_USER_MAPPING', + ); + my $objtype = $objtypes{$catalog->{relation_oid_macro}}; + + # XXX This just uses the catalog name as a string rather than the + # previous natural-language description. This could be changed if + # we decide on a place to store this. But maybe for what we are + # using it, this is actually better. + my $class_descr = qq{"${catname}"}; + + push @object_properties, { + class_descr => $class_descr, + class_oid => $catalog->{relation_oid_macro}, + _class_oid => $catalog->{relation_oid}, + oid_index_oid => $oid_index, + oid_catcache_id => $oid_syscache || '-1', + name_catcache_id => $name_syscache || '-1', + attnum_oid => $attnum_oid, + attnum_name => $attnum_name, + attnum_namespace => $attnum_namespace, + attnum_owner => $attnum_owner, + attnum_acl => $attnum_acl, + objtype => $objtype, + is_nsp_name_unique => $is_nsp_name_unique ? 'true' : 'false', } + unless grep { $_ eq $catalog->{relation_oid_macro} } @skip; } # Complain and exit if we found any duplicate OIDs. @@ -441,6 +574,9 @@ my $syscache_info_c = $output_path . 'syscache_info.c.h'; open my $syscache_info_c_fh, '>', $syscache_info_c . $tmpext or die "can't open $syscache_info_c$tmpext: $!"; +my $objectproperty_info = $output_path . 'objectproperty_info.c.h'; +open my $objectproperty_info_fh, '>', $objectproperty_info . $tmpext + or die "can't open $objectproperty_info$tmpext: $!"; # Generate postgres.bki and pg_*_d.h headers. @@ -810,6 +946,35 @@ print $syscache_info_c_fh "};\n"; +# Now generate objectproperty_info + +print_boilerplate($objectproperty_info_fh, "objectproperty_info.h", "object property data"); +print $objectproperty_info_fh "\n"; +foreach my $catname (@catnames) +{ + print $objectproperty_info_fh qq{#include "catalog/${catname}_d.h"\n}; +} +print $objectproperty_info_fh "\n"; +print $objectproperty_info_fh "static const ObjectPropertyType ObjectProperty[] = +{ +"; + +my @class_oids; +foreach my $op (@object_properties) +{ + print $objectproperty_info_fh "\t{\n"; + foreach my $p (sort keys %{$op}) + { + printf $objectproperty_info_fh "\t\t.%s = %s,\n", $p, ${$op}{$p} if $p !~ /^_/ && ${$op}{$p}; + } + push @class_oids, pack('N', ${$op}{_class_oid}); + print $objectproperty_info_fh "\t},\n"; +} + +print $objectproperty_info_fh "};\n"; + +print $objectproperty_info_fh "\nstatic " . PerfectHash::generate_hash_function(\@class_oids, 'objectproperty_hash_func', fixed_key_length => 4); + # We're done emitting data close $bki; close $schemapg; @@ -817,6 +982,7 @@ close $constraints; close $syscache_info_h_fh; close $syscache_info_c_fh; +close $objectproperty_info_fh; # Finally, rename the completed files into place. Catalog::RenameTempFile($bkifile, $tmpext); @@ -825,6 +991,7 @@ Catalog::RenameTempFile($constraints_file, $tmpext); Catalog::RenameTempFile($syscache_info_h, $tmpext); Catalog::RenameTempFile($syscache_info_c, $tmpext); +Catalog::RenameTempFile($objectproperty_info, $tmpext); exit($num_errors != 0 ? 1 : 0); diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index bb4efcad202..6ba84ee86c8 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -32,19 +32,10 @@ #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" -#include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" -#include "catalog/pg_enum.h" -#include "catalog/pg_event_trigger.h" -#include "catalog/pg_extension.h" -#include "catalog/pg_foreign_data_wrapper.h" -#include "catalog/pg_foreign_server.h" #include "catalog/pg_language.h" #include "catalog/pg_largeobject.h" -#include "catalog/pg_largeobject_metadata.h" -#include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" -#include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_parameter_acl.h" #include "catalog/pg_policy.h" @@ -54,8 +45,6 @@ #include "catalog/pg_publication_rel.h" #include "catalog/pg_rewrite.h" #include "catalog/pg_statistic_ext.h" -#include "catalog/pg_subscription.h" -#include "catalog/pg_tablespace.h" #include "catalog/pg_transform.h" #include "catalog/pg_trigger.h" #include "catalog/pg_ts_config.h" @@ -119,6 +108,9 @@ typedef struct * object of this class? */ } ObjectPropertyType; +#if 1 +#include "catalog/objectproperty_info.c.h" +#else static const ObjectPropertyType ObjectProperty[] = { { @@ -640,6 +632,7 @@ static const ObjectPropertyType ObjectProperty[] = false }, }; +#endif /* * This struct maps the string object types as returned by @@ -2791,6 +2784,7 @@ get_object_property_data(Oid class_id) { static const ObjectPropertyType *prop_last = NULL; int index; + uint32 hashkey; /* * A shortcut to speed up multiple consecutive lookups of a particular @@ -2799,7 +2793,13 @@ get_object_property_data(Oid class_id) if (prop_last && prop_last->class_oid == class_id) return prop_last; +#if 1 + hashkey = pg_hton32(class_id); + index = objectproperty_hash_func(&hashkey); + if (index >= 0 && index < lengthof(ObjectProperty)) +#else for (index = 0; index < lengthof(ObjectProperty); index++) +#endif { if (ObjectProperty[index].class_oid == class_id) { diff --git a/src/include/catalog/meson.build b/src/include/catalog/meson.build index 39d8e94ae27..3362733b5ab 100644 --- a/src/include/catalog/meson.build +++ b/src/include/catalog/meson.build @@ -103,6 +103,7 @@ bki_data_f = files(bki_data) input = [] output_files = [ + 'objectproperty_info.c.h', 'postgres.bki', 'system_constraints.sql', 'schemapg.h', @@ -111,6 +112,7 @@ output_files = [ 'system_fk_info.h', ] output_install = [ + dir_data, dir_data, dir_data, dir_include_server / 'catalog', diff --git a/src/tools/pginclude/cpluspluscheck b/src/tools/pginclude/cpluspluscheck index 56ed4235679..49e3ca7a7bd 100755 --- a/src/tools/pginclude/cpluspluscheck +++ b/src/tools/pginclude/cpluspluscheck @@ -119,6 +119,9 @@ do test "$f" = src/include/common/unicode_nonspacing_table.h && continue test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue + test "$f" = src/backend/catalog/objectproperty_info.c.h && continue + test "$f" = src/include/catalog/objectproperty_info.c.h && continue + test "$f" = src/backend/catalog/syscache_info.c.h && continue test "$f" = src/backend/catalog/syscache_info.h && continue test "$f" = src/include/catalog/syscache_info.c.h && continue diff --git a/src/tools/pginclude/headerscheck b/src/tools/pginclude/headerscheck index 477e4dd7e6b..a1ea2ef6925 100755 --- a/src/tools/pginclude/headerscheck +++ b/src/tools/pginclude/headerscheck @@ -114,6 +114,9 @@ do test "$f" = src/include/common/unicode_nonspacing_table.h && continue test "$f" = src/include/common/unicode_east_asian_fw_table.h && continue + test "$f" = src/backend/catalog/objectproperty_info.c.h && continue + test "$f" = src/include/catalog/objectproperty_info.c.h && continue + test "$f" = src/backend/catalog/syscache_info.c.h && continue test "$f" = src/backend/catalog/syscache_info.h && continue test "$f" = src/include/catalog/syscache_info.c.h && continue -- 2.42.0