Merge pull request #1277 from citusdata/error_out_incomplete_installation

Error out if binary citus version does not match installed extension

cr: @jasonmp85
pull/1314/head
Jason Petersen 2017-04-04 16:49:03 -06:00 committed by GitHub
commit 8c5d0f686b
14 changed files with 659 additions and 32 deletions

View File

@ -13,14 +13,14 @@ include Makefile.global
all: extension
# build extension
extension:
extension: $(citus_abs_srcdir)/src/include/citus_version.h
$(MAKE) -C src/backend/distributed/ all
install-extension: extension
$(MAKE) -C src/backend/distributed/ install
install-headers: extension
$(MKDIR_P) '$(DESTDIR)$(includedir_server)/distributed/'
# generated headers are located in the build directory
$(INSTALL_DATA) src/include/citus_config.h '$(DESTDIR)$(includedir_server)/'
$(INSTALL_DATA) $(citus_abs_srcdir)/src/include/citus_version.h '$(DESTDIR)$(includedir_server)/'
# the rest in the source tree
$(INSTALL_DATA) $(citus_abs_srcdir)/src/include/distributed/*.h '$(DESTDIR)$(includedir_server)/distributed/'
clean-extension:

View File

@ -44,8 +44,8 @@ $(citus_top_builddir)/Makefile.global: $(citus_abs_top_srcdir)/configure $(citus
# Ensure configuration is generated by the most recent configure,
# useful for longer existing build directories.
$(citus_top_builddir)/config.status: $(citus_abs_top_srcdir)/configure
cd @abs_top_builddir@ && ./config.status --recheck
$(citus_top_builddir)/config.status: $(citus_abs_top_srcdir)/configure $(citus_abs_top_srcdir)/src/backend/distributed/citus.control
cd @abs_top_builddir@ && ./config.status --recheck && ./config.status
# Regenerate configure if configure.in changed
$(citus_abs_top_srcdir)/configure: $(citus_abs_top_srcdir)/configure.in

104
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for Citus 5.0.
# Generated by GNU Autoconf 2.69 for Citus 6.2devel.
#
#
# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@ -9,7 +9,7 @@
# This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it.
#
# Copyright (c) 2012-2016, Citus Data, Inc.
# Copyright (c) 2012-2017, Citus Data, Inc.
## -------------------- ##
## M4sh Initialization. ##
## -------------------- ##
@ -579,8 +579,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='Citus'
PACKAGE_TARNAME='citus'
PACKAGE_VERSION='5.0'
PACKAGE_STRING='Citus 5.0'
PACKAGE_VERSION='6.2devel'
PACKAGE_STRING='Citus 6.2devel'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
@ -600,6 +600,7 @@ vpath_build
PATH
PG_CONFIG
FLEX
AWK
SED
target_alias
host_alias
@ -1194,7 +1195,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures Citus 5.0 to adapt to many kinds of systems.
\`configure' configures Citus 6.2devel to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1255,7 +1256,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of Citus 5.0:";;
short | recursive ) echo "Configuration of Citus 6.2devel:";;
esac
cat <<\_ACEOF
@ -1343,14 +1344,14 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
Citus configure 5.0
Citus configure 6.2devel
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
Copyright (c) 2012-2016, Citus Data, Inc.
Copyright (c) 2012-2017, Citus Data, Inc.
_ACEOF
exit
fi
@ -1400,7 +1401,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by Citus $as_me 5.0, which was
It was created by Citus $as_me 6.2devel, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@ -1750,6 +1751,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
# we'll need sed and awk for some of the version commands
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
$as_echo_n "checking for a sed that does not truncate output... " >&6; }
if ${ac_cv_path_SED+:} false; then :
@ -1819,6 +1821,82 @@ $as_echo "$ac_cv_path_SED" >&6; }
SED="$ac_cv_path_SED"
rm -f conftest.sed
for ac_prog in gawk mawk nawk awk
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_AWK+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AWK"; then
ac_cv_prog_AWK="$AWK" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
AWK=$ac_cv_prog_AWK
if test -n "$AWK"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
$as_echo "$AWK" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
test -n "$AWK" && break
done
# CITUS_VERSION definition
cat >>confdefs.h <<_ACEOF
#define CITUS_VERSION "$PACKAGE_VERSION"
_ACEOF
# CITUS_MAJORVERSION definition
CITUS_MAJORVERSION=`expr "$PACKAGE_VERSION" : '\([0-9][0-9]*\.[0-9][0-9]*\)'`
cat >>confdefs.h <<_ACEOF
#define CITUS_MAJORVERSION "$CITUS_MAJORVERSION"
_ACEOF
# CITUS_VERSION_NUM definition
# awk -F is a regex on some platforms, and not on others, so make "." a tab
CITUS_VERSION_NUM="`echo "$PACKAGE_VERSION" | sed 's/[A-Za-z].*$//' |
tr '.' ' ' |
$AWK '{printf "%d%02d%02d", $1, $2, (NF >= 3) ? $3 : 0}'`"
cat >>confdefs.h <<_ACEOF
#define CITUS_VERSION_NUM $CITUS_VERSION_NUM
_ACEOF
# CITUS_EXTENSIONVERSION definition
CITUS_EXTENSIONVERSION="`grep '^default_version' $srcdir/src/backend/distributed/citus.control | cut -d\' -f2`"
cat >>confdefs.h <<_ACEOF
#define CITUS_EXTENSIONVERSION "$CITUS_EXTENSIONVERSION"
_ACEOF
# Re-check for flex. That allows to compile citus against a postgres
# which was built without flex available (possible because generated
@ -2974,7 +3052,7 @@ POSTGRES_BUILDDIR="$POSTGRES_BUILDDIR"
ac_config_files="$ac_config_files Makefile.global"
ac_config_headers="$ac_config_headers src/include/citus_config.h"
ac_config_headers="$ac_config_headers src/include/citus_config.h src/include/citus_version.h"
cat >confcache <<\_ACEOF
@ -3483,7 +3561,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by Citus $as_me 5.0, which was
This file was extended by Citus $as_me 6.2devel, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -3545,7 +3623,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
Citus config.status 5.0
Citus config.status 6.2devel
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@ -3555,6 +3633,7 @@ gives unlimited permission to copy, distribute and modify it."
ac_pwd='$ac_pwd'
srcdir='$srcdir'
AWK='$AWK'
test -n "\$AWK" || AWK=awk
_ACEOF
@ -3668,6 +3747,7 @@ do
case $ac_config_target in
"Makefile.global") CONFIG_FILES="$CONFIG_FILES Makefile.global" ;;
"src/include/citus_config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/include/citus_config.h" ;;
"src/include/citus_version.h") CONFIG_HEADERS="$CONFIG_HEADERS src/include/citus_version.h" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac

View File

@ -5,10 +5,30 @@
# everyone needing autoconf installed, the resulting files are checked
# into the SCM.
AC_INIT([Citus], [5.0], [], [citus], [])
AC_COPYRIGHT([Copyright (c) 2012-2016, Citus Data, Inc.])
AC_INIT([Citus], [6.2devel])
AC_COPYRIGHT([Copyright (c) 2012-2017, Citus Data, Inc.])
# we'll need sed and awk for some of the version commands
AC_PROG_SED
AC_PROG_AWK
# CITUS_VERSION definition
AC_DEFINE_UNQUOTED(CITUS_VERSION, "$PACKAGE_VERSION", [Citus version as a string])
# CITUS_MAJORVERSION definition
[CITUS_MAJORVERSION=`expr "$PACKAGE_VERSION" : '\([0-9][0-9]*\.[0-9][0-9]*\)'`]
AC_DEFINE_UNQUOTED(CITUS_MAJORVERSION, "$CITUS_MAJORVERSION", [Citus major version as a string])
# CITUS_VERSION_NUM definition
# awk -F is a regex on some platforms, and not on others, so make "." a tab
[CITUS_VERSION_NUM="`echo "$PACKAGE_VERSION" | sed 's/[A-Za-z].*$//' |
tr '.' ' ' |
$AWK '{printf "%d%02d%02d", $1, $2, (NF >= 3) ? $3 : 0}'`"]
AC_DEFINE_UNQUOTED(CITUS_VERSION_NUM, $CITUS_VERSION_NUM, [Citus version as a number])
# CITUS_EXTENSIONVERSION definition
[CITUS_EXTENSIONVERSION="`grep '^default_version' $srcdir/src/backend/distributed/citus.control | cut -d\' -f2`"]
AC_DEFINE_UNQUOTED([CITUS_EXTENSIONVERSION], "$CITUS_EXTENSIONVERSION", [Extension version expected by this Citus build])
# Re-check for flex. That allows to compile citus against a postgres
# which was built without flex available (possible because generated
@ -122,7 +142,7 @@ AC_SUBST(POSTGRES_SRCDIR, "$POSTGRES_SRCDIR")
AC_SUBST(POSTGRES_BUILDDIR, "$POSTGRES_BUILDDIR")
AC_CONFIG_FILES([Makefile.global])
AC_CONFIG_HEADERS([src/include/citus_config.h])
AC_CONFIG_HEADERS([src/include/citus_config.h] [src/include/citus_version.h])
AH_TOP([
/*
* citus_config.h.in is generated by autoconf/autoheader and

View File

@ -28,6 +28,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "citus_version.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/prepare.h"
@ -82,7 +83,6 @@
bool EnableDDLPropagation = true; /* ddl propagation is enabled */
/*
* This struct defines the state for the callback for drop statements.
* It is copied as it is from commands/tablecmds.c in Postgres source.
@ -95,6 +95,10 @@ struct DropRelationCallbackState
};
/* Local functions forward declarations for deciding when to perform processing/checks */
static bool SkipCitusProcessingForUtility(Node *parsetree);
static bool IsCitusExtensionStmt(Node *parsetree);
/* Local functions forward declarations for Transmit statement */
static bool IsTransmitStmt(Node *parsetree);
static void VerifyTransmitStmt(CopyStmt *copyStatement);
@ -120,6 +124,7 @@ static char * DeparseVacuumColumnNames(List *columnNameList);
/* Local functions forward declarations for unsupported command checks */
static void ErrorIfUnstableCreateOrAlterExtensionStmt(Node *parsetree);
static void ErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement);
static void ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement);
static void ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement);
@ -130,6 +135,7 @@ static bool OptionsSpecifyOwnedBy(List *optionList, Oid *ownedByTableId);
static void ErrorIfDistributedRenameStmt(RenameStmt *renameStatement);
/* Local functions forward declarations for helper functions */
static char * ExtractNewExtensionVersion(Node *parsetree);
static void CreateLocalTable(RangeVar *relation, char *nodeName, int32 nodePort);
static bool IsAlterTableRenameStmt(RenameStmt *renameStatement);
static void ExecuteDistributedDDLJob(DDLJob *ddlJob);
@ -170,18 +176,20 @@ multi_ProcessUtility(Node *parsetree,
Oid savedUserId = InvalidOid;
int savedSecurityContext = 0;
List *ddlJobs = NIL;
bool skipCitusProcessing = SkipCitusProcessingForUtility(parsetree);
if (IsA(parsetree, TransactionStmt))
if (skipCitusProcessing)
{
/*
* Transaction statements (e.g. ABORT, COMMIT) can be run in aborted
* transactions in which case a lot of checks cannot be done safely in
* that state. Since we never need to intercept transaction statements,
* skip our checks and immediately fall into standard_ProcessUtility.
*/
bool checkExtensionVersion = IsCitusExtensionStmt(parsetree);
standard_ProcessUtility(parsetree, queryString, context,
params, dest, completionTag);
if (EnableVersionChecks && checkExtensionVersion)
{
ErrorIfUnstableCreateOrAlterExtensionStmt(parsetree);
}
return;
}
@ -401,6 +409,86 @@ multi_ProcessUtility(Node *parsetree,
}
/*
* SkipCitusProcessingForUtility simply returns whether a given utility should
* bypass Citus processing and checks and be handled exclusively by standard
* PostgreSQL utility processing. At present, CREATE/ALTER/DROP EXTENSION,
* ABORT, COMMIT, ROLLBACK, and SET (GUC) statements are exempt from Citus.
*/
static bool
SkipCitusProcessingForUtility(Node *parsetree)
{
switch (parsetree->type)
{
/*
* In the CitusHasBeenLoaded check, we compare versions of loaded code,
* the installed extension, and available extension. If they differ, we
* force user to execute ALTER EXTENSION citus UPDATE. To allow this,
* CREATE/DROP/ALTER extension must be omitted from Citus processing.
*/
case T_DropStmt:
{
DropStmt *dropStatement = (DropStmt *) parsetree;
if (dropStatement->removeType != OBJECT_EXTENSION)
{
return false;
}
}
/* no break, fall through */
case T_CreateExtensionStmt:
case T_AlterExtensionStmt:
/*
* Transaction statements (e.g. ABORT, COMMIT) can be run in aborted
* transactions in which case a lot of checks cannot be done safely in
* that state. Since we never need to intercept transaction statements,
* skip our checks and immediately fall into standard_ProcessUtility.
*/
case T_TransactionStmt:
/*
* Skip processing of variable set statements, to allow changing the
* enable_version_checks GUC during testing.
*/
case T_VariableSetStmt:
{
return true;
}
default:
{
return false;
}
}
}
/*
* IsCitusExtensionStmt returns whether a given utility is a CREATE or ALTER
* EXTENSION statement which references the citus extension. This function
* returns false for all other inputs.
*/
static bool
IsCitusExtensionStmt(Node *parsetree)
{
char *extensionName = "";
if (IsA(parsetree, CreateExtensionStmt))
{
extensionName = ((CreateExtensionStmt *) parsetree)->extname;
}
else if (IsA(parsetree, AlterExtensionStmt))
{
extensionName = ((AlterExtensionStmt *) parsetree)->extname;
}
return (strcmp(extensionName, "citus") == 0);
}
/* Is the passed in statement a transmit statement? */
static bool
IsTransmitStmt(Node *parsetree)
@ -1256,6 +1344,83 @@ DeparseVacuumColumnNames(List *columnNameList)
}
/*
* ErrorIfUnstableCreateOrAlterExtensionStmt compares CITUS_EXTENSIONVERSION
* and version given CREATE/ALTER EXTENSION statement will create/update to. If
* they are not same in major or minor version numbers, this function errors
* out. It ignores the schema version.
*/
static void
ErrorIfUnstableCreateOrAlterExtensionStmt(Node *parsetree)
{
char *newExtensionVersion = ExtractNewExtensionVersion(parsetree);
if (newExtensionVersion != NULL)
{
/* explicit version provided in CREATE or ALTER EXTENSION UPDATE; verify */
if (!MajorVersionsCompatible(newExtensionVersion, CITUS_EXTENSIONVERSION))
{
ereport(ERROR, (errmsg("specified version incompatible with loaded "
"Citus library"),
errdetail("Loaded library requires %s, but %s was specified.",
CITUS_MAJORVERSION, newExtensionVersion),
errhint("If a newer library is present, restart the database "
"and try the command again.")));
}
}
else
{
/*
* No version was specified, so PostgreSQL will use the default_version
* from the citus.control file. In case a new default is available, we
* will force a compatibility check of the latest available version.
*/
availableExtensionVersion = NULL;
ErrorIfAvailableVersionMismatch();
}
}
/*
* ExtractNewExtensionVersion returns the new extension version specified by
* a CREATE or ALTER EXTENSION statement. Other inputs are not permitted. This
* function returns NULL for statements with no explicit version specified.
*/
static char *
ExtractNewExtensionVersion(Node *parsetree)
{
char *newVersion = NULL;
List *optionsList = NIL;
ListCell *optionsCell = NULL;
if (IsA(parsetree, CreateExtensionStmt))
{
optionsList = ((CreateExtensionStmt *) parsetree)->options;
}
else if (IsA(parsetree, AlterExtensionStmt))
{
optionsList = ((AlterExtensionStmt *) parsetree)->options;
}
else
{
/* input must be one of the two above types */
Assert(false);
}
foreach(optionsCell, optionsList)
{
DefElem *defElement = (DefElem *) lfirst(optionsCell);
if (strncmp(defElement->defname, "new_version", NAMEDATALEN) == 0)
{
newVersion = strVal(defElement->arg);
break;
}
}
return newVersion;
}
/*
* ErrorIfUnsupportedIndexStmt checks if the corresponding index statement is
* supported for distributed tables and errors out if it is not.

View File

@ -16,6 +16,7 @@
#include "fmgr.h"
#include "miscadmin.h"
#include "citus_version.h"
#include "commands/explain.h"
#include "executor/executor.h"
#include "distributed/citus_nodefuncs.h"
@ -48,6 +49,8 @@
/* marks shared object as one loadable by the postgres version compiled against */
PG_MODULE_MAGIC;
static char *CitusVersion = CITUS_VERSION;
void _PG_init(void);
static void CreateRequiredDirectories(void);
@ -610,6 +613,26 @@ RegisterCitusConfigVariables(void)
0,
NULL, NULL, NULL);
DefineCustomStringVariable(
"citus.version",
gettext_noop("Shows the Citus library version"),
NULL,
&CitusVersion,
CITUS_VERSION,
PGC_INTERNAL,
0,
NULL, NULL, NULL);
DefineCustomBoolVariable(
"citus.enable_version_checks",
gettext_noop("Enables version checks during CREATE/ALTER EXTENSION commands"),
NULL,
&EnableVersionChecks,
true,
PGC_USERSET,
GUC_NO_SHOW_ALL,
NULL, NULL, NULL);
/* warn about config items in the citus namespace that are not registered above */
EmitWarningsOnPlaceholders("citus");
}

