Skip to content

Commit

Permalink
Change return signature of pgtle.available_extensions() to match `p…
Browse files Browse the repository at this point in the history
…gtle.available_extension_versions()`. (#287)
  • Loading branch information
adamguo0 authored Feb 12, 2025
1 parent 82a7a45 commit 25f3bf9
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 77 deletions.
14 changes: 10 additions & 4 deletions docs/03_managing_extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ If a schema is not specified in a `pg_tle`-compatible extension, all objects (e.

### `pgtle.available_extensions()`

`available_extensions` is a set-returning functions that returns a list of all available Trusted Language Extensions in a database. Each row contains information about a single extension.
`available_extensions` is a set-returning function that returns a list of all available Trusted Language Extensions in a database. Each row contains information about a single extension.

#### Role

Expand All @@ -46,6 +46,11 @@ None.

* `name`: The name of the extension.
* `default_version`: The version of the extension to use when `CREATE EXTENSION` is called without a version.
* `superuser`: This is always `false` for a pg_tle-compatible extension.
* `trusted`: This is always `false` for a pg_tle-compatible extension.
* `relocatable`: This is always `false` for a pg_tle-compatible extension.
* `schema`: This is set if the extension must be installed into a specific schema.
* `requires`: An array of extension names that this extension depends on.
* `comment`: A more detailed description about the extension.

#### Example
Expand All @@ -56,7 +61,7 @@ SELECT * FROM pgtle.available_extensions();

### `pgtle.available_extension_versions()`

`available_extension_versions` is a set-returning functions that returns a list of all available Trusted Language Extensions and their versions. Each row contains information about an individual version of an extension, including if it requires additional privileges for installation.
`available_extension_versions` is a set-returning function that returns a list of all available Trusted Language Extensions and their versions. Each row contains information about an individual version of an extension, including if it requires additional privileges for installation.

For more information on the output values, please read the [extension files](https://www.postgresql.org/docs/current/extend-extensions.html#id-1.8.3.20.11) section in the PostgreSQL documentation.

Expand Down Expand Up @@ -87,7 +92,7 @@ SELECT * FROM pgtle.available_extension_versions();

### `pgtle.extension_update_paths(name text)`

`extension_update_paths` is a set-returning functions that returns a list of all the possible update paths for a Trusted Language Extension. Each row shows the path for how to upgrade/downgrade an extension.
`extension_update_paths` is a set-returning function that returns a list of all the possible update paths for a Trusted Language Extension. Each row shows the path for how to upgrade/downgrade an extension.

#### Role

Expand All @@ -109,7 +114,7 @@ None.
SELECT * FROM pgtle.extension_update_paths('pg_tle_test');
```

### `pgtle.install_extension(name text, version text, description text, ext text, requires text[] DEFAULT NULL::text[])`
### `pgtle.install_extension(name text, version text, description text, ext text, requires text[] DEFAULT NULL::text[], schema text DEFAULT NULL)`

`install_extension` lets users install a `pg_tle`-compatible extensions and make them available within a database.

Expand All @@ -126,6 +131,7 @@ This functions returns `'OK'` on success and an error otherwise..
* `description`: A detailed description about the extension. This is displayed in the `comment` field in `pgtle.available_extensions()`.
* `ext`: The contents of the extension. This contains objects such as functions.
* `requires`: An optional parameter that specifies dependencies for this extension. `pg_tle` is automatically added as a dependency.
* `schema`: An optional parameter that specifies the schema that the extension must be installed in.

Many of the above values are part of the [extension control file](https://www.postgresql.org/docs/current/extend-extensions.html#id-1.8.3.20.11) used to provide information about how to install a PostgreSQL extension. For more information about how each of these values work, please see the PostgreSQL documentation on [extension control files](https://www.postgresql.org/docs/current/extend-extensions.html#id-1.8.3.20.11).

Expand Down
22 changes: 22 additions & 0 deletions pg_tle--1.4.1--1.5.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,25 @@ GRANT EXECUTE ON FUNCTION pgtle.install_extension
requires text[],
schema text
) TO pgtle_admin;

DROP FUNCTION pgtle.available_extensions
(
OUT name name,
OUT default_version text,
OUT comment text
);

CREATE FUNCTION pgtle.available_extensions
(
OUT name name,
OUT default_version text,
OUT superuser boolean,
OUT trusted boolean,
OUT relocatable boolean,
OUT schema name,
OUT requires name[],
OUT comment text
)
RETURNS SETOF RECORD
AS 'MODULE_PATHNAME', 'pg_tle_available_extensions'
LANGUAGE C STABLE STRICT;
198 changes: 160 additions & 38 deletions src/tleextension.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ static void check_requires_list(List *requires);
static bool is_pgtle_defined_c_func(Oid funcid, bool *is_operator_func);
static bool is_pgtle_used_user_func(Oid funcid, bool *is_operator_func);
static void check_pgtle_used_func(Oid funcid);
static void available_extensions_before_1_5_0(ReturnSetInfo *rsinfo,
char *fname);
static void available_extensions_on_or_after_1_5_0(ReturnSetInfo *rsinfo,
char *fname);

#if PG_VERSION_NUM < 150001
/* flag bits for InitMaterializedSRF() */
Expand Down Expand Up @@ -2520,47 +2524,41 @@ pg_tle_available_extensions(PG_FUNCTION_ARGS)
elog(ERROR, "search for %%.control in schema %u failed", schemaOid);

oldcontext = MemoryContextSwitchTo(ctx);
for (i = 0; i < SPI_processed; i++)
{
ExtensionControlFile *control;
char *extname;
Datum values[3];
bool nulls[3];
char *fname = SPI_getvalue(SPI_tuptable->vals[i],
SPI_tuptable->tupdesc, 1);

if (!pg_tle_is_extension_control_filename(fname))
continue;

/* extract extension name from 'name.control' filename */
extname = pstrdup(fname);
*strrchr(extname, '.') = '\0';

/* ignore it if it's an auxiliary control file */
if (strstr(extname, "--"))
continue;

control = read_extension_control_file(extname);

memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
/*
* In pg_tle 1.5.0, the return signature of
* pgtle.available_extensions() was changed. Check which return
* signature is expected in rsinfo and call the corresponding helper
* function to populate the return set.
*/
if (rsinfo->setDesc->natts == 3)
{
for (i = 0; i < SPI_processed; i++)
{
char *fname = SPI_getvalue(SPI_tuptable->vals[i],
SPI_tuptable->tupdesc, 1);

/* name */
values[0] = DirectFunctionCall1(namein,
CStringGetDatum(control->name));
/* default_version */
if (control->default_version == NULL)
nulls[1] = true;
else
values[1] = CStringGetTextDatum(control->default_version);
/* comment */
if (control->comment == NULL)
nulls[2] = true;
else
values[2] = CStringGetTextDatum(control->comment);
available_extensions_before_1_5_0(rsinfo, fname);
}
}
else if (rsinfo->setDesc->natts == 8)
{
for (i = 0; i < SPI_processed; i++)
{
char *fname = SPI_getvalue(SPI_tuptable->vals[i],
SPI_tuptable->tupdesc, 1);

tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
values, nulls);
available_extensions_on_or_after_1_5_0(rsinfo, fname);
}
}
else
{
/* this should be unreachable */
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("pgtle.available_extensions returns an unexpected number of fields: %d",
rsinfo->setDesc->natts),
errdetail("Expected to return 3 or 8 fields.")));
}
MemoryContextSwitchTo(oldcontext);

Expand All @@ -2575,6 +2573,130 @@ pg_tle_available_extensions(PG_FUNCTION_ARGS)
return (Datum) 0;
}

/*
* Helper function to populate the return set for pgtle.available_extensions()
* on pg_tle versions before 1.5.0. On these versions, the function returns 3
* fields:
*
* name name
* default_version text
* comment text
*/
static void
available_extensions_before_1_5_0(ReturnSetInfo *rsinfo, char *fname)
{
ExtensionControlFile *control;
char *extname;
Datum values[3];
bool nulls[3];

if (!pg_tle_is_extension_control_filename(fname))
return;

/* extract extension name from 'name.control' filename */
extname = pstrdup(fname);
*strrchr(extname, '.') = '\0';

/* ignore it if it's an auxiliary control file */
if (strstr(extname, "--"))
return;

control = read_extension_control_file(extname);

memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));

/* name */
values[0] = DirectFunctionCall1(namein,
CStringGetDatum(control->name));
/* default_version */
if (control->default_version == NULL)
nulls[1] = true;
else
values[1] = CStringGetTextDatum(control->default_version);
/* comment */
if (control->comment == NULL)
nulls[2] = true;
else
values[2] = CStringGetTextDatum(control->comment);

tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
values, nulls);
}

/*
* Helper function to populate the return set for pgtle.available_extensions()
* on pg_tle versions on or after 1.5.0. On these versions, the function
* returns 8 fields:
*
* name name
* default_version text
* superuser boolean
* trusted boolean
* relocatable boolean
* schema name
* requires name[]
* comment text
*/
static void
available_extensions_on_or_after_1_5_0(ReturnSetInfo *rsinfo, char *fname)
{
ExtensionControlFile *control;
char *extname;
Datum values[8];
bool nulls[8];

if (!pg_tle_is_extension_control_filename(fname))
return;

/* extract extension name from 'name.control' filename */
extname = pstrdup(fname);
*strrchr(extname, '.') = '\0';

/* ignore it if it's an auxiliary control file */
if (strstr(extname, "--"))
return;

control = read_extension_control_file(extname);

memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));

/* name */
values[0] = DirectFunctionCall1(namein,
CStringGetDatum(control->name));
/* default_version */
if (control->default_version == NULL)
nulls[1] = true;
else
values[1] = CStringGetTextDatum(control->default_version);
/* superuser */
values[2] = BoolGetDatum(control->superuser);
/* trusted */
values[3] = BoolGetDatum(control->trusted);
/* relocatable */
values[4] = BoolGetDatum(control->relocatable);
/* schema */
if (control->schema == NULL)
nulls[5] = true;
else
values[5] = DirectFunctionCall1(namein,
CStringGetDatum(control->schema));
/* requires */
if (control->requires == NIL)
nulls[6] = true;
else
values[6] = convert_requires_to_datum(control->requires);
/* comment */
if (control->comment == NULL)
nulls[7] = true;
else
values[7] = CStringGetTextDatum(control->comment);

tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
values, nulls);
}

/*
* This function lists the available extension versions (one row per
* extension installation script). For each version, we parse the related
Expand Down
38 changes: 23 additions & 15 deletions test/expected/pg_tle_extension_schema.out
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ DROP EXTENSION my_tle CASCADE;
-- Upgrade pg_tle to 1.5.0 and repeat the test.
ALTER EXTENSION pg_tle UPDATE TO '1.5.0';
SELECT * FROM pgtle.available_extensions() ORDER BY name;
name | default_version | comment
--------+-----------------+---------
my_tle | 1.0 | My TLE
name | default_version | superuser | trusted | relocatable | schema | requires | comment
--------+-----------------+-----------+---------+-------------+--------+----------+---------
my_tle | 1.0 | f | f | f | | {pg_tle} | My TLE
(1 row)

CREATE EXTENSION my_tle SCHEMA my_tle_schema_1;
Expand All @@ -70,6 +70,14 @@ SELECT my_tle_schema_1.my_tle_func();
1
(1 row)

-- By specifying the columns explicitly, we can get the same output from
-- pgtle.available_extensions() in 1.5.0 as in 1.4.1.
SELECT name, default_version, comment FROM pgtle.available_extensions();
name | default_version | comment
--------+-----------------+---------
my_tle | 1.0 | My TLE
(1 row)

-- Clean up.
DROP EXTENSION my_tle CASCADE;
SELECT pgtle.uninstall_extension('my_tle');
Expand Down Expand Up @@ -99,9 +107,9 @@ SELECT pgtle.install_extension('my_tle', '1.0', 'My TLE',
(1 row)

SELECT * FROM pgtle.available_extensions() ORDER BY name;
name | default_version | comment
--------+-----------------+---------
my_tle | 1.0 | My TLE
name | default_version | superuser | trusted | relocatable | schema | requires | comment
--------+-----------------+-----------+---------+-------------+-----------------+----------+---------
my_tle | 1.0 | f | f | f | my_tle_schema_1 | {pg_tle} | My TLE
(1 row)

-- my_tle cannot be installed in my_tle_schema_2.
Expand Down Expand Up @@ -141,9 +149,9 @@ SELECT pgtle.install_extension('my_tle', '1.0', 'My TLE',
(1 row)

SELECT * FROM pgtle.available_extensions() ORDER BY name;
name | default_version | comment
--------+-----------------+---------
my_tle | 1.0 | My TLE
name | default_version | superuser | trusted | relocatable | schema | requires | comment
--------+-----------------+-----------+---------+-------------+-----------------+----------+---------
my_tle | 1.0 | f | f | f | my_tle_schema_1 | {pg_tle} | My TLE
(1 row)

CREATE EXTENSION my_tle;
Expand Down Expand Up @@ -226,12 +234,12 @@ CREATE EXTENSION my_tle_3;
CREATE EXTENSION my_tle_4;
-- Validate the output of these functions.
SELECT * FROM pgtle.available_extensions() ORDER BY name;
name | default_version | comment
----------+-----------------+---------
my_tle_1 | 1.0 | My TLE
my_tle_2 | 1.0 | My TLE
my_tle_3 | 1.0 | My TLE
my_tle_4 | 1.0 | My TLE
name | default_version | superuser | trusted | relocatable | schema | requires | comment
----------+-----------------+-----------+---------+-------------+-----------------+-------------------+---------
my_tle_1 | 1.0 | f | f | f | | {pg_tle} | My TLE
my_tle_2 | 1.0 | f | f | f | | {my_tle_1,pg_tle} | My TLE
my_tle_3 | 1.0 | f | f | f | my_tle_schema_1 | {pg_tle} | My TLE
my_tle_4 | 1.0 | f | f | f | my_tle_schema_2 | {my_tle_3,pg_tle} | My TLE
(4 rows)

SELECT * from pgtle.available_extension_versions() ORDER BY name;
Expand Down
Loading

0 comments on commit 25f3bf9

Please sign in to comment.