github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/datastore/crdb/version.go (about)

     1  package crdb
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"regexp"
     8  	"strconv"
     9  
    10  	"github.com/jackc/pgx/v5"
    11  	"github.com/jackc/pgx/v5/pgconn"
    12  	"github.com/rs/zerolog"
    13  
    14  	pgxcommon "github.com/authzed/spicedb/internal/datastore/postgres/common"
    15  )
    16  
    17  const (
    18  	queryVersionJSON = "SELECT crdb_internal.active_version()::jsonb;"
    19  	queryVersion     = "SELECT version();"
    20  
    21  	errFunctionDoesNotExist = "42883"
    22  )
    23  
    24  var versionRegex = regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?`)
    25  
    26  func queryServerVersion(ctx context.Context, db pgxcommon.DBFuncQuerier, version *crdbVersion) error {
    27  	if err := db.QueryRowFunc(ctx, func(ctx context.Context, row pgx.Row) error {
    28  		return row.Scan(version)
    29  	}, queryVersionJSON); err != nil {
    30  		var pgerr *pgconn.PgError
    31  		if !errors.As(err, &pgerr) || pgerr.Code != errFunctionDoesNotExist {
    32  			return err
    33  		}
    34  
    35  		// The crdb_internal.active_version() wasn't added until v22.1.X, try to parse the version
    36  		var versionStr string
    37  		if err := db.QueryRowFunc(ctx, func(ctx context.Context, row pgx.Row) error {
    38  			return row.Scan(&versionStr)
    39  		}, queryVersion); err != nil {
    40  			return err
    41  		}
    42  
    43  		return parseVersionStringInto(versionStr, version)
    44  	}
    45  
    46  	return nil
    47  }
    48  
    49  func parseVersionStringInto(versionStr string, version *crdbVersion) error {
    50  	found := versionRegex.FindStringSubmatch(versionStr)
    51  	if found == nil {
    52  		return fmt.Errorf("could not parse version from string: %s", versionStr)
    53  	}
    54  
    55  	var err error
    56  	version.Major, err = strconv.Atoi(found[1])
    57  	if err != nil {
    58  		return fmt.Errorf("invalid major version: %s", found[1])
    59  	}
    60  	version.Minor, err = strconv.Atoi(found[2])
    61  	if err != nil {
    62  		return fmt.Errorf("invalid minor version: %s", found[2])
    63  	}
    64  	version.Patch, err = strconv.Atoi(found[3])
    65  	if err != nil {
    66  		return fmt.Errorf("invalid patch version: %s", found[3])
    67  	}
    68  
    69  	return nil
    70  }
    71  
    72  type crdbVersion struct {
    73  	Internal int `json:"internal"`
    74  	Major    int `json:"major"`
    75  	Minor    int `json:"minor"`
    76  	Patch    int `json:"patch"`
    77  }
    78  
    79  func (v crdbVersion) MarshalZerologObject(e *zerolog.Event) {
    80  	e.Int("major", v.Major).Int("minor", v.Minor).Int("patch", v.Patch)
    81  }