View File

@ -22,6 +22,7 @@
#include "catalog/pg_extension.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_type.h"
#include "citus_version.h"
#include "commands/extension.h"
#include "commands/trigger.h"
#include "distributed/colocation_utils.h"
@ -35,6 +36,7 @@
#include "distributed/shardinterval_utils.h"
#include "distributed/worker_manager.h"
#include "distributed/worker_protocol.h"
#include "executor/executor.h"
#include "parser/parse_func.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
@ -98,6 +100,12 @@ static Oid distTransactionRelationId = InvalidOid;
static Oid distTransactionGroupIndexId = InvalidOid;
static Oid extraDataContainerFuncId = InvalidOid;
/* Citus extension version variables */
bool EnableVersionChecks = true; /* version checks are enabled */
char *availableExtensionVersion = NULL;
static char *installedExtensionVersion = NULL;
/* Hash table for informations about each partition */
static HTAB *DistTableCacheHash = NULL;
@ -133,6 +141,9 @@ static bool HasUniformHashDistribution(ShardInterval **shardIntervalArray,
int shardIntervalArrayLength);
static bool HasUninitializedShardInterval(ShardInterval **sortedShardIntervalArray,
int shardCount);
static void ErrorIfInstalledVersionMismatch(void);
static char * AvailableExtensionVersion(void);
static char * InstalledExtensionVersion(void);
static void InitializeDistTableCache(void);
static void InitializeWorkerNodeCache(void);
static uint32 WorkerNodeHashCode(const void *key, Size keySize);
@ -927,7 +938,7 @@ bool
CitusHasBeenLoaded(void)
{
/* recheck presence until citus has been loaded */
if (!extensionLoaded)
if (!extensionLoaded || creating_extension)
{
bool extensionPresent = false;
bool extensionScriptExecuted = true;
@ -964,13 +975,286 @@ CitusHasBeenLoaded(void)
* present during early stages of upgrade operation.
*/
DistPartitionRelationId();
/*
* We also set installedExtensionVersion to NULL so that it will be re-read
* in case of extension update.
*/
installedExtensionVersion = NULL;
}
}
if (extensionLoaded)
{
ErrorIfAvailableVersionMismatch();
ErrorIfInstalledVersionMismatch();
}
return extensionLoaded;
}
/*
* ErrorIfAvailableExtensionVersionMismatch compares CITUS_EXTENSIONVERSION and
* currently available version from citus.control file. If they are not same in
* major or minor version numbers, this function errors out. It ignores the schema
* version.
*/
void
ErrorIfAvailableVersionMismatch(void)
{
char *availableVersion = NULL;
if (!EnableVersionChecks)
{
return;
}
availableVersion = AvailableExtensionVersion();
if (!MajorVersionsCompatible(availableVersion, CITUS_EXTENSIONVERSION))
{
ereport(ERROR, (errmsg("loaded Citus library version differs from latest "
"available extension version"),
errdetail("Loaded library requires %s, but the latest control "
"file specifies %s.", CITUS_MAJORVERSION,
availableVersion),
errhint("Restart the database to load the latest Citus "
"library.")));
}
}
/*
* ErrorIfInstalledVersionMismatch compares CITUS_EXTENSIONVERSION and currently
* and catalog version from pg_extemsion catalog table. If they are not same in
* major or minor version numbers, this function errors out. It ignores the schema
* version.
*/
static void
ErrorIfInstalledVersionMismatch(void)
{
char *installedVersion = NULL;
if (!EnableVersionChecks)
{
return;
}
installedVersion = InstalledExtensionVersion();
if (!MajorVersionsCompatible(installedVersion, CITUS_EXTENSIONVERSION))
{
ereport(ERROR, (errmsg("loaded Citus library version differs from installed "
"extension version"),
errdetail("Loaded library requires %s, but the installed "
"extension version is %s.", CITUS_MAJORVERSION,
installedVersion),
errhint("Run ALTER EXTENSION citus UPDATE and try again.")));
}
}
/*
* MajorVersionsCompatible compares given two versions. If they are same in major
* and minor version numbers, this function returns true. It ignores the schema
* version.
*/
bool
MajorVersionsCompatible(char *leftVersion, char *rightVersion)
{
const char schemaVersionSeparator = '-';
char *leftSeperatorPosition = strchr(leftVersion, schemaVersionSeparator);
char *rightSeperatorPosition = strchr(rightVersion, schemaVersionSeparator);
int leftComparisionLimit = 0;
int rightComparisionLimit = 0;
if (leftSeperatorPosition != NULL)
{
leftComparisionLimit = leftSeperatorPosition - leftVersion;
}
else
{
leftComparisionLimit = strlen(leftVersion);
}
if (rightSeperatorPosition != NULL)
{
rightComparisionLimit = rightSeperatorPosition - rightVersion;
}
else
{
rightComparisionLimit = strlen(leftVersion);
}
/* we can error out early if hypens are not in the same position */
if (leftComparisionLimit != rightComparisionLimit)
{
return false;
}
return strncmp(leftVersion, rightVersion, leftComparisionLimit) == 0;
}
/*
* AvailableExtensionVersion returns the Citus version from citus.control file. It also
* saves the result, thus consecutive calls to CitusExtensionAvailableVersion will
* not read the citus.control file again.
*/
static char *
AvailableExtensionVersion(void)
{
ReturnSetInfo *extensionsResultSet = NULL;
TupleTableSlot *tupleTableSlot = NULL;
FunctionCallInfoData *fcinfo = NULL;
FmgrInfo *flinfo = NULL;
int argumentCount = 0;
EState *estate = NULL;
bool hasTuple = false;
bool goForward = true;
bool doCopy = false;
/* if we cached the result before, return it*/
if (availableExtensionVersion != NULL)
{
return availableExtensionVersion;
}
estate = CreateExecutorState();
extensionsResultSet = makeNode(ReturnSetInfo);
extensionsResultSet->econtext = GetPerTupleExprContext(estate);
extensionsResultSet->allowedModes = SFRM_Materialize;
fcinfo = palloc0(sizeof(FunctionCallInfoData));
flinfo = palloc0(sizeof(FmgrInfo));
fmgr_info(F_PG_AVAILABLE_EXTENSIONS, flinfo);
InitFunctionCallInfoData(*fcinfo, flinfo, argumentCount, InvalidOid, NULL,
(Node *) extensionsResultSet);
/* pg_available_extensions returns result set containing all available extensions */
(*pg_available_extensions)(fcinfo);
tupleTableSlot = MakeSingleTupleTableSlot(extensionsResultSet->setDesc);
hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward, doCopy,
tupleTableSlot);
while (hasTuple)
{
Datum extensionNameDatum = 0;
char *extensionName = NULL;
bool isNull = false;
extensionNameDatum = slot_getattr(tupleTableSlot, 1, &isNull);
extensionName = NameStr(*DatumGetName(extensionNameDatum));
if (strcmp(extensionName, "citus") == 0)
{
MemoryContext oldMemoryContext = NULL;
Datum availableVersion = slot_getattr(tupleTableSlot, 2, &isNull);
/* we will cache the result of citus version to prevent catalog access */
if (CacheMemoryContext == NULL)
{
CreateCacheMemoryContext();
}
oldMemoryContext = MemoryContextSwitchTo(CacheMemoryContext);
availableExtensionVersion = text_to_cstring(DatumGetTextPP(availableVersion));
MemoryContextSwitchTo(oldMemoryContext);
ExecClearTuple(tupleTableSlot);
ExecDropSingleTupleTableSlot(tupleTableSlot);
return availableExtensionVersion;
}
ExecClearTuple(tupleTableSlot);
hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,
doCopy, tupleTableSlot);
}
ExecDropSingleTupleTableSlot(tupleTableSlot);
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("citus extension is not found")));
return NULL;
}
/*
* InstalledExtensionVersion returns the Citus version in PostgreSQL pg_extension table.
* It also saves the result, thus consecutive calls to CitusExtensionCatalogVersion
* will not read the catalog tables again.
*/
static char *
InstalledExtensionVersion(void)
{
Relation relation = NULL;
SysScanDesc scandesc;
ScanKeyData entry[1];
HeapTuple extensionTuple = NULL;
/* if we cached the result before, return it*/
if (installedExtensionVersion != NULL)
{
return installedExtensionVersion;
}
relation = heap_open(ExtensionRelationId, AccessShareLock);
ScanKeyInit(&entry[0], Anum_pg_extension_extname, BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum("citus"));
scandesc = systable_beginscan(relation, ExtensionNameIndexId, true,
NULL, 1, entry);
extensionTuple = systable_getnext(scandesc);
/* We assume that there can be at most one matching tuple */
if (HeapTupleIsValid(extensionTuple))
{
MemoryContext oldMemoryContext = NULL;
int extensionIndex = Anum_pg_extension_extversion;
TupleDesc tupleDescriptor = RelationGetDescr(relation);
bool isNull = false;
Datum installedVersion = heap_getattr(extensionTuple, extensionIndex,
tupleDescriptor, &isNull);
if (isNull)
{
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("citus extension version is null")));
}
/* we will cache the result of citus version to prevent catalog access */
if (CacheMemoryContext == NULL)
{
CreateCacheMemoryContext();
}
oldMemoryContext = MemoryContextSwitchTo(CacheMemoryContext);
installedExtensionVersion = text_to_cstring(DatumGetTextPP(installedVersion));
MemoryContextSwitchTo(oldMemoryContext);
}
else
{
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("citus extension is not loaded")));
}
systable_endscan(scandesc);
heap_close(relation, AccessShareLock);
return installedExtensionVersion;
}
/* return oid of pg_dist_shard relation */
Oid
DistShardRelationId(void)

