CVE-2022-24128 (published 2022-03-13T18:15Z)

Timescale TimescaleDB 1.x and 2.x before 2.5.2 may allow privilege escalation during extension installation. The installation process uses commands such as CREATE x IF NOT EXIST that allow an unprivileged user to precreate objects. These objects will be used by the installer (which executes as Superuser), leading to privilege escalation. In order to be able to take advantage of this, an unprivileged user would need to be able to create objects in a database and then get a Superuser to install TimescaleDB into their database. (In the fixed versions, the installation aborts when it finds that an object already exists.)

Severity: MEDIUM
CVSS v2 base score: 6.0 (AV:N/AC:M/Au:S/C:P/I:P/A:P)
CVSS v3 base score: 8.0, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-863
cwe_name: Incorrect Authorization
description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are incorrectly applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/timescale/timescaledb
commit_details: repo_url: https://github.com/timescale/timescaledb
author: Sven Klemm
author_date: 2022-02-08 19:55:28+01:00
msg: Fix extension installation privilege escalation

TimescaleDB was vulnerable to a privilege escalation attack in
the extension installation script. An attacker could precreate
objects normally owned by the extension and get those objects
used in the installation script since the script would only try
to create them if they did not already exist. Thanks to Pedro
Gallegos for reporting the problem.

This patch changes the schema, table and function creation to fail
and abort the installation when the object already exists instead
of using the existing object.

Security: CVE-2022-24128
repository: repo_url: https://github.com/timescale/timescaledb
repo_name: timescale/timescaledb
description: An open-source time-series SQL database optimized for fast ingest and complex queries.  Packaged as a PostgreSQL extension.
repo_language: C
owner: timescale
file_changes: filename: code_style.yaml
change_type: ModificationType.MODIFY
diff: @@ -35,7 +35,7 @@ jobs:
       - name: Check for diff
         run: git diff --exit-code
   misc_checks:
-    name: Check formatting, license and git hooks
+    name: Check formatting, license, update scripts and git hooks
     runs-on: ubuntu-20.04
     strategy:
       fail-fast: false
@@ -62,3 +62,6 @@ jobs:
         sudo update-alternatives --set clang-format /usr/bin/clang-format-8
         ./scripts/clang_format_all.sh
         git diff --exit-code
+    - name: Check update scripts
+      if: always()
+      run: ./scripts/check_update_scripts.sh

programming_language: Shell
filename: GenerateScripts.cmake
change_type: ModificationType.MODIFY
diff: @@ -159,7 +159,7 @@ function(generate_downgrade_script)
     ${CMAKE_BINARY_DIR}/v${_downgrade_TARGET_VERSION}/cmake/ScriptFiles.cmake)
 
   set(_downgrade_PRE_FILES ${PRE_UPDATE_FILES})
-  set(_downgrade_POST_FILES ${SOURCE_FILES} ${SET_POST_UPDATE_STAGE}
+  set(_downgrade_POST_FILES "${PRE_INSTALL_FUNCTION_FILES};${SOURCE_FILES}" ${SET_POST_UPDATE_STAGE}
                             ${POST_UPDATE_FILES} ${UNSET_UPDATE_STAGE})
 
   # Fetch prolog and epilog from target version.

programming_language: Shell
filename: ScriptFiles.cmake
change_type: ModificationType.MODIFY
diff: @@ -15,15 +15,18 @@ set(PRE_INSTALL_SOURCE_FILES
     pre_install/types.functions.sql
     pre_install/types.post.sql # Must be before tables.sql
     pre_install/tables.sql
+    pre_install/cache.sql
     pre_install/insert_data.sql
-    pre_install/bgw_scheduler_startup.sql
     pre_install/fdw_functions.sql
     pre_install/timescaledb_fdw.sql)
 
+# Source files that define functions and need to be rerun in update
+set(PRE_INSTALL_FUNCTION_FILES
+    pre_install/types.functions.sql
+    pre_install/fdw_functions.sql)
+
 # The rest of the source files defining mostly functions
 set(SOURCE_FILES
-    pre_install/types.functions.sql
-    pre_install/fdw_functions.sql
     hypertable.sql
     chunk.sql
     data_node.sql
@@ -43,7 +46,6 @@ set(SOURCE_FILES
     version.sql
     size_utils.sql
     histogram.sql
-    cache.sql
     bgw_scheduler.sql
     metadata.sql
     dist_internal.sql
@@ -57,7 +59,8 @@ set(SOURCE_FILES
     policy_api.sql
     policy_internal.sql
     cagg_utils.sql
-	telemetry.sql)
+	  telemetry.sql
+    bgw_startup.sql)
 
 # These files should be pre-pended to update scripts so that they are executed
 # before anything else during updates

programming_language: Shell
filename: check_update_scripts.sh
change_type: ModificationType.ADD
diff: @@ -0,0 +1,16 @@
+#! /bin/bash
+
+SCRIPT_DIR=$(dirname ${0})
+SRC_DIR=$(dirname ${SCRIPT_DIR})
+
+if grep -ir "IF NOT EXISTS" ${SRC_DIR}/sql; then
+  cat <<EOF
+
+Update scripts must unconditionally add new objects and fail when the object
+already exists otherwise this might enable privilege escalation attacks where
+an attacker can precreate objects that get used in later parts of the scripts
+instead of the objects created by timescaledb.
+
+EOF
+  exit 1
+fi

programming_language: Shell
filename: CMakeLists.txt
change_type: ModificationType.MODIFY
diff: @@ -107,6 +107,8 @@ endfunction()
 version_files("${PRE_UPDATE_FILES}" PRE_UPDATE_FILES_VERSIONED)
 version_files("${POST_UPDATE_FILES}" POST_UPDATE_FILES_VERSIONED)
 version_files("${PRE_INSTALL_SOURCE_FILES}" PRE_INSTALL_SOURCE_FILES_VERSIONED)
+version_files("${PRE_INSTALL_FUNCTION_FILES}"
+              PRE_INSTALL_FUNCTION_FILES_VERSIONED)
 version_files("${SOURCE_FILES}" SOURCE_FILES_VERSIONED)
 version_files("${MOD_FILES}" MOD_FILES_VERSIONED)
 version_files("updates/latest-dev.sql" LASTEST_MOD_VERSIONED)
@@ -115,25 +117,49 @@ version_files("notice.sql" NOTICE_FILE)
 # Function to concatenate all files in SRC_FILE_LIST into file OUTPUT_FILE
 function(cat_files SRC_FILE_LIST OUTPUT_FILE)
   if(WIN32)
-    # Make list of files into string of files separated by "+" to make Windows
-    # copy concatenate them
-    file(TO_NATIVE_PATH "${SRC_FILE_LIST}" SRC_FILE_LIST_NATIVE)
-    string(REPLACE ";" ";+;" SQL_LIST_JOINED "${SRC_FILE_LIST_NATIVE}")
-    file(TO_NATIVE_PATH "${OUTPUT_FILE}" OUTPUT_FILE_NATIVE)
-    set(CAT_CMD copy /B /y ${SQL_LIST_JOINED} "\"${OUTPUT_FILE_NATIVE}\"" >NUL)
+    set("SRC_ARG" "-DSRC_FILE_LIST=${SRC_FILE_LIST}")
   else()
-    set(CAT_CMD cat ${SRC_FILE_LIST} > ${OUTPUT_FILE})
+    set("SRC_ARG" "'-DSRC_FILE_LIST=${SRC_FILE_LIST}'")
   endif()
   add_custom_command(
     OUTPUT ${OUTPUT_FILE}
     DEPENDS ${SRC_FILE_LIST}
     WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-    COMMAND ${CAT_CMD}
+    COMMAND ${CMAKE_COMMAND} "${SRC_ARG}" "-DOUTPUT_FILE=${OUTPUT_FILE}" -P
+            cat.cmake
     COMMENT "Generating ${OUTPUT_FILE}")
 endfunction()
 
+# Concatenate all files in SRC_FILE_LIST into file OUTPUT_FILE Additionally
+# strips out `OR REPLACE` from the generated file as this would allow a
+# privilege escalation attack when present in the CREATE script
+function(cat_create_files SRC_FILE_LIST OUTPUT_FILE)
+  if(WIN32)
+    set("SRC_ARG" "-DSRC_FILE_LIST=${SRC_FILE_LIST}")
+  else()
+    set("SRC_ARG" "'-DSRC_FILE_LIST=${SRC_FILE_LIST}'")
+  endif()
+  if(WIN32)
+    add_custom_command(
+      OUTPUT ${OUTPUT_FILE}
+      DEPENDS ${SRC_FILE_LIST}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      COMMAND ${CMAKE_COMMAND} "${SRC_ARG}" "-DOUTPUT_FILE=${OUTPUT_FILE}"
+              "-DSTRIP_REPLACE=ON" -P cat.cmake
+      COMMENT "Generating ${OUTPUT_FILE}")
+  else()
+    add_custom_command(
+      OUTPUT ${OUTPUT_FILE}
+      DEPENDS ${SRC_FILE_LIST}
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+      COMMAND ${CMAKE_COMMAND} "'-DSRC_FILE_LIST=${SRC_FILE_LIST}'"
+              "-DOUTPUT_FILE=${OUTPUT_FILE}" "-DSTRIP_REPLACE=ON" -P cat.cmake
+      COMMENT "Generating ${OUTPUT_FILE}")
+  endif()
+endfunction()
+
 # Generate the extension file used with CREATE EXTENSION
-cat_files(
+cat_create_files(
   "${PRE_INSTALL_SOURCE_FILES_VERSIONED};${SOURCE_FILES_VERSIONED};${NOTICE_FILE}"
   ${CMAKE_CURRENT_BINARY_DIR}/${INSTALL_FILE})
 add_custom_target(sqlfile ALL
@@ -200,11 +226,12 @@ foreach(transition_mod_file ${MOD_FILES_LIST})
   list(APPEND UPDATE_SCRIPTS ${UPDATE_SCRIPT})
   if(CURR_MOD_FILES)
     cat_files(
-      "${PRE_FILES};${CURR_MOD_FILES};${SOURCE_FILES_VERSIONED};${POST_FILES_PROCESSED}"
+      "${PRE_FILES};${CURR_MOD_FILES};${PRE_INSTALL_FUNCTION_FILES_VERSIONED};${SOURCE_FILES_VERSIONED};${POST_FILES_PROCESSED}"
       ${UPDATE_SCRIPT})
   else()
-    cat_files("${PRE_FILES};${SOURCE_FILES_VERSIONED};${POST_FILES_PROCESSED}"
-              ${UPDATE_SCRIPT})
+    cat_files(
+      "${PRE_FILES};${PRE_INSTALL_FUNCTION_FILES_VERSIONED};${SOURCE_FILES_VERSIONED};${POST_FILES_PROCESSED}"
+      ${UPDATE_SCRIPT})
   endif()
 endforeach(transition_mod_file)
 

programming_language: Shell
filename: bgw_startup.sql
change_type: ModificationType.RENAME
diff: @@ -2,9 +2,4 @@
 -- Please see the included NOTICE for copyright information and
 -- LICENSE-APACHE for a copy of the license.
 
-CREATE OR REPLACE FUNCTION _timescaledb_internal.restart_background_workers()
-RETURNS BOOL 
-AS '@LOADER_PATHNAME@', 'ts_bgw_db_workers_restart'
-LANGUAGE C VOLATILE;
-
 SELECT _timescaledb_internal.restart_background_workers();

programming_language: SQL
filename: cat.cmake
change_type: ModificationType.ADD
diff: @@ -0,0 +1,26 @@
+
+IF(POLICY CMP0012)
+  CMAKE_POLICY(SET CMP0012 NEW)
+ENDIF()
+
+if (NOT DEFINED STRIP_REPLACE)
+  set(STRIP_REPLACE OFF)
+endif()
+
+function(append_file IN_FILE OUT_FILE STRIP_REPLACE)
+  file(READ ${IN_FILE} CONTENTS)
+  if (${STRIP_REPLACE})
+    string(REPLACE " OR REPLACE " " " CONTENTS "${CONTENTS}")
+  endif()
+  file(APPEND ${OUT_FILE} "${CONTENTS}")
+endfunction()
+
+# Function to concatenate all files in SRC_FILE_LIST into file OUTPUT_FILE
+function(cat SRC_FILE_LIST OUTPUT_FILE STRIP_REPLACE)
+  file(WRITE ${OUTPUT_FILE} "")
+  foreach(SRC_FILE ${SRC_FILE_LIST})
+    append_file(${SRC_FILE} ${OUTPUT_FILE} ${STRIP_REPLACE})
+  endforeach()
+endfunction()
+
+cat("${SRC_FILE_LIST}" "${OUTPUT_FILE}" "${STRIP_REPLACE}")

programming_language: Ruby
filename: cache.sql
change_type: ModificationType.RENAME
diff: @@ -5,10 +5,10 @@
 -- This file contains infrastructure for cache invalidation of TimescaleDB
 -- metadata caches kept in C. Please look at cache_invalidate.c for a
 -- description of how this works.
-CREATE TABLE IF NOT EXISTS  _timescaledb_cache.cache_inval_hypertable();
+CREATE TABLE _timescaledb_cache.cache_inval_hypertable();
 
 -- For notifying the scheduler of changes to the bgw_job table.
-CREATE TABLE IF NOT EXISTS  _timescaledb_cache.cache_inval_bgw_job();
+CREATE TABLE _timescaledb_cache.cache_inval_bgw_job();
 
 -- This is pretty subtle. We create this dummy cache_inval_extension table
 -- solely for the purpose of getting a relcache invalidation event when it is
@@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS  _timescaledb_cache.cache_inval_bgw_job();
 -- invalidated, all backends will be notified and will know that they must
 -- invalidate all cached information, including catalog table and index OIDs,
 -- etc.
-CREATE TABLE IF NOT EXISTS  _timescaledb_cache.cache_inval_extension();
+CREATE TABLE _timescaledb_cache.cache_inval_extension();
 
 -- not actually strictly needed but good for sanity as all tables should be dumped.
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_cache.cache_inval_hypertable', '');

programming_language: SQL
filename: schemas.sql
change_type: ModificationType.MODIFY
diff: @@ -2,11 +2,12 @@
 -- Please see the included NOTICE for copyright information and
 -- LICENSE-APACHE for a copy of the license.
 
-CREATE SCHEMA IF NOT EXISTS _timescaledb_catalog;
-CREATE SCHEMA IF NOT EXISTS _timescaledb_internal;
-CREATE SCHEMA IF NOT EXISTS _timescaledb_cache;
-CREATE SCHEMA IF NOT EXISTS _timescaledb_config;
-CREATE SCHEMA IF NOT EXISTS timescaledb_experimental;
-GRANT USAGE ON SCHEMA _timescaledb_cache, _timescaledb_catalog, _timescaledb_internal, _timescaledb_config TO PUBLIC;
-GRANT USAGE ON SCHEMA timescaledb_experimental TO PUBLIC;
+CREATE SCHEMA _timescaledb_catalog;
+CREATE SCHEMA _timescaledb_internal;
+CREATE SCHEMA _timescaledb_cache;
+CREATE SCHEMA _timescaledb_config;
+CREATE SCHEMA timescaledb_experimental;
+CREATE SCHEMA timescaledb_information;
+
+GRANT USAGE ON SCHEMA _timescaledb_cache, _timescaledb_catalog, _timescaledb_internal, _timescaledb_config, timescaledb_information, timescaledb_experimental TO PUBLIC;
 

programming_language: SQL
filename: tables.sql
change_type: ModificationType.MODIFY
diff: @@ -37,9 +37,9 @@
 -- chunks.
 -- The unique constraint is table_name +schema_name. The ordering is
 -- important as we want index access when we filter by table_name
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.hypertable_id_seq MINVALUE 1;
+CREATE SEQUENCE _timescaledb_catalog.hypertable_id_seq MINVALUE 1;
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable (
+CREATE TABLE _timescaledb_catalog.hypertable (
   id INTEGER PRIMARY KEY DEFAULT nextval('_timescaledb_catalog.hypertable_id_seq'), 
   schema_name name NOT NULL CHECK (schema_name != '_timescaledb_catalog'),
   table_name name NOT NULL,
@@ -67,7 +67,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable_id_s
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable', '');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable_data_node (
+CREATE TABLE _timescaledb_catalog.hypertable_data_node (
   hypertable_id integer NOT NULL REFERENCES _timescaledb_catalog.hypertable (id),
   node_hypertable_id integer NULL,
   node_name name NOT NULL,
@@ -80,7 +80,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable_data
 
 -- The tablespace table maps tablespaces to hypertables.
 -- This allows spreading a hypertable's chunks across multiple disks.
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.tablespace (
+CREATE TABLE _timescaledb_catalog.tablespace (
   id serial PRIMARY KEY,
   hypertable_id int NOT NULL REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   tablespace_name name NOT NULL,
@@ -90,7 +90,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.tablespace (
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.tablespace', '');
 
 -- A dimension represents an axis along which data is partitioned.
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.dimension (
+CREATE TABLE _timescaledb_catalog.dimension (
   id serial NOT NULL PRIMARY KEY,
   hypertable_id integer NOT NULL REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   column_name name NOT NULL,
@@ -115,7 +115,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.dimension', '')
 SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_timescaledb_catalog.dimension', 'id'), '');
 
 -- A dimension slice defines a keyspace range along a dimension axis.
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.dimension_slice (
+CREATE TABLE _timescaledb_catalog.dimension_slice (
   id serial NOT NULL PRIMARY KEY,
   dimension_id integer NOT NULL REFERENCES _timescaledb_catalog.dimension (id) ON DELETE CASCADE,
   range_start bigint NOT NULL,
@@ -133,9 +133,9 @@ SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_timescaledb_
 -- the chunk's hypercube. Tuples that fall within the chunk's
 -- hypercube are stored in the chunk's data table, as given by
 -- 'schema_name' and 'table_name'.
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.chunk_id_seq MINVALUE 1;
+CREATE SEQUENCE _timescaledb_catalog.chunk_id_seq MINVALUE 1;
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk (
+CREATE TABLE _timescaledb_catalog.chunk (
   id integer PRIMARY KEY DEFAULT nextval('_timescaledb_catalog.chunk_id_seq'),
   hypertable_id int NOT NULL REFERENCES _timescaledb_catalog.hypertable (id),
   schema_name name NOT NULL,
@@ -147,9 +147,9 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk (
 );
 ALTER SEQUENCE _timescaledb_catalog.chunk_id_seq OWNED BY _timescaledb_catalog.chunk.id;
 
-CREATE INDEX IF NOT EXISTS chunk_hypertable_id_idx ON _timescaledb_catalog.chunk (hypertable_id);
+CREATE INDEX chunk_hypertable_id_idx ON _timescaledb_catalog.chunk (hypertable_id);
 
-CREATE INDEX IF NOT EXISTS chunk_compressed_chunk_id_idx ON _timescaledb_catalog.chunk (compressed_chunk_id);
+CREATE INDEX chunk_compressed_chunk_id_idx ON _timescaledb_catalog.chunk (compressed_chunk_id);
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk', '');
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_id_seq', '');
@@ -157,7 +157,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_id_seq',
 -- A chunk constraint maps a dimension slice to a chunk. Each
 -- constraint associated with a chunk will also be a table constraint
 -- on the chunk's data table.
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_constraint (
+CREATE TABLE _timescaledb_catalog.chunk_constraint (
   chunk_id integer NOT NULL REFERENCES _timescaledb_catalog.chunk (id),
   dimension_slice_id integer NULL REFERENCES _timescaledb_catalog.dimension_slice (id),
   constraint_name name NOT NULL,
@@ -167,13 +167,13 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_constraint (
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_constraint', '');
 
-CREATE INDEX IF NOT EXISTS chunk_constraint_chunk_id_dimension_slice_id_idx ON _timescaledb_catalog.chunk_constraint (chunk_id, dimension_slice_id);
+CREATE INDEX chunk_constraint_chunk_id_dimension_slice_id_idx ON _timescaledb_catalog.chunk_constraint (chunk_id, dimension_slice_id);
 
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.chunk_constraint_name;
+CREATE SEQUENCE _timescaledb_catalog.chunk_constraint_name;
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_constraint_name', '');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_index (
+CREATE TABLE _timescaledb_catalog.chunk_index (
   chunk_id integer NOT NULL REFERENCES _timescaledb_catalog.chunk (id) ON DELETE CASCADE,
   index_name name NOT NULL,
   hypertable_id integer NOT NULL REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
@@ -181,11 +181,11 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_index (
   UNIQUE (chunk_id, index_name)
 );
 
-CREATE INDEX IF NOT EXISTS chunk_index_hypertable_id_hypertable_index_name_idx ON _timescaledb_catalog.chunk_index (hypertable_id, hypertable_index_name);
+CREATE INDEX chunk_index_hypertable_id_hypertable_index_name_idx ON _timescaledb_catalog.chunk_index (hypertable_id, hypertable_index_name);
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_index', '');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_data_node (
+CREATE TABLE _timescaledb_catalog.chunk_data_node (
   chunk_id integer NOT NULL REFERENCES _timescaledb_catalog.chunk (id),
   node_chunk_id integer NOT NULL,
   node_name name NOT NULL,
@@ -198,12 +198,12 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_data_node
 -- Default jobs are given the id space [1,1000). User-installed jobs and any jobs created inside tests
 -- are given the id space [1000, INT_MAX). That way, we do not pg_dump jobs that are always default-installed
 -- inside other .sql scripts. This avoids insertion conflicts during pg_restore.
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_config.bgw_job_id_seq
+CREATE SEQUENCE _timescaledb_config.bgw_job_id_seq
 MINVALUE 1000;
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job_id_seq', '');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_config.bgw_job (
+CREATE TABLE _timescaledb_config.bgw_job (
   id integer PRIMARY KEY DEFAULT nextval('_timescaledb_config.bgw_job_id_seq'),
   application_name name NOT NULL,
   schedule_interval interval NOT NULL,
@@ -220,11 +220,11 @@ CREATE TABLE IF NOT EXISTS _timescaledb_config.bgw_job (
 
 ALTER SEQUENCE _timescaledb_config.bgw_job_id_seq OWNED BY _timescaledb_config.bgw_job.id;
 
-CREATE INDEX IF NOT EXISTS bgw_job_proc_hypertable_id_idx ON _timescaledb_config.bgw_job (proc_schema, proc_name, hypertable_id);
+CREATE INDEX bgw_job_proc_hypertable_id_idx ON _timescaledb_config.bgw_job (proc_schema, proc_name, hypertable_id);
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job', 'WHERE id >= 1000');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_internal.bgw_job_stat (
+CREATE TABLE _timescaledb_internal.bgw_job_stat (
   job_id integer PRIMARY KEY REFERENCES _timescaledb_config.bgw_job (id) ON DELETE CASCADE,
   last_start timestamptz NOT NULL DEFAULT NOW(),
   last_finish timestamptz NOT NULL,
@@ -244,7 +244,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_internal.bgw_job_stat (
 --the statistics probably aren't very meaningful across instances.
 -- Now we define a special stats table for each job/chunk pair. This will be used by the scheduler
 -- to determine whether to run a specific job on a specific chunk.
-CREATE TABLE IF NOT EXISTS _timescaledb_internal.bgw_policy_chunk_stats (
+CREATE TABLE _timescaledb_internal.bgw_policy_chunk_stats (
   job_id integer NOT NULL REFERENCES _timescaledb_config.bgw_job (id) ON DELETE CASCADE,
   chunk_id integer NOT NULL REFERENCES _timescaledb_catalog.chunk (id) ON DELETE CASCADE,
   num_times_job_run integer,
@@ -252,7 +252,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_internal.bgw_policy_chunk_stats (
   UNIQUE (job_id, chunk_id)
 );
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.metadata (
+CREATE TABLE _timescaledb_catalog.metadata (
   key NAME NOT NULL PRIMARY KEY,
   value text NOT NULL,
   include_in_telemetry boolean NOT NULL
@@ -261,7 +261,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.metadata (
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.metadata', $$
   WHERE KEY = 'exported_uuid' $$);
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_agg (
+CREATE TABLE _timescaledb_catalog.continuous_agg (
   mat_hypertable_id integer PRIMARY KEY REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   raw_hypertable_id integer NOT NULL REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   user_view_schema name NOT NULL,
@@ -276,12 +276,12 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_agg (
   UNIQUE (partial_view_schema, partial_view_name)
 );
 
-CREATE INDEX IF NOT EXISTS continuous_agg_raw_hypertable_id_idx ON _timescaledb_catalog.continuous_agg (raw_hypertable_id);
+CREATE INDEX continuous_agg_raw_hypertable_id_idx ON _timescaledb_catalog.continuous_agg (raw_hypertable_id);
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_agg', '');
 
 -- See the comments for ContinuousAggsBucketFunction structure.
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_aggs_bucket_function(
+CREATE TABLE _timescaledb_catalog.continuous_aggs_bucket_function(
   mat_hypertable_id integer PRIMARY KEY REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   -- The schema of the function. Equals TRUE for "timescaledb_experimental", FALSE otherwise.
   experimental bool NOT NULL,
@@ -297,7 +297,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_aggs_bucket_function(
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_aggs_bucket_function', '');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_aggs_invalidation_threshold (
+CREATE TABLE _timescaledb_catalog.continuous_aggs_invalidation_threshold (
   hypertable_id integer PRIMARY KEY REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   watermark bigint NOT NULL
 );
@@ -306,7 +306,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_aggs
 
 -- this does not have an FK on the materialization table since INSERTs to this
 -- table are performance critical
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_aggs_hypertable_invalidation_log (
+CREATE TABLE _timescaledb_catalog.continuous_aggs_hypertable_invalidation_log (
   hypertable_id integer NOT NULL,
   lowest_modified_value bigint NOT NULL,
   greatest_modified_value bigint NOT NULL
@@ -317,7 +317,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_aggs
 CREATE INDEX continuous_aggs_hypertable_invalidation_log_idx ON _timescaledb_catalog.continuous_aggs_hypertable_invalidation_log (hypertable_id, lowest_modified_value ASC);
 
 -- per cagg copy of invalidation log
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (
+CREATE TABLE _timescaledb_catalog.continuous_aggs_materialization_invalidation_log (
   materialization_id integer REFERENCES _timescaledb_catalog.continuous_agg (mat_hypertable_id) ON DELETE CASCADE,
   lowest_modified_value bigint NOT NULL,
   greatest_modified_value bigint NOT NULL
@@ -331,14 +331,14 @@ CREATE INDEX continuous_aggs_materialization_invalidation_log_idx ON _timescaled
 /* the source of this data is the enum from the source code that lists
  *  the algorithms. This table is NOT dumped.
  */
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.compression_algorithm (
+CREATE TABLE _timescaledb_catalog.compression_algorithm (
   id smallint PRIMARY KEY,
   version smallint NOT NULL,
   name name NOT NULL,
   description text
 );
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable_compression (
+CREATE TABLE _timescaledb_catalog.hypertable_compression (
   hypertable_id integer REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   attname name NOT NULL,
   compression_algorithm_id smallint REFERENCES _timescaledb_catalog.compression_algorithm (id),
@@ -353,7 +353,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable_compression (
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable_compression', '');
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.compression_chunk_size (
+CREATE TABLE _timescaledb_catalog.compression_chunk_size (
   chunk_id integer REFERENCES _timescaledb_catalog.chunk (id) ON DELETE CASCADE,
   compressed_chunk_id integer REFERENCES _timescaledb_catalog.chunk (id) ON DELETE CASCADE,
   uncompressed_heap_size bigint NOT NULL,
@@ -380,7 +380,7 @@ CREATE TABLE _timescaledb_catalog.remote_txn (
   PRIMARY KEY (remote_transaction_id)
 );
 
-CREATE INDEX IF NOT EXISTS remote_txn_data_node_name_idx ON _timescaledb_catalog.remote_txn (data_node_name);
+CREATE INDEX remote_txn_data_node_name_idx ON _timescaledb_catalog.remote_txn (data_node_name);
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.remote_txn', '');
 
@@ -402,9 +402,9 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.remote_txn', ''
 -- carry over chunk copy/move operations from earlier (if it makes sense at all)
 --
 
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.chunk_copy_operation_id_seq MINVALUE 1;
+CREATE SEQUENCE _timescaledb_catalog.chunk_copy_operation_id_seq MINVALUE 1;
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_copy_operation (
+CREATE TABLE _timescaledb_catalog.chunk_copy_operation (
   operation_id name PRIMARY KEY, -- the publisher/subscriber identifier used
   backend_pid integer NOT NULL, -- the pid of the backend running this activity
   completed_stage name NOT NULL, -- the completed stage/step

programming_language: SQL
filename: 1.7.5--2.0.0-rc1.sql
change_type: ModificationType.MODIFY
diff: @@ -41,7 +41,7 @@ DROP VIEW IF EXISTS timescaledb_information.reorder_policies;
 ALTER TABLE _timescaledb_catalog.hypertable ADD COLUMN replication_factor SMALLINT NULL CHECK (replication_factor > 0);
 
 -- Table for hypertable -> node mappings
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable_data_node (
+CREATE TABLE _timescaledb_catalog.hypertable_data_node (
     hypertable_id          INTEGER NOT NULL     REFERENCES _timescaledb_catalog.hypertable(id),
     node_hypertable_id   INTEGER NULL,
     node_name            NAME NOT NULL,
@@ -54,7 +54,7 @@ SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable_data
 GRANT SELECT ON _timescaledb_catalog.hypertable_data_node TO PUBLIC;
 
 -- Table for chunk -> nodes mappings
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_data_node (
+CREATE TABLE _timescaledb_catalog.chunk_data_node (
     chunk_id               INTEGER NOT NULL     REFERENCES _timescaledb_catalog.chunk(id),
     node_chunk_id        INTEGER NOT NULL,
     node_name            NAME NOT NULL,
@@ -85,7 +85,7 @@ CREATE TABLE _timescaledb_catalog.remote_txn (
     remote_transaction_id    TEXT CHECK (remote_transaction_id::rxid is not null),
     PRIMARY KEY (remote_transaction_id)
 );
-CREATE INDEX IF NOT EXISTS remote_txn_data_node_name_idx
+CREATE INDEX remote_txn_data_node_name_idx
 ON _timescaledb_catalog.remote_txn(data_node_name);
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.remote_txn', '');
 
@@ -95,8 +95,8 @@ DROP VIEW IF EXISTS timescaledb_information.compressed_hypertable_stats;
 DROP VIEW IF EXISTS timescaledb_information.compressed_chunk_stats;
 
 -- all existing compressed chunks have NULL value for the new columns
-ALTER TABLE IF EXISTS _timescaledb_catalog.compression_chunk_size ADD COLUMN IF NOT EXISTS numrows_pre_compression BIGINT;
-ALTER TABLE IF EXISTS _timescaledb_catalog.compression_chunk_size ADD COLUMN IF NOT EXISTS numrows_post_compression BIGINT;
+ALTER TABLE IF EXISTS _timescaledb_catalog.compression_chunk_size ADD COLUMN numrows_pre_compression BIGINT;
+ALTER TABLE IF EXISTS _timescaledb_catalog.compression_chunk_size ADD COLUMN numrows_post_compression BIGINT;
 
 --rewrite catalog table to not break catalog scans on tables with missingval optimization
 CLUSTER  _timescaledb_catalog.compression_chunk_size USING compression_chunk_size_pkey;
@@ -276,12 +276,12 @@ CREATE TABLE tmp_bgw_job_seq_value AS SELECT last_value, is_called FROM _timesca
 
 DROP TABLE _timescaledb_config.bgw_job;
 
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_config.bgw_job_id_seq MINVALUE 1000;
+CREATE SEQUENCE _timescaledb_config.bgw_job_id_seq MINVALUE 1000;
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_config.bgw_job_id_seq', '');
 SELECT setval('_timescaledb_config.bgw_job_id_seq', last_value, is_called) FROM tmp_bgw_job_seq_value;
 DROP TABLE tmp_bgw_job_seq_value;
 
-CREATE TABLE IF NOT EXISTS _timescaledb_config.bgw_job (
+CREATE TABLE _timescaledb_config.bgw_job (
     id                  INTEGER PRIMARY KEY DEFAULT nextval('_timescaledb_config.bgw_job_id_seq'),
     application_name    NAME        NOT NULL,
     schedule_interval   INTERVAL    NOT NULL,
@@ -297,7 +297,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_config.bgw_job (
 );
 
 ALTER SEQUENCE _timescaledb_config.bgw_job_id_seq OWNED BY _timescaledb_config.bgw_job.id;
-CREATE INDEX IF NOT EXISTS bgw_job_proc_hypertable_id_idx ON _timescaledb_config.bgw_job(proc_schema,proc_name,hypertable_id);
+CREATE INDEX bgw_job_proc_hypertable_id_idx ON _timescaledb_config.bgw_job(proc_schema,proc_name,hypertable_id);
 
 INSERT INTO _timescaledb_config.bgw_job SELECT id, application_name, schedule_interval, max_runtime, max_retries, retry_period, proc_schema, proc_name, owner, scheduled, hypertable_id, config FROM _timescaledb_config.bgw_job_tmp ORDER BY id;
 DROP TABLE _timescaledb_config.bgw_job_tmp;
@@ -367,7 +367,7 @@ ALTER TABLE _timescaledb_catalog.continuous_aggs_materialization_invalidation_lo
 ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.continuous_agg;
 DROP TABLE _timescaledb_catalog.continuous_agg;
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_agg (
+CREATE TABLE _timescaledb_catalog.continuous_agg (
     mat_hypertable_id INTEGER PRIMARY KEY REFERENCES _timescaledb_catalog.hypertable(id) ON DELETE CASCADE,
     raw_hypertable_id INTEGER NOT NULL REFERENCES  _timescaledb_catalog.hypertable(id) ON DELETE CASCADE,
     user_view_schema NAME NOT NULL,
@@ -382,7 +382,7 @@ CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_agg (
     UNIQUE(partial_view_schema, partial_view_name)
 );
 
-CREATE INDEX IF NOT EXISTS continuous_agg_raw_hypertable_id_idx
+CREATE INDEX continuous_agg_raw_hypertable_id_idx
     ON _timescaledb_catalog.continuous_agg(raw_hypertable_id);
 
 SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.continuous_agg', '');

programming_language: SQL
filename: 2.0.0-rc3--2.0.0-rc4.sql
change_type: ModificationType.MODIFY
diff: @@ -35,10 +35,10 @@ ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.hypertable;
 ALTER EXTENSION timescaledb DROP SEQUENCE _timescaledb_catalog.hypertable_id_seq;
 DROP TABLE _timescaledb_catalog.hypertable;
 
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.hypertable_id_seq MINVALUE 1;
+CREATE SEQUENCE _timescaledb_catalog.hypertable_id_seq MINVALUE 1;
 
 -- now create table without self referential foreign key
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable(
+CREATE TABLE _timescaledb_catalog.hypertable(
   id INTEGER PRIMARY KEY DEFAULT nextval('_timescaledb_catalog.hypertable_id_seq'),
   schema_name name NOT NULL CHECK (schema_name != '_timescaledb_catalog'),
   table_name name NOT NULL,

programming_language: SQL
filename: 2.2.1--2.3.0.sql
change_type: ModificationType.MODIFY
diff: @@ -29,10 +29,10 @@ ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.chunk;
 ALTER EXTENSION timescaledb DROP SEQUENCE _timescaledb_catalog.chunk_id_seq;
 DROP TABLE _timescaledb_catalog.chunk;
 
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.chunk_id_seq MINVALUE 1;
+CREATE SEQUENCE _timescaledb_catalog.chunk_id_seq MINVALUE 1;
 
 -- now create table without self referential foreign key
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk (
+CREATE TABLE _timescaledb_catalog.chunk (
   id integer PRIMARY KEY DEFAULT nextval('_timescaledb_catalog.chunk_id_seq'),
   hypertable_id int NOT NULL REFERENCES _timescaledb_catalog.hypertable (id),
   schema_name name NOT NULL,
@@ -56,8 +56,8 @@ SELECT id, hypertable_id, schema_name, table_name,
 FROM _timescaledb_catalog.chunk_tmp;
 
 --add indexes to the chunk table
-CREATE INDEX IF NOT EXISTS chunk_hypertable_id_idx ON _timescaledb_catalog.chunk (hypertable_id);
-CREATE INDEX IF NOT EXISTS chunk_compressed_chunk_id_idx ON _timescaledb_catalog.chunk (compressed_chunk_id);
+CREATE INDEX chunk_hypertable_id_idx ON _timescaledb_catalog.chunk (hypertable_id);
+CREATE INDEX chunk_compressed_chunk_id_idx ON _timescaledb_catalog.chunk (compressed_chunk_id);
 
 ALTER SEQUENCE _timescaledb_catalog.chunk_id_seq OWNED BY _timescaledb_catalog.chunk.id;
 SELECT setval('_timescaledb_catalog.chunk_id_seq', last_value, is_called) FROM tmp_chunk_seq_value;

programming_language: SQL
filename: 2.3.1--2.4.0.sql
change_type: ModificationType.MODIFY
diff: @@ -1,13 +1,13 @@
-CREATE SCHEMA IF NOT EXISTS timescaledb_experimental;
+CREATE SCHEMA timescaledb_experimental;
 GRANT USAGE ON SCHEMA timescaledb_experimental TO PUBLIC;
 DROP FUNCTION IF EXISTS _timescaledb_internal.block_new_chunks;
 DROP FUNCTION IF EXISTS _timescaledb_internal.allow_new_chunks;
 DROP FUNCTION IF EXISTS _timescaledb_internal.refresh_continuous_aggregate;
 DROP FUNCTION IF EXISTS _timescaledb_internal.create_chunk;
 
-CREATE SEQUENCE IF NOT EXISTS _timescaledb_catalog.chunk_copy_operation_id_seq MINVALUE 1;
+CREATE SEQUENCE _timescaledb_catalog.chunk_copy_operation_id_seq MINVALUE 1;
 
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.chunk_copy_operation (
+CREATE TABLE _timescaledb_catalog.chunk_copy_operation (
   operation_id name PRIMARY KEY, -- the publisher/subscriber identifier used
   backend_pid integer NOT NULL, -- the pid of the backend running this activity
   completed_stage name NOT NULL, -- the completed stage/step

programming_language: SQL
filename: latest-dev.sql
change_type: ModificationType.MODIFY
diff: @@ -3,7 +3,7 @@ DROP FUNCTION IF EXISTS delete_data_node;
 DROP FUNCTION IF EXISTS get_telemetry_report;
 
 -- Also see the comments for ContinuousAggsBucketFunction structure.
-CREATE TABLE IF NOT EXISTS _timescaledb_catalog.continuous_aggs_bucket_function(
+CREATE TABLE _timescaledb_catalog.continuous_aggs_bucket_function(
   mat_hypertable_id integer PRIMARY KEY REFERENCES _timescaledb_catalog.hypertable (id) ON DELETE CASCADE,
   -- The schema of the function. Equals TRUE for "timescaledb_experimental", FALSE otherwise.
   experimental bool NOT NULL,

programming_language: SQL
filename: reverse-dev.sql
change_type: ModificationType.MODIFY
diff: @@ -15,68 +15,68 @@ BEGIN
         RAISE EXCEPTION 'Downgrade is impossible since % continuous aggregates exist which use variable buckets: %', caggs_nr, caggs
               USING HINT = 'Remove the corresponding continuous aggregates manually before downgrading';
     END IF;
+END
+$$ LANGUAGE 'plpgsql';
 
-    -- It's safe to drop the table.
-    -- ALTER EXTENSION is required to revert the effect of pg_extension_config_dump()
-    -- See "The list of tables configured to be dumped" test in test/sql/updates/post.catalog.sql
-    ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.continuous_aggs_bucket_function;
+-- It's safe to drop the table.
+-- ALTER EXTENSION is required to revert the effect of pg_extension_config_dump()
+-- See "The list of tables configured to be dumped" test in test/sql/updates/post.catalog.sql
+ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.continuous_aggs_bucket_function;
 
-    -- Actually drop the table.
-    -- ALTER EXTENSION only removes the table from the extension but doesn't drop it.
-    DROP TABLE IF EXISTS _timescaledb_catalog.continuous_aggs_bucket_function;
+-- Actually drop the table.
+-- ALTER EXTENSION only removes the table from the extension but doesn't drop it.
+DROP TABLE _timescaledb_catalog.continuous_aggs_bucket_function;
 
-    -- Drop overloaded versions of invalidation_process_hypertable_log() and invalidation_process_cagg_log()
-    -- with bucket_functions argument.
+-- Drop overloaded versions of invalidation_process_hypertable_log() and invalidation_process_cagg_log()
+-- with bucket_functions argument.
 
-    ALTER EXTENSION timescaledb DROP FUNCTION _timescaledb_internal.invalidation_process_hypertable_log(
-        mat_hypertable_id INTEGER,
-        raw_hypertable_id INTEGER,
-        dimtype REGTYPE,
-        mat_hypertable_ids INTEGER[],
-        bucket_widths BIGINT[],
-        max_bucket_widths BIGINT[],
-        bucket_functions TEXT[]
-    );
+ALTER EXTENSION timescaledb DROP FUNCTION _timescaledb_internal.invalidation_process_hypertable_log(
+    mat_hypertable_id INTEGER,
+    raw_hypertable_id INTEGER,
+    dimtype REGTYPE,
+    mat_hypertable_ids INTEGER[],
+    bucket_widths BIGINT[],
+    max_bucket_widths BIGINT[],
+    bucket_functions TEXT[]
+);
 
-    DROP FUNCTION IF EXISTS _timescaledb_internal.invalidation_process_hypertable_log(
-        mat_hypertable_id INTEGER,
-        raw_hypertable_id INTEGER,
-        dimtype REGTYPE,
-        mat_hypertable_ids INTEGER[],
-        bucket_widths BIGINT[],
-        max_bucket_widths BIGINT[],
-        bucket_functions TEXT[]
-    );
+DROP FUNCTION _timescaledb_internal.invalidation_process_hypertable_log(
+    mat_hypertable_id INTEGER,
+    raw_hypertable_id INTEGER,
+    dimtype REGTYPE,
+    mat_hypertable_ids INTEGER[],
+    bucket_widths BIGINT[],
+    max_bucket_widths BIGINT[],
+    bucket_functions TEXT[]
+);
 
-    ALTER EXTENSION timescaledb DROP FUNCTION _timescaledb_internal.invalidation_process_cagg_log(
-        mat_hypertable_id INTEGER,
-        raw_hypertable_id INTEGER,
-        dimtype REGTYPE,
-        window_start BIGINT,
-        window_end BIGINT,
-        mat_hypertable_ids INTEGER[],
-        bucket_widths BIGINT[],
-        max_bucket_widths BIGINT[],
-        bucket_functions TEXT[],
-        OUT ret_window_start BIGINT,
-        OUT ret_window_end BIGINT
-    );
+ALTER EXTENSION timescaledb DROP FUNCTION _timescaledb_internal.invalidation_process_cagg_log(
+    mat_hypertable_id INTEGER,
+    raw_hypertable_id INTEGER,
+    dimtype REGTYPE,
+    window_start BIGINT,
+    window_end BIGINT,
+    mat_hypertable_ids INTEGER[],
+    bucket_widths BIGINT[],
+    max_bucket_widths BIGINT[],
+    bucket_functions TEXT[],
+    OUT ret_window_start BIGINT,
+    OUT ret_window_end BIGINT
+);
 
-    DROP FUNCTION IF EXISTS _timescaledb_internal.invalidation_process_cagg_log(
-        mat_hypertable_id INTEGER,
-        raw_hypertable_id INTEGER,
-        dimtype REGTYPE,
-        window_start BIGINT,
-        window_end BIGINT,
-        mat_hypertable_ids INTEGER[],
-        bucket_widths BIGINT[],
-        max_bucket_widths BIGINT[],
-        bucket_functions TEXT[],
-        OUT ret_window_start BIGINT,
-        OUT ret_window_end BIGINT
-    );
-END
-$$ LANGUAGE 'plpgsql';
+DROP FUNCTION _timescaledb_internal.invalidation_process_cagg_log(
+    mat_hypertable_id INTEGER,
+    raw_hypertable_id INTEGER,
+    dimtype REGTYPE,
+    window_start BIGINT,
+    window_end BIGINT,
+    mat_hypertable_ids INTEGER[],
+    bucket_widths BIGINT[],
+    max_bucket_widths BIGINT[],
+    bucket_functions TEXT[],
+    OUT ret_window_start BIGINT,
+    OUT ret_window_end BIGINT
+);
 
 --undo compression feature for caggs
 --check that all continuous aggregates have compression disabled
@@ -88,21 +88,21 @@ DECLARE
   cnt INTEGER := 0;
 BEGIN
     FOR cagg_name IN
-        SELECT view_name, 
-               materialization_hypertable_schema ,
-               materialization_hypertable_name   
+        SELECT view_name,
+               materialization_hypertable_schema,
+               materialization_hypertable_name
         FROM timescaledb_information.continuous_aggregates
         WHERE compression_enabled is TRUE
     LOOP
         RAISE NOTICE 'Compression is enabled for continuous aggregate: %', cagg_name;
-        cnt := cnt + 1; 
+        cnt := cnt + 1;
     END LOOP;
     IF cnt > 0 THEN
        RAISE EXCEPTION 'Cannot downgrade as compression is enabled for continuous aggregates' 
             USING DETAIL = 'Please disable compression on all continuous aggregates before downgrading',
-            HINT = ('To disable compression, call decompress_chunk to decompress chunks, then drop any existing compression policy on the continuous aggregate, and finally run ALTER MATERIALIZED VIEW % SET timescaledb.compress = ''false'' ');
+            HINT = 'To disable compression, call decompress_chunk to decompress chunks, then drop any existing compression policy on the continuous aggregate, and finally run ALTER MATERIALIZED VIEW % SET timescaledb.compress = ''false'' ';
     END IF;
 END $$;
 
 -- revert changes to continuous aggregates view definition
-DROP VIEW IF EXISTS timescaledb_information.continuous_aggregates;
+DROP VIEW timescaledb_information.continuous_aggregates;

programming_language: SQL
filename: views.sql
change_type: ModificationType.MODIFY
diff: @@ -2,8 +2,6 @@
 -- Please see the included NOTICE for copyright information and
 -- LICENSE-APACHE for a copy of the license.
 
-CREATE SCHEMA IF NOT EXISTS timescaledb_information;
-
 -- Convenience view to list all hypertables
 CREATE OR REPLACE VIEW timescaledb_information.hypertables AS
 SELECT ht.schema_name AS hypertable_schema,
@@ -298,6 +296,4 @@ ORDER BY table_name,
   segmentby_column_index,
   orderby_column_index;
 
-GRANT USAGE ON SCHEMA timescaledb_information TO PUBLIC;
-
 GRANT SELECT ON ALL TABLES IN SCHEMA timescaledb_information TO PUBLIC;

programming_language: SQL
filename: extension_scripts.out
change_type: ModificationType.ADD
diff: @@ -0,0 +1,56 @@
+-- This file and its contents are licensed under the Apache License 2.0.
+-- Please see the included NOTICE for copyright information and
+-- LICENSE-APACHE for a copy of the license.
+\c :TEST_DBNAME :ROLE_SUPERUSER
+DROP EXTENSION timescaledb;
+-- test that installation script errors when any of our internal schemas already exists
+\set ON_ERROR_STOP 0
+CREATE SCHEMA _timescaledb_catalog;
+CREATE EXTENSION timescaledb;
+ERROR:  schema "_timescaledb_catalog" already exists
+DROP SCHEMA _timescaledb_catalog;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA _timescaledb_internal;
+CREATE EXTENSION timescaledb;
+ERROR:  schema "_timescaledb_internal" already exists
+DROP SCHEMA _timescaledb_internal;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA _timescaledb_cache;
+CREATE EXTENSION timescaledb;
+ERROR:  schema "_timescaledb_cache" already exists
+DROP SCHEMA _timescaledb_cache;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA _timescaledb_config;
+CREATE EXTENSION timescaledb;
+ERROR:  schema "_timescaledb_config" already exists
+DROP SCHEMA _timescaledb_config;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA timescaledb_experimental;
+CREATE EXTENSION timescaledb;
+ERROR:  schema "timescaledb_experimental" already exists
+DROP SCHEMA timescaledb_experimental;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA timescaledb_information;
+CREATE EXTENSION timescaledb;
+ERROR:  schema "timescaledb_information" already exists
+DROP SCHEMA timescaledb_information;
+-- test that installation script errors when any of the function in public schema already exists
+-- we don't test every public function but just a few common ones
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE FUNCTION get_telemetry_report() RETURNS JSONB LANGUAGE SQL AS $$ SELECT NULL::jsonb; $$;
+CREATE EXTENSION timescaledb;
+ERROR:  function "get_telemetry_report" already exists with same argument types
+DROP FUNCTION get_telemetry_report;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE FUNCTION time_bucket(int,int) RETURNS int LANGUAGE SQL AS $$ SELECT 1::int; $$;
+CREATE EXTENSION timescaledb;
+ERROR:  function "time_bucket" already exists with same argument types
+DROP FUNCTION time_bucket;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE OR REPLACE FUNCTION show_chunks(relation regclass, older_than "any" DEFAULT NULL, newer_than "any" DEFAULT NULL) RETURNS SETOF regclass language internal as 'pg_partition_ancestors';
+CREATE EXTENSION timescaledb;
+ERROR:  function "show_chunks" already exists with same argument types
+DROP FUNCTION show_chunks;
+\c :TEST_DBNAME :ROLE_SUPERUSER
+SET client_min_messages TO ERROR;
+CREATE EXTENSION timescaledb;

programming_language: SQL
filename: CMakeLists.txt
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,7 @@ set(TEST_FILES
     drop_schema.sql
     dump_meta.sql
     extension.sql
+    extension_scripts.sql
     generated_as_identity.sql
     grant_hypertable.sql
     hash.sql

programming_language: Shell
filename: extension_scripts.sql
change_type: ModificationType.ADD
diff: @@ -0,0 +1,59 @@
+-- This file and its contents are licensed under the Apache License 2.0.
+-- Please see the included NOTICE for copyright information and
+-- LICENSE-APACHE for a copy of the license.
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+DROP EXTENSION timescaledb;
+
+-- test that installation script errors when any of our internal schemas already exists
+\set ON_ERROR_STOP 0
+CREATE SCHEMA _timescaledb_catalog;
+CREATE EXTENSION timescaledb;
+DROP SCHEMA _timescaledb_catalog;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA _timescaledb_internal;
+CREATE EXTENSION timescaledb;
+DROP SCHEMA _timescaledb_internal;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA _timescaledb_cache;
+CREATE EXTENSION timescaledb;
+DROP SCHEMA _timescaledb_cache;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA _timescaledb_config;
+CREATE EXTENSION timescaledb;
+DROP SCHEMA _timescaledb_config;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA timescaledb_experimental;
+CREATE EXTENSION timescaledb;
+DROP SCHEMA timescaledb_experimental;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE SCHEMA timescaledb_information;
+CREATE EXTENSION timescaledb;
+DROP SCHEMA timescaledb_information;
+
+-- test that installation script errors when any of the function in public schema already exists
+-- we don't test every public function but just a few common ones
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE FUNCTION get_telemetry_report() RETURNS JSONB LANGUAGE SQL AS $$ SELECT NULL::jsonb; $$;
+CREATE EXTENSION timescaledb;
+DROP FUNCTION get_telemetry_report;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE FUNCTION time_bucket(int,int) RETURNS int LANGUAGE SQL AS $$ SELECT 1::int; $$;
+CREATE EXTENSION timescaledb;
+DROP FUNCTION time_bucket;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+CREATE OR REPLACE FUNCTION show_chunks(relation regclass, older_than "any" DEFAULT NULL, newer_than "any" DEFAULT NULL) RETURNS SETOF regclass language internal as 'pg_partition_ancestors';
+CREATE EXTENSION timescaledb;
+DROP FUNCTION show_chunks;
+
+\c :TEST_DBNAME :ROLE_SUPERUSER
+SET client_min_messages TO ERROR;
+CREATE EXTENSION timescaledb;
+

programming_language: SQL
filename: data_node_bootstrap.out
change_type: ModificationType.MODIFY
diff: @@ -188,8 +188,7 @@ SELECT * FROM delete_data_node('bootstrap_test');
 CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
 SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
@@ -214,8 +213,7 @@ DROP DATABASE bootstrap_test;
 CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
 SELECT * FROM add_data_node('bootstrap_test', host => 'localhost',
@@ -275,8 +273,7 @@ CREATE DATABASE bootstrap_test
       OWNER :ROLE_CLUSTER_SUPERUSER;
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
 \set ON_ERROR_STOP 0
@@ -293,8 +290,7 @@ CREATE DATABASE bootstrap_test
       OWNER :ROLE_CLUSTER_SUPERUSER;
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
 \set ON_ERROR_STOP 0
@@ -311,8 +307,7 @@ CREATE DATABASE bootstrap_test
       OWNER :ROLE_CLUSTER_SUPERUSER;
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
 \set ON_ERROR_STOP 0
@@ -398,8 +393,8 @@ DROP DATABASE bootstrap_test;
 CREATE DATABASE access_node OWNER :ROLE_CLUSTER_SUPERUSER;
 \c access_node :ROLE_CLUSTER_SUPERUSER
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE SCHEMA ts_non_default AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
+CREATE EXTENSION timescaledb WITH SCHEMA ts_non_default CASCADE;
 SET client_min_messages TO NOTICE;
 -- Show the schema for the extension to verify that it is not public.
 SELECT extname FROM pg_extension WHERE extname = 'timescaledb';
@@ -412,18 +407,18 @@ SELECT extname FROM pg_extension WHERE extname = 'timescaledb';
 CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
+CREATE SCHEMA ts_non_default AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO NOTICE;
 \c access_node :ROLE_CLUSTER_SUPERUSER
 -- Add data node and delete it under error suppression. We want to
 -- avoid later tests to have random failures because the add succeeds.
 \set ON_ERROR_STOP 0
-SELECT * FROM _timescaledb_catalog.add_data_node(
+SELECT * FROM ts_non_default.add_data_node(
        'bootstrap_test', host => 'localhost',
        database => 'bootstrap_test', bootstrap => true);
 NOTICE:  database "bootstrap_test" already exists on data node, skipping
-ERROR:  schema "_timescaledb_catalog" already exists in database, aborting
-SELECT * FROM _timescaledb_catalog.delete_data_node('bootstrap_test');
+ERROR:  schema "ts_non_default" already exists in database, aborting
+SELECT * FROM ts_non_default.delete_data_node('bootstrap_test');
 ERROR:  server "bootstrap_test" does not exist
 \set ON_ERROR_STOP 1
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER

programming_language: SQL
filename: data_node_bootstrap.sql
change_type: ModificationType.MODIFY
diff: @@ -128,8 +128,7 @@ CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
 
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
@@ -148,8 +147,7 @@ CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
 
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
@@ -206,8 +204,7 @@ CREATE DATABASE bootstrap_test
 
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
@@ -227,8 +224,7 @@ CREATE DATABASE bootstrap_test
 
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
@@ -248,8 +244,7 @@ CREATE DATABASE bootstrap_test
 
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE EXTENSION timescaledb;
 SET client_min_messages TO NOTICE;
 
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
@@ -308,8 +303,8 @@ CREATE DATABASE access_node OWNER :ROLE_CLUSTER_SUPERUSER;
 
 \c access_node :ROLE_CLUSTER_SUPERUSER
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
-CREATE EXTENSION timescaledb WITH SCHEMA _timescaledb_catalog CASCADE;
+CREATE SCHEMA ts_non_default AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
+CREATE EXTENSION timescaledb WITH SCHEMA ts_non_default CASCADE;
 SET client_min_messages TO NOTICE;
 
 -- Show the schema for the extension to verify that it is not public.
@@ -320,7 +315,7 @@ CREATE DATABASE bootstrap_test OWNER :ROLE_CLUSTER_SUPERUSER;
 
 \c bootstrap_test :ROLE_CLUSTER_SUPERUSER
 SET client_min_messages TO ERROR;
-CREATE SCHEMA _timescaledb_catalog AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
+CREATE SCHEMA ts_non_default AUTHORIZATION :ROLE_CLUSTER_SUPERUSER;
 SET client_min_messages TO NOTICE;
 
 \c access_node :ROLE_CLUSTER_SUPERUSER
@@ -328,10 +323,10 @@ SET client_min_messages TO NOTICE;
 -- Add data node and delete it under error suppression. We want to
 -- avoid later tests to have random failures because the add succeeds.
 \set ON_ERROR_STOP 0
-SELECT * FROM _timescaledb_catalog.add_data_node(
+SELECT * FROM ts_non_default.add_data_node(
        'bootstrap_test', host => 'localhost',
        database => 'bootstrap_test', bootstrap => true);
-SELECT * FROM _timescaledb_catalog.delete_data_node('bootstrap_test');
+SELECT * FROM ts_non_default.delete_data_node('bootstrap_test');
 \set ON_ERROR_STOP 1
 
 \c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER

programming_language: SQL