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 all: extension
# build extension # build extension
extension: extension: $(citus_abs_srcdir)/src/include/citus_version.h
$(MAKE) -C src/backend/distributed/ all $(MAKE) -C src/backend/distributed/ all
install-extension: extension install-extension: extension
$(MAKE) -C src/backend/distributed/ install $(MAKE) -C src/backend/distributed/ install
install-headers: extension install-headers: extension
$(MKDIR_P) '$(DESTDIR)$(includedir_server)/distributed/' $(MKDIR_P) '$(DESTDIR)$(includedir_server)/distributed/'
# generated headers are located in the build directory # 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 # the rest in the source tree
$(INSTALL_DATA) $(citus_abs_srcdir)/src/include/distributed/*.h '$(DESTDIR)$(includedir_server)/distributed/' $(INSTALL_DATA) $(citus_abs_srcdir)/src/include/distributed/*.h '$(DESTDIR)$(includedir_server)/distributed/'
clean-extension: 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, # Ensure configuration is generated by the most recent configure,
# useful for longer existing build directories. # useful for longer existing build directories.
$(citus_top_builddir)/config.status: $(citus_abs_top_srcdir)/configure $(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 cd @abs_top_builddir@ && ./config.status --recheck && ./config.status
# Regenerate configure if configure.in changed # Regenerate configure if configure.in changed
$(citus_abs_top_srcdir)/configure: $(citus_abs_top_srcdir)/configure.in $(citus_abs_top_srcdir)/configure: $(citus_abs_top_srcdir)/configure.in

104
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # 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. # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@ -9,7 +9,7 @@
# This configure script is free software; the Free Software Foundation # This configure script is free software; the Free Software Foundation
# gives unlimited permission to copy, distribute and modify it. # 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. ## ## M4sh Initialization. ##
## -------------------- ## ## -------------------- ##
@ -579,8 +579,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='Citus' PACKAGE_NAME='Citus'
PACKAGE_TARNAME='citus' PACKAGE_TARNAME='citus'
PACKAGE_VERSION='5.0' PACKAGE_VERSION='6.2devel'
PACKAGE_STRING='Citus 5.0' PACKAGE_STRING='Citus 6.2devel'
PACKAGE_BUGREPORT='' PACKAGE_BUGREPORT=''
PACKAGE_URL='' PACKAGE_URL=''
@ -600,6 +600,7 @@ vpath_build
PATH PATH
PG_CONFIG PG_CONFIG
FLEX FLEX
AWK
SED SED
target_alias target_alias
host_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. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF 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]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1255,7 +1256,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of Citus 5.0:";; short | recursive ) echo "Configuration of Citus 6.2devel:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1343,14 +1344,14 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
Citus configure 5.0 Citus configure 6.2devel
generated by GNU Autoconf 2.69 generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc. Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it. gives unlimited permission to copy, distribute and modify it.
Copyright (c) 2012-2016, Citus Data, Inc. Copyright (c) 2012-2017, Citus Data, Inc.
_ACEOF _ACEOF
exit exit
fi fi
@ -1400,7 +1401,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. 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 generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@ $ $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 "$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; } $as_echo_n "checking for a sed that does not truncate output... " >&6; }
if ${ac_cv_path_SED+:} false; then : if ${ac_cv_path_SED+:} false; then :
@ -1819,6 +1821,82 @@ $as_echo "$ac_cv_path_SED" >&6; }
SED="$ac_cv_path_SED" SED="$ac_cv_path_SED"
rm -f conftest.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 # Re-check for flex. That allows to compile citus against a postgres
# which was built without flex available (possible because generated # 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_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 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 # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" 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 generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -3545,7 +3623,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
Citus config.status 5.0 Citus config.status 6.2devel
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"
@ -3555,6 +3633,7 @@ gives unlimited permission to copy, distribute and modify it."
ac_pwd='$ac_pwd' ac_pwd='$ac_pwd'
srcdir='$srcdir' srcdir='$srcdir'
AWK='$AWK'
test -n "\$AWK" || AWK=awk test -n "\$AWK" || AWK=awk
_ACEOF _ACEOF
@ -3668,6 +3747,7 @@ do
case $ac_config_target in case $ac_config_target in
"Makefile.global") CONFIG_FILES="$CONFIG_FILES Makefile.global" ;; "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_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;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac esac

View File

@ -5,10 +5,30 @@
# everyone needing autoconf installed, the resulting files are checked # everyone needing autoconf installed, the resulting files are checked
# into the SCM. # into the SCM.
AC_INIT([Citus], [5.0], [], [citus], []) AC_INIT([Citus], [6.2devel])
AC_COPYRIGHT([Copyright (c) 2012-2016, Citus Data, Inc.]) 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_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 # Re-check for flex. That allows to compile citus against a postgres
# which was built without flex available (possible because generated # 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_SUBST(POSTGRES_BUILDDIR, "$POSTGRES_BUILDDIR")
AC_CONFIG_FILES([Makefile.global]) 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([ AH_TOP([
/* /*
* citus_config.h.in is generated by autoconf/autoheader and * citus_config.h.in is generated by autoconf/autoheader and

View File

@ -28,6 +28,7 @@
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_attribute.h" #include "catalog/pg_attribute.h"
#include "catalog/pg_class.h" #include "catalog/pg_class.h"
#include "citus_version.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "commands/prepare.h" #include "commands/prepare.h"
@ -82,7 +83,6 @@
bool EnableDDLPropagation = true; /* ddl propagation is enabled */ bool EnableDDLPropagation = true; /* ddl propagation is enabled */
/* /*
* This struct defines the state for the callback for drop statements. * This struct defines the state for the callback for drop statements.
* It is copied as it is from commands/tablecmds.c in Postgres source. * 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 */ /* Local functions forward declarations for Transmit statement */
static bool IsTransmitStmt(Node *parsetree); static bool IsTransmitStmt(Node *parsetree);
static void VerifyTransmitStmt(CopyStmt *copyStatement); static void VerifyTransmitStmt(CopyStmt *copyStatement);
@ -120,6 +124,7 @@ static char * DeparseVacuumColumnNames(List *columnNameList);
/* Local functions forward declarations for unsupported command checks */ /* Local functions forward declarations for unsupported command checks */
static void ErrorIfUnstableCreateOrAlterExtensionStmt(Node *parsetree);
static void ErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement); static void ErrorIfUnsupportedIndexStmt(IndexStmt *createIndexStatement);
static void ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement); static void ErrorIfUnsupportedDropIndexStmt(DropStmt *dropIndexStatement);
static void ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement); static void ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement);
@ -130,6 +135,7 @@ static bool OptionsSpecifyOwnedBy(List *optionList, Oid *ownedByTableId);
static void ErrorIfDistributedRenameStmt(RenameStmt *renameStatement); static void ErrorIfDistributedRenameStmt(RenameStmt *renameStatement);
/* Local functions forward declarations for helper functions */ /* Local functions forward declarations for helper functions */
static char * ExtractNewExtensionVersion(Node *parsetree);
static void CreateLocalTable(RangeVar *relation, char *nodeName, int32 nodePort); static void CreateLocalTable(RangeVar *relation, char *nodeName, int32 nodePort);
static bool IsAlterTableRenameStmt(RenameStmt *renameStatement); static bool IsAlterTableRenameStmt(RenameStmt *renameStatement);
static void ExecuteDistributedDDLJob(DDLJob *ddlJob); static void ExecuteDistributedDDLJob(DDLJob *ddlJob);
@ -170,18 +176,20 @@ multi_ProcessUtility(Node *parsetree,
Oid savedUserId = InvalidOid; Oid savedUserId = InvalidOid;
int savedSecurityContext = 0; int savedSecurityContext = 0;
List *ddlJobs = NIL; List *ddlJobs = NIL;
bool skipCitusProcessing = SkipCitusProcessingForUtility(parsetree);
if (IsA(parsetree, TransactionStmt)) if (skipCitusProcessing)
{ {
/* bool checkExtensionVersion = IsCitusExtensionStmt(parsetree);
* 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.
*/
standard_ProcessUtility(parsetree, queryString, context, standard_ProcessUtility(parsetree, queryString, context,
params, dest, completionTag); params, dest, completionTag);
if (EnableVersionChecks && checkExtensionVersion)
{
ErrorIfUnstableCreateOrAlterExtensionStmt(parsetree);
}
return; 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? */ /* Is the passed in statement a transmit statement? */
static bool static bool
IsTransmitStmt(Node *parsetree) 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 * ErrorIfUnsupportedIndexStmt checks if the corresponding index statement is
* supported for distributed tables and errors out if it is not. * supported for distributed tables and errors out if it is not.

View File

@ -16,6 +16,7 @@
#include "fmgr.h" #include "fmgr.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "citus_version.h"
#include "commands/explain.h" #include "commands/explain.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "distributed/citus_nodefuncs.h" #include "distributed/citus_nodefuncs.h"
@ -48,6 +49,8 @@
/* marks shared object as one loadable by the postgres version compiled against */ /* marks shared object as one loadable by the postgres version compiled against */
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
static char *CitusVersion = CITUS_VERSION;
void _PG_init(void); void _PG_init(void);
static void CreateRequiredDirectories(void); static void CreateRequiredDirectories(void);
@ -610,6 +613,26 @@ RegisterCitusConfigVariables(void)
0, 0,
NULL, NULL, NULL); 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 */ /* warn about config items in the citus namespace that are not registered above */
EmitWarningsOnPlaceholders("citus"); EmitWarningsOnPlaceholders("citus");
} }