View File

@ -2,3 +2,5 @@
/stamp-ext-h
/citus_config.h
/citus_config.h.in~
/citus_version.h
/citus_version.h.in~

View File

@ -10,6 +10,18 @@
*/
/* Extension version expected by this Citus build */
#undef CITUS_EXTENSIONVERSION
/* Citus major version as a string */
#undef CITUS_MAJORVERSION
/* Citus version as a string */
#undef CITUS_VERSION
/* Citus version as a number */
#undef CITUS_VERSION_NUM
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT

View File

@ -0,0 +1,13 @@
/* This file is created manually */
/* Extension version expected by this Citus build */
#undef CITUS_EXTENSIONVERSION
/* Citus major version as a string */
#undef CITUS_MAJORVERSION
/* Citus version as a string */
#undef CITUS_VERSION
/* Citus version as a number */
#undef CITUS_VERSION_NUM

View File

@ -17,6 +17,8 @@
#include "distributed/worker_manager.h"
#include "utils/hsearch.h"
extern bool EnableVersionChecks;
extern char *availableExtensionVersion;
/*
* Representation of a table's metadata that is frequently used for
@ -68,6 +70,8 @@ extern void CitusInvalidateRelcacheByRelid(Oid relationId);
extern void CitusInvalidateRelcacheByShardId(int64 shardId);
extern bool CitusHasBeenLoaded(void);
void ErrorIfAvailableVersionMismatch(void);
bool MajorVersionsCompatible(char *leftVersion, char *rightVersion);
/* access WorkerNodeHash */
extern HTAB * GetWorkerNodeHash(void);

View File

@ -13,6 +13,7 @@
#include "tcop/utility.h"
extern bool EnableDDLPropagation;
extern bool EnableVersionChecks;
/*
* A DDLJob encapsulates the remote tasks and commands needed to process all or

View File

@ -24,7 +24,8 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
-- DROP EXTENSION pre-created by the regression suite
DROP EXTENSION citus;
\c
-- Create extension in oldest version, test every upgrade step
SET citus.enable_version_checks TO 'false';
-- Create extension in oldest version
CREATE EXTENSION citus VERSION '5.0';
ALTER EXTENSION citus UPDATE TO '5.0-1';
ALTER EXTENSION citus UPDATE TO '5.0-2';
@ -77,6 +78,13 @@ ALTER EXTENSION citus UPDATE TO '6.1-16';
ALTER EXTENSION citus UPDATE TO '6.1-17';
ALTER EXTENSION citus UPDATE TO '6.2-1';
ALTER EXTENSION citus UPDATE TO '6.2-2';
-- show running version
SHOW citus.version;
citus.version
---------------
6.2devel
(1 row)
-- ensure no objects were created outside pg_catalog
SELECT COUNT(*)
FROM pg_depend AS pgd,
@ -91,7 +99,13 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
0
(1 row)
-- drop extension an re-create in newest version
-- see incompatible version errors out
RESET citus.enable_version_checks;
DROP EXTENSION citus;
CREATE EXTENSION citus VERSION '5.0';
ERROR: specified version incompatible with loaded Citus library
DETAIL: Loaded library requires 6.2, but 5.0 was specified.
HINT: If a newer library is present, restart the database and try the command again.
-- re-create in newest version
\c
CREATE EXTENSION citus;

View File

@ -24,7 +24,9 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
DROP EXTENSION citus;
\c
-- Create extension in oldest version, test every upgrade step
SET citus.enable_version_checks TO 'false';
-- Create extension in oldest version
CREATE EXTENSION citus VERSION '5.0';
ALTER EXTENSION citus UPDATE TO '5.0-1';
ALTER EXTENSION citus UPDATE TO '5.0-2';
@ -78,6 +80,9 @@ ALTER EXTENSION citus UPDATE TO '6.1-17';
ALTER EXTENSION citus UPDATE TO '6.2-1';
ALTER EXTENSION citus UPDATE TO '6.2-2';
-- show running version
SHOW citus.version;
-- ensure no objects were created outside pg_catalog
SELECT COUNT(*)
FROM pg_depend AS pgd,
@ -88,7 +93,11 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
pge.extname = 'citus' AND
pgio.schema NOT IN ('pg_catalog', 'citus');
-- drop extension an re-create in newest version
-- see incompatible version errors out
RESET citus.enable_version_checks;
DROP EXTENSION citus;
CREATE EXTENSION citus VERSION '5.0';
-- re-create in newest version
\c
CREATE EXTENSION citus;