mirror of
https://github.com/zebrajr/postgres.git
synced 2026-01-15 12:15:21 +00:00
Add paths of extensions to pg_available_extensions
Add a new "location" column to the pg_available_extensions and pg_available_extension_versions views, exposing the directory where the extension is located. The default system location is shown as '$system', the same value that can be used to configure the extension_control_path GUC. User-defined locations are only visible for super users, otherwise '<insufficient privilege>' is returned as a column value, the same behaviour that we already use in pg_stat_activity. I failed to resist the temptation to do a little extra editorializing of the TAP test script. Catalog version bumped. Author: Matheus Alcantara <mths.dev@pm.me> Reviewed-By: Chao Li <li.evan.chao@gmail.com> Reviewed-By: Rohit Prasad <rohit.prasad@arm.com> Reviewed-By: Michael Banck <mbanck@gmx.net> Reviewed-By: Manni Wood <manni.wood@enterprisedb.com> Reviewed-By: Euler Taveira <euler@eulerto.com> Reviewed-By: Quan Zongliang <quanzongliang@yeah.net>
This commit is contained in:
@@ -599,6 +599,20 @@
|
|||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
|
<structfield>location</structfield> <type>text</type>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The location where the extension is installed. If it is in the standard
|
||||||
|
system location, then the value will be <literal>$system</literal>,
|
||||||
|
while if it is found in the path specified by the
|
||||||
|
<link linkend="guc-extension-control-path"><structname>extension_control_path</structname></link>
|
||||||
|
GUC then the full path will be shown.
|
||||||
|
Only superusers can see this information.
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
<structfield>comment</structfield> <type>text</type>
|
<structfield>comment</structfield> <type>text</type>
|
||||||
@@ -723,6 +737,20 @@
|
|||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
|
<structfield>location</structfield> <type>text</type>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The location where the extension is installed. If it is in the standard
|
||||||
|
system location, then the value will be <literal>$system</literal>,
|
||||||
|
while if it is found in the path specified by the
|
||||||
|
<link linkend="guc-extension-control-path"><structname>extension_control_path</structname></link>
|
||||||
|
GUC then the full path will be shown.
|
||||||
|
Only superusers can see this information.
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry role="catalog_table_entry"><para role="column_definition">
|
<entry role="catalog_table_entry"><para role="column_definition">
|
||||||
<structfield>comment</structfield> <type>text</type>
|
<structfield>comment</structfield> <type>text</type>
|
||||||
|
|||||||
@@ -412,14 +412,14 @@ CREATE VIEW pg_cursors AS
|
|||||||
|
|
||||||
CREATE VIEW pg_available_extensions AS
|
CREATE VIEW pg_available_extensions AS
|
||||||
SELECT E.name, E.default_version, X.extversion AS installed_version,
|
SELECT E.name, E.default_version, X.extversion AS installed_version,
|
||||||
E.comment
|
E.location, E.comment
|
||||||
FROM pg_available_extensions() AS E
|
FROM pg_available_extensions() AS E
|
||||||
LEFT JOIN pg_extension AS X ON E.name = X.extname;
|
LEFT JOIN pg_extension AS X ON E.name = X.extname;
|
||||||
|
|
||||||
CREATE VIEW pg_available_extension_versions AS
|
CREATE VIEW pg_available_extension_versions AS
|
||||||
SELECT E.name, E.version, (X.extname IS NOT NULL) AS installed,
|
SELECT E.name, E.version, (X.extname IS NOT NULL) AS installed,
|
||||||
E.superuser, E.trusted, E.relocatable,
|
E.superuser, E.trusted, E.relocatable,
|
||||||
E.schema, E.requires, E.comment
|
E.schema, E.requires, E.location, E.comment
|
||||||
FROM pg_available_extension_versions() AS E
|
FROM pg_available_extension_versions() AS E
|
||||||
LEFT JOIN pg_extension AS X
|
LEFT JOIN pg_extension AS X
|
||||||
ON E.name = X.extname AND E.version = X.extversion;
|
ON E.name = X.extname AND E.version = X.extversion;
|
||||||
|
|||||||
@@ -126,6 +126,21 @@ typedef struct
|
|||||||
ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */
|
ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */
|
||||||
} script_error_callback_arg;
|
} script_error_callback_arg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A location based on the extension_control_path GUC.
|
||||||
|
*
|
||||||
|
* The macro field stores the name of a macro (for example “$system”) that
|
||||||
|
* the extension_control_path processing supports, and which can be replaced
|
||||||
|
* by a system value stored in loc.
|
||||||
|
*
|
||||||
|
* For non-system paths the macro field is NULL.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *macro;
|
||||||
|
char *loc;
|
||||||
|
} ExtensionLocation;
|
||||||
|
|
||||||
/* Local functions */
|
/* Local functions */
|
||||||
static List *find_update_path(List *evi_list,
|
static List *find_update_path(List *evi_list,
|
||||||
ExtensionVersionInfo *evi_start,
|
ExtensionVersionInfo *evi_start,
|
||||||
@@ -140,7 +155,8 @@ static Oid get_required_extension(char *reqExtensionName,
|
|||||||
bool is_create);
|
bool is_create);
|
||||||
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
static void get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
||||||
Tuplestorestate *tupstore,
|
Tuplestorestate *tupstore,
|
||||||
TupleDesc tupdesc);
|
TupleDesc tupdesc,
|
||||||
|
ExtensionLocation *location);
|
||||||
static Datum convert_requires_to_datum(List *requires);
|
static Datum convert_requires_to_datum(List *requires);
|
||||||
static void ApplyExtensionUpdates(Oid extensionOid,
|
static void ApplyExtensionUpdates(Oid extensionOid,
|
||||||
ExtensionControlFile *pcontrol,
|
ExtensionControlFile *pcontrol,
|
||||||
@@ -157,6 +173,29 @@ static ExtensionControlFile *new_ExtensionControlFile(const char *extname);
|
|||||||
|
|
||||||
char *find_in_paths(const char *basename, List *paths);
|
char *find_in_paths(const char *basename, List *paths);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the extension location. If the current user doesn't have sufficient
|
||||||
|
* privilege, don't show the location.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
get_extension_location(ExtensionLocation *loc)
|
||||||
|
{
|
||||||
|
/* We only want to show extension paths for superusers. */
|
||||||
|
if (superuser())
|
||||||
|
{
|
||||||
|
/* Return the macro value if present to avoid showing system paths. */
|
||||||
|
if (loc->macro != NULL)
|
||||||
|
return loc->macro;
|
||||||
|
else
|
||||||
|
return loc->loc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Similar to pg_stat_activity for unprivileged users */
|
||||||
|
return "<insufficient privilege>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get_extension_oid - given an extension name, look up the OID
|
* get_extension_oid - given an extension name, look up the OID
|
||||||
*
|
*
|
||||||
@@ -354,7 +393,11 @@ get_extension_control_directories(void)
|
|||||||
|
|
||||||
if (strlen(Extension_control_path) == 0)
|
if (strlen(Extension_control_path) == 0)
|
||||||
{
|
{
|
||||||
paths = lappend(paths, system_dir);
|
ExtensionLocation *location = palloc_object(ExtensionLocation);
|
||||||
|
|
||||||
|
location->macro = NULL;
|
||||||
|
location->loc = system_dir;
|
||||||
|
paths = lappend(paths, location);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -366,6 +409,7 @@ get_extension_control_directories(void)
|
|||||||
int len;
|
int len;
|
||||||
char *mangled;
|
char *mangled;
|
||||||
char *piece = first_path_var_separator(ecp);
|
char *piece = first_path_var_separator(ecp);
|
||||||
|
ExtensionLocation *location = palloc_object(ExtensionLocation);
|
||||||
|
|
||||||
/* Get the length of the next path on ecp */
|
/* Get the length of the next path on ecp */
|
||||||
if (piece == NULL)
|
if (piece == NULL)
|
||||||
@@ -382,15 +426,21 @@ get_extension_control_directories(void)
|
|||||||
* suffix if it is a custom extension control path.
|
* suffix if it is a custom extension control path.
|
||||||
*/
|
*/
|
||||||
if (strcmp(piece, "$system") == 0)
|
if (strcmp(piece, "$system") == 0)
|
||||||
|
{
|
||||||
|
location->macro = pstrdup(piece);
|
||||||
mangled = substitute_path_macro(piece, "$system", system_dir);
|
mangled = substitute_path_macro(piece, "$system", system_dir);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
location->macro = NULL;
|
||||||
mangled = psprintf("%s/extension", piece);
|
mangled = psprintf("%s/extension", piece);
|
||||||
|
}
|
||||||
pfree(piece);
|
pfree(piece);
|
||||||
|
|
||||||
/* Canonicalize the path based on the OS and add to the list */
|
/* Canonicalize the path based on the OS and add to the list */
|
||||||
canonicalize_path(mangled);
|
canonicalize_path(mangled);
|
||||||
paths = lappend(paths, mangled);
|
location->loc = mangled;
|
||||||
|
paths = lappend(paths, location);
|
||||||
|
|
||||||
/* Break if ecp is empty or move to the next path on ecp */
|
/* Break if ecp is empty or move to the next path on ecp */
|
||||||
if (ecp[len] == '\0')
|
if (ecp[len] == '\0')
|
||||||
@@ -2215,9 +2265,9 @@ pg_available_extensions(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
locations = get_extension_control_directories();
|
locations = get_extension_control_directories();
|
||||||
|
|
||||||
foreach_ptr(char, location, locations)
|
foreach_ptr(ExtensionLocation, location, locations)
|
||||||
{
|
{
|
||||||
dir = AllocateDir(location);
|
dir = AllocateDir(location->loc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the control directory doesn't exist, we want to silently return
|
* If the control directory doesn't exist, we want to silently return
|
||||||
@@ -2229,13 +2279,13 @@ pg_available_extensions(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while ((de = ReadDir(dir, location)) != NULL)
|
while ((de = ReadDir(dir, location->loc)) != NULL)
|
||||||
{
|
{
|
||||||
ExtensionControlFile *control;
|
ExtensionControlFile *control;
|
||||||
char *extname;
|
char *extname;
|
||||||
String *extname_str;
|
String *extname_str;
|
||||||
Datum values[3];
|
Datum values[4];
|
||||||
bool nulls[3];
|
bool nulls[4];
|
||||||
|
|
||||||
if (!is_extension_control_filename(de->d_name))
|
if (!is_extension_control_filename(de->d_name))
|
||||||
continue;
|
continue;
|
||||||
@@ -2259,7 +2309,7 @@ pg_available_extensions(PG_FUNCTION_ARGS)
|
|||||||
found_ext = lappend(found_ext, extname_str);
|
found_ext = lappend(found_ext, extname_str);
|
||||||
|
|
||||||
control = new_ExtensionControlFile(extname);
|
control = new_ExtensionControlFile(extname);
|
||||||
control->control_dir = pstrdup(location);
|
control->control_dir = pstrdup(location->loc);
|
||||||
parse_extension_control_file(control, NULL);
|
parse_extension_control_file(control, NULL);
|
||||||
|
|
||||||
memset(values, 0, sizeof(values));
|
memset(values, 0, sizeof(values));
|
||||||
@@ -2273,11 +2323,15 @@ pg_available_extensions(PG_FUNCTION_ARGS)
|
|||||||
nulls[1] = true;
|
nulls[1] = true;
|
||||||
else
|
else
|
||||||
values[1] = CStringGetTextDatum(control->default_version);
|
values[1] = CStringGetTextDatum(control->default_version);
|
||||||
|
|
||||||
|
/* location */
|
||||||
|
values[2] = CStringGetTextDatum(get_extension_location(location));
|
||||||
|
|
||||||
/* comment */
|
/* comment */
|
||||||
if (control->comment == NULL)
|
if (control->comment == NULL)
|
||||||
nulls[2] = true;
|
nulls[3] = true;
|
||||||
else
|
else
|
||||||
values[2] = CStringGetTextDatum(control->comment);
|
values[3] = CStringGetTextDatum(control->comment);
|
||||||
|
|
||||||
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
|
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
|
||||||
values, nulls);
|
values, nulls);
|
||||||
@@ -2313,9 +2367,9 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
locations = get_extension_control_directories();
|
locations = get_extension_control_directories();
|
||||||
|
|
||||||
foreach_ptr(char, location, locations)
|
foreach_ptr(ExtensionLocation, location, locations)
|
||||||
{
|
{
|
||||||
dir = AllocateDir(location);
|
dir = AllocateDir(location->loc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the control directory doesn't exist, we want to silently return
|
* If the control directory doesn't exist, we want to silently return
|
||||||
@@ -2327,7 +2381,7 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while ((de = ReadDir(dir, location)) != NULL)
|
while ((de = ReadDir(dir, location->loc)) != NULL)
|
||||||
{
|
{
|
||||||
ExtensionControlFile *control;
|
ExtensionControlFile *control;
|
||||||
char *extname;
|
char *extname;
|
||||||
@@ -2356,12 +2410,13 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
/* read the control file */
|
/* read the control file */
|
||||||
control = new_ExtensionControlFile(extname);
|
control = new_ExtensionControlFile(extname);
|
||||||
control->control_dir = pstrdup(location);
|
control->control_dir = pstrdup(location->loc);
|
||||||
parse_extension_control_file(control, NULL);
|
parse_extension_control_file(control, NULL);
|
||||||
|
|
||||||
/* scan extension's script directory for install scripts */
|
/* scan extension's script directory for install scripts */
|
||||||
get_available_versions_for_extension(control, rsinfo->setResult,
|
get_available_versions_for_extension(control, rsinfo->setResult,
|
||||||
rsinfo->setDesc);
|
rsinfo->setDesc,
|
||||||
|
location);
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeDir(dir);
|
FreeDir(dir);
|
||||||
@@ -2378,7 +2433,8 @@ pg_available_extension_versions(PG_FUNCTION_ARGS)
|
|||||||
static void
|
static void
|
||||||
get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
||||||
Tuplestorestate *tupstore,
|
Tuplestorestate *tupstore,
|
||||||
TupleDesc tupdesc)
|
TupleDesc tupdesc,
|
||||||
|
ExtensionLocation *location)
|
||||||
{
|
{
|
||||||
List *evi_list;
|
List *evi_list;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
@@ -2391,8 +2447,8 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
|||||||
{
|
{
|
||||||
ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
|
ExtensionVersionInfo *evi = (ExtensionVersionInfo *) lfirst(lc);
|
||||||
ExtensionControlFile *control;
|
ExtensionControlFile *control;
|
||||||
Datum values[8];
|
Datum values[9];
|
||||||
bool nulls[8];
|
bool nulls[9];
|
||||||
ListCell *lc2;
|
ListCell *lc2;
|
||||||
|
|
||||||
if (!evi->installable)
|
if (!evi->installable)
|
||||||
@@ -2428,11 +2484,15 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
|||||||
nulls[6] = true;
|
nulls[6] = true;
|
||||||
else
|
else
|
||||||
values[6] = convert_requires_to_datum(control->requires);
|
values[6] = convert_requires_to_datum(control->requires);
|
||||||
|
|
||||||
|
/* location */
|
||||||
|
values[7] = CStringGetTextDatum(get_extension_location(location));
|
||||||
|
|
||||||
/* comment */
|
/* comment */
|
||||||
if (control->comment == NULL)
|
if (control->comment == NULL)
|
||||||
nulls[7] = true;
|
nulls[8] = true;
|
||||||
else
|
else
|
||||||
values[7] = CStringGetTextDatum(control->comment);
|
values[8] = CStringGetTextDatum(control->comment);
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||||
|
|
||||||
@@ -2473,7 +2533,7 @@ get_available_versions_for_extension(ExtensionControlFile *pcontrol,
|
|||||||
values[6] = convert_requires_to_datum(control->requires);
|
values[6] = convert_requires_to_datum(control->requires);
|
||||||
nulls[6] = false;
|
nulls[6] = false;
|
||||||
}
|
}
|
||||||
/* comment stays the same */
|
/* comment and location stay the same */
|
||||||
|
|
||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
||||||
}
|
}
|
||||||
@@ -3903,7 +3963,8 @@ find_in_paths(const char *basename, List *paths)
|
|||||||
|
|
||||||
foreach(cell, paths)
|
foreach(cell, paths)
|
||||||
{
|
{
|
||||||
char *path = lfirst(cell);
|
ExtensionLocation *location = lfirst(cell);
|
||||||
|
char *path = location->loc;
|
||||||
char *full;
|
char *full;
|
||||||
|
|
||||||
Assert(path != NULL);
|
Assert(path != NULL);
|
||||||
|
|||||||
@@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202512301
|
#define CATALOG_VERSION_NO 202601011
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10750,16 +10750,16 @@
|
|||||||
{ oid => '3082', descr => 'list available extensions',
|
{ oid => '3082', descr => 'list available extensions',
|
||||||
proname => 'pg_available_extensions', procost => '10', prorows => '100',
|
proname => 'pg_available_extensions', procost => '10', prorows => '100',
|
||||||
proretset => 't', provolatile => 's', prorettype => 'record',
|
proretset => 't', provolatile => 's', prorettype => 'record',
|
||||||
proargtypes => '', proallargtypes => '{name,text,text}',
|
proargtypes => '', proallargtypes => '{name,text,text,text}',
|
||||||
proargmodes => '{o,o,o}', proargnames => '{name,default_version,comment}',
|
proargmodes => '{o,o,o,o}', proargnames => '{name,default_version,location,comment}',
|
||||||
prosrc => 'pg_available_extensions' },
|
prosrc => 'pg_available_extensions' },
|
||||||
{ oid => '3083', descr => 'list available extension versions',
|
{ oid => '3083', descr => 'list available extension versions',
|
||||||
proname => 'pg_available_extension_versions', procost => '10',
|
proname => 'pg_available_extension_versions', procost => '10',
|
||||||
prorows => '100', proretset => 't', provolatile => 's',
|
prorows => '100', proretset => 't', provolatile => 's',
|
||||||
prorettype => 'record', proargtypes => '',
|
prorettype => 'record', proargtypes => '',
|
||||||
proallargtypes => '{name,text,bool,bool,bool,name,_name,text}',
|
proallargtypes => '{name,text,bool,bool,bool,name,_name,text,text}',
|
||||||
proargmodes => '{o,o,o,o,o,o,o,o}',
|
proargmodes => '{o,o,o,o,o,o,o,o,o}',
|
||||||
proargnames => '{name,version,superuser,trusted,relocatable,schema,requires,comment}',
|
proargnames => '{name,version,superuser,trusted,relocatable,schema,requires,location,comment}',
|
||||||
prosrc => 'pg_available_extension_versions' },
|
prosrc => 'pg_available_extension_versions' },
|
||||||
{ oid => '3084', descr => 'list an extension\'s version update paths',
|
{ oid => '3084', descr => 'list an extension\'s version update paths',
|
||||||
proname => 'pg_extension_update_paths', procost => '10', prorows => '100',
|
proname => 'pg_extension_update_paths', procost => '10', prorows => '100',
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ my $ext_name2 = "test_custom_ext_paths_using_directory";
|
|||||||
mkpath("$ext_dir/$ext_name2");
|
mkpath("$ext_dir/$ext_name2");
|
||||||
create_extension($ext_name2, $ext_dir, $ext_name2);
|
create_extension($ext_name2, $ext_dir, $ext_name2);
|
||||||
|
|
||||||
|
# Make windows path use Unix slashes as canonicalize_path() is called when
|
||||||
|
# collecting extension control paths. See get_extension_control_directories().
|
||||||
|
my $ext_dir_canonicalized = $ext_dir;
|
||||||
|
$ext_dir_canonicalized =~ s!\\!/!g if $windows_os;
|
||||||
|
|
||||||
# Use the correct separator and escape \ when running on Windows.
|
# Use the correct separator and escape \ when running on Windows.
|
||||||
my $sep = $windows_os ? ";" : ":";
|
my $sep = $windows_os ? ";" : ":";
|
||||||
$node->append_conf(
|
$node->append_conf(
|
||||||
@@ -35,6 +40,10 @@ extension_control_path = '\$system$sep@{[ $windows_os ? ($ext_dir =~ s/\\/\\\\/g
|
|||||||
# Start node
|
# Start node
|
||||||
$node->start;
|
$node->start;
|
||||||
|
|
||||||
|
# Create an user to test permissions to read extension locations.
|
||||||
|
my $user = "user01";
|
||||||
|
$node->safe_psql('postgres', "CREATE USER $user");
|
||||||
|
|
||||||
my $ecp = $node->safe_psql('postgres', 'show extension_control_path;');
|
my $ecp = $node->safe_psql('postgres', 'show extension_control_path;');
|
||||||
|
|
||||||
is($ecp, "\$system$sep$ext_dir$sep$ext_dir2",
|
is($ecp, "\$system$sep$ext_dir$sep$ext_dir2",
|
||||||
@@ -46,48 +55,65 @@ $node->safe_psql('postgres', "CREATE EXTENSION $ext_name2");
|
|||||||
my $ret = $node->safe_psql('postgres',
|
my $ret = $node->safe_psql('postgres',
|
||||||
"select * from pg_available_extensions where name = '$ext_name'");
|
"select * from pg_available_extensions where name = '$ext_name'");
|
||||||
is( $ret,
|
is( $ret,
|
||||||
"test_custom_ext_paths|1.0|1.0|Test extension_control_path",
|
"test_custom_ext_paths|1.0|1.0|$ext_dir_canonicalized/extension|Test extension_control_path",
|
||||||
"extension is installed correctly on pg_available_extensions");
|
"extension is shown correctly in pg_available_extensions");
|
||||||
|
|
||||||
$ret = $node->safe_psql('postgres',
|
$ret = $node->safe_psql('postgres',
|
||||||
"select * from pg_available_extension_versions where name = '$ext_name'");
|
"select * from pg_available_extension_versions where name = '$ext_name'");
|
||||||
is( $ret,
|
is( $ret,
|
||||||
"test_custom_ext_paths|1.0|t|t|f|t|||Test extension_control_path",
|
"test_custom_ext_paths|1.0|t|t|f|t|||$ext_dir_canonicalized/extension|Test extension_control_path",
|
||||||
"extension is installed correctly on pg_available_extension_versions");
|
"extension is shown correctly in pg_available_extension_versions");
|
||||||
|
|
||||||
$ret = $node->safe_psql('postgres',
|
$ret = $node->safe_psql('postgres',
|
||||||
"select * from pg_available_extensions where name = '$ext_name2'");
|
"select * from pg_available_extensions where name = '$ext_name2'");
|
||||||
is( $ret,
|
is( $ret,
|
||||||
"test_custom_ext_paths_using_directory|1.0|1.0|Test extension_control_path",
|
"test_custom_ext_paths_using_directory|1.0|1.0|$ext_dir_canonicalized/extension|Test extension_control_path",
|
||||||
"extension is installed correctly on pg_available_extensions");
|
"extension is shown correctly in pg_available_extensions");
|
||||||
|
|
||||||
$ret = $node->safe_psql('postgres',
|
$ret = $node->safe_psql('postgres',
|
||||||
"select * from pg_available_extension_versions where name = '$ext_name2'"
|
"select * from pg_available_extension_versions where name = '$ext_name2'"
|
||||||
);
|
);
|
||||||
is( $ret,
|
is( $ret,
|
||||||
"test_custom_ext_paths_using_directory|1.0|t|t|f|t|||Test extension_control_path",
|
"test_custom_ext_paths_using_directory|1.0|t|t|f|t|||$ext_dir_canonicalized/extension|Test extension_control_path",
|
||||||
"extension is installed correctly on pg_available_extension_versions");
|
"extension is shown correctly in pg_available_extension_versions");
|
||||||
|
|
||||||
# Ensure that extensions installed on $system is still visible when using with
|
# Test that a non-superuser is not able to read the extension location in
|
||||||
|
# pg_available_extensions
|
||||||
|
$ret = $node->safe_psql('postgres',
|
||||||
|
"select location from pg_available_extensions where name = '$ext_name2'",
|
||||||
|
connstr => "user=$user");
|
||||||
|
is( $ret,
|
||||||
|
"<insufficient privilege>",
|
||||||
|
"extension location is hidden in pg_available_extensions for users with insufficient privilege");
|
||||||
|
|
||||||
|
# Test that a non-superuser is not able to read the extension location in
|
||||||
|
# pg_available_extension_versions
|
||||||
|
$ret = $node->safe_psql('postgres',
|
||||||
|
"select location from pg_available_extension_versions where name = '$ext_name2'",
|
||||||
|
connstr => "user=$user");
|
||||||
|
is( $ret,
|
||||||
|
"<insufficient privilege>",
|
||||||
|
"extension location is hidden in pg_available_extension_versions for users with insufficient privilege");
|
||||||
|
|
||||||
|
# Ensure that extensions installed in $system are still visible when used with
|
||||||
# custom extension control path.
|
# custom extension control path.
|
||||||
$ret = $node->safe_psql('postgres',
|
$ret = $node->safe_psql('postgres',
|
||||||
"select count(*) > 0 as ok from pg_available_extensions where name = 'plpgsql'"
|
"select count(*) > 0 as ok from pg_available_extensions where name = 'plpgsql'"
|
||||||
);
|
);
|
||||||
is($ret, "t",
|
is($ret, "t",
|
||||||
"\$system extension is installed correctly on pg_available_extensions");
|
"\$system extension is shown correctly in pg_available_extensions");
|
||||||
|
|
||||||
|
|
||||||
$ret = $node->safe_psql('postgres',
|
$ret = $node->safe_psql('postgres',
|
||||||
"set extension_control_path = ''; select count(*) > 0 as ok from pg_available_extensions where name = 'plpgsql'"
|
"set extension_control_path = ''; select count(*) > 0 as ok from pg_available_extensions where name = 'plpgsql'"
|
||||||
);
|
);
|
||||||
is($ret, "t",
|
is($ret, "t",
|
||||||
"\$system extension is installed correctly on pg_available_extensions with empty extension_control_path"
|
"\$system extension is shown correctly in pg_available_extensions with empty extension_control_path"
|
||||||
);
|
);
|
||||||
|
|
||||||
# Test with an extension that does not exists
|
# Test with an extension that does not exists
|
||||||
my ($code, $stdout, $stderr) =
|
my ($code, $stdout, $stderr) =
|
||||||
$node->psql('postgres', "CREATE EXTENSION invalid");
|
$node->psql('postgres', "CREATE EXTENSION invalid");
|
||||||
is($code, 3, 'error to create an extension that does not exists');
|
is($code, 3, 'error creating an extension that does not exist');
|
||||||
like($stderr, qr/ERROR: extension "invalid" is not available/);
|
like($stderr, qr/ERROR: extension "invalid" is not available/);
|
||||||
|
|
||||||
sub create_extension
|
sub create_extension
|
||||||
|
|||||||
@@ -1310,14 +1310,16 @@ pg_available_extension_versions| SELECT e.name,
|
|||||||
e.relocatable,
|
e.relocatable,
|
||||||
e.schema,
|
e.schema,
|
||||||
e.requires,
|
e.requires,
|
||||||
|
e.location,
|
||||||
e.comment
|
e.comment
|
||||||
FROM (pg_available_extension_versions() e(name, version, superuser, trusted, relocatable, schema, requires, comment)
|
FROM (pg_available_extension_versions() e(name, version, superuser, trusted, relocatable, schema, requires, location, comment)
|
||||||
LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion))));
|
LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion))));
|
||||||
pg_available_extensions| SELECT e.name,
|
pg_available_extensions| SELECT e.name,
|
||||||
e.default_version,
|
e.default_version,
|
||||||
x.extversion AS installed_version,
|
x.extversion AS installed_version,
|
||||||
|
e.location,
|
||||||
e.comment
|
e.comment
|
||||||
FROM (pg_available_extensions() e(name, default_version, comment)
|
FROM (pg_available_extensions() e(name, default_version, location, comment)
|
||||||
LEFT JOIN pg_extension x ON ((e.name = x.extname)));
|
LEFT JOIN pg_extension x ON ((e.name = x.extname)));
|
||||||
pg_backend_memory_contexts| SELECT name,
|
pg_backend_memory_contexts| SELECT name,
|
||||||
ident,
|
ident,
|
||||||
|
|||||||
@@ -800,6 +800,7 @@ ExtensibleNodeEntry
|
|||||||
ExtensibleNodeMethods
|
ExtensibleNodeMethods
|
||||||
ExtensionControlFile
|
ExtensionControlFile
|
||||||
ExtensionInfo
|
ExtensionInfo
|
||||||
|
ExtensionLocation
|
||||||
ExtensionVersionInfo
|
ExtensionVersionInfo
|
||||||
FDWCollateState
|
FDWCollateState
|
||||||
FD_SET
|
FD_SET
|
||||||
@@ -1583,7 +1584,6 @@ LoadStmt
|
|||||||
LocalBufferLookupEnt
|
LocalBufferLookupEnt
|
||||||
LocalPgBackendStatus
|
LocalPgBackendStatus
|
||||||
LocalTransactionId
|
LocalTransactionId
|
||||||
Location
|
|
||||||
LocationIndex
|
LocationIndex
|
||||||
LocationLen
|
LocationLen
|
||||||
LockAcquireResult
|
LockAcquireResult
|
||||||
|
|||||||
Reference in New Issue
Block a user