View File

@ -22,6 +22,7 @@
#include "catalog/pg_extension.h" #include "catalog/pg_extension.h"
#include "catalog/pg_namespace.h" #include "catalog/pg_namespace.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "citus_version.h"
#include "commands/extension.h" #include "commands/extension.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "distributed/colocation_utils.h" #include "distributed/colocation_utils.h"
@ -35,6 +36,7 @@
#include "distributed/shardinterval_utils.h" #include "distributed/shardinterval_utils.h"
#include "distributed/worker_manager.h" #include "distributed/worker_manager.h"
#include "distributed/worker_protocol.h" #include "distributed/worker_protocol.h"
#include "executor/executor.h"
#include "parser/parse_func.h" #include "parser/parse_func.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/catcache.h" #include "utils/catcache.h"
@ -98,6 +100,12 @@ static Oid distTransactionRelationId = InvalidOid;
static Oid distTransactionGroupIndexId = InvalidOid; static Oid distTransactionGroupIndexId = InvalidOid;
static Oid extraDataContainerFuncId = 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 */ /* Hash table for informations about each partition */
static HTAB *DistTableCacheHash = NULL; static HTAB *DistTableCacheHash = NULL;
@ -133,6 +141,9 @@ static bool HasUniformHashDistribution(ShardInterval **shardIntervalArray,
int shardIntervalArrayLength); int shardIntervalArrayLength);
static bool HasUninitializedShardInterval(ShardInterval **sortedShardIntervalArray, static bool HasUninitializedShardInterval(ShardInterval **sortedShardIntervalArray,
int shardCount); int shardCount);
static void ErrorIfInstalledVersionMismatch(void);
static char * AvailableExtensionVersion(void);
static char * InstalledExtensionVersion(void);
static void InitializeDistTableCache(void); static void InitializeDistTableCache(void);
static void InitializeWorkerNodeCache(void); static void InitializeWorkerNodeCache(void);
static uint32 WorkerNodeHashCode(const void *key, Size keySize); static uint32 WorkerNodeHashCode(const void *key, Size keySize);
@ -927,7 +938,7 @@ bool
CitusHasBeenLoaded(void) CitusHasBeenLoaded(void)
{ {
/* recheck presence until citus has been loaded */ /* recheck presence until citus has been loaded */
if (!extensionLoaded) if (!extensionLoaded || creating_extension)
{ {
bool extensionPresent = false; bool extensionPresent = false;
bool extensionScriptExecuted = true; bool extensionScriptExecuted = true;
@ -964,13 +975,286 @@ CitusHasBeenLoaded(void)
* present during early stages of upgrade operation. * present during early stages of upgrade operation.
*/ */
DistPartitionRelationId(); 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; 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 */ /* return oid of pg_dist_shard relation */
Oid Oid
DistShardRelationId(void) DistShardRelationId(void)

View File

@ -2,3 +2,5 @@
/stamp-ext-h /stamp-ext-h
/citus_config.h /citus_config.h
/citus_config.h.in~ /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. */ /* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT #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 "distributed/worker_manager.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
extern bool EnableVersionChecks;
extern char *availableExtensionVersion;
/* /*
* Representation of a table's metadata that is frequently used for * 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 void CitusInvalidateRelcacheByShardId(int64 shardId);
extern bool CitusHasBeenLoaded(void); extern bool CitusHasBeenLoaded(void);
void ErrorIfAvailableVersionMismatch(void);
bool MajorVersionsCompatible(char *leftVersion, char *rightVersion);
/* access WorkerNodeHash */ /* access WorkerNodeHash */
extern HTAB * GetWorkerNodeHash(void); extern HTAB * GetWorkerNodeHash(void);

View File

@ -13,6 +13,7 @@
#include "tcop/utility.h" #include "tcop/utility.h"
extern bool EnableDDLPropagation; extern bool EnableDDLPropagation;
extern bool EnableVersionChecks;
/* /*
* A DDLJob encapsulates the remote tasks and commands needed to process all or * 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 pre-created by the regression suite
DROP EXTENSION citus; DROP EXTENSION citus;
\c \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'; CREATE EXTENSION citus VERSION '5.0';
ALTER EXTENSION citus UPDATE TO '5.0-1'; ALTER EXTENSION citus UPDATE TO '5.0-1';
ALTER EXTENSION citus UPDATE TO '5.0-2'; 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.1-17';
ALTER EXTENSION citus UPDATE TO '6.2-1'; ALTER EXTENSION citus UPDATE TO '6.2-1';
ALTER EXTENSION citus UPDATE TO '6.2-2'; 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 -- ensure no objects were created outside pg_catalog
SELECT COUNT(*) SELECT COUNT(*)
FROM pg_depend AS pgd, FROM pg_depend AS pgd,
@ -91,7 +99,13 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
0 0
(1 row) (1 row)
-- drop extension an re-create in newest version -- see incompatible version errors out
RESET citus.enable_version_checks;
DROP EXTENSION citus; 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 \c
CREATE EXTENSION citus; CREATE EXTENSION citus;

View File

@ -24,7 +24,9 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
DROP EXTENSION citus; DROP EXTENSION citus;
\c \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'; CREATE EXTENSION citus VERSION '5.0';
ALTER EXTENSION citus UPDATE TO '5.0-1'; ALTER EXTENSION citus UPDATE TO '5.0-1';
ALTER EXTENSION citus UPDATE TO '5.0-2'; 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-1';
ALTER EXTENSION citus UPDATE TO '6.2-2'; ALTER EXTENSION citus UPDATE TO '6.2-2';
-- show running version
SHOW citus.version;
-- ensure no objects were created outside pg_catalog -- ensure no objects were created outside pg_catalog
SELECT COUNT(*) SELECT COUNT(*)
FROM pg_depend AS pgd, FROM pg_depend AS pgd,
@ -88,7 +93,11 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND
pge.extname = 'citus' AND pge.extname = 'citus' AND
pgio.schema NOT IN ('pg_catalog', 'citus'); 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; DROP EXTENSION citus;
CREATE EXTENSION citus VERSION '5.0';
-- re-create in newest version
\c \c
CREATE EXTENSION citus; CREATE EXTENSION citus;