PostgreSQL Extensions in VMware Data Services Manager v1.5

I had an interesting query earlier this week in relation to PostgreSQL Extensions in VMware Data Services Manager (DSM) v1.5. We do not give out superuser (postgres user) privileges when creating databases via DSM, although it is still possible to find those credentials and access the database with those privileges if necessary. Instead, we focus on the default user that we create when we deploy a database, a user that we have named ‘dbaas’. The query was how should extensions be added to PostgreSQL if ‘dbaas’ is not a superuser? The answer lies in our DSM documentation where we describe how to work with extensions. In a nutshell, we provide an alternate method of managing PostgreSQL extensions via user-defined functions (UDFs). This avoids having to give users ‘superuser’ permissions, and avoids a potential security concern. And since the UDFs are in the template1 database, it means that any PostgreSQL database created from template1 have access to them. With all that said, let’s see it in action.

For the purposes of this demo, I am going to add two extensions. The first is ‘earthdistance‘, which can calculate distances between two points of latitude and longitude on the Earth’s surface. It does have a dependency on another extension however. This is the ‘cube‘ extension which is a data type for multidimensional cubes. Let’s begin by first listing all available extensions. Then we will install the ‘earthdistance‘ and ‘cube‘ extensions before finally using their functions to calculate a distance between two points on the Earth’s surface.

dbaas=> SELECT * from pg_available_extensions;
        name        | default_version | installed_version |                                            comment                                            
--------------------+-----------------+-------------------+-----------------------------------------------------------------------------------------------
 refint             | 1.0            |                  | functions for implementing referential integrity (obsolete)
 lo                 | 1.1            |                  | Large Object maintenance
 pgautofailover     | 2.0            |                  | pg_auto_failover
 pg_surgery         | 1.0            |                  | extension to perform surgery on a damaged relation
 moddatetime        | 1.0            |                  | functions for tracking last modification time
 pg_walinspect      | 1.0            |                  | functions to inspect contents of PostgreSQL Write-Ahead Log
 dblink             | 1.2            |                  | connect to other PostgreSQL databases from within a database
 intagg             | 1.1            |                  | integer aggregator and enumerator (obsolete)
 tsm_system_time    | 1.0            |                  | TABLESAMPLE method which accepts time in milliseconds as a limit
 bloom              | 1.0            |                  | bloom access method - signature file based index
 tablefunc          | 1.0            |                  | functions that manipulate whole tables, including crosstab
 pgstattuple        | 1.5            |                  | show tuple-level statistics
 earthdistance      | 1.1            |                  | calculate great-circle distances on the surface of the Earth
 file_fdw           | 1.0            |                  | foreign-data wrapper for flat file access
 isn                | 1.2            |                  | data types for international product numbering standards
 pgaudit            | 1.7            |                  | provides auditing functionality
 xml2               | 1.1            |                  | XPath querying and XSLT
 btree_gist         | 1.7            |                  | support for indexing common datatypes in GiST
 pg_prewarm         | 1.2            |                  | prewarm relation data
 tsm_system_rows    | 1.0            |                  | TABLESAMPLE method which accepts number of rows as a limit
 citext             | 1.6            |                  | data type for case-insensitive character strings
 unaccent           | 1.1            |                  | text search dictionary that removes accents
 dict_int           | 1.0            |                  | text search dictionary template for integers
 btree_gin          | 1.3            |                  | support for indexing common datatypes in GIN
 intarray           | 1.5            |                  | functions, operators, and index support for 1-D arrays of integers
 insert_username    | 1.0            |                  | functions for tracking who changed a table
 sslinfo            | 1.2            |                  | information about SSL certificates
 adminpack          | 2.1            |                  | administrative functions for PostgreSQL
 orafce             | 3.25           |                  | Functions and operators that emulate a subset of functions and packages from the Oracle RDBMS
 hstore             | 1.8            |                  | data type for storing sets of (key, value) pairs
 pgcrypto           | 1.3            |                  | cryptographic functions
 cube               | 1.5            |                  | data type for multidimensional cubes
 postgres_fdw       | 1.1            |                  | foreign-data wrapper for remote PostgreSQL servers
 dict_xsyn          | 1.0            |                  | text search dictionary template for extended synonym processing
 seg                | 1.4            |                  | data type for representing line segments or floating-point intervals
 autoinc            | 1.0            |                  | functions for autoincrementing fields
 amcheck            | 1.3            |                  | functions for verifying relation integrity
 old_snapshot       | 1.0            |                  | utilities in support of old_snapshot_threshold
 pg_freespacemap    | 1.2            |                  | examine the free space map (FSM)
 pageinspect        | 1.11           |                  | inspect the contents of database pages at a low level
 ltree              | 1.2            |                  | data type for hierarchical tree-like structures
 pg_visibility      | 1.2            |                  | examine the visibility map (VM) and page-level visibility info
 pg_buffercache     | 1.3            |                  | examine the shared buffer cache
 plpgsql            | 1.0            | 1.0              | PL/pgSQL procedural language
 pg_stat_statements | 1.10           | 1.10             | track planning and execution statistics of all SQL statements executed
 tcn                | 1.0            |                  | Triggered change notifications
 fuzzystrmatch      | 1.1            |                  | determine similarities and distance between strings
 pg_trgm            | 1.6            |                  | text similarity measurement and index searching based on trigrams
 pgrowlocks         | 1.2            |                  | show row-level locking information
 uuid-ossp          | 1.1            |                  | generate universally unique identifiers (UUIDs)
(50 rows)

The extensions that we are interested in are earthdistance and cube, as highlighted in blue above. We use the “dms_create_extension” UDF to install them. Note that if we try to add the earthdistance extension prior to adding cube, we get the following dependency failure:

dbaas=> select dms_create_extension('earthdistance');
NOTICE:  Running create extension sql: CREATE EXTENSION IF NOT EXISTS earthdistance
ERROR:  required extension "cube" is not installed
HINT:  Use CREATE EXTENSION ... CASCADE to install required extensions too.
CONTEXT:  SQL statement "CREATE EXTENSION IF NOT EXISTS earthdistance"
PL/pgSQL function dms_create_extension(text) line 6 at EXECUTE

To add these extensions to our database, run these commands in the following order.

dbaas=> select dms_create_extension('cube');
NOTICE: Running create extension sql: CREATE EXTENSION IF NOT EXISTS cube
 dms_create_extension 
----------------------

(1 row)


dbaas=> select dms_create_extension('earthdistance');
NOTICE: Running create extension sql: CREATE EXTENSION IF NOT EXISTS earthdistance
 dms_create_extension 
----------------------

(1 row)

And that is how simple it is to install PostgreSQL extensions in a database that has been provisioned by DSM as the ‘dbaas’ user. Again, ‘dbaas’ is not a superuser like ‘postgres’, which is a design decision based on security concerns.

You can now examine the installed extensions by using the \dx command.

dbaas=> \dx
                                            List of installed extensions
        Name        | Version |  Schema    |                              Description                              
--------------------+---------+------------+------------------------------------------------------------------------
 cube               | 1.5     | public     | data type for multidimensional cubes
 earthdistance      | 1.1     | public     | calculate great-circle distances on the surface of the Earth
 plpgsql            | 1.0     | pg_catalog | PL/pgSQL procedural language
(3 rows)

There are also UDFs to update and drop any extensions that have been added to a database.

We can now test the functions. As per the official documentation for the earthdistance extension, the function ll_to_earth() returns the location of a point on the surface of the Earth given its latitude and longitude in degrees. The function earth_distance() returns the great circle distance between two points on the surface of the Earth. So, for example, if I wanted to find the distance between my home town in Ireland to say, Salto in Brazil, I would make a call similar to the following:

dbaas=> select earth_distance(ll_to_earth(51.95, -7.85056),ll_to_earth(-23.20083, -47.28694)) as distance_in_meters;
 distance_in_meters 
--------------------
  9204656.163251864

(1 row)

The value returned is in meters. If I wanted to display kilometers or miles, I would simply add a divide by 1000 or divide by 1609.344 accordingly to the result.

dbaas=> select earth_distance(ll_to_earth(51.95, -7.85056),ll_to_earth(-23.20083, -47.28694)) / 1000 as distance_in_kms;
  distance_in_kms  
-------------------
 9204.656163251864

(1 row)


dbaas=> select earth_distance(ll_to_earth(51.95, -7.85056),ll_to_earth(-23.20083, -47.28694)) / 1609.344 as distance_in_miles;
 distance_in_miles 
-------------------
 5719.508174294535

(1 row)

dbaas=>

And there we have it. This is a very simple example, but hopefully it demonstrates how easy it is to add PostgreSQL extensions to a PostgreSQL database that has been provisioned via VMware Data Services Manager. There is no need to grant superuser privileges to users to install extensions, which could be a security concern. It can be achieved through user-defined functions that we have specifically added to the PostgreSQL database template. A much safer approach.