public inbox for [email protected]help / color / mirror / Atom feed
[PATCH 2/3] Do much more stuff in genbki.pl 50+ messages / 5 participants [nested] [flat]
* [PATCH 2/3] Do much more stuff in genbki.pl @ 2015-02-21 23:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: Andres Freund @ 2015-02-21 23:06 UTC (permalink / raw) --- src/backend/catalog/Catalog.pm | 261 +++++++++++++++++++++++++- src/backend/catalog/Makefile | 7 +- src/backend/catalog/genbki.pl | 163 ++++++++++++++-- src/backend/utils/Gen_fmgrtab.pl | 8 +- src/backend/utils/Makefile | 2 +- src/backend/utils/adt/regproc.c | 82 +------- src/include/c.h | 4 + src/include/catalog/pg_aggregate.h | 4 +- src/include/catalog/pg_amop.h | 4 +- src/include/catalog/pg_amproc.h | 4 +- src/include/catalog/pg_attrdef.h | 2 +- src/include/catalog/pg_attribute.h | 4 +- src/include/catalog/pg_cast.h | 6 +- src/include/catalog/pg_class.h | 2 +- src/include/catalog/pg_depend.h | 4 +- src/include/catalog/pg_description.h | 2 +- src/include/catalog/pg_enum.h | 2 +- src/include/catalog/pg_event_trigger.h | 2 +- src/include/catalog/pg_foreign_data_wrapper.h | 4 +- src/include/catalog/pg_index.h | 4 +- src/include/catalog/pg_inherits.h | 4 +- src/include/catalog/pg_language.h | 6 +- src/include/catalog/pg_opclass.h | 4 +- src/include/catalog/pg_operator.h | 6 +- src/include/catalog/pg_policy.h | 2 +- src/include/catalog/pg_proc.h | 67 +++---- src/include/catalog/pg_range.h | 4 +- src/include/catalog/pg_seclabel.h | 2 +- src/include/catalog/pg_shdepend.h | 4 +- src/include/catalog/pg_shdescription.h | 2 +- src/include/catalog/pg_statistic.h | 2 +- src/include/catalog/pg_trigger.h | 8 +- src/include/catalog/pg_type.h | 8 +- src/test/regress/expected/opr_sanity.out | 22 +-- src/test/regress/expected/rules.out | 64 +++---- 35 files changed, 547 insertions(+), 229 deletions(-) diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm index c7b1c17..d00bc85 100644 --- a/src/backend/catalog/Catalog.pm +++ b/src/backend/catalog/Catalog.pm @@ -16,17 +16,104 @@ package Catalog; use strict; use warnings; +use Carp; + require Exporter; our @ISA = qw(Exporter); -our @EXPORT = (); +our @EXPORT = qw(regstarout regstarin); our @EXPORT_OK = qw(Catalogs RenameTempFile); + +use feature 'say'; +use Data::Dumper; + +sub remember_oid +{ + my $catalogs = shift; + my $oid = shift; + my $data = shift; + + if (!defined $oid) + { + return; + } + + if (defined $catalogs->{oids}->{$oid}) + { + croak "oid $oid is already used: ", + Dumper($catalogs->{oids}->{$oid}), + " while inserting", Dumper($data); + } + + $catalogs->{oids}->{$oid} = $data; +} + +sub assign_oid +{ + my $catalogs = shift; + my $freeoid; + + #say Dumper($catalogs->{oids}); + while (1) + { + if (defined $catalogs->{oids}->{$catalogs->{auto_oid}}) + { + print Dumper($catalogs->{oids}->{$catalogs->{auto_oid}}); + $catalogs->{auto_oid}++; + next; + } + return $catalogs->{auto_oid}++; + } +} + +sub regstarin +{ + my $catalogs = shift; + my $catname = shift; + my $value = shift; + + if ($value ne '-' && $value !~ /^\d+$/) + { + my $replacement = $catalogs->{$catname}->{byname}->{$value}; + if (!defined $replacement) + { + die "could not map $value by name via $catname"; + } + #say "mapping $value to $replacement"; + $value = $replacement; + } + return $value +} + +sub regstarout +{ + my $catalogs = shift; + my $catname = shift; + my $value = shift; + + if ($value ne '-' && $value ne '0' && $value =~ /^\d+$/) + { + my $replacement = $catalogs->{$catname}->{byoid}->{$value}; + #say "mapping $value to $replacement"; + if (!defined $replacement) + { + die "could not map $value via $catname"; + } + $value = $replacement; + } + + return $value +} + + # Call this function with an array of names of header files to parse. # Returns a nested data structure describing the data in the headers. sub Catalogs { my (%catalogs, $catname, $declaring_attributes, $most_recent); $catalogs{names} = []; + $catalogs{oids} = {}; + $catalogs{auto_oid} = 4500; # There are a few types which are given one name in the C source, but a # different name at the SQL level. These are enumerated here. @@ -36,7 +123,10 @@ sub Catalogs 'int64' => 'int8', 'Oid' => 'oid', 'NameData' => 'name', - 'TransactionId' => 'xid'); + 'TransactionId' => 'xid', + 'regtypea' => 'oidvector', + 'pseudoregtype_' => '_oid' + ); foreach my $input_file (@_) { @@ -83,9 +173,9 @@ sub Catalogs { die "DESCR() does not apply to any catalog ($input_file)"; } - if (!defined $most_recent->{oid}) + elsif ($catalog{without_oids} ne '') { - die "DESCR() does not apply to any oid ($input_file)"; + die "DESCR() only applies to tables with oids ($input_file)"; } elsif ($1 ne '') { @@ -102,9 +192,9 @@ sub Catalogs die "SHDESCR() does not apply to any catalog ($input_file)"; } - if (!defined $most_recent->{oid}) + elsif ($catalog{without_oids} ne '') { - die "SHDESCR() does not apply to any oid ($input_file)"; + die "SHDESCR() only applies to tables with oids ($input_file)"; } elsif ($1 ne '') { @@ -117,6 +207,9 @@ sub Catalogs my ($toast_name, $toast_oid, $index_oid) = ($1, $2, $3); push @{ $catalog{data} }, "declare toast $toast_oid $index_oid on $toast_name\n"; + + remember_oid(\%catalogs, $toast_oid, + {catalog => $catname, toast_name => $toast_name}); } elsif (/^DECLARE_(UNIQUE_)?INDEX\(\s*(\w+),\s*(\d+),\s*(.+)\)/) { @@ -128,6 +221,9 @@ sub Catalogs "declare %sindex %s %s %s\n", $is_unique ? 'unique ' : '', $index_name, $index_oid, $using); + + remember_oid(\%catalogs, $index_oid, + {catalog => $catname, toast_name => $index_name}); } elsif (/^BUILD_INDICES/) { @@ -150,6 +246,13 @@ sub Catalogs /BKI_ROWTYPE_OID\((\d+)\)/ ? " rowtype_oid $1" : ''; $catalog{schema_macro} = /BKI_SCHEMA_MACRO/ ? 'True' : ''; $declaring_attributes = 1; + + # skip, these will be in pg_class again + if (!$catalog{bootstrap}) + { + remember_oid(\%catalogs, $catalog{relation_oid}, + {catalog => $catname, relation_name => $catname}); + } } elsif ($declaring_attributes) { @@ -164,10 +267,14 @@ sub Catalogs my %row; my ($atttype, $attname, $attopt) = split /\s+/, $_; die "parse error ($input_file)" unless $attname; + + $row{'original_type'} = $atttype; + if (exists $RENAME_ATTTYPE{$atttype}) { $atttype = $RENAME_ATTTYPE{$atttype}; } + if ($attname =~ /(.*)\[.*\]/) # array attribute { $attname = $1; @@ -196,9 +303,151 @@ sub Catalogs } } } + + # allow to lookup columns by name + $catalog{columns_byname} = {}; + my @columnnames; + my @columntypes; + + foreach my $column (@{ $catalog{columns} }) + { + $catalog{column_byname}{$column->{'name'}} = $column; + push @columnnames, $column->{'name'}; + push @columntypes, $column->{'type'}; + } + $catalog{columnnames} = \@columnnames; + $catalog{columntypes} = \@columntypes; + $catalogs{$catname} = \%catalog; close INPUT_FILE; } + + # now that we parsed all the data, we can do some cleanup of the + # data and build lookup tables + foreach my $catname (@{ $catalogs{names} }) + { + my $catalog = $catalogs{$catname}; + my %byname; + my %byoid; + my $name; + + # Column to use for lookup mapping + if ($catname eq 'pg_type') + { + $name = 'typname'; + } + elsif ($catname eq 'pg_proc') + { + $name = 'proname'; + } + elsif ($catname eq 'pg_class') + { + $name = 'relname'; + } + + foreach my $row (@{ $catalog->{data} }) + { + my %valuesbyname; + + # split data into actual columns, similar grammar to bootscanner.l + my @values = $row->{bki_values} =~ /([^\s\"]+|\"[^\"]*\")(?:\s+|$)/g; + + # remove quotes from the second alternative above + for (@values) { + s/^\"([^\"]*)\"$/$1/; + } + # store values in a more useful format + $row->{values} = \@values; + + # Assign oid if needed + if ($catalog->{without_oids} eq '' && !defined $row->{oid} && + (defined $name || defined $row->{descr})) + { + $row->{oid_is_auto} = 1; + $row->{oid} = assign_oid(\%catalogs).''; + } + + # build lookup table if necessary + if ($name and defined $row->{oid}) + { + @valuesbyname{ @{ $catalog->{columnnames} } } = @values; + $byname{$valuesbyname{$name}} = $row->{oid}; + $byoid{$row->{oid}} = $valuesbyname{$name}; + } + } + if (%byname) + { + $catalog->{byname} = \%byname; + $catalog->{byoid} = \%byoid; + } + } + + # Now that we've built lookup tables, we can resolve some references + foreach my $catname (@{ $catalogs{names} }) + { + my $catalog = $catalogs{$catname}; + + foreach my $row (@{ $catalog->{data} }) + { + my $colno = 0; + foreach my $column (@{ $catalog->{columns} }) + { + my $value = $row->{values}->[$colno]; + + if ($value eq '_null_') + { + # no point in mapping + } + elsif ($column->{type} eq 'regproc') + { + $value = regstarin(\%catalogs, 'pg_proc', $value); + } + elsif ($column->{type} eq 'regtype') + { + $value = regstarin(\%catalogs, 'pg_type', $value); + } + elsif ($column->{type} eq 'regclass') + { + $value = regstarin(\%catalogs, 'pg_class', $value); + } + elsif ($column->{original_type} eq 'regtypea') + { + my @vals; + for my $cval (split /\s+/, $value) + { + if ($cval ne '-' && $cval !~ /^\d+$/) + { + push @vals, regstarin(\%catalogs, 'pg_type', $cval); + } + push @vals, $cval; + } + $value = join(' ', @vals); + } + elsif ($column->{original_type} eq 'pseudoregtype_') + { + my $origvalue = $value; + my @vals; + + # strip curlies + $value =~ s/^\{([^\"]*)\}$/$1/; + + for my $cval (split /,/, $value) + { + $cval = regstarin(\%catalogs, 'pg_type', $cval); + push @vals, $cval; + } + $value = '{'.join(',', @vals).'}'; + } + + # and replace the stored value + $row->{values}->[$colno] = $value; + + $colno++; + } + } + } + + return \%catalogs; } diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index a403c64..d0bcdff 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -17,7 +17,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ pg_operator.o pg_proc.o pg_range.o pg_db_role_setting.o pg_shdepend.o \ pg_type.o storage.o toasting.o -BKIFILES = postgres.bki postgres.description postgres.shdescription +BKIFILES = postgres.bki postgres.data postgres.description postgres.shdescription include $(top_srcdir)/src/backend/common.mk @@ -55,6 +55,8 @@ postgres.description: postgres.bki ; postgres.shdescription: postgres.bki ; +postgres.data: postgres.bki ; + schemapg.h: postgres.bki ; # Technically, this should depend on Makefile.global, but then @@ -64,7 +66,8 @@ schemapg.h: postgres.bki ; # changes. postgres.bki: genbki.pl Catalog.pm $(POSTGRES_BKI_SRCS) $(top_srcdir)/configure $(top_srcdir)/src/include/catalog/duplicate_oids cd $(top_srcdir)/src/include/catalog && $(PERL) ./duplicate_oids - $(PERL) -I $(catalogdir) $< $(pg_includes) --set-version=$(MAJORVERSION) $(POSTGRES_BKI_SRCS) + echo $(PERL) -I $(catalogdir) $< $(pg_includes) --set-version=$(MAJORVERSION) $(POSTGRES_BKI_SRCS) + $(PERL) -I $(catalogdir) $< $(pg_includes) --set-version=$(MAJORVERSION) --srcdir="$(srcdir)" $(POSTGRES_BKI_SRCS) .PHONY: install-data install-data: $(BKIFILES) installdirs diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index a5c78ee..0723555 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -15,13 +15,19 @@ #---------------------------------------------------------------------- use Catalog; +use Data::Dumper; use strict; use warnings; +use feature 'say'; + +use Carp; + my @input_files; our @include_path; my $output_path = ''; +my $srcdir = ''; my $major_version; # Process command line switches. @@ -46,6 +52,10 @@ while (@ARGV) die "Version must be in format nn.nn.\n" if !($major_version =~ /^\d+\.\d+$/); } + elsif ($arg =~ /^--srcdir=(.*)$/) + { + $srcdir = $1; + } else { usage(); @@ -104,6 +114,8 @@ my %schemapg_entries; my @tables_needing_macros; our @types; +my %catalogs_by_name; + # produce output, one catalog at a time foreach my $catname (@{ $catalogs->{names} }) { @@ -116,8 +128,6 @@ foreach my $catname (@{ $catalogs->{names} }) . $catalog->{without_oids} . $catalog->{rowtype_oid} . "\n"; - my %bki_attr; - my @attnames; my $first = 1; print BKI " (\n"; @@ -125,8 +135,6 @@ foreach my $catname (@{ $catalogs->{names} }) { my $attname = $column->{name}; my $atttype = $column->{type}; - $bki_attr{$attname} = $column; - push @attnames, $attname; if (!$first) { @@ -160,24 +168,20 @@ foreach my $catname (@{ $catalogs->{names} }) foreach my $row (@{ $catalog->{data} }) { - # substitute constant values we acquired above - $row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g; - $row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g; - # Save pg_type info for pg_attribute processing below if ($catname eq 'pg_type') { my %type; $type{oid} = $row->{oid}; - @type{@attnames} = split /\s+/, $row->{bki_values}; + @type{@{ $catalog->{columnnames} } } = @{$row->{values}}; push @types, \%type; } # Write to postgres.bki my $oid = $row->{oid} ? "OID = $row->{oid} " : ''; - printf BKI "insert %s( %s)\n", $oid, $row->{bki_values}; + printf BKI "insert %s( %s)\n", $oid, join(' ', map bki_escape($_), @{$row->{values}}); - # Write comments to postgres.description and postgres.shdescription + # Write comments to postgres.description and postgres.shdescription if (defined $row->{descr}) { printf DESCR "%s\t%s\t0\t%s\n", $row->{oid}, $catname, @@ -218,22 +222,27 @@ foreach my $catname (@{ $catalogs->{names} }) my $row = emit_pgattr_row($table_name, $attr, $priornotnull); $row->{attnum} = $attnum; $row->{attstattarget} = '-1'; + if (!defined $row->{attnotnull}) + { + say STDERR Dumper($row); + } $priornotnull &= ($row->{attnotnull} eq 't'); # If it's bootstrapped, put an entry in postgres.bki. if ($is_bootstrap eq ' bootstrap') { - bki_insert($row, @attnames); + bki_insert($row, @{$catalog->{columnnames} }); } # Store schemapg entries for later. + # XXX: Note that this doesn't escape correctly. $row = emit_schemapg_row($row, - grep { $bki_attr{$_}{type} eq 'bool' } @attnames); + grep { $catalog->{column_byname}->{$_}{type} eq 'bool' } @{ $catalog->{columnnames} }); push @{ $schemapg_entries{$table_name} }, '{ ' . join( ', ', grep { defined $_ } - map $row->{$_}, @attnames) . ' }'; + map $row->{$_}, @{ $catalog->{columnnames} }) . ' }'; } # Generate entries for system attributes. @@ -261,7 +270,7 @@ foreach my $catname (@{ $catalogs->{names} }) if $table->{without_oids} eq ' without_oids' && $row->{attname} eq 'oid'; - bki_insert($row, @attnames); + bki_insert($row, @{ $catalog->{columnnames} }); } } } @@ -321,6 +330,103 @@ foreach my $table_name (@tables_needing_macros) # Closing boilerplate for schemapg.h print SCHEMAPG "\n#endif /* SCHEMAPG_H */\n"; +# write out data in a nicer format +my %output; +foreach my $catname (@{ $catalogs->{names} }) +{ + my $catalog = $catalogs->{$catname}; + + my @data; # one row per output row + + foreach my $row (@{ $catalog->{data} }) + { + my %row; + my %rowdata; + + my $colno = 0; + + foreach my $column (@{ $catalog->{columns} }) + { + my $value = $row->{values}->[$colno]; + + # map values back to a more convenient format + if ($value eq '_null_') + { + $value = undef; + } + elsif ($column->{type} eq 'regproc') + { + $value = regstarout($catalogs, 'pg_proc', $value); + } + elsif ($column->{type} eq 'regtype') + { + $value = regstarout($catalogs, 'pg_type', $value); + } + elsif ($column->{type} eq 'regclass') + { + $value = regstarout($catalogs, 'pg_class', $value); + } + elsif ($column->{original_type} eq 'regtypea') + { + my @vals; + for my $cval (split /\s+/, $value) + { + $cval = regstarout($catalogs, 'pg_type', $cval); + push @vals, $cval; + } + $value = join(' ', @vals); + } + elsif ($column->{original_type} eq 'pseudoregtype_') + { + my $origvalue = $value; + my @vals; + + # strip curlies + $value =~ s/^\{([^\"]*)\}$/$1/; + + for my $cval (split /,/, $value) + { + $cval = regstarout($catalogs, 'pg_type', $cval); + push @vals, $cval; + } + $value = '{'.join(',', @vals).'}'; + } + + # only set value if there's one - no need to carry pointless stuff around + if (defined $value) + { + $rowdata{$column->{name}} = $value; + } + + $colno++; + } + + $row{data} = \%rowdata; + + if (defined $row->{descr}) + { + $row{description} = $row->{descr}; + } + + # Dump assigned oids unless automatically asssigned + if (defined $row->{oid} && !defined $row->{oid_is_auto}) + { + $row{oid} = $row->{oid}; + } + push @data, \%row; + } + $output{$catname} = \@data; + + my $newdatafile = $srcdir .'/'. $catname. '.data'; + open NEWDATA, '>', $newdatafile . $tmpext + or die "can't open $newdatafile$tmpext: $!"; + my $dumper = Data::Dumper->new([\@data]); + print NEWDATA $dumper->Sortkeys(1)->Terse(1)->Indent(1)->Quotekeys(0)->Dump; + close NEWDATA; + Catalog::RenameTempFile($newdatafile, $tmpext); +} + + # We're done emitting data close BKI; close SCHEMAPG; @@ -418,12 +524,37 @@ sub emit_pgattr_row } # Write a pg_attribute entry to postgres.bki +sub bki_escape +{ + my $val = shift; + + if (!defined $val) + { + croak("undefined value"); + } + elsif ($val eq 'PGUID') + { + $val = $BOOTSTRAP_SUPERUSERID; + } + elsif ($val eq 'PGNSP') + { + $val = $PG_CATALOG_NAMESPACE; + } + elsif ($val eq '_null_') + { + } + elsif ($val =~ /[^0-9a-zA-Z]/ || $val eq '') + { + $val = '"'.$val.'"'; + } + return $val; +} sub bki_insert { my $row = shift; my @attnames = @_; my $oid = $row->{oid} ? "OID = $row->{oid} " : ''; - my $bki_values = join ' ', map $row->{$_}, @attnames; + my $bki_values = join ' ', map bki_escape($row->{$_}), @attnames; printf BKI "insert %s( %s)\n", $oid, $bki_values; } diff --git a/src/backend/utils/Gen_fmgrtab.pl b/src/backend/utils/Gen_fmgrtab.pl index f5cc265..e4e2ee6 100644 --- a/src/backend/utils/Gen_fmgrtab.pl +++ b/src/backend/utils/Gen_fmgrtab.pl @@ -19,14 +19,14 @@ use strict; use warnings; # Collect arguments -my $infile; # pg_proc.h +my @input_files; my $output_path = ''; while (@ARGV) { my $arg = shift @ARGV; if ($arg !~ /^-/) { - $infile = $arg; + push @input_files, $arg; } elsif ($arg =~ /^-o/) { @@ -45,7 +45,7 @@ if ($output_path ne '' && substr($output_path, -1) ne '/') } # Read all the data from the include/catalog files. -my $catalogs = Catalog::Catalogs($infile); +my $catalogs = Catalog::Catalogs(@input_files); # Collect the raw data from pg_proc.h. my @fmgr = (); @@ -110,7 +110,6 @@ qq|/*------------------------------------------------------------------------- * ****************************** * * It has been GENERATED by $0 - * from $infile * *------------------------------------------------------------------------- */ @@ -146,7 +145,6 @@ qq|/*------------------------------------------------------------------------- * ****************************** * * It has been GENERATED by $0 - * from $infile * *------------------------------------------------------------------------- */ diff --git a/src/backend/utils/Makefile b/src/backend/utils/Makefile index 8374533..f16e1a9 100644 --- a/src/backend/utils/Makefile +++ b/src/backend/utils/Makefile @@ -24,7 +24,7 @@ $(SUBDIRS:%=%-recursive): fmgroids.h fmgroids.h: fmgrtab.c ; fmgrtab.c: Gen_fmgrtab.pl $(catalogdir)/Catalog.pm $(top_srcdir)/src/include/catalog/pg_proc.h - $(PERL) -I $(catalogdir) $< $(top_srcdir)/src/include/catalog/pg_proc.h + $(PERL) -I $(catalogdir) $< $(top_srcdir)/src/include/catalog/pg_proc.h $(top_srcdir)/src/include/catalog/pg_type.h errcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-errcodes.pl $(PERL) $(srcdir)/generate-errcodes.pl $< > $@ diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 3d1bb32..f7c99ff 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -84,51 +84,11 @@ regprocin(PG_FUNCTION_ARGS) /* Else it's a name, possibly schema-qualified */ /* - * In bootstrap mode we assume the given name is not schema-qualified, and - * just search pg_proc for a unique match. This is needed for - * initializing other system catalogs (pg_namespace may not exist yet, and - * certainly there are no schemas other than pg_catalog). + * We should never get here in bootstrap mode, as all references should + * have been resolved by genbki.pl. */ if (IsBootstrapProcessingMode()) - { - int matches = 0; - Relation hdesc; - ScanKeyData skey[1]; - SysScanDesc sysscan; - HeapTuple tuple; - - ScanKeyInit(&skey[0], - Anum_pg_proc_proname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(pro_name_or_oid)); - - hdesc = heap_open(ProcedureRelationId, AccessShareLock); - sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true, - NULL, 1, skey); - - while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) - { - result = (RegProcedure) HeapTupleGetOid(tuple); - if (++matches > 1) - break; - } - - systable_endscan(sysscan); - heap_close(hdesc, AccessShareLock); - - if (matches == 0) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("function \"%s\" does not exist", pro_name_or_oid))); - - else if (matches > 1) - ereport(ERROR, - (errcode(ERRCODE_AMBIGUOUS_FUNCTION), - errmsg("more than one function named \"%s\"", - pro_name_or_oid))); - - PG_RETURN_OID(result); - } + elog(ERROR, "regprocin with textual values is not supported in bootstrap mode"); /* * Normal case: parse the name into components and see if it matches any @@ -1196,41 +1156,11 @@ regtypein(PG_FUNCTION_ARGS) /* Else it's a type name, possibly schema-qualified or decorated */ /* - * In bootstrap mode we assume the given name is not schema-qualified, and - * just search pg_type for a match. This is needed for initializing other - * system catalogs (pg_namespace may not exist yet, and certainly there - * are no schemas other than pg_catalog). + * We should never get here in bootstrap mode, as all references should + * have been resolved by genbki.pl. */ if (IsBootstrapProcessingMode()) - { - Relation hdesc; - ScanKeyData skey[1]; - SysScanDesc sysscan; - HeapTuple tuple; - - ScanKeyInit(&skey[0], - Anum_pg_type_typname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(typ_name_or_oid)); - - hdesc = heap_open(TypeRelationId, AccessShareLock); - sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true, - NULL, 1, skey); - - if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) - result = HeapTupleGetOid(tuple); - else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("type \"%s\" does not exist", typ_name_or_oid))); - - /* We assume there can be only one match */ - - systable_endscan(sysscan); - heap_close(hdesc, AccessShareLock); - - PG_RETURN_OID(result); - } + elog(ERROR, "regtypein with textual values is not supported in bootstrap mode"); /* * Normal case: invoke the full parser to deal with special cases such as diff --git a/src/include/c.h b/src/include/c.h index ee615ee..d04c686 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -347,6 +347,10 @@ typedef double float8; typedef Oid regproc; typedef regproc RegProcedure; + +typedef Oid regtype; +typedef Oid regclass; + typedef uint32 TransactionId; typedef uint32 LocalTransactionId; diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 5e1196d..598e5bc 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -61,9 +61,9 @@ CATALOG(pg_aggregate,2600) BKI_WITHOUT_OIDS bool aggfinalextra; bool aggmfinalextra; Oid aggsortop; - Oid aggtranstype; + regtype aggtranstype; int32 aggtransspace; - Oid aggmtranstype; + regtype aggmtranstype; int32 aggmtransspace; #ifdef CATALOG_VARLEN /* variable-length fields start here */ diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index 5aab896..40aee6a 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -56,8 +56,8 @@ CATALOG(pg_amop,2602) { Oid amopfamily; /* the index opfamily this entry is for */ - Oid amoplefttype; /* operator's left input data type */ - Oid amoprighttype; /* operator's right input data type */ + regtype amoplefttype; /* operator's left input data type */ + regtype amoprighttype; /* operator's right input data type */ int16 amopstrategy; /* operator strategy number */ char amoppurpose; /* is operator for 's'earch or 'o'rdering? */ Oid amopopr; /* the operator's pg_operator OID */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 49d3d13..c4fa1c5 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -45,8 +45,8 @@ CATALOG(pg_amproc,2603) { Oid amprocfamily; /* the index opfamily this entry is for */ - Oid amproclefttype; /* procedure's left input data type */ - Oid amprocrighttype; /* procedure's right input data type */ + regtype amproclefttype; /* procedure's left input data type */ + regtype amprocrighttype; /* procedure's right input data type */ int16 amprocnum; /* support procedure index */ regproc amproc; /* OID of the proc */ } FormData_pg_amproc; diff --git a/src/include/catalog/pg_attrdef.h b/src/include/catalog/pg_attrdef.h index 1011040..171edbb 100644 --- a/src/include/catalog/pg_attrdef.h +++ b/src/include/catalog/pg_attrdef.h @@ -30,7 +30,7 @@ CATALOG(pg_attrdef,2604) { - Oid adrelid; /* OID of table containing attribute */ + regclass adrelid; /* OID of table containing attribute */ int16 adnum; /* attnum of attribute */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index 87a3462..21b8854 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -35,7 +35,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BKI_SCHEMA_MACRO { - Oid attrelid; /* OID of relation containing this attribute */ + regclass attrelid; /* OID of relation containing this attribute */ NameData attname; /* name of attribute */ /* @@ -45,7 +45,7 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK * attributes of this instance, so they had better match or Postgres will * fail. */ - Oid atttypid; + regtype atttypid; /* * attstattarget is the target number of statistics datapoints to collect diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index b314369..55457cc 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -32,9 +32,9 @@ CATALOG(pg_cast,2605) { - Oid castsource; /* source datatype for cast */ - Oid casttarget; /* destination datatype for cast */ - Oid castfunc; /* cast function; 0 = binary coercible */ + regtype castsource; /* source datatype for cast */ + regtype casttarget; /* destination datatype for cast */ + regproc castfunc; /* cast function; 0 = binary coercible */ char castcontext; /* contexts in which cast can be used */ char castmethod; /* cast method */ } FormData_pg_cast; diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 8b4c35c..ccf33e6 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -35,7 +35,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO Oid relnamespace; /* OID of namespace containing this class */ Oid reltype; /* OID of entry in pg_type for table's * implicit row type */ - Oid reloftype; /* OID of entry in pg_type for underlying + regtype reloftype; /* OID of entry in pg_type for underlying * composite type */ Oid relowner; /* class owner */ Oid relam; /* index access method; 0 if not an index */ diff --git a/src/include/catalog/pg_depend.h b/src/include/catalog/pg_depend.h index 038e564..e276a3d 100644 --- a/src/include/catalog/pg_depend.h +++ b/src/include/catalog/pg_depend.h @@ -35,14 +35,14 @@ CATALOG(pg_depend,2608) BKI_WITHOUT_OIDS * * These fields are all zeroes for a DEPENDENCY_PIN entry. */ - Oid classid; /* OID of table containing object */ + regclass classid; /* OID of table containing object */ Oid objid; /* OID of object itself */ int32 objsubid; /* column number, or 0 if not used */ /* * Identification of the independent (referenced) object. */ - Oid refclassid; /* OID of table containing object */ + regclass refclassid; /* OID of table containing object */ Oid refobjid; /* OID of object itself */ int32 refobjsubid; /* column number, or 0 if not used */ diff --git a/src/include/catalog/pg_description.h b/src/include/catalog/pg_description.h index 692455f..c38a4b8 100644 --- a/src/include/catalog/pg_description.h +++ b/src/include/catalog/pg_description.h @@ -48,7 +48,7 @@ CATALOG(pg_description,2609) BKI_WITHOUT_OIDS { Oid objoid; /* OID of object itself */ - Oid classoid; /* OID of table containing object */ + regclass classoid; /* OID of table containing object */ int32 objsubid; /* column number, or 0 if not used */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h index 1ce9795..2ea9a75 100644 --- a/src/include/catalog/pg_enum.h +++ b/src/include/catalog/pg_enum.h @@ -33,7 +33,7 @@ CATALOG(pg_enum,3501) { - Oid enumtypid; /* OID of owning enum type */ + regtype enumtypid; /* OID of owning enum type */ float4 enumsortorder; /* sort position of this enum value */ NameData enumlabel; /* text representation of enum value */ } FormData_pg_enum; diff --git a/src/include/catalog/pg_event_trigger.h b/src/include/catalog/pg_event_trigger.h index ca81a81..0f27e45 100644 --- a/src/include/catalog/pg_event_trigger.h +++ b/src/include/catalog/pg_event_trigger.h @@ -33,7 +33,7 @@ CATALOG(pg_event_trigger,3466) NameData evtname; /* trigger's name */ NameData evtevent; /* trigger's event */ Oid evtowner; /* trigger's owner */ - Oid evtfoid; /* OID of function to be called */ + regproc evtfoid; /* OID of function to be called */ char evtenabled; /* trigger's firing configuration WRT * session_replication_role */ diff --git a/src/include/catalog/pg_foreign_data_wrapper.h b/src/include/catalog/pg_foreign_data_wrapper.h index 1c7db04..046e5e2 100644 --- a/src/include/catalog/pg_foreign_data_wrapper.h +++ b/src/include/catalog/pg_foreign_data_wrapper.h @@ -32,8 +32,8 @@ CATALOG(pg_foreign_data_wrapper,2328) { NameData fdwname; /* foreign-data wrapper name */ Oid fdwowner; /* FDW owner */ - Oid fdwhandler; /* handler function, or 0 if none */ - Oid fdwvalidator; /* option validation function, or 0 if none */ + regproc fdwhandler; /* handler function, or 0 if none */ + regproc fdwvalidator; /* option validation function, or 0 if none */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ aclitem fdwacl[1]; /* access permissions */ diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h index 45c96e3..92eba70 100644 --- a/src/include/catalog/pg_index.h +++ b/src/include/catalog/pg_index.h @@ -30,8 +30,8 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO { - Oid indexrelid; /* OID of the index */ - Oid indrelid; /* OID of the relation it indexes */ + regclass indexrelid; /* OID of the index */ + regclass indrelid; /* OID of the relation it indexes */ int16 indnatts; /* number of columns in index */ bool indisunique; /* is this a unique index? */ bool indisprimary; /* is this index for primary key? */ diff --git a/src/include/catalog/pg_inherits.h b/src/include/catalog/pg_inherits.h index af48d24..17895d3 100644 --- a/src/include/catalog/pg_inherits.h +++ b/src/include/catalog/pg_inherits.h @@ -30,8 +30,8 @@ CATALOG(pg_inherits,2611) BKI_WITHOUT_OIDS { - Oid inhrelid; - Oid inhparent; + regclass inhrelid; + regclass inhparent; int32 inhseqno; } FormData_pg_inherits; diff --git a/src/include/catalog/pg_language.h b/src/include/catalog/pg_language.h index b82cca2..555b050 100644 --- a/src/include/catalog/pg_language.h +++ b/src/include/catalog/pg_language.h @@ -34,9 +34,9 @@ CATALOG(pg_language,2612) Oid lanowner; /* Language's owner */ bool lanispl; /* Is a procedural language */ bool lanpltrusted; /* PL is trusted */ - Oid lanplcallfoid; /* Call handler for PL */ - Oid laninline; /* Optional anonymous-block handler function */ - Oid lanvalidator; /* Optional validation function */ + regproc lanplcallfoid; /* Call handler for PL */ + regproc laninline; /* Optional anonymous-block handler function */ + regproc lanvalidator; /* Optional validation function */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ aclitem lanacl[1]; /* Access privileges */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index b3acef6..f911afb 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -55,9 +55,9 @@ CATALOG(pg_opclass,2616) Oid opcnamespace; /* namespace of this opclass */ Oid opcowner; /* opclass owner */ Oid opcfamily; /* containing operator family */ - Oid opcintype; /* type of data indexed by opclass */ + regtype opcintype; /* type of data indexed by opclass */ bool opcdefault; /* T if opclass is default for opcintype */ - Oid opckeytype; /* type of data in index, or InvalidOid */ + regtype opckeytype; /* type of data in index, or InvalidOid */ } FormData_pg_opclass; /* ---------------- diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index af991d3..2a8edad 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -40,9 +40,9 @@ CATALOG(pg_operator,2617) char oprkind; /* 'l', 'r', or 'b' */ bool oprcanmerge; /* can be used in merge join? */ bool oprcanhash; /* can be used in hash join? */ - Oid oprleft; /* left arg type, or 0 if 'l' oprkind */ - Oid oprright; /* right arg type, or 0 if 'r' oprkind */ - Oid oprresult; /* result datatype */ + regtype oprleft; /* left arg type, or 0 if 'l' oprkind */ + regtype oprright; /* right arg type, or 0 if 'r' oprkind */ + regtype oprresult; /* result datatype */ Oid oprcom; /* OID of commutator oper, or 0 if none */ Oid oprnegate; /* OID of negator oper, or 0 if none */ regproc oprcode; /* OID of underlying function */ diff --git a/src/include/catalog/pg_policy.h b/src/include/catalog/pg_policy.h index ae71f3f..6299e93 100644 --- a/src/include/catalog/pg_policy.h +++ b/src/include/catalog/pg_policy.h @@ -21,7 +21,7 @@ CATALOG(pg_policy,3256) { NameData polname; /* Policy name. */ - Oid polrelid; /* Oid of the relation with policy. */ + regclass polrelid; /* Oid of the relation with policy. */ char polcmd; /* One of ACL_*_CHR, or '*' for all */ #ifdef CATALOG_VARLEN diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 4268b99..4d78210 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -25,6 +25,9 @@ #include "catalog/genbki.h" +typedef Oid pseudoregtype_; +typedef oidvector regtypea; + /* ---------------- * pg_proc definition. cpp turns this into * typedef struct FormData_pg_proc @@ -41,7 +44,7 @@ CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO Oid prolang; /* OID of pg_language entry */ float4 procost; /* estimated execution cost */ float4 prorows; /* estimated # of rows out (if proretset) */ - Oid provariadic; /* element type of variadic array, or 0 */ + regtype provariadic; /* element type of variadic array, or 0 */ regproc protransform; /* transforms calls to it during planning */ bool proisagg; /* is it an aggregate? */ bool proiswindow; /* is it a window function? */ @@ -52,17 +55,17 @@ CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO char provolatile; /* see PROVOLATILE_ categories below */ int16 pronargs; /* number of arguments */ int16 pronargdefaults; /* number of arguments with defaults */ - Oid prorettype; /* OID of result type */ + regtype prorettype; /* OID of result type */ /* * variable-length fields start here, but we allow direct access to * proargtypes */ - oidvector proargtypes; /* parameter types (excludes OUT params) */ + regtypea proargtypes; /* parameter types (excludes OUT params) */ #ifdef CATALOG_VARLEN - Oid proallargtypes[1]; /* all param types (NULL if IN only) */ - char proargmodes[1]; /* parameter modes (NULL if IN only) */ + pseudoregtype_ proallargtypes; /* all param types (NULL if IN only) */ + char proargmodes[1]; /* FIXME: type: parameter modes (NULL if IN only) */ text proargnames[1]; /* parameter names (NULL if no names) */ pg_node_tree proargdefaults;/* list of expression trees for argument * defaults (NULL if none) */ @@ -153,7 +156,7 @@ DATA(insert OID = 1245 ( charin PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 18 DESCR("I/O"); DATA(insert OID = 33 ( charout PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "18" _null_ _null_ _null_ _null_ charout _null_ _null_ _null_ )); DESCR("I/O"); -DATA(insert OID = 34 ( namein PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 19 "2275" _null_ _null_ _null_ _null_ namein _null_ _null_ _null_ )); +DATA(insert OID = 34 ( namein PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 name "2275" _null_ _null_ _null_ _null_ namein _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 35 ( nameout PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "19" _null_ _null_ _null_ _null_ nameout _null_ _null_ _null_ )); DESCR("I/O"); @@ -674,11 +677,11 @@ DATA(insert OID = 401 ( text PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 DESCR("convert char(n) to text"); DATA(insert OID = 406 ( text PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "19" _null_ _null_ _null_ _null_ name_text _null_ _null_ _null_ )); DESCR("convert name to text"); -DATA(insert OID = 407 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 19 "25" _null_ _null_ _null_ _null_ text_name _null_ _null_ _null_ )); +DATA(insert OID = 407 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 name "25" _null_ _null_ _null_ _null_ text_name _null_ _null_ _null_ )); DESCR("convert text to name"); DATA(insert OID = 408 ( bpchar PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1042 "19" _null_ _null_ _null_ _null_ name_bpchar _null_ _null_ _null_ )); DESCR("convert name to char(n)"); -DATA(insert OID = 409 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 19 "1042" _null_ _null_ _null_ _null_ bpchar_name _null_ _null_ _null_ )); +DATA(insert OID = 409 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 name "1042" _null_ _null_ _null_ _null_ bpchar_name _null_ _null_ _null_ )); DESCR("convert char(n) to name"); DATA(insert OID = 440 ( hashgettuple PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ hashgettuple _null_ _null_ _null_ )); @@ -819,7 +822,7 @@ DATA(insert OID = 680 ( oidvectorge PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 DATA(insert OID = 681 ( oidvectorgt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "30 30" _null_ _null_ _null_ _null_ oidvectorgt _null_ _null_ _null_ )); /* OIDS 700 - 799 */ -DATA(insert OID = 710 ( getpgusername PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); +DATA(insert OID = 710 ( getpgusername PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); DESCR("deprecated, use current_user instead"); DATA(insert OID = 716 ( oidlt PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0 16 "26 26" _null_ _null_ _null_ _null_ oidlt _null_ _null_ _null_ )); DATA(insert OID = 717 ( oidle PGNSP PGUID 12 1 0 0 0 f f f t t f i 2 0 16 "26 26" _null_ _null_ _null_ _null_ oidle _null_ _null_ _null_ )); @@ -851,9 +854,9 @@ DATA(insert OID = 741 ( text_le PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 DATA(insert OID = 742 ( text_gt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_gt _null_ _null_ _null_ )); DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_ge _null_ _null_ _null_ )); -DATA(insert OID = 745 ( current_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); +DATA(insert OID = 745 ( current_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); DESCR("current user name"); -DATA(insert OID = 746 ( session_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ session_user _null_ _null_ _null_ )); +DATA(insert OID = 746 ( session_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ session_user _null_ _null_ _null_ )); DESCR("session user name"); DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ array_eq _null_ _null_ _null_ )); @@ -1017,7 +1020,7 @@ DATA(insert OID = 859 ( namenlike PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 DATA(insert OID = 860 ( bpchar PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1042 "18" _null_ _null_ _null_ _null_ char_bpchar _null_ _null_ _null_ )); DESCR("convert char to char(n)"); -DATA(insert OID = 861 ( current_database PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_database _null_ _null_ _null_ )); +DATA(insert OID = 861 ( current_database PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ current_database _null_ _null_ _null_ )); DESCR("name of the current database"); DATA(insert OID = 817 ( current_query PGNSP PGUID 12 1 0 0 0 f f f f f f v 0 0 25 "" _null_ _null_ _null_ _null_ current_query _null_ _null_ _null_ )); DESCR("get the currently executing query"); @@ -1341,11 +1344,11 @@ DESCR("transform an interval length coercion"); DATA(insert OID = 1200 ( interval PGNSP PGUID 12 1 0 0 interval_transform f f f f t f i 2 0 1186 "1186 23" _null_ _null_ _null_ _null_ interval_scale _null_ _null_ _null_ )); DESCR("adjust interval precision"); -DATA(insert OID = 1215 ( obj_description PGNSP PGUID 14 100 0 0 0 f f f f t f s 2 0 25 "26 19" _null_ _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = PGNSP) and objsubid = 0" _null_ _null_ _null_ )); +DATA(insert OID = 1215 ( obj_description PGNSP PGUID 14 100 0 0 0 f f f f t f s 2 0 25 "26 19" _null_ _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = 11) and objsubid = 0" _null_ _null_ _null_ )); DESCR("get description for object id and catalog name"); DATA(insert OID = 1216 ( col_description PGNSP PGUID 14 100 0 0 0 f f f f t f s 2 0 25 "26 23" _null_ _null_ _null_ _null_ "select description from pg_catalog.pg_description where objoid = $1 and classoid = ''pg_catalog.pg_class''::pg_catalog.regclass and objsubid = $2" _null_ _null_ _null_ )); DESCR("get description for table column"); -DATA(insert OID = 1993 ( shobj_description PGNSP PGUID 14 100 0 0 0 f f f f t f s 2 0 25 "26 19" _null_ _null_ _null_ _null_ "select description from pg_catalog.pg_shdescription where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = PGNSP)" _null_ _null_ _null_ )); +DATA(insert OID = 1993 ( shobj_description PGNSP PGUID 14 100 0 0 0 f f f f t f s 2 0 25 "26 19" _null_ _null_ _null_ _null_ "select description from pg_catalog.pg_shdescription where objoid = $1 and classoid = (select oid from pg_catalog.pg_class where relname = $2 and relnamespace = 11)" _null_ _null_ _null_ )); DESCR("get description for object id and shared catalog name"); DATA(insert OID = 1217 ( date_trunc PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 1184 "25 1184" _null_ _null_ _null_ _null_ timestamptz_trunc _null_ _null_ _null_ )); @@ -1605,12 +1608,12 @@ DESCR("absolute value"); /* OIDS 1400 - 1499 */ -DATA(insert OID = 1400 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 19 "1043" _null_ _null_ _null_ _null_ text_name _null_ _null_ _null_ )); +DATA(insert OID = 1400 ( name PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 name "1043" _null_ _null_ _null_ _null_ text_name _null_ _null_ _null_ )); DESCR("convert varchar to name"); DATA(insert OID = 1401 ( varchar PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1043 "19" _null_ _null_ _null_ _null_ name_text _null_ _null_ _null_ )); DESCR("convert name to varchar"); -DATA(insert OID = 1402 ( current_schema PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_schema _null_ _null_ _null_ )); +DATA(insert OID = 1402 ( current_schema PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ current_schema _null_ _null_ _null_ )); DESCR("current schema name"); DATA(insert OID = 1403 ( current_schemas PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 1003 "16" _null_ _null_ _null_ _null_ current_schemas _null_ _null_ _null_ )); DESCR("current schema search list"); @@ -1967,11 +1970,11 @@ DESCR("convert int8 number to hex"); /* for character set encoding support */ /* return database encoding name */ -DATA(insert OID = 1039 ( getdatabaseencoding PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ getdatabaseencoding _null_ _null_ _null_ )); +DATA(insert OID = 1039 ( getdatabaseencoding PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ getdatabaseencoding _null_ _null_ _null_ )); DESCR("encoding name of current database"); /* return client encoding name i.e. session encoding */ -DATA(insert OID = 810 ( pg_client_encoding PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ pg_client_encoding _null_ _null_ _null_ )); +DATA(insert OID = 810 ( pg_client_encoding PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 name "" _null_ _null_ _null_ _null_ pg_client_encoding _null_ _null_ _null_ )); DESCR("encoding name of current database"); DATA(insert OID = 1713 ( length PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 23 "17 19" _null_ _null_ _null_ _null_ length_in_encoding _null_ _null_ _null_ )); @@ -1989,7 +1992,7 @@ DESCR("convert string with specified encoding names"); DATA(insert OID = 1264 ( pg_char_to_encoding PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "19" _null_ _null_ _null_ _null_ PG_char_to_encoding _null_ _null_ _null_ )); DESCR("convert encoding name to encoding id"); -DATA(insert OID = 1597 ( pg_encoding_to_char PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 19 "23" _null_ _null_ _null_ _null_ PG_encoding_to_char _null_ _null_ _null_ )); +DATA(insert OID = 1597 ( pg_encoding_to_char PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 name "23" _null_ _null_ _null_ _null_ PG_encoding_to_char _null_ _null_ _null_ )); DESCR("convert encoding id to encoding name"); DATA(insert OID = 2319 ( pg_encoding_max_length PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "23" _null_ _null_ _null_ _null_ pg_encoding_max_length_sql _null_ _null_ _null_ )); @@ -2005,7 +2008,7 @@ DATA(insert OID = 1640 ( pg_get_viewdef PGNSP PGUID 12 1 0 0 0 f f f f t f s DESCR("select statement of a view"); DATA(insert OID = 1641 ( pg_get_viewdef PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_get_viewdef _null_ _null_ _null_ )); DESCR("select statement of a view"); -DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 19 "26" _null_ _null_ _null_ _null_ pg_get_userbyid _null_ _null_ _null_ )); +DATA(insert OID = 1642 ( pg_get_userbyid PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 name "26" _null_ _null_ _null_ _null_ pg_get_userbyid _null_ _null_ _null_ )); DESCR("role name by OID (with fallback)"); DATA(insert OID = 1643 ( pg_get_indexdef PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "26" _null_ _null_ _null_ _null_ pg_get_indexdef _null_ _null_ _null_ )); DESCR("index description"); @@ -3765,7 +3768,7 @@ DATA(insert OID = 2420 ( oidvectorrecv PGNSP PGUID 12 1 0 0 0 f f f f t f i DESCR("I/O"); DATA(insert OID = 2421 ( oidvectorsend PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "30" _null_ _null_ _null_ _null_ oidvectorsend _null_ _null_ _null_ )); DESCR("I/O"); -DATA(insert OID = 2422 ( namerecv PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 19 "2281" _null_ _null_ _null_ _null_ namerecv _null_ _null_ _null_ )); +DATA(insert OID = 2422 ( namerecv PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 name "2281" _null_ _null_ _null_ _null_ namerecv _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2423 ( namesend PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 17 "19" _null_ _null_ _null_ _null_ namesend _null_ _null_ _null_ )); DESCR("I/O"); @@ -5065,30 +5068,30 @@ DATA(insert OID = 3473 ( spg_range_quad_leaf_consistent PGNSP PGUID 12 1 0 0 0 DESCR("SP-GiST support for quad tree over range"); /* replication slots */ -DATA(insert OID = 3779 ( pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,19,3220}" "{i,o,o}" "{slot_name,slot_name,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ )); +DATA(insert ( pg_create_physical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2249 "19" "{19,19,3220}" "{i,o,o}" "{slot_name,slot_name,xlog_position}" _null_ pg_create_physical_replication_slot _null_ _null_ _null_ )); DESCR("create a physical replication slot"); -DATA(insert OID = 3780 ( pg_drop_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "19" _null_ _null_ _null_ _null_ pg_drop_replication_slot _null_ _null_ _null_ )); +DATA(insert ( pg_drop_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 1 0 2278 "19" _null_ _null_ _null_ _null_ pg_drop_replication_slot _null_ _null_ _null_ )); DESCR("drop a replication slot"); -DATA(insert OID = 3781 ( pg_get_replication_slots PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{19,19,25,26,16,28,28,3220}" "{o,o,o,o,o,o,o,o}" "{slot_name,plugin,slot_type,datoid,active,xmin,catalog_xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ )); +DATA(insert ( pg_get_replication_slots PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{name,name,text,oid,bool,xid,xid,pg_lsn}" "{o,o,o,o,o,o,o,o}" "{slot_name,plugin,slot_type,datoid,active,xmin,catalog_xmin,restart_lsn}" _null_ pg_get_replication_slots _null_ _null_ _null_ )); DESCR("information about replication slots currently in use"); -DATA(insert OID = 3786 ( pg_create_logical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 2 0 2249 "19 19" "{19,19,25,3220}" "{i,i,o,o}" "{slot_name,plugin,slot_name,xlog_position}" _null_ pg_create_logical_replication_slot _null_ _null_ _null_ )); +DATA(insert ( pg_create_logical_replication_slot PGNSP PGUID 12 1 0 0 0 f f f f f f v 2 0 2249 "19 19" "{19,19,25,3220}" "{i,i,o,o}" "{slot_name,plugin,slot_name,xlog_position}" _null_ pg_create_logical_replication_slot _null_ _null_ _null_ )); DESCR("set up a logical replication slot"); -DATA(insert OID = 3782 ( pg_logical_slot_get_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,25}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_get_changes _null_ _null_ _null_ )); +DATA(insert ( pg_logical_slot_get_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,25}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_get_changes _null_ _null_ _null_ )); DESCR("get changes from replication slot"); -DATA(insert OID = 3783 ( pg_logical_slot_get_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_get_binary_changes _null_ _null_ _null_ )); +DATA(insert ( pg_logical_slot_get_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_get_binary_changes _null_ _null_ _null_ )); DESCR("get binary changes from replication slot"); -DATA(insert OID = 3784 ( pg_logical_slot_peek_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,25}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_changes _null_ _null_ _null_ )); +DATA(insert ( pg_logical_slot_peek_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,25}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_changes _null_ _null_ _null_ )); DESCR("peek at changes from replication slot"); -DATA(insert OID = 3785 ( pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ )); +DATA(insert ( pg_logical_slot_peek_binary_changes PGNSP PGUID 12 1000 1000 25 0 f f f f f t v 4 0 2249 "19 3220 23 1009" "{19,3220,23,1009,3220,28,17}" "{i,i,i,v,o,o,o}" "{slot_name,upto_lsn,upto_nchanges,options,location,xid,data}" _null_ pg_logical_slot_peek_binary_changes _null_ _null_ _null_ )); DESCR("peek at binary changes from replication slot"); /* event triggers */ -DATA(insert OID = 3566 ( pg_event_trigger_dropped_objects PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ )); +DATA(insert ( pg_event_trigger_dropped_objects PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ )); DESCR("list objects dropped by the current command"); -DATA(insert OID = 4566 ( pg_event_trigger_table_rewrite_oid PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 26 "" "{26}" "{o}" "{oid}" _null_ pg_event_trigger_table_rewrite_oid _null_ _null_ _null_ )); +DATA(insert ( pg_event_trigger_table_rewrite_oid PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 26 "" "{26}" "{o}" "{oid}" _null_ pg_event_trigger_table_rewrite_oid _null_ _null_ _null_ )); DESCR("return Oid of the table getting rewritten"); -DATA(insert OID = 4567 ( pg_event_trigger_table_rewrite_reason PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_event_trigger_table_rewrite_reason _null_ _null_ _null_ )); +DATA(insert ( pg_event_trigger_table_rewrite_reason PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_event_trigger_table_rewrite_reason _null_ _null_ _null_ )); DESCR("return reason code for table getting rewritten"); /* generic transition functions for ordered-set aggregates */ diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h index 9a15890..c493b8d 100644 --- a/src/include/catalog/pg_range.h +++ b/src/include/catalog/pg_range.h @@ -33,8 +33,8 @@ CATALOG(pg_range,3541) BKI_WITHOUT_OIDS { - Oid rngtypid; /* OID of owning range type */ - Oid rngsubtype; /* OID of range's element type (subtype) */ + regtype rngtypid; /* OID of owning range type */ + regtype rngsubtype; /* OID of range's element type (subtype) */ Oid rngcollation; /* collation for this range type, or 0 */ Oid rngsubopc; /* subtype's btree opclass */ regproc rngcanonical; /* canonicalize range, or 0 */ diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h index c9f5b0c..87ac5ae 100644 --- a/src/include/catalog/pg_seclabel.h +++ b/src/include/catalog/pg_seclabel.h @@ -23,7 +23,7 @@ CATALOG(pg_seclabel,3596) BKI_WITHOUT_OIDS { Oid objoid; /* OID of the object itself */ - Oid classoid; /* OID of table containing the object */ + regclass classoid; /* OID of table containing the object */ int32 objsubid; /* column number, or 0 if not used */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ diff --git a/src/include/catalog/pg_shdepend.h b/src/include/catalog/pg_shdepend.h index 1268698..95e40ec 100644 --- a/src/include/catalog/pg_shdepend.h +++ b/src/include/catalog/pg_shdepend.h @@ -37,7 +37,7 @@ CATALOG(pg_shdepend,1214) BKI_SHARED_RELATION BKI_WITHOUT_OIDS * be zero to denote a shared object. */ Oid dbid; /* OID of database containing object */ - Oid classid; /* OID of table containing object */ + regclass classid; /* OID of table containing object */ Oid objid; /* OID of object itself */ int32 objsubid; /* column number, or 0 if not used */ @@ -46,7 +46,7 @@ CATALOG(pg_shdepend,1214) BKI_SHARED_RELATION BKI_WITHOUT_OIDS * a shared object, so we need no database ID field. We don't bother with * a sub-object ID either. */ - Oid refclassid; /* OID of table containing object */ + regclass refclassid; /* OID of table containing object */ Oid refobjid; /* OID of object itself */ /* diff --git a/src/include/catalog/pg_shdescription.h b/src/include/catalog/pg_shdescription.h index c524099..4b1b380 100644 --- a/src/include/catalog/pg_shdescription.h +++ b/src/include/catalog/pg_shdescription.h @@ -41,7 +41,7 @@ CATALOG(pg_shdescription,2396) BKI_SHARED_RELATION BKI_WITHOUT_OIDS { Oid objoid; /* OID of object itself */ - Oid classoid; /* OID of table containing object */ + regclass classoid; /* OID of table containing object */ #ifdef CATALOG_VARLEN /* variable-length fields start here */ text description BKI_FORCE_NOT_NULL; /* description of object */ diff --git a/src/include/catalog/pg_statistic.h b/src/include/catalog/pg_statistic.h index 62a00fe..e437cbe 100644 --- a/src/include/catalog/pg_statistic.h +++ b/src/include/catalog/pg_statistic.h @@ -31,7 +31,7 @@ CATALOG(pg_statistic,2619) BKI_WITHOUT_OIDS { /* These fields form the unique key for the entry: */ - Oid starelid; /* relation containing attribute */ + regclass starelid; /* relation containing attribute */ int16 staattnum; /* attribute (column) stats are for */ bool stainherit; /* true if inheritance children are included */ diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h index bff8fcf..4c7be4e 100644 --- a/src/include/catalog/pg_trigger.h +++ b/src/include/catalog/pg_trigger.h @@ -35,16 +35,16 @@ CATALOG(pg_trigger,2620) { - Oid tgrelid; /* relation trigger is attached to */ + regclass tgrelid; /* relation trigger is attached to */ NameData tgname; /* trigger's name */ - Oid tgfoid; /* OID of function to be called */ + regproc tgfoid; /* OID of function to be called */ int16 tgtype; /* BEFORE/AFTER/INSTEAD, UPDATE/DELETE/INSERT, * ROW/STATEMENT; see below */ char tgenabled; /* trigger's firing configuration WRT * session_replication_role */ bool tgisinternal; /* trigger is system-generated */ - Oid tgconstrrelid; /* constraint's FROM table, if any */ - Oid tgconstrindid; /* constraint's supporting index, if any */ + regclass tgconstrrelid; /* constraint's FROM table, if any */ + regclass tgconstrindid; /* constraint's supporting index, if any */ Oid tgconstraint; /* associated pg_constraint entry, if any */ bool tgdeferrable; /* constraint trigger is deferrable */ bool tginitdeferred; /* constraint trigger is deferred initially */ diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 0a900dd..e970389 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -86,7 +86,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO char typdelim; /* delimiter for arrays of this type */ - Oid typrelid; /* 0 if not a composite type */ + regclass typrelid; /* 0 if not a composite type */ /* * If typelem is not 0 then it identifies another row in pg_type. The @@ -99,13 +99,13 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO * * typelem != 0 and typlen == -1. */ - Oid typelem; + regtype typelem; /* * If there is a "true" array type having this type as element type, * typarray links to it. Zero if no associated "true" array type. */ - Oid typarray; + regtype typarray; /* * I/O conversion procedures for the datatype. @@ -178,7 +178,7 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO * Domains use typbasetype to show the base (or domain) type that the * domain is based on. Zero if the type is not a domain. */ - Oid typbasetype; + regtype typbasetype; /* * Domains use typtypmod to record the typmod to be applied to their base diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 6b248f2..af28ecd 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -154,10 +154,10 @@ WHERE p1.oid != p2.oid AND p2.prosrc NOT LIKE E'range\\_constructor_' AND (p1.prorettype < p2.prorettype) ORDER BY 1, 2; - prorettype | prorettype -------------+------------ - 25 | 1043 - 1114 | 1184 + prorettype | prorettype +-----------------------------+-------------------------- + text | character varying + timestamp without time zone | timestamp with time zone (2 rows) SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0] @@ -764,13 +764,13 @@ WHERE c.castmethod = 'b' AND k.casttarget = c.castsource); castsource | casttarget | castfunc | castcontext -------------------+-------------------+----------+------------- - text | character | 0 | i - character varying | character | 0 | i - pg_node_tree | text | 0 | i - cidr | inet | 0 | i - xml | text | 0 | a - xml | character varying | 0 | a - xml | character | 0 | a + text | character | - | i + character varying | character | - | i + pg_node_tree | text | - | i + cidr | inet | - | i + xml | text | - | a + xml | character varying | - | a + xml | character | - | a (7 rows) -- **************** pg_operator **************** diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index d50b103..44f51fb 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1321,8 +1321,8 @@ pg_indexes| SELECT n.nspname AS schemaname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x - JOIN pg_class c ON ((c.oid = x.indrelid))) - JOIN pg_class i ON ((i.oid = x.indexrelid))) + JOIN pg_class c ON ((c.oid = (x.indrelid)::oid))) + JOIN pg_class i ON ((i.oid = (x.indexrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = ANY (ARRAY['r'::"char", 'm'::"char"])) AND (i.relkind = 'i'::"char")); @@ -1371,10 +1371,10 @@ pg_policies| SELECT n.nspname AS schemaname, WHEN '*'::"char" THEN 'ALL'::text ELSE NULL::text END AS cmd, - pg_get_expr(pol.polqual, pol.polrelid) AS qual, - pg_get_expr(pol.polwithcheck, pol.polrelid) AS with_check + pg_get_expr(pol.polqual, (pol.polrelid)::oid) AS qual, + pg_get_expr(pol.polwithcheck, (pol.polrelid)::oid) AS with_check FROM ((pg_policy pol - JOIN pg_class c ON ((c.oid = pol.polrelid))) + JOIN pg_class c ON ((c.oid = (pol.polrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))); pg_prepared_statements| SELECT p.name, p.statement, @@ -1444,7 +1444,7 @@ pg_seclabels| SELECT l.objoid, l.provider, l.label FROM ((pg_seclabel l - JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) + JOIN pg_class rel ON ((((l.classoid)::oid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL @@ -1461,8 +1461,8 @@ UNION ALL l.provider, l.label FROM (((pg_seclabel l - JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) - JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) + JOIN pg_class rel ON ((((l.classoid)::oid = rel.tableoid) AND (l.objoid = rel.oid)))) + JOIN pg_attribute att ON (((rel.oid = (att.attrelid)::oid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0) UNION ALL @@ -1483,7 +1483,7 @@ UNION ALL l.provider, l.label FROM ((pg_seclabel l - JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) + JOIN pg_proc pro ON ((((l.classoid)::oid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL @@ -1502,7 +1502,7 @@ UNION ALL l.provider, l.label FROM ((pg_seclabel l - JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) + JOIN pg_type typ ON ((((l.classoid)::oid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL @@ -1516,7 +1516,7 @@ UNION ALL l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) - WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0)) + WHERE (((l.classoid)::oid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, @@ -1527,7 +1527,7 @@ UNION ALL l.provider, l.label FROM (pg_seclabel l - JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) + JOIN pg_language lan ON ((((l.classoid)::oid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, @@ -1539,7 +1539,7 @@ UNION ALL l.provider, l.label FROM (pg_seclabel l - JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) + JOIN pg_namespace nsp ON ((((l.classoid)::oid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, @@ -1551,7 +1551,7 @@ UNION ALL l.provider, l.label FROM (pg_seclabel l - JOIN pg_event_trigger evt ON (((l.classoid = evt.tableoid) AND (l.objoid = evt.oid)))) + JOIN pg_event_trigger evt ON ((((l.classoid)::oid = evt.tableoid) AND (l.objoid = evt.oid)))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, @@ -1647,8 +1647,8 @@ pg_stat_all_indexes| SELECT c.oid AS relid, pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch FROM (((pg_class c - JOIN pg_index x ON ((c.oid = x.indrelid))) - JOIN pg_class i ON ((i.oid = x.indexrelid))) + JOIN pg_index x ON ((c.oid = (x.indrelid)::oid))) + JOIN pg_class i ON ((i.oid = (x.indexrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); pg_stat_all_tables| SELECT c.oid AS relid, @@ -1656,8 +1656,8 @@ pg_stat_all_tables| SELECT c.oid AS relid, c.relname, pg_stat_get_numscans(c.oid) AS seq_scan, pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, - (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan, - ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch, + (sum(pg_stat_get_numscans((i.indexrelid)::oid)))::bigint AS idx_scan, + ((sum(pg_stat_get_tuples_fetched((i.indexrelid)::oid)))::bigint + pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, @@ -1674,7 +1674,7 @@ pg_stat_all_tables| SELECT c.oid AS relid, pg_stat_get_analyze_count(c.oid) AS analyze_count, pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count FROM ((pg_class c - LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) + LEFT JOIN pg_index i ON ((c.oid = (i.indrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname; @@ -1827,14 +1827,14 @@ pg_stat_xact_all_tables| SELECT c.oid AS relid, c.relname, pg_stat_get_xact_numscans(c.oid) AS seq_scan, pg_stat_get_xact_tuples_returned(c.oid) AS seq_tup_read, - (sum(pg_stat_get_xact_numscans(i.indexrelid)))::bigint AS idx_scan, - ((sum(pg_stat_get_xact_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, + (sum(pg_stat_get_xact_numscans((i.indexrelid)::oid)))::bigint AS idx_scan, + ((sum(pg_stat_get_xact_tuples_fetched((i.indexrelid)::oid)))::bigint + pg_stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd FROM ((pg_class c - LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) + LEFT JOIN pg_index i ON ((c.oid = (i.indrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname; @@ -1881,8 +1881,8 @@ pg_statio_all_indexes| SELECT c.oid AS relid, (pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS idx_blks_read, pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit FROM (((pg_class c - JOIN pg_index x ON ((c.oid = x.indrelid))) - JOIN pg_class i ON ((i.oid = x.indexrelid))) + JOIN pg_index x ON ((c.oid = (x.indrelid)::oid))) + JOIN pg_class i ON ((i.oid = (x.indexrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])); pg_statio_all_sequences| SELECT c.oid AS relid, @@ -1898,16 +1898,16 @@ pg_statio_all_tables| SELECT c.oid AS relid, c.relname, (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS heap_blks_read, pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit, - (sum((pg_stat_get_blocks_fetched(i.indexrelid) - pg_stat_get_blocks_hit(i.indexrelid))))::bigint AS idx_blks_read, - (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit, + (sum((pg_stat_get_blocks_fetched((i.indexrelid)::oid) - pg_stat_get_blocks_hit((i.indexrelid)::oid))))::bigint AS idx_blks_read, + (sum(pg_stat_get_blocks_hit((i.indexrelid)::oid)))::bigint AS idx_blks_hit, (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read, pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit, - (sum((pg_stat_get_blocks_fetched(x.indexrelid) - pg_stat_get_blocks_hit(x.indexrelid))))::bigint AS tidx_blks_read, - (sum(pg_stat_get_blocks_hit(x.indexrelid)))::bigint AS tidx_blks_hit + (sum((pg_stat_get_blocks_fetched((x.indexrelid)::oid) - pg_stat_get_blocks_hit((x.indexrelid)::oid))))::bigint AS tidx_blks_read, + (sum(pg_stat_get_blocks_hit((x.indexrelid)::oid)))::bigint AS tidx_blks_hit FROM ((((pg_class c - LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) + LEFT JOIN pg_index i ON ((c.oid = (i.indrelid)::oid))) LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) - LEFT JOIN pg_index x ON ((t.oid = x.indrelid))) + LEFT JOIN pg_index x ON ((t.oid = (x.indrelid)::oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indrelid; @@ -2033,8 +2033,8 @@ pg_stats| SELECT n.nspname AS schemaname, ELSE NULL::real[] END AS elem_count_histogram FROM (((pg_statistic s - JOIN pg_class c ON ((c.oid = s.starelid))) - JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) + JOIN pg_class c ON ((c.oid = (s.starelid)::oid))) + JOIN pg_attribute a ON (((c.oid = (a.attrelid)::oid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text)); pg_tables| SELECT n.nspname AS schemaname, -- 2.10.2 --4kwzxvc3povogjkv Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0003-example-conversion.patch" ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v5] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 68fe6a95b4..066431fd3c 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>bit_count</primary> + </indexterm> + <function>bit_count</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>bit_count('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>bit_count</primary> + </indexterm> + <function>bit_count</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>bit_count(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index e259531f60..feb00eccf9 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'bit_count', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3876,6 +3879,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'bit_count', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 0bc345aa4d..95091887a9 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3440,6 +3440,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..9b6b3d0c4f 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT bit_count(B'0101011100'::bit(10)); + bit_count +----------- + 5 +(1 row) + +SELECT bit_count(B'1111111111'::bit(10)); + bit_count +----------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index fb4573d85f..e8c6a99e9d 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2227,3 +2227,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT bit_count(E'\\xdeadbeef'::bytea); + bit_count +----------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..271aa5ea3c 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT bit_count(B'0101011100'::bit(10)); +SELECT bit_count(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index 57a48c9d0b..3fb2d66a9f 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -742,3 +742,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT bit_count(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v5] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 68fe6a95b4..066431fd3c 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>bit_count</primary> + </indexterm> + <function>bit_count</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>bit_count('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>bit_count</primary> + </indexterm> + <function>bit_count</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>bit_count(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index e259531f60..feb00eccf9 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'bit_count', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3876,6 +3879,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'bit_count', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 0bc345aa4d..95091887a9 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3440,6 +3440,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..9b6b3d0c4f 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT bit_count(B'0101011100'::bit(10)); + bit_count +----------- + 5 +(1 row) + +SELECT bit_count(B'1111111111'::bit(10)); + bit_count +----------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index fb4573d85f..e8c6a99e9d 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2227,3 +2227,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT bit_count(E'\\xdeadbeef'::bytea); + bit_count +----------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..271aa5ea3c 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT bit_count(B'0101011100'::bit(10)); +SELECT bit_count(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index 57a48c9d0b..3fb2d66a9f 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -742,3 +742,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT bit_count(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v1] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 5021ac1ca9..439a3a4a06 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>integer</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>int4</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index 139f4a08bd..0e6d0636fa 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int4', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int4', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 3c03459f51..e92d1a7f8f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -29,6 +29,8 @@ *------------------------------------------------------------------------- */ +#include <math.h> + #include "postgres.h" #include "access/htup_details.h" @@ -36,6 +38,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1872,3 +1875,24 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len, popcount; + + len = ceil(VARBITLEN(arg1) / (float4)BITS_PER_BYTE); + p = VARBITS(arg1); + + popcount = (int)pg_popcount((const char *)p, len); + + PG_RETURN_INT32(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 9300d19e0c..9e788babd3 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3415,6 +3415,21 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len, result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT32(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a1fab7ebcb..c91ad3d0d5 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -681,6 +681,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 298b6c48c2..9780639038 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2126,3 +2126,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index 7681d4ab4d..3cd9fb65d3 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -207,6 +207,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ad5221ab6b..a35e993ab0 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -717,3 +717,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v2] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 5021ac1ca9..439a3a4a06 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>integer</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>int4</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index 139f4a08bd..82e3d50114 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 3c03459f51..a71c2b6bf4 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1872,3 +1873,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 9300d19e0c..9c5e5b5592 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3415,6 +3415,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a1fab7ebcb..c91ad3d0d5 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -681,6 +681,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 298b6c48c2..9780639038 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2126,3 +2126,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index 7681d4ab4d..3cd9fb65d3 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -207,6 +207,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ad5221ab6b..a35e993ab0 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -717,3 +717,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v4] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index aa99665e2e..7626edeee7 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4030,6 +4030,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>count_set_bits('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>count_set_bits</primary> + </indexterm> + <function>count_set_bits</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>count_set_bits(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4869,6 +4903,7 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index b5f52d4e4a..416ee0c2fb 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'count_set_bits', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..c9c6c73422 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,30 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit string. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + /* There's really no chance of an overflow here because + * to get to INT64_MAX set bits, an object would have to be + * an exbibyte long, exceeding what PostgreSQL can currently + * store by a factor of 2^28 + */ + int64 popcount; + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + + p = VARBITS(arg1); + len = (VARBITLEN(arg1) + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + + popcount = pg_popcount((char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 479ed9ae54..3f1179a0e8 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3439,6 +3439,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int64 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..1187076a8d 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); + count_set_bits +---------------- + 5 +(1 row) + +SELECT count_set_bits(B'1111111111'::bit(10)); + count_set_bits +---------------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 7c91afa6e4..e94e76000b 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2179,3 +2179,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); + count_set_bits +---------------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..8893d38b0d 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT count_set_bits(B'0101011100'::bit(10)); +SELECT count_set_bits(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ef4bfb008a..a4f7879b16 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -730,3 +730,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT count_set_bits(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* [PATCH v3] popcount @ 2020-12-30 10:51 David Fetter <[email protected]> 0 siblings, 0 replies; 50+ messages in thread From: David Fetter @ 2020-12-30 10:51 UTC (permalink / raw) Now it's accessible to SQL for the BIT VARYING and BYTEA types. diff --git doc/src/sgml/func.sgml doc/src/sgml/func.sgml index 02a37658ad..1d86d610dd 100644 --- doc/src/sgml/func.sgml +++ doc/src/sgml/func.sgml @@ -4069,6 +4069,23 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); </para></entry> </row> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bytes</parameter> <type>bytea</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the number of bits set in a binary string. + </para> + <para> + <literal>popcount('\xdeadbeef'::bytea)</literal> + <returnvalue>24</returnvalue> + </para></entry> + </row> + <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm> @@ -4830,6 +4847,24 @@ SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three'); <returnvalue>101010001010101010</returnvalue> </para></entry> </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>popcount</primary> + </indexterm> + <function>popcount</function> ( <parameter>bits</parameter> <type>bit</type> ) + <returnvalue>bigint</returnvalue> + </para> + <para> + Counts the bits set in a bit string. + </para> + <para> + <literal>popcount(B'101010101010101010')</literal> + <returnvalue>9</returnvalue> + </para></entry> + </row> + </tbody> </tgroup> </table> diff --git src/include/catalog/pg_proc.dat src/include/catalog/pg_proc.dat index d7b55f57ea..2d53e0d46d 100644 --- src/include/catalog/pg_proc.dat +++ src/include/catalog/pg_proc.dat @@ -1446,6 +1446,9 @@ { oid => '752', descr => 'substitute portion of string', proname => 'overlay', prorettype => 'bytea', proargtypes => 'bytea bytea int4', prosrc => 'byteaoverlay_no_len' }, +{ oid => '8436', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bytea', + prosrc => 'byteapopcount'}, { oid => '725', proname => 'dist_pl', prorettype => 'float8', proargtypes => 'point line', @@ -3865,6 +3868,9 @@ { oid => '3033', descr => 'set bit', proname => 'set_bit', prorettype => 'bit', proargtypes => 'bit int4 int4', prosrc => 'bitsetbit' }, +{ oid => '8435', descr => 'count set bits', + proname => 'popcount', prorettype => 'int8', proargtypes => 'bit', + prosrc => 'bitpopcount'}, # for macaddr type support { oid => '436', descr => 'I/O', diff --git src/backend/utils/adt/varbit.c src/backend/utils/adt/varbit.c index 2235866244..a6a44f3f4f 100644 --- src/backend/utils/adt/varbit.c +++ src/backend/utils/adt/varbit.c @@ -36,6 +36,7 @@ #include "libpq/pqformat.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" +#include "port/pg_bitutils.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/varbit.h" @@ -1878,3 +1879,29 @@ bitgetbit(PG_FUNCTION_ARGS) else PG_RETURN_INT32(0); } + +/* + * bitpopcount + * + * Returns the number of bits set in a bit array. + * + */ +Datum +bitpopcount(PG_FUNCTION_ARGS) +{ + VarBit *arg1 = PG_GETARG_VARBIT_P(0); + bits8 *p; + int len; + int8 popcount; + + /* + * ceil(VARBITLEN(ARG1)/BITS_PER_BYTE) + * done to minimize branches and instructions. + */ + len = (VARBITLEN(arg1) + BITS_PER_BYTE + 1) / BITS_PER_BYTE; + p = VARBITS(arg1); + + popcount = pg_popcount((const char *)p, len); + + PG_RETURN_INT64(popcount); +} diff --git src/backend/utils/adt/varlena.c src/backend/utils/adt/varlena.c index 4838bfb4ff..da3ba769c4 100644 --- src/backend/utils/adt/varlena.c +++ src/backend/utils/adt/varlena.c @@ -3433,6 +3433,22 @@ bytea_overlay(bytea *t1, bytea *t2, int sp, int sl) return result; } +/* + * popcount + */ +Datum +byteapopcount(PG_FUNCTION_ARGS) +{ + bytea *t1 = PG_GETARG_BYTEA_PP(0); + int len; + int8 result; + + len = VARSIZE_ANY_EXHDR(t1); + result = pg_popcount(VARDATA_ANY(t1), len); + + PG_RETURN_INT64(result); +} + /* * byteapos - * Return the position of the specified substring. diff --git src/test/regress/expected/bit.out src/test/regress/expected/bit.out index a7f95b846d..228f992ced 100644 --- src/test/regress/expected/bit.out +++ src/test/regress/expected/bit.out @@ -710,6 +710,19 @@ SELECT overlay(B'0101011100' placing '001' from 20); 0101011100001 (1 row) +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); + popcount +---------- + 5 +(1 row) + +SELECT popcount(B'1111111111'::bit(10)); + popcount +---------- + 10 +(1 row) + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/expected/strings.out src/test/regress/expected/strings.out index 595bd2446e..dd962e5f48 100644 --- src/test/regress/expected/strings.out +++ src/test/regress/expected/strings.out @@ -2167,3 +2167,10 @@ SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 Th\000o\x02\x03 (1 row) +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); + popcount +---------- + 24 +(1 row) + diff --git src/test/regress/sql/bit.sql src/test/regress/sql/bit.sql index ea01742c4a..fa77bf45c4 100644 --- src/test/regress/sql/bit.sql +++ src/test/regress/sql/bit.sql @@ -215,6 +215,10 @@ SELECT overlay(B'0101011100' placing '101' from 6); SELECT overlay(B'0101011100' placing '001' from 11); SELECT overlay(B'0101011100' placing '001' from 20); +-- Popcount +SELECT popcount(B'0101011100'::bit(10)); +SELECT popcount(B'1111111111'::bit(10)); + -- This table is intentionally left around to exercise pg_dump/pg_upgrade CREATE TABLE bit_defaults( b1 bit(4) DEFAULT '1001', diff --git src/test/regress/sql/strings.sql src/test/regress/sql/strings.sql index ca465d050f..48964e71cb 100644 --- src/test/regress/sql/strings.sql +++ src/test/regress/sql/strings.sql @@ -728,3 +728,6 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape'); SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape'); + +SET bytea_output TO hex; +SELECT popcount(E'\\xdeadbeef'::bytea); ^ permalink raw reply [nested|flat] 50+ messages in thread
* Subscription statistics are not dropped at DROP SUBSCRIPTION in some cases @ 2023-05-08 07:23 Masahiko Sawada <[email protected]> 0 siblings, 2 replies; 50+ messages in thread From: Masahiko Sawada @ 2023-05-08 07:23 UTC (permalink / raw) To: PostgreSQL Hackers <[email protected]> Hi, We call pgstat_drop_subscription() at the end of DropSubscription() but we could leave from this function earlier e.g. when no slot is associated with the subscription. In this case, the statistics entry for the subscription remains. To fix it, I think we need to call it earlier, just after removing the catalog tuple. There is a chance the transaction dropping the subscription fails due to network error etc but we don't need to worry about it as reporting the subscription drop is transactional. I've attached the patch. Feedback is very welcome. Regards, -- Masahiko Sawada Amazon Web Services: https://aws.amazon.com Attachments: [application/octet-stream] fix_drop_subscription_stats.patch (3.4K, ../../CAD21AoB71zkP7uPT7JDPsZcvp0749ExEQnOJxeNKPDFisHar+w@mail.gmail.com/2-fix_drop_subscription_stats.patch) download | inline diff: diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c index 8b3de032ee..09971ae45a 100644 --- a/src/backend/commands/subscriptioncmds.c +++ b/src/backend/commands/subscriptioncmds.c @@ -1644,6 +1644,12 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel) ReplicationOriginNameForLogicalRep(subid, InvalidOid, originname, sizeof(originname)); replorigin_drop_by_name(originname, true, false); + /* + * Tell the cumulative stats system that the subscription is getting + * dropped. + */ + pgstat_drop_subscription(subid); + /* * If there is no slot associated with the subscription, we can finish * here. @@ -1732,12 +1738,6 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel) } PG_END_TRY(); - /* - * Tell the cumulative stats system that the subscription is getting - * dropped. - */ - pgstat_drop_subscription(subid); - table_close(rel, NoLock); } diff --git a/src/test/regress/expected/subscription.out b/src/test/regress/expected/subscription.out index d736246259..e9d6166d27 100644 --- a/src/test/regress/expected/subscription.out +++ b/src/test/regress/expected/subscription.out @@ -73,6 +73,8 @@ SELECT :'prev_stats_reset' < stats_reset FROM pg_stat_subscription_stats WHERE s t (1 row) +-- Remember the subscription oid for checking later if the statistics gets removed. +SELECT oid as suboid FROM pg_subscription WHERE subname = 'regress_testsub' \gset -- fail - name already exists CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false); ERROR: subscription "regress_testsub" already exists @@ -240,6 +242,14 @@ ALTER SUBSCRIPTION regress_testsub SET (slot_name = NONE); BEGIN; DROP SUBSCRIPTION regress_testsub; COMMIT; +-- check if the statistics of the dropped subscription doesn't remain, by trying +-- directly fetching the stats. +SELECT stats_reset IS NULL stats_reset_is_null FROM pg_stat_get_subscription_stats(:'suboid'); + stats_reset_is_null +--------------------- + t +(1 row) + DROP SUBSCRIPTION IF EXISTS regress_testsub; NOTICE: subscription "regress_testsub" does not exist, skipping DROP SUBSCRIPTION regress_testsub; -- fail diff --git a/src/test/regress/sql/subscription.sql b/src/test/regress/sql/subscription.sql index 55d7dbc9ab..68be70dce5 100644 --- a/src/test/regress/sql/subscription.sql +++ b/src/test/regress/sql/subscription.sql @@ -42,6 +42,9 @@ SELECT stats_reset as prev_stats_reset FROM pg_stat_subscription_stats WHERE sub SELECT pg_stat_reset_subscription_stats(oid) FROM pg_subscription WHERE subname = 'regress_testsub'; SELECT :'prev_stats_reset' < stats_reset FROM pg_stat_subscription_stats WHERE subname = 'regress_testsub'; +-- Remember the subscription oid for checking later if the statistics gets removed. +SELECT oid as suboid FROM pg_subscription WHERE subname = 'regress_testsub' \gset + -- fail - name already exists CREATE SUBSCRIPTION regress_testsub CONNECTION 'dbname=regress_doesnotexist' PUBLICATION testpub WITH (connect = false); @@ -158,6 +161,10 @@ BEGIN; DROP SUBSCRIPTION regress_testsub; COMMIT; +-- check if the statistics of the dropped subscription doesn't remain, by trying +-- directly fetching the stats. +SELECT stats_reset IS NULL stats_reset_is_null FROM pg_stat_get_subscription_stats(:'suboid'); + DROP SUBSCRIPTION IF EXISTS regress_testsub; DROP SUBSCRIPTION regress_testsub; -- fail ^ permalink raw reply [nested|flat] 50+ messages in thread
* Re: Subscription statistics are not dropped at DROP SUBSCRIPTION in some cases @ 2023-05-09 00:07 Nathan Bossart <[email protected]> parent: Masahiko Sawada <[email protected]> 1 sibling, 0 replies; 50+ messages in thread From: Nathan Bossart @ 2023-05-09 00:07 UTC (permalink / raw) To: Masahiko Sawada <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> On Mon, May 08, 2023 at 04:23:15PM +0900, Masahiko Sawada wrote: > We call pgstat_drop_subscription() at the end of DropSubscription() > but we could leave from this function earlier e.g. when no slot is > associated with the subscription. In this case, the statistics entry > for the subscription remains. To fix it, I think we need to call it > earlier, just after removing the catalog tuple. There is a chance the > transaction dropping the subscription fails due to network error etc > but we don't need to worry about it as reporting the subscription drop > is transactional. Looks reasonable to me. IIUC calling pgstat_drop_subscription() earlier makes no real difference (besides avoiding this bug) because it is uѕing pgstat_drop_transactional() behind the scenes. -- Nathan Bossart Amazon Web Services: https://aws.amazon.com ^ permalink raw reply [nested|flat] 50+ messages in thread
* Re: Subscription statistics are not dropped at DROP SUBSCRIPTION in some cases @ 2023-05-10 11:58 Melih Mutlu <[email protected]> parent: Masahiko Sawada <[email protected]> 1 sibling, 0 replies; 50+ messages in thread From: Melih Mutlu @ 2023-05-10 11:58 UTC (permalink / raw) To: Masahiko Sawada <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]> Hi, Masahiko Sawada <[email protected]>, 8 May 2023 Pzt, 10:24 tarihinde şunu yazdı: > I've attached the patch. Feedback is very welcome. > Thanks for the patch, nice catch. I can confirm that the issue exists on HEAD and gets resolved by this patch. Also it looks like stats are really not affected if transaction fails for some reason, as you explained. IMO, the patch will be OK after commit message is added. Thanks, -- Melih Mutlu Microsoft ^ permalink raw reply [nested|flat] 50+ messages in thread
end of thread, other threads:[~2023-05-10 11:58 UTC | newest] Thread overview: 50+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2015-02-21 23:06 [PATCH 2/3] Do much more stuff in genbki.pl Andres Freund <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v1] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v5] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v5] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v2] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v3] popcount David Fetter <[email protected]> 2020-12-30 10:51 [PATCH v4] popcount David Fetter <[email protected]> 2023-05-08 07:23 Subscription statistics are not dropped at DROP SUBSCRIPTION in some cases Masahiko Sawada <[email protected]> 2023-05-09 00:07 ` Re: Subscription statistics are not dropped at DROP SUBSCRIPTION in some cases Nathan Bossart <[email protected]> 2023-05-10 11:58 ` Re: Subscription statistics are not dropped at DROP SUBSCRIPTION in some cases Melih Mutlu <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox