Fix proposal for comparaison bugs in PostgreSQL::Version - Mailing list pgsql-hackers

From Jehan-Guillaume de Rorthais
Subject Fix proposal for comparaison bugs in PostgreSQL::Version
Date
Msg-id 20220628225325.53d97b8d@karst
Whole thread Raw
Responses Re: Fix proposal for comparaison bugs in PostgreSQL::Version
List pgsql-hackers
Hi,

I found a comparaison bug when using the PostgreSQL::Version module. See:

  $ perl -I. -MPostgreSQL::Version -le '
    my $v = PostgreSQL::Version->new("9.6");
  
    print "not 9.6 > 9.0" unless $v >  9.0;
    print "not 9.6 < 9.0" unless $v <  9.0;
    print "9.6 <= 9.0"    if     $v <= 9.0;
    print "9.6 >= 9.0"    if     $v >= 9.0;'
  not 9.6 > 9.0
  not 9.6 < 9.0
  9.6 <= 9.0
  9.6 >= 9.0

When using < or >, 9.6 is neither greater or lesser than 9.0. 
When using <= or >=, 9.6 is equally greater and lesser than 9.0.
The bug does not show up if you compare with "9.0" instead of 9.0.
This bug is triggered with devel versions, eg. 14beta1 <=> 14.

The bug appears when both objects have a different number of digit in the
internal array representation:

  $ perl -I. -MPostgreSQL::Version -MData::Dumper -le '
    print Dumper(PostgreSQL::Version->new("9.0")->{num});
    print Dumper(PostgreSQL::Version->new(9.0)->{num});
    print Dumper(PostgreSQL::Version->new(14)->{num});
    print Dumper(PostgreSQL::Version->new("14beta1")->{num});'
  $VAR1 = [ '9', '0' ];
  $VAR1 = [ '9' ];
  $VAR1 = [ '14' ];
  $VAR1 = [ '14', -1 ];

Because of this, The following loop in "_version_cmp" is wrong because we are
comparing two versions with different size of 'num' array:

    for (my $idx = 0;; $idx++)
    {
        return 0 unless (defined $an->[$idx] && defined $bn->[$idx]);
        return $an->[$idx] <=> $bn->[$idx]
          if ($an->[$idx] <=> $bn->[$idx]);
    }


If we want to keep this internal array representation, the only fix I can think
of would be to always use a 4 element array defaulted to 0. Previous examples
would be:

  $VAR1 = [ 9, 0, 0, 0 ];
  $VAR1 = [ 9, 0, 0, 0 ];
  $VAR1 = [ 14, 0, 0, 0 ];
  $VAR1 = [ 14, 0, 0, -1 ];

A better fix would be to store the version internally as version_num that are
trivial to compute and compare. Please, find in attachment an implementation of
this.

The patch is a bit bigger because it improved the devel version to support
rc/beta/alpha comparison like 14rc2 > 14rc1.

Moreover, it adds a bunch of TAP tests to check various use cases.

Regards,

Attachment

pgsql-hackers by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: First draft of the PG 15 release notes
Next
From: Hannu Krosing
Date:
Subject: Re: Hardening PostgreSQL via (optional) ban on local file system access