mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 18:13:18 +08:00
Move enterprise features to community
This patch removes enterprise license support and moves move_chunk() function under community license (TSL). Licensing validation code been reworked and simplified. Previously used timescaledb.license_key guc been renamed to timescaledb.license. This change also makes testing code more strict against used license. Apache test suite now can test only apache-licensed functions. Fixes #2359
This commit is contained in:
parent
66c63476e5
commit
a51aa6d04b
@ -183,22 +183,17 @@ test_script:
|
||||
|
||||
#right now we only run timescale regression tests, others will be set up later
|
||||
|
||||
docker exec -e IGNORES="bgw_db_scheduler chunk_utils loader" -e SKIPS="bgw_db_scheduler" -e TEST_TABLESPACE1_PATH="C:\Users\$env:UserName\Documents\tablespace1\" -e TEST_TABLESPACE2_PATH="C:\Users\$env:UserName\Documents\tablespace2\" -e TEST_SPINWAIT_ITERS=10000 -e USER=postgres -e PG_REGRESS_OPTS="--bindir=/usr/local/bin/" -it pgregress /bin/bash -c "cd /timescaledb/build && make regresschecklocal"
|
||||
docker exec -e IGNORES="chunk_utils loader license" -e SKIPS="license" -e TEST_TABLESPACE1_PATH="C:\Users\$env:UserName\Documents\tablespace1\" -e TEST_TABLESPACE2_PATH="C:\Users\$env:UserName\Documents\tablespace2\" -e TEST_SPINWAIT_ITERS=10000 -e USER=postgres -e PG_REGRESS_OPTS="--bindir=/usr/local/bin/" -it pgregress /bin/bash -c "cd /timescaledb/build && make regresschecklocal"
|
||||
|
||||
$TESTS1 = $?
|
||||
|
||||
# Normally we use different config files for apache and enterprise tests, but Windows was having problems with that
|
||||
# Thus, just append the license key to the regular config file instead
|
||||
|
||||
Add-Content "C:\Program Files\postgresql\12\data\postgresql.conf" "timescaledb.license_key = 'E1eyJlbmRfdGltZSI6IjIwMTgtMTAtMDEgKzAwMDAiLCAic3RhcnRfdGltZSI6IjIwMTgtMDktMDEgKzAwMDAiLCAiaWQiOiI0OTBGQjI2MC1BMjkyLTRBRDktOUFBMi0wMzYwODM1NzkxQjgiLCAia2luZCI6InRyaWFsIn0K'"
|
||||
|
||||
Restart-Service postgresql-x64-12
|
||||
|
||||
# Windows doesn't support SIGTERM used by the node
|
||||
# killer. Therefore, we need to ignore the results of the
|
||||
# remote_connection and remote_txn tests.
|
||||
|
||||
docker exec -e IGNORES="compression_algos continuous_aggs_bgw remote_connection remote_txn" -e TEST_TABLESPACE1_PATH="C:\Users\$env:UserName\Documents\tablespace1\" -e TEST_TABLESPACE2_PATH="C:\Users\$env:UserName\Documents\tablespace2\" -e TEST_SPINWAIT_ITERS=10000 -e USER=postgres -e PG_REGRESS_OPTS="--bindir=/usr/local/bin/" -it pgregress /bin/bash -c "cd /timescaledb/build && make regresschecklocal-t"
|
||||
docker exec -e IGNORES="bgw_db_scheduler compression_algos continuous_aggs_bgw remote_connection remote_txn" -e SKIPS="bgw_db_scheduler" -e TEST_TABLESPACE1_PATH="C:\Users\$env:UserName\Documents\tablespace1\" -e TEST_TABLESPACE2_PATH="C:\Users\$env:UserName\Documents\tablespace2\" -e TEST_SPINWAIT_ITERS=10000 -e USER=postgres -e PG_REGRESS_OPTS="--bindir=/usr/local/bin/" -it pgregress /bin/bash -c "cd /timescaledb/build && make regresschecklocal-t"
|
||||
|
||||
if( -not $? -or -not $TESTS1 ) { exit 1 }
|
||||
|
||||
|
@ -25,7 +25,6 @@ set(SOURCE_FILES
|
||||
hypertable.sql
|
||||
chunk.sql
|
||||
ddl_internal.sql
|
||||
edition.sql
|
||||
util_time.sql
|
||||
util_internal_table_ddl.sql
|
||||
chunk_constraint.sql
|
||||
|
@ -1,37 +0,0 @@
|
||||
-- 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.
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.enterprise_enabled() RETURNS BOOLEAN
|
||||
AS '@MODULE_PATHNAME@', 'ts_enterprise_enabled' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.current_license_key() RETURNS TEXT
|
||||
AS '@MODULE_PATHNAME@', 'ts_current_license_key' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_loaded() RETURNS BOOLEAN
|
||||
AS '@MODULE_PATHNAME@', 'ts_tsl_loaded' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.license_expiration_time() RETURNS TIMESTAMPTZ
|
||||
AS '@MODULE_PATHNAME@', 'ts_license_expiration_time' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.print_license_expiration_info() RETURNS VOID
|
||||
AS '@MODULE_PATHNAME@', 'ts_print_tsl_license_expiration_info' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.license_edition() RETURNS TEXT
|
||||
AS '@MODULE_PATHNAME@', 'ts_license_edition' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.current_db_set_license_key(new_key TEXT) RETURNS TEXT AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
db text;
|
||||
BEGIN
|
||||
SELECT current_database() INTO db;
|
||||
EXECUTE format('ALTER DATABASE %I SET timescaledb.license_key = %L', db, new_key);
|
||||
EXECUTE format('SET SESSION timescaledb.license_key = %L', new_key);
|
||||
PERFORM _timescaledb_internal.restart_background_workers();
|
||||
RETURN new_key;
|
||||
END
|
||||
$BODY$
|
||||
LANGUAGE PLPGSQL;
|
||||
|
||||
|
@ -4,19 +4,8 @@
|
||||
|
||||
DO language plpgsql $$
|
||||
DECLARE
|
||||
end_time TIMESTAMPTZ;
|
||||
expiration_time_string TEXT;
|
||||
telemetry_string TEXT;
|
||||
BEGIN
|
||||
end_time := _timescaledb_internal.license_expiration_time();
|
||||
|
||||
IF end_time IS NOT NULL AND isfinite(end_time)
|
||||
THEN
|
||||
expiration_time_string = format(E'\nYour license expires on %s\n', end_time);
|
||||
ELSE
|
||||
expiration_time_string = '';
|
||||
END IF;
|
||||
|
||||
IF current_setting('timescaledb.telemetry_level') = 'off'
|
||||
THEN
|
||||
telemetry_string = E'Note: Please enable telemetry to help us improve our product by running: ALTER DATABASE "' || current_database() || E'" SET timescaledb.telemetry_level = ''basic'';';
|
||||
@ -24,7 +13,7 @@ BEGIN
|
||||
telemetry_string = E'Note: TimescaleDB collects anonymous reports to better understand and assist our users.\nFor more information and how to disable, please see our docs https://docs.timescaledb.com/using-timescaledb/telemetry.';
|
||||
END IF;
|
||||
|
||||
RAISE WARNING E'%\n%\n%',
|
||||
RAISE WARNING E'%\n%\n',
|
||||
E'\nWELCOME TO\n' ||
|
||||
E' _____ _ _ ____________ \n' ||
|
||||
E'|_ _(_) | | | _ \\ ___ \\ \n' ||
|
||||
@ -39,18 +28,6 @@ BEGIN
|
||||
E' 1. Getting started: https://docs.timescale.com/getting-started\n' ||
|
||||
E' 2. API reference documentation: https://docs.timescale.com/api\n' ||
|
||||
E' 3. How TimescaleDB is designed: https://docs.timescale.com/introduction/architecture\n',
|
||||
telemetry_string,
|
||||
expiration_time_string;
|
||||
|
||||
IF now() > end_time
|
||||
THEN
|
||||
RAISE WARNING E'%\n', format('Your license expired on %s', end_time);
|
||||
ELSIF now() + INTERVAL '1 week' >= end_time
|
||||
THEN
|
||||
RAISE WARNING E'%\n', format('Your license will expire on %s', end_time);
|
||||
END IF;
|
||||
|
||||
telemetry_string;
|
||||
END;
|
||||
$$;
|
||||
|
||||
select _timescaledb_internal.print_license_expiration_info();
|
||||
|
@ -13,6 +13,7 @@ DROP FUNCTION IF EXISTS _timescaledb_internal.range_value_to_pretty;
|
||||
DROP FUNCTION IF EXISTS hypertable_approximate_row_count;
|
||||
DROP VIEW IF EXISTS timescaledb_information.compressed_chunk_stats;
|
||||
DROP VIEW IF EXISTS timescaledb_information.compressed_hypertable_stats;
|
||||
DROP VIEW IF EXISTS timescaledb_information.license;
|
||||
|
||||
-- Add new function definitions, columns and tables for distributed hypertables
|
||||
DROP FUNCTION IF EXISTS create_hypertable(regclass,name,name,integer,name,name,anyelement,boolean,boolean,regproc,boolean,text,regproc,regproc);
|
||||
@ -23,6 +24,12 @@ DROP FUNCTION IF EXISTS show_chunks;
|
||||
DROP FUNCTION IF EXISTS add_compress_chunks_policy;
|
||||
DROP FUNCTION IF EXISTS remove_compress_chunks_policy;
|
||||
DROP FUNCTION IF EXISTS alter_job_schedule;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.enterprise_enabled;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.current_license_key;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.license_expiration_time;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.print_license_expiration_info;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.license_edition;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.current_db_set_license_key;
|
||||
|
||||
DROP VIEW IF EXISTS timescaledb_information.policy_stats;
|
||||
DROP VIEW IF EXISTS timescaledb_information.drop_chunks_policies;
|
||||
|
@ -11,3 +11,6 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.get_os_info()
|
||||
|
||||
CREATE OR REPLACE FUNCTION get_telemetry_report(always_display_report boolean DEFAULT false) RETURNS TEXT
|
||||
AS '@MODULE_PATHNAME@', 'ts_get_telemetry_report' LANGUAGE C STABLE PARALLEL SAFE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.tsl_loaded() RETURNS BOOLEAN
|
||||
AS '@MODULE_PATHNAME@', 'ts_tsl_loaded' LANGUAGE C;
|
||||
|
@ -42,11 +42,6 @@ CREATE OR REPLACE VIEW timescaledb_information.hypertables AS
|
||||
WHERE ht.compressed is false --> no internal compression tables
|
||||
AND ca.mat_hypertable_id IS NULL;
|
||||
|
||||
CREATE OR REPLACE VIEW timescaledb_information.license AS
|
||||
SELECT _timescaledb_internal.license_edition() as edition,
|
||||
_timescaledb_internal.license_expiration_time() <= now() AS expired,
|
||||
_timescaledb_internal.license_expiration_time() AS expiration_time;
|
||||
|
||||
CREATE OR REPLACE VIEW timescaledb_information.job_stats as
|
||||
SELECT format('%1$I.%2$I', ht.schema_name, ht.table_name)::regclass as hypertable, j.id AS job_id,
|
||||
js.last_start as last_run_started_at,
|
||||
|
@ -127,21 +127,8 @@ error_no_default_fn_community(void)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("functionality not supported under the current license \"%s\", license",
|
||||
ts_guc_license_key),
|
||||
errhint(
|
||||
"Upgrade to a Timescale-licensed binary to access this free community feature")));
|
||||
}
|
||||
|
||||
static void
|
||||
error_no_default_fn_enterprise(void)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("functionality not supported under the current license \"%s\", license",
|
||||
ts_guc_license_key),
|
||||
errhint("Request a trial license to try this feature for free or contact us for more "
|
||||
"information at https://www.timescale.com/pricing")));
|
||||
errmsg("functionality not supported under the current \"%s\" license", ts_guc_license),
|
||||
errhint("Upgrade your license to 'timescale' to use this free community feature.")));
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -151,26 +138,6 @@ error_no_default_fn_bool_void_community(void)
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
static bool
|
||||
error_no_default_fn_bool_void_enterprise(void)
|
||||
{
|
||||
error_no_default_fn_enterprise();
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
static void
|
||||
tsl_license_on_assign_default_fn(const char *newval, const void *license)
|
||||
{
|
||||
error_no_default_fn_community();
|
||||
}
|
||||
|
||||
static TimestampTz
|
||||
license_end_time_default_fn(void)
|
||||
{
|
||||
error_no_default_fn_community();
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
static void
|
||||
add_tsl_telemetry_info_default(JsonbParseState **parse_state)
|
||||
{
|
||||
@ -197,11 +164,10 @@ error_no_default_fn_pg_community(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("function \"%s\" is not supported under the current license \"%s\"",
|
||||
errmsg("function \"%s\" is not supported under the current \"%s\" license",
|
||||
get_func_name(fcinfo->flinfo->fn_oid),
|
||||
ts_guc_license_key),
|
||||
errhint(
|
||||
"Upgrade to a Timescale-licensed binary to access this free community feature")));
|
||||
ts_guc_license),
|
||||
errhint("Upgrade your license to 'timescale' to use this free community feature.")));
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
@ -226,19 +192,6 @@ cache_syscache_invalidate_default(Datum arg, int cacheid, uint32 hashvalue)
|
||||
/* The default is a no-op */
|
||||
}
|
||||
|
||||
static Datum
|
||||
error_no_default_fn_pg_enterprise(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("function \"%s\" is not supported under the current license \"%s\"",
|
||||
get_func_name(fcinfo->flinfo->fn_oid),
|
||||
ts_guc_license_key),
|
||||
errhint("Request a trial license to try this feature for free or contact us for more "
|
||||
"information at https://www.timescale.com/pricing")));
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
static DDLResult
|
||||
process_cagg_viewstmt_default(Node *stmt, const char *query_string, void *pstmt,
|
||||
WithClauseResult *with_clause_options)
|
||||
@ -308,18 +261,20 @@ func_call_on_data_nodes_default(FunctionCallInfo finfo, List *data_node_oids)
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
TS_FUNCTION_INFO_V1(ts_tsl_loaded);
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_tsl_loaded(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_BOOL(ts_cm_functions != &ts_cm_functions_default);
|
||||
}
|
||||
|
||||
/*
|
||||
* Define cross-module functions' default values:
|
||||
* If the submodule isn't activated, using one of the cm functions will throw an
|
||||
* exception.
|
||||
*/
|
||||
TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
|
||||
.tsl_license_on_assign = tsl_license_on_assign_default_fn,
|
||||
.enterprise_enabled_internal = error_no_default_fn_bool_void_enterprise,
|
||||
.check_tsl_loaded = error_no_default_fn_bool_void_community,
|
||||
.license_end_time = license_end_time_default_fn,
|
||||
.print_tsl_license_expiration_info_hook = NULL,
|
||||
.module_shutdown_hook = NULL,
|
||||
.add_tsl_telemetry_info = add_tsl_telemetry_info_default,
|
||||
.create_upper_paths_hook = NULL,
|
||||
.set_rel_pathlist_dml = NULL,
|
||||
@ -358,7 +313,7 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
|
||||
.job_run = error_no_default_fn_pg_community,
|
||||
.job_execute = job_execute_default_fn,
|
||||
|
||||
.move_chunk = error_no_default_fn_pg_enterprise,
|
||||
.move_chunk = error_no_default_fn_pg_community,
|
||||
.reorder_chunk = error_no_default_fn_pg_community,
|
||||
|
||||
.partialize_agg = error_no_default_fn_pg_community,
|
||||
|
@ -38,12 +38,6 @@ typedef struct CopyChunkState CopyChunkState;
|
||||
|
||||
typedef struct CrossModuleFunctions
|
||||
{
|
||||
void (*tsl_license_on_assign)(const char *newval, const void *license);
|
||||
bool (*enterprise_enabled_internal)(void);
|
||||
bool (*check_tsl_loaded)(void);
|
||||
TimestampTz (*license_end_time)(void);
|
||||
void (*print_tsl_license_expiration_info_hook)(void);
|
||||
void (*module_shutdown_hook)(void);
|
||||
void (*add_tsl_telemetry_info)(JsonbParseState **parse_state);
|
||||
|
||||
PGFunction policy_compression_add;
|
||||
|
@ -14,6 +14,8 @@
|
||||
#define TSL_LIBRARY_NAME "timescaledb-tsl"
|
||||
#define TS_LIBDIR "$libdir/"
|
||||
#define EXTENSION_SO TS_LIBDIR "" EXTENSION_NAME
|
||||
#define EXTENSION_TSL_SO TS_LIBDIR TSL_LIBRARY_NAME "-" TIMESCALEDB_VERSION_MOD
|
||||
|
||||
#define MAX_VERSION_LEN (NAMEDATALEN + 1)
|
||||
#define MAX_SO_NAME_LEN \
|
||||
(8 + NAMEDATALEN + 1 + MAX_VERSION_LEN) /* "$libdir/"+extname+"-"+version \
|
||||
|
16
src/guc.c
16
src/guc.c
@ -54,7 +54,7 @@ int ts_guc_max_open_chunks_per_insert = 10;
|
||||
int ts_guc_max_cached_chunks_per_hypertable = 10;
|
||||
int ts_guc_telemetry_level = TELEMETRY_DEFAULT;
|
||||
|
||||
TSDLLEXPORT char *ts_guc_license_key = TS_DEFAULT_LICENSE;
|
||||
TSDLLEXPORT char *ts_guc_license = TS_LICENSE_DEFAULT;
|
||||
char *ts_last_tune_time = NULL;
|
||||
char *ts_last_tune_version = NULL;
|
||||
char *ts_telemetry_cloud = NULL;
|
||||
@ -360,15 +360,15 @@ _guc_init(void)
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DefineCustomStringVariable(/* name= */ "timescaledb.license_key",
|
||||
/* short_dec= */ "TimescaleDB license key",
|
||||
DefineCustomStringVariable(/* name= */ "timescaledb.license",
|
||||
/* short_dec= */ "TimescaleDB license type",
|
||||
/* long_dec= */ "Determines which features are enabled",
|
||||
/* valueAddr= */ &ts_guc_license_key,
|
||||
/* bootValue= */ TS_DEFAULT_LICENSE,
|
||||
/* valueAddr= */ &ts_guc_license,
|
||||
/* bootValue= */ TS_LICENSE_DEFAULT,
|
||||
/* context= */ PGC_SUSET,
|
||||
/* flags= */ GUC_SUPERUSER_ONLY,
|
||||
/* check_hook= */ ts_license_update_check,
|
||||
/* assign_hook= */ ts_license_on_assign,
|
||||
/* flags= */ 0,
|
||||
/* check_hook= */ ts_license_guc_check_hook,
|
||||
/* assign_hook= */ ts_license_guc_assign_hook,
|
||||
/* show_hook= */ NULL);
|
||||
|
||||
DefineCustomStringVariable(/* name= */ "timescaledb.last_tuned",
|
||||
|
@ -26,7 +26,7 @@ extern bool ts_guc_restoring;
|
||||
extern int ts_guc_max_open_chunks_per_insert;
|
||||
extern int ts_guc_max_cached_chunks_per_hypertable;
|
||||
extern int ts_guc_telemetry_level;
|
||||
extern TSDLLEXPORT char *ts_guc_license_key;
|
||||
extern TSDLLEXPORT char *ts_guc_license;
|
||||
extern char *ts_last_tune_time;
|
||||
extern char *ts_last_tune_version;
|
||||
extern char *ts_telemetry_cloud;
|
||||
|
@ -15,90 +15,84 @@
|
||||
#include "license_guc.h"
|
||||
#include "cross_module_fn.h"
|
||||
|
||||
TS_FUNCTION_INFO_V1(ts_enterprise_enabled);
|
||||
TS_FUNCTION_INFO_V1(ts_current_license_key);
|
||||
TS_FUNCTION_INFO_V1(ts_tsl_loaded);
|
||||
TS_FUNCTION_INFO_V1(ts_print_tsl_license_expiration_info);
|
||||
TS_FUNCTION_INFO_V1(ts_license_expiration_time);
|
||||
TS_FUNCTION_INFO_V1(ts_license_edition);
|
||||
TS_FUNCTION_INFO_V1(ts_allow_downgrade_to_apache);
|
||||
|
||||
static bool downgrade_to_apache_enabled = false;
|
||||
static void *tsl_handle = NULL;
|
||||
static PGFunction tsl_validate_license_fn = NULL;
|
||||
static PGFunction tsl_startup_fn = NULL;
|
||||
static bool can_load = false;
|
||||
static bool load_enabled = false;
|
||||
static GucSource load_source = PGC_S_DEFAULT;
|
||||
|
||||
#define TS_LICENSE_APACHE_ONLY_PRINT_TEXT "apache"
|
||||
#define TS_LICENSE_COMMUNITY_PRINT_TEXT "community"
|
||||
#define TS_LICENSE_ENTERPRISE_PRINT_TEXT "enterprise"
|
||||
static void *tsl_handle = NULL;
|
||||
static PGFunction tsl_init_fn = NULL;
|
||||
|
||||
/*
|
||||
* License Functions
|
||||
* License Functions.
|
||||
*
|
||||
* License validation is performed via guc update-hooks.
|
||||
* In this file we check if the type of license supplied warrants loading an
|
||||
* additional module. It loading one is warranted, that module performs the
|
||||
* final validation that the license is correct.
|
||||
* additional module.
|
||||
*
|
||||
*/
|
||||
|
||||
static bool load_tsl();
|
||||
|
||||
static bool
|
||||
current_license_can_downgrade_to_apache(void)
|
||||
{
|
||||
if (downgrade_to_apache_enabled)
|
||||
return true;
|
||||
|
||||
return (ts_guc_license_key == NULL || TS_CURRENT_LICENSE_IS_APACHE_ONLY()) &&
|
||||
tsl_handle == NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* GUC checks work in two parts:
|
||||
*
|
||||
* 1. In the check function, all validation of the new value is performed
|
||||
* and any auxiliary state is setup but _not_ installed. Postgres is
|
||||
* allowed to call the check function in cases where the newval will never
|
||||
* be used, so any effects of this function must be confined to `extra` or
|
||||
* otherwise be un-observable. Further, this function is _not_ allowed to
|
||||
* throw exceptions.
|
||||
* and any auxiliary state is setup but not installed. This function
|
||||
* is not allowed to throw exceptions.
|
||||
*
|
||||
* 2. In the assign function all user-visible state is installed. This
|
||||
* function *MUST NOT FAIL* as it can be called from such places as
|
||||
* transaction commitment, and will cause database restarts if it fails.
|
||||
*
|
||||
* Therefore our license validation also works in two parts, corresponding to
|
||||
* Therefore license validation also works in two parts, corresponding to
|
||||
* check and assign:
|
||||
* 1. In the first stage we check the license kind, load the submodule into
|
||||
* memory if needed (but don't link any of the cross-module functions yet)
|
||||
* and, if it's an enterprise license, validate the license key.
|
||||
* 2. In the second stage we link all of the cross-module functions.
|
||||
*
|
||||
* The first stage will fail if the provided license key is invalid, or it's
|
||||
* trying to downgrade from an edition that uses the submodule
|
||||
* (Community and Enterprise) to one that does not (ApacheOnly). Currently only
|
||||
* upgrading is allowed within a session; downgrading requires starting a new
|
||||
* session.
|
||||
* 1. In the first stage we check the license type, load the submodule into
|
||||
* memory if needed (but don't link any of the cross-module functions yet).
|
||||
*
|
||||
* 2. In the second stage we link all of the cross-module functions and init
|
||||
* tsl module.
|
||||
*
|
||||
* In order for restoring libraries to work (e.g. in parallel workers), loading
|
||||
* the submodule must happen strictly after the main timescaledb module is
|
||||
* loaded. In order to ensure that the initial value doesn't break this, we
|
||||
* disable loading submodules until the post_load_init.
|
||||
*
|
||||
* No license change from user session is allowed. License can be changed only
|
||||
* if it is set from server configuration file or the server command line.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
LICENSE_UNDEF,
|
||||
LICENSE_APACHE,
|
||||
LICENSE_TIMESCALE
|
||||
} LicenseType;
|
||||
|
||||
static LicenseType
|
||||
license_type_of(const char *string)
|
||||
{
|
||||
if (string == NULL)
|
||||
return LICENSE_UNDEF;
|
||||
if (strcmp(string, TS_LICENSE_TIMESCALE) == 0)
|
||||
return LICENSE_TIMESCALE;
|
||||
if (strcmp(string, TS_LICENSE_APACHE) == 0)
|
||||
return LICENSE_APACHE;
|
||||
return LICENSE_UNDEF;
|
||||
}
|
||||
|
||||
bool
|
||||
ts_license_is_apache(void)
|
||||
{
|
||||
return license_type_of(ts_guc_license) == LICENSE_APACHE;
|
||||
}
|
||||
|
||||
TSDLLEXPORT void
|
||||
ts_license_enable_module_loading(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (can_load)
|
||||
if (load_enabled)
|
||||
return;
|
||||
|
||||
can_load = true;
|
||||
/* re-set the license key to actually load the submodule if needed */
|
||||
result = set_config_option("timescaledb.license_key",
|
||||
ts_guc_license_key,
|
||||
load_enabled = true;
|
||||
|
||||
/* re-set the license to actually load the submodule if needed */
|
||||
result = set_config_option("timescaledb.license",
|
||||
ts_guc_license,
|
||||
PGC_SUSET,
|
||||
load_source,
|
||||
GUC_ACTION_SET,
|
||||
@ -107,279 +101,100 @@ ts_license_enable_module_loading(void)
|
||||
false);
|
||||
|
||||
if (result <= 0)
|
||||
elog(ERROR, "invalid value for timescaledb.license_key '%s'", ts_guc_license_key);
|
||||
elog(ERROR, "invalid value for timescaledb.license: \"%s\"", ts_guc_license);
|
||||
}
|
||||
|
||||
/*
|
||||
* `ts_license_update_check`
|
||||
* Used to validate license keys in preparation for `ts_license_on_assign`
|
||||
* TSL module load function.
|
||||
*
|
||||
* Load the module, but do not start it. Set tsl_handle and
|
||||
* tsl_init_fn module init function pointer (tsl/src/init.c).
|
||||
*
|
||||
* This function is idempotent, and will not reload the module
|
||||
* if called multiple times.
|
||||
*/
|
||||
static bool
|
||||
tsl_module_load(void)
|
||||
{
|
||||
void *function;
|
||||
void *handle;
|
||||
|
||||
if (tsl_handle != NULL)
|
||||
return true;
|
||||
|
||||
function = load_external_function(EXTENSION_TSL_SO, "ts_module_init", false, &handle);
|
||||
if (function == NULL || handle == NULL)
|
||||
return false;
|
||||
tsl_init_fn = function;
|
||||
tsl_handle = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
tsl_module_init(void)
|
||||
{
|
||||
Assert(tsl_handle != NULL);
|
||||
Assert(tsl_init_fn != NULL);
|
||||
DirectFunctionCall1(tsl_init_fn, CharGetDatum(0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check hook function set by license guc.
|
||||
*
|
||||
* Used to validate license string before the assign hook
|
||||
* ts_license_guc_assign_hook() call.
|
||||
*/
|
||||
bool
|
||||
ts_license_update_check(char **newval, void **extra, GucSource source)
|
||||
ts_license_guc_check_hook(char **newval, void **extra, GucSource source)
|
||||
{
|
||||
Datum module_can_start;
|
||||
bool try_to_load_tsl = true;
|
||||
LicenseType type = license_type_of(*newval);
|
||||
|
||||
if (*newval == NULL)
|
||||
return false;
|
||||
|
||||
if (!TS_LICENSE_TYPE_IS_VALID(*newval))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* we can shutdown the submodule if it was never loaded, or it has a valid
|
||||
* shutdown function.
|
||||
*/
|
||||
if (TS_LICENSE_IS_APACHE_ONLY(*newval))
|
||||
/* Allow setting a license only if is is set from postgresql.conf
|
||||
* or the server command line */
|
||||
switch (type)
|
||||
{
|
||||
if (!current_license_can_downgrade_to_apache())
|
||||
{
|
||||
GUC_check_errdetail("Cannot downgrade a running session to Apache Only.");
|
||||
GUC_check_errhint("change the license in the configuration file");
|
||||
case LICENSE_APACHE:
|
||||
case LICENSE_TIMESCALE:
|
||||
if (source == PGC_S_FILE || source == PGC_S_ARGV || source == PGC_S_DEFAULT)
|
||||
break;
|
||||
GUC_check_errdetail("Cannot change a license in a running session.");
|
||||
GUC_check_errhint(
|
||||
"Change the license in the configuration file or server command line.");
|
||||
return false;
|
||||
case LICENSE_UNDEF:
|
||||
GUC_check_errdetail("Unrecognized license type.");
|
||||
GUC_check_errhint("Supported license types are 'timescale' or 'apache'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try_to_load_tsl = false;
|
||||
}
|
||||
|
||||
if (!can_load)
|
||||
/* If loading is delayed, save the GucSource for later retry
|
||||
* in the ts_license_enable_module_loading() */
|
||||
if (!load_enabled)
|
||||
{
|
||||
load_source = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!try_to_load_tsl)
|
||||
return true;
|
||||
|
||||
if (!load_tsl())
|
||||
if (type == LICENSE_TIMESCALE && !tsl_module_load())
|
||||
{
|
||||
GUC_check_errdetail("Could not find additional timescaledb module");
|
||||
GUC_check_errhint("check that %s-%s is available",
|
||||
TSL_LIBRARY_NAME,
|
||||
TIMESCALEDB_VERSION_MOD);
|
||||
GUC_check_errdetail("Could not find TSL timescaledb module.");
|
||||
GUC_check_errhint("Check that \"%s\" is available.", EXTENSION_TSL_SO);
|
||||
return false;
|
||||
}
|
||||
|
||||
Assert(tsl_handle != NULL);
|
||||
Assert(tsl_validate_license_fn != NULL);
|
||||
Assert(tsl_startup_fn != NULL);
|
||||
#ifdef WIN32
|
||||
|
||||
/*
|
||||
* freeing the guc extra causes heap corruption on windows so instead we
|
||||
* re-parse in the assign hook
|
||||
*/
|
||||
extra = NULL;
|
||||
#endif
|
||||
|
||||
module_can_start = DirectFunctionCall2(tsl_validate_license_fn,
|
||||
CStringGetDatum(*newval),
|
||||
PointerGetDatum(extra));
|
||||
|
||||
return DatumGetBool(module_can_start);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
static void *
|
||||
revalidate_license(const char *newval)
|
||||
{
|
||||
void *extra = NULL;
|
||||
void **extra_p = &extra;
|
||||
|
||||
Assert(extra == NULL);
|
||||
|
||||
/*
|
||||
* Due to windows issues we cannot use the extra parameter. Instead
|
||||
* re-call the validation function, if we reach this point the license
|
||||
* must be valid so the function cannot fail cannot fail.
|
||||
*/
|
||||
DirectFunctionCall2(tsl_validate_license_fn, CStringGetDatum(newval), PointerGetDatum(extra_p));
|
||||
return extra;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* `ts_license_on_assign`
|
||||
* Links the cross-module function pointers.
|
||||
* Assign hook function set by license guc, executed right after
|
||||
* ts_license_guc_check_hook() hook call.
|
||||
*
|
||||
* Executes tsl module init function (tsl/src/init.c) which sets the
|
||||
* cross-module function pointers.
|
||||
*/
|
||||
void
|
||||
ts_license_on_assign(const char *newval, void *extra)
|
||||
ts_license_guc_assign_hook(const char *newval, void *extra)
|
||||
{
|
||||
if (!can_load)
|
||||
return;
|
||||
|
||||
Assert(newval != NULL);
|
||||
Assert(TS_LICENSE_TYPE_IS_VALID(newval));
|
||||
if (TS_LICENSE_IS_APACHE_ONLY(newval))
|
||||
{
|
||||
Assert(current_license_can_downgrade_to_apache());
|
||||
Assert(extra == NULL);
|
||||
if (ts_cm_functions->module_shutdown_hook != NULL)
|
||||
ts_cm_functions->module_shutdown_hook();
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(tsl_handle != NULL);
|
||||
Assert(tsl_startup_fn != NULL);
|
||||
DirectFunctionCall1(tsl_startup_fn, CharGetDatum(0));
|
||||
#ifdef WIN32
|
||||
Assert(extra == NULL);
|
||||
extra = revalidate_license(newval);
|
||||
#endif
|
||||
Assert(extra != NULL);
|
||||
ts_cm_functions->tsl_license_on_assign(newval, extra);
|
||||
}
|
||||
|
||||
/* Module Functions */
|
||||
|
||||
/*
|
||||
* Load the module, but do not start it.
|
||||
* If this function succeeds, `tsl_handle`, and
|
||||
* `tsl_startup_fn` will be set, but no `TS_FN` functions will
|
||||
* change. `tsl_startup_fn` can be used to enable the submodule
|
||||
* versions of `TS_FN`s.
|
||||
*
|
||||
* This function is idempotent, and will _not_ reload the module
|
||||
* if called multiple times.
|
||||
*
|
||||
* returns:
|
||||
* a function pointer to `tsl_license_update_check` or NULL if the
|
||||
* loading fails
|
||||
*
|
||||
*/
|
||||
static bool load_tsl(void);
|
||||
|
||||
static bool
|
||||
load_tsl(void)
|
||||
{
|
||||
char soname[MAX_SO_NAME_LEN] = { 0 };
|
||||
|
||||
if (tsl_handle != NULL)
|
||||
{
|
||||
Assert(tsl_startup_fn != NULL);
|
||||
|
||||
/*
|
||||
* We don't want to reload the submodule if it was loaded successfully
|
||||
* in the past because that may relocate symbols we're using. Instead
|
||||
* skip to loading the validation function.
|
||||
*/
|
||||
goto get_validation_function;
|
||||
}
|
||||
|
||||
snprintf(soname, MAX_SO_NAME_LEN, TS_LIBDIR "%s-%s", TSL_LIBRARY_NAME, TIMESCALEDB_VERSION_MOD);
|
||||
|
||||
tsl_startup_fn = load_external_function(
|
||||
/* filename= */ soname,
|
||||
/* funcname= */ "ts_module_init",
|
||||
/* signalNotFound= */ false,
|
||||
/* filehandle= */ &tsl_handle);
|
||||
|
||||
if (tsl_handle == NULL || tsl_startup_fn == NULL)
|
||||
goto loading_failed;
|
||||
|
||||
get_validation_function:
|
||||
tsl_validate_license_fn = lookup_external_function(tsl_handle, "tsl_license_update_check");
|
||||
|
||||
if (tsl_validate_license_fn == NULL)
|
||||
goto loading_failed;
|
||||
|
||||
return true;
|
||||
|
||||
/*
|
||||
* We want this function to be atomic, either all three relevant values are set
|
||||
* or none of them are: If we fail to find one of the values set the two static
|
||||
* ones back to NULL. (If they were non-NULL to start with that means the
|
||||
* function must have been called successfully in the past, so the lookups
|
||||
* should still succeed)
|
||||
*/
|
||||
loading_failed:
|
||||
tsl_handle = NULL;
|
||||
tsl_startup_fn = NULL;
|
||||
tsl_validate_license_fn = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* SQL functions */
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_tsl_loaded(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (TS_CURRENT_LICENSE_IS_APACHE_ONLY())
|
||||
PG_RETURN_BOOL(false);
|
||||
|
||||
PG_RETURN_BOOL(ts_cm_functions->check_tsl_loaded());
|
||||
}
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_enterprise_enabled(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (TS_CURRENT_LICENSE_IS_APACHE_ONLY())
|
||||
PG_RETURN_BOOL(false);
|
||||
|
||||
PG_RETURN_BOOL(ts_cm_functions->enterprise_enabled_internal());
|
||||
}
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_current_license_key(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (!is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_SETTINGS))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser or a member of pg_read_all_settings to examine the "
|
||||
"license key")));
|
||||
Assert(ts_guc_license_key != NULL);
|
||||
PG_RETURN_TEXT_P(cstring_to_text(ts_guc_license_key));
|
||||
}
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_print_tsl_license_expiration_info(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (ts_cm_functions->print_tsl_license_expiration_info_hook != NULL)
|
||||
ts_cm_functions->print_tsl_license_expiration_info_hook();
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_license_expiration_time(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (ts_cm_functions->print_tsl_license_expiration_info_hook == NULL)
|
||||
PG_RETURN_TIMESTAMPTZ(DT_NOEND);
|
||||
|
||||
PG_RETURN_TIMESTAMPTZ(ts_cm_functions->license_end_time());
|
||||
}
|
||||
|
||||
PGDLLEXPORT Datum
|
||||
ts_license_edition(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *edition = NULL;
|
||||
|
||||
switch (TS_CURRENT_LICENSE_TYPE())
|
||||
{
|
||||
case LICENSE_TYPE_APACHE_ONLY:
|
||||
edition = TS_LICENSE_APACHE_ONLY_PRINT_TEXT;
|
||||
break;
|
||||
case LICENSE_TYPE_COMMUNITY:
|
||||
edition = TS_LICENSE_COMMUNITY_PRINT_TEXT;
|
||||
break;
|
||||
case LICENSE_TYPE_ENTERPRISE:
|
||||
edition = TS_LICENSE_ENTERPRISE_PRINT_TEXT;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Invalid license key '%s'", ts_guc_license_key);
|
||||
pg_unreachable();
|
||||
}
|
||||
|
||||
PG_RETURN_TEXT_P(cstring_to_text(edition));
|
||||
}
|
||||
|
||||
/*
|
||||
* For testing purposes we occasionally need the ability to set the license to
|
||||
* Apache only. This function allows us to bypass the test that usually disables
|
||||
* that.
|
||||
*/
|
||||
PGDLLEXPORT Datum
|
||||
ts_allow_downgrade_to_apache(PG_FUNCTION_ARGS)
|
||||
{
|
||||
downgrade_to_apache_enabled = true;
|
||||
PG_RETURN_VOID();
|
||||
if (load_enabled && license_type_of(newval) == LICENSE_TIMESCALE)
|
||||
tsl_module_init();
|
||||
}
|
||||
|
@ -13,61 +13,22 @@
|
||||
#include <export.h>
|
||||
#include <guc.h>
|
||||
|
||||
/*
|
||||
* The license for using only Apache features is 'ApacheOnly'
|
||||
*
|
||||
* For documentation on other license types,
|
||||
* and information on the license format,
|
||||
* see tsl/src/license.c
|
||||
* (NB: This requires accepting LICENSE-TIMESCALE)
|
||||
*/
|
||||
typedef enum LicenseType
|
||||
{
|
||||
LICENSE_TYPE_APACHE_ONLY = 'A',
|
||||
LICENSE_TYPE_COMMUNITY = 'C',
|
||||
LICENSE_TYPE_ENTERPRISE = 'E',
|
||||
} LicenseType;
|
||||
|
||||
#define TS_APACHE_ONLY_LICENSE "ApacheOnly"
|
||||
#define TS_COMMUNITY_LICENSE "CommunityLicense"
|
||||
#define TS_LICENSE_APACHE "apache"
|
||||
#define TS_LICENSE_TIMESCALE "timescale"
|
||||
|
||||
/*
|
||||
* If compiled with APACHE_ONLY, default to using only Apache code.
|
||||
*/
|
||||
#ifdef APACHE_ONLY
|
||||
#define TS_DEFAULT_LICENSE TS_APACHE_ONLY_LICENSE
|
||||
#define TS_LICENSE_DEFAULT TS_LICENSE_APACHE
|
||||
#else
|
||||
#define TS_DEFAULT_LICENSE TS_COMMUNITY_LICENSE
|
||||
#define TS_LICENSE_DEFAULT TS_LICENSE_TIMESCALE
|
||||
#endif
|
||||
|
||||
#define TS_LICENSE_TYPE(license) license[0]
|
||||
extern bool ts_license_guc_check_hook(char **newval, void **extra, GucSource source);
|
||||
extern void ts_license_guc_assign_hook(const char *newval, void *extra);
|
||||
|
||||
#define TS_LICENSE_TYPE_IS_VALID(license) \
|
||||
(TS_LICENSE_TYPE(license) == LICENSE_TYPE_APACHE_ONLY || \
|
||||
TS_LICENSE_TYPE(license) == LICENSE_TYPE_COMMUNITY || \
|
||||
TS_LICENSE_TYPE(license) == LICENSE_TYPE_ENTERPRISE)
|
||||
extern TSDLLEXPORT void ts_license_enable_module_loading(void);
|
||||
extern bool ts_license_is_apache(void);
|
||||
|
||||
#define TS_LICENSE_IS_APACHE_ONLY(license) (TS_LICENSE_TYPE(license) == LICENSE_TYPE_APACHE_ONLY)
|
||||
|
||||
#define TS_CURRENT_LICENSE_TYPE() TS_LICENSE_TYPE(ts_guc_license_key)
|
||||
|
||||
#define TS_CURRENT_LICENSE_IS_APACHE_ONLY() TS_LICENSE_IS_APACHE_ONLY(ts_guc_license_key)
|
||||
/*
|
||||
* guc updating happens in two parts:
|
||||
* 1. The new guc value is validated, and any fallible code is run, but no
|
||||
* externally-visible changes are performed
|
||||
* 2. The guc is set, and the externally-visible changes are performed
|
||||
*
|
||||
* This means that `ts_license_update_check` should not actually change anything
|
||||
* except for its `extra` parameter. (We cheat a little since in that we might
|
||||
* load a dynamic library during the `ts_license_update_check`, but we don't
|
||||
* consider that change to be visible.)
|
||||
*/
|
||||
|
||||
/* Each of these functions takes a LicenseUpdateExtra for their extra param */
|
||||
extern bool ts_license_update_check(char **newval, void **extra, GucSource source);
|
||||
extern void ts_license_on_assign(const char *newval, void *extra);
|
||||
|
||||
extern void TSDLLEXPORT ts_license_enable_module_loading(void);
|
||||
|
||||
#endif /* LICENSE_GUC */
|
||||
#endif /* TIMESCALEDB_LICENSE_GUC_H */
|
||||
|
@ -59,6 +59,7 @@
|
||||
#define REQ_RELATED_EXTENSIONS "related_extensions"
|
||||
#define REQ_METADATA "db_metadata"
|
||||
#define REQ_LICENSE_EDITION_APACHE "apache_only"
|
||||
#define REQ_LICENSE_EDITION_COMMUNITY "community"
|
||||
#define REQ_TS_LAST_TUNE_TIME "last_tuned_time"
|
||||
#define REQ_TS_LAST_TUNE_VERSION "last_tuned_version"
|
||||
#define REQ_INSTANCE_METADATA "instance_metadata"
|
||||
@ -376,22 +377,21 @@ build_version_body(void)
|
||||
pushJsonbValue(&parse_state, WJB_KEY, &ext_key);
|
||||
add_related_extensions(parse_state);
|
||||
|
||||
if (TS_CURRENT_LICENSE_IS_APACHE_ONLY())
|
||||
{
|
||||
/* license */
|
||||
license_info_key.type = jbvString;
|
||||
license_info_key.val.string.val = REQ_LICENSE_INFO;
|
||||
license_info_key.val.string.len = strlen(REQ_LICENSE_INFO);
|
||||
pushJsonbValue(&parse_state, WJB_KEY, &license_info_key);
|
||||
pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL);
|
||||
/* license */
|
||||
license_info_key.type = jbvString;
|
||||
license_info_key.val.string.val = REQ_LICENSE_INFO;
|
||||
license_info_key.val.string.len = strlen(REQ_LICENSE_INFO);
|
||||
pushJsonbValue(&parse_state, WJB_KEY, &license_info_key);
|
||||
pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL);
|
||||
if (ts_license_is_apache())
|
||||
ts_jsonb_add_str(parse_state, REQ_LICENSE_EDITION, REQ_LICENSE_EDITION_APACHE);
|
||||
pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* add license and distributed database fields */
|
||||
ts_jsonb_add_str(parse_state, REQ_LICENSE_EDITION, REQ_LICENSE_EDITION_COMMUNITY);
|
||||
pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL);
|
||||
|
||||
/* add distributed database fields */
|
||||
if (!ts_license_is_apache())
|
||||
ts_cm_functions->add_tsl_telemetry_info(&parse_state);
|
||||
}
|
||||
|
||||
/* add tuned info, which is optional */
|
||||
if (ts_last_tune_time != NULL)
|
||||
|
@ -1,114 +0,0 @@
|
||||
-- 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
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
current_license_key
|
||||
---------------------
|
||||
CommunityLicense
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
\unset ECHO
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
ERROR: invalid value for parameter "timescaledb.license_key": "ApacheOnly"
|
||||
DETAIL: Cannot downgrade a running session to Apache Only.
|
||||
HINT: change the license in the configuration file
|
||||
SELECT allow_downgrade_to_apache();
|
||||
allow_downgrade_to_apache
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
select * from timescaledb_information.license;
|
||||
edition | expired | expiration_time
|
||||
---------+---------+-----------------
|
||||
apache | f | infinity
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.current_db_set_license_key('CommunityLicense');
|
||||
current_db_set_license_key
|
||||
----------------------------
|
||||
CommunityLicense
|
||||
(1 row)
|
||||
|
||||
select * from timescaledb_information.license;
|
||||
edition | expired | expiration_time
|
||||
-----------+---------+-----------------
|
||||
community | f | infinity
|
||||
(1 row)
|
||||
|
||||
create or replace function get_sqlstate(in_text TEXT) RETURNS TEXT AS
|
||||
$$
|
||||
BEGIN
|
||||
BEGIN
|
||||
EXECUTE in_text;
|
||||
EXCEPTION WHEN others THEN GET STACKED DIAGNOSTICS in_text = RETURNED_SQLSTATE;
|
||||
END;
|
||||
RETURN in_text;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE PLPGSQL;
|
||||
SELECT * FROM timescaledb_information.license;
|
||||
edition | expired | expiration_time
|
||||
-----------+---------+-----------------
|
||||
community | f | infinity
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.license_expiration_time();
|
||||
license_expiration_time
|
||||
-------------------------
|
||||
infinity
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.print_license_expiration_info();
|
||||
print_license_expiration_info
|
||||
-------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.license_edition();
|
||||
license_edition
|
||||
-----------------
|
||||
community
|
||||
(1 row)
|
||||
|
||||
SELECT get_sqlstate($$SELECT _timescaledb_internal.current_db_set_license_key('ApacheOnly')$$);
|
||||
get_sqlstate
|
||||
--------------
|
||||
42501
|
||||
(1 row)
|
||||
|
||||
SELECT get_sqlstate($$SHOW timescaledb.license_key;$$);
|
||||
get_sqlstate
|
||||
--------------
|
||||
42501
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_internal.current_license_key();
|
||||
ERROR: must be superuser or a member of pg_read_all_settings to examine the license key
|
||||
drop function get_sqlstate(TEXT);
|
@ -1,20 +0,0 @@
|
||||
-- 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.
|
||||
\unset ECHO
|
||||
SELECT allow_downgrade_to_apache();
|
||||
allow_downgrade_to_apache
|
||||
---------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
SELECT locf(1);
|
||||
ERROR: function "locf" is not supported under the current license "ApacheOnly"
|
||||
HINT: Upgrade to a Timescale-licensed binary to access this free community feature
|
||||
SELECT interpolate(1);
|
||||
ERROR: function "interpolate" is not supported under the current license "ApacheOnly"
|
||||
HINT: Upgrade to a Timescale-licensed binary to access this free community feature
|
||||
SELECT time_bucket_gapfill(1,1,1,1);
|
||||
ERROR: function "time_bucket_gapfill" is not supported under the current license "ApacheOnly"
|
||||
HINT: Upgrade to a Timescale-licensed binary to access this free community feature
|
58
test/expected/license.out
Normal file
58
test/expected/license.out
Normal file
@ -0,0 +1,58 @@
|
||||
-- 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
|
||||
\set ECHO queries
|
||||
SHOW timescaledb.license;
|
||||
timescaledb.license
|
||||
---------------------
|
||||
apache
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.license='apache';
|
||||
ERROR: invalid value for parameter "timescaledb.license": "apache"
|
||||
DETAIL: Cannot change a license in a running session.
|
||||
HINT: Change the license in the configuration file or server command line.
|
||||
SET timescaledb.license='timescale';
|
||||
ERROR: invalid value for parameter "timescaledb.license": "timescale"
|
||||
DETAIL: Cannot change a license in a running session.
|
||||
HINT: Change the license in the configuration file or server command line.
|
||||
SET timescaledb.license='something_else';
|
||||
ERROR: invalid value for parameter "timescaledb.license": "something_else"
|
||||
DETAIL: Unrecognized license type.
|
||||
HINT: Supported license types are 'timescale' or 'apache'.
|
||||
SELECT locf(1);
|
||||
ERROR: function "locf" is not supported under the current "apache" license
|
||||
HINT: Upgrade your license to 'timescale' to use this free community feature.
|
||||
SELECT interpolate(1);
|
||||
ERROR: function "interpolate" is not supported under the current "apache" license
|
||||
HINT: Upgrade your license to 'timescale' to use this free community feature.
|
||||
SELECT time_bucket_gapfill(1,1,1,1);
|
||||
ERROR: function "time_bucket_gapfill" is not supported under the current "apache" license
|
||||
HINT: Upgrade your license to 'timescale' to use this free community feature.
|
||||
CREATE OR REPLACE FUNCTION custom_func(jobid int, args jsonb) RETURNS VOID AS $$
|
||||
DECLARE
|
||||
BEGIN
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
SELECT add_job('custom_func','1h', config:='{"type":"function"}'::jsonb);
|
||||
ERROR: function "add_job" is not supported under the current "apache" license
|
||||
HINT: Upgrade your license to 'timescale' to use this free community feature.
|
||||
DROP FUNCTION custom_func;
|
||||
CREATE TABLE metrics(time timestamptz NOT NULL, value float);
|
||||
SELECT create_hypertable('metrics', 'time');
|
||||
create_hypertable
|
||||
----------------------
|
||||
(1,public,metrics,t)
|
||||
(1 row)
|
||||
|
||||
ALTER TABLE metrics SET (timescaledb.compress);
|
||||
ERROR: functionality not supported under the current "apache" license
|
||||
HINT: Upgrade your license to 'timescale' to use this free community feature.
|
||||
DROP TABLE metrics;
|
@ -542,7 +542,6 @@ WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND
|
||||
timescaledb_information.continuous_aggregates
|
||||
timescaledb_information.jobs
|
||||
timescaledb_information.job_stats
|
||||
timescaledb_information.license
|
||||
timescaledb_information.hypertables
|
||||
_timescaledb_internal.compressed_chunk_stats
|
||||
_timescaledb_internal.hypertable_chunk_local_size
|
||||
@ -550,7 +549,7 @@ WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND
|
||||
_timescaledb_internal.bgw_policy_chunk_stats
|
||||
_timescaledb_internal.bgw_job_stat
|
||||
_timescaledb_catalog.tablespace_id_seq
|
||||
(15 rows)
|
||||
(14 rows)
|
||||
|
||||
-- Make sure we can't run our restoring functions as a normal perm user as that would disable functionality for the whole db
|
||||
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
||||
|
@ -6,6 +6,7 @@ shared_preload_libraries=timescaledb
|
||||
max_worker_processes=16
|
||||
autovacuum=false
|
||||
random_page_cost=1.0
|
||||
timescaledb.license='apache'
|
||||
timescaledb.telemetry_level=off
|
||||
timescaledb.last_tuned='1971-02-03 04:05:06.789012 -0300'
|
||||
timescaledb.last_tuned_version='0.0.1'
|
||||
|
@ -21,7 +21,6 @@ set(TEST_FILES
|
||||
drop_schema.sql
|
||||
dump_meta.sql
|
||||
extension.sql
|
||||
gapfill.sql
|
||||
generated_as_identity.sql
|
||||
grant_hypertable.sql
|
||||
hash.sql
|
||||
@ -51,23 +50,14 @@ set(TEST_FILES
|
||||
vacuum.sql
|
||||
version.sql
|
||||
views.sql
|
||||
license.sql
|
||||
)
|
||||
|
||||
# edition tests need community edition
|
||||
if(NOT APACHE_ONLY)
|
||||
list(APPEND TEST_FILES
|
||||
chunk_utils_compression.sql
|
||||
edition.sql
|
||||
telemetry_community.sql
|
||||
)
|
||||
endif()
|
||||
|
||||
# tests that fail or are unreliable when run in parallel
|
||||
# bgw tests need to run first otherwise they are flaky
|
||||
set(SOLO_TESTS
|
||||
alter
|
||||
alternate_users
|
||||
bgw_db_scheduler
|
||||
bgw_launcher
|
||||
index
|
||||
net
|
||||
@ -79,7 +69,6 @@ set(SOLO_TESTS
|
||||
if (CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
list(APPEND TEST_FILES
|
||||
bgw_launcher.sql
|
||||
bgw_db_scheduler.sql
|
||||
c_unit_tests.sql
|
||||
guc_options.sql
|
||||
loader.sql
|
||||
|
@ -1,63 +0,0 @@
|
||||
-- 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
|
||||
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
|
||||
\unset ECHO
|
||||
\o /dev/null
|
||||
\ir include/test_utils.sql
|
||||
\o
|
||||
\set ECHO queries
|
||||
\set VERBOSITY default
|
||||
|
||||
\c :TEST_DBNAME :ROLE_SUPERUSER
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
SELECT allow_downgrade_to_apache();
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
select * from timescaledb_information.license;
|
||||
|
||||
SELECT _timescaledb_internal.current_db_set_license_key('CommunityLicense');
|
||||
select * from timescaledb_information.license;
|
||||
|
||||
-- Default perm user shouldn't be able to change the license key.
|
||||
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
||||
|
||||
-- Hides error messages in cases where error messages differe between Postgres versions
|
||||
create or replace function get_sqlstate(in_text TEXT) RETURNS TEXT AS
|
||||
$$
|
||||
BEGIN
|
||||
BEGIN
|
||||
EXECUTE in_text;
|
||||
EXCEPTION WHEN others THEN GET STACKED DIAGNOSTICS in_text = RETURNED_SQLSTATE;
|
||||
END;
|
||||
RETURN in_text;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE PLPGSQL;
|
||||
|
||||
--allowed
|
||||
SELECT * FROM timescaledb_information.license;
|
||||
SELECT * FROM _timescaledb_internal.enterprise_enabled();
|
||||
SELECT * FROM _timescaledb_internal.tsl_loaded();
|
||||
SELECT * FROM _timescaledb_internal.license_expiration_time();
|
||||
SELECT * FROM _timescaledb_internal.print_license_expiration_info();
|
||||
SELECT * FROM _timescaledb_internal.license_edition();
|
||||
|
||||
|
||||
--disallowd
|
||||
\set ON_ERROR_STOP 0
|
||||
SELECT get_sqlstate($$SELECT _timescaledb_internal.current_db_set_license_key('ApacheOnly')$$);
|
||||
SELECT get_sqlstate($$SHOW timescaledb.license_key;$$);
|
||||
SELECT * FROM _timescaledb_internal.current_license_key();
|
||||
\set ON_ERROR_STOP 1
|
||||
drop function get_sqlstate(TEXT);
|
@ -1,21 +0,0 @@
|
||||
-- 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.
|
||||
|
||||
\unset ECHO
|
||||
\o /dev/null
|
||||
\ir include/test_utils.sql
|
||||
\o
|
||||
\set ECHO queries
|
||||
\set VERBOSITY default
|
||||
|
||||
\c :TEST_DBNAME :ROLE_SUPERUSER
|
||||
|
||||
SELECT allow_downgrade_to_apache();
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
SELECT locf(1);
|
||||
SELECT interpolate(1);
|
||||
SELECT time_bucket_gapfill(1,1,1,1);
|
||||
\set ON_ERROR_STOP 1
|
@ -27,12 +27,3 @@ BEGIN
|
||||
END IF;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
\c :DBNAME :ROLE_SUPERUSER
|
||||
|
||||
CREATE OR REPLACE FUNCTION allow_downgrade_to_apache()
|
||||
RETURNS VOID
|
||||
AS :MODULE_PATHNAME, 'ts_allow_downgrade_to_apache'
|
||||
LANGUAGE C;
|
||||
|
||||
\c :DBNAME :ROLE_DEFAULT_PERM_USER
|
||||
|
40
test/sql/license.sql
Normal file
40
test/sql/license.sql
Normal file
@ -0,0 +1,40 @@
|
||||
-- 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
|
||||
|
||||
\set ECHO queries
|
||||
\set VERBOSITY default
|
||||
|
||||
SHOW timescaledb.license;
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
|
||||
-- User shouldn't be able to change the license in the session
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.license='apache';
|
||||
SET timescaledb.license='timescale';
|
||||
SET timescaledb.license='something_else';
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- make sure apache license blocks tsl features
|
||||
\set ON_ERROR_STOP 0
|
||||
|
||||
SELECT locf(1);
|
||||
SELECT interpolate(1);
|
||||
SELECT time_bucket_gapfill(1,1,1,1);
|
||||
|
||||
CREATE OR REPLACE FUNCTION custom_func(jobid int, args jsonb) RETURNS VOID AS $$
|
||||
DECLARE
|
||||
BEGIN
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
SELECT add_job('custom_func','1h', config:='{"type":"function"}'::jsonb);
|
||||
DROP FUNCTION custom_func;
|
||||
|
||||
CREATE TABLE metrics(time timestamptz NOT NULL, value float);
|
||||
SELECT create_hypertable('metrics', 'time');
|
||||
ALTER TABLE metrics SET (timescaledb.compress);
|
||||
DROP TABLE metrics;
|
||||
|
||||
\set ON_ERROR_STOP 1
|
@ -20,10 +20,6 @@ set_target_properties(${TESTS_LIB_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_compile_definitions(${TESTS_LIB_NAME} PUBLIC MODULE_NAME=timescaledb)
|
||||
target_include_directories(${TESTS_LIB_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if(APACHE_ONLY)
|
||||
add_definitions(-DAPACHE_ONLY)
|
||||
endif()
|
||||
|
||||
add_subdirectory(bgw)
|
||||
add_subdirectory(net)
|
||||
add_subdirectory(telemetry)
|
||||
|
@ -32,8 +32,8 @@ static post_parse_analyze_hook_type prev_post_parse_analyze_hook;
|
||||
bool ts_extension_invalidate(Oid relid);
|
||||
bool ts_extension_is_loaded(void);
|
||||
void ts_extension_check_version(const char *actual_version);
|
||||
bool ts_license_update_check(char **newval, void **extra, GucSource source);
|
||||
void ts_license_on_assign(const char *newval, void *extra);
|
||||
bool ts_license_guc_check_hook(char **newval, void **extra, GucSource source);
|
||||
void ts_license_guc_assign_hook(const char *newval, void *extra);
|
||||
|
||||
TS_FUNCTION_INFO_V1(ts_post_load_init);
|
||||
|
||||
@ -121,12 +121,12 @@ ts_post_load_init(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
bool
|
||||
ts_license_update_check(char **newval, void **extra, GucSource source)
|
||||
ts_license_guc_check_hook(char **newval, void **extra, GucSource source)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ts_license_on_assign(const char *newval, void *extra)
|
||||
ts_license_guc_assign_hook(const char *newval, void *extra)
|
||||
{
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ set(SOURCES
|
||||
dist_util.c
|
||||
hypertable.c
|
||||
init.c
|
||||
license.c
|
||||
partialize_finalize.c
|
||||
planner.c
|
||||
process_utility.c
|
||||
|
35
tsl/src/README.module.md
Normal file
35
tsl/src/README.module.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Submodule Licensing and Initialization #
|
||||
|
||||
## Loading and Activation ##
|
||||
|
||||
We link module loading and activation to the license GUC itself.
|
||||
We have a single GUC, the license, and load submodules based on
|
||||
what capabilities the license enables, i.e., an `apache` license-key does
|
||||
not load this module, while `timescale` key does. This
|
||||
ensures that the loader "does the right thing" with respect to the license, and
|
||||
a user cannot accidentally activate features they aren't licensed to use.
|
||||
|
||||
The actual loading and activation is done through `check` and `assign` hooks on
|
||||
the license GUC. On `check` we validate the license type
|
||||
and on `assign` we set the capabilities-struct in this module,
|
||||
if needed. The `check` and `assign` functions can be found in
|
||||
[`license_guc.c/h`](/src/license_guc.c) in the Apache-Licensed src.
|
||||
|
||||
### Cross License Functions ###
|
||||
|
||||
To enable binaries which only contain Apache-Licensed code,
|
||||
we dynamically link in Timescale-Licensed code on license activation,
|
||||
and handle all function calls into the module via function pointers.
|
||||
|
||||
The registry in `ts_cm_functions` of type `CrossModuleFunctions` (declared in
|
||||
[`cross_module_fn.h`](/src/cross_module_fn.h) and defined in
|
||||
[`cross_module_fn.c`](/src/cross_module_fn.c)) stores all of the cross-module
|
||||
functions.
|
||||
|
||||
To add a new cross-module function you must:
|
||||
|
||||
- Add a struct member `CrossModuleFunctions.<function name>`.
|
||||
- Add default function to `ts_cm_functions_default` that will be called from the Apache version, usually this function should just call `error_no_default_fn`. **NOTE** Due to function-pointer casting rules, the default function must have the exact same signature as the function pointer; you may _not_ cast another function pointer of another type.
|
||||
- Add the overriding function to `tsl_cm_functions`in `init.c` in this module.
|
||||
|
||||
To call a cross-module functions use `ts_cm_functions-><function name>(args)`.
|
@ -1,82 +0,0 @@
|
||||
# Submodule Licensing and Initialization #
|
||||
|
||||
## Loading and Activation ##
|
||||
|
||||
We link module loading and activation to the license key GUC itself.
|
||||
While keeping multiple GUCs, e.g., one for module loading and one licensing,
|
||||
is tempting, it leads to hairy edge cases when the GUCs disagree.
|
||||
Instead, we have a single GUC, the license key, and load submodules based on
|
||||
what capabilities the license key enables, i.e., an `ApacheOnly` license-key does
|
||||
not load this module, while an `Enterprise` or `Community` key does. This
|
||||
ensures that the loader "does the right thing" with respect to the license, and
|
||||
a user cannot accidentally activate features they aren't licensed to use.
|
||||
|
||||
The actual loading and activation is done through `check` and `assign` hooks on
|
||||
the license key GUC. On `check` we validate the license-key and deserialize it
|
||||
if needed, and on `assign` we set the capabilities-struct in this module,
|
||||
if needed. The `check` and `assign` functions can be found in
|
||||
[`license_guc.c/h`](/src/license_guc.c) in the Apache-Licensed src, and the
|
||||
validation code is in [`license.c/h`](./license.c) in this module.
|
||||
|
||||
### Cross License Functions ###
|
||||
|
||||
To enable binaries which only contain Apache-Licensed code,
|
||||
we dynamically link in Timescale-Licensed code on license key activation,
|
||||
and handle all function calls into the module via function pointers.
|
||||
|
||||
The registry in `ts_cm_functions` of type `CrossModuleFunctions` (declared in
|
||||
[`cross_module_fn.h`](/src/cross_module_fn.h) and defined in
|
||||
[`cross_module_fn.c`](/src/cross_module_fn.c)) stores all of the cross-module
|
||||
functions. To add a new cross-module function you must:
|
||||
- Add a struct member `CrossModuleFunctions.<function name>`.
|
||||
- Add default function to `ts_cm_functions_default` that will be called from the Apache version, usually this function should just call `error_no_default_fn`. **NOTE** Due to function-pointer casting rules, the default function must have the exact same signature as the function pointer; you may _not_ cast another function pointer of another type.
|
||||
- Add the overriding function to `tsl_cm_functions`in `init.c` in this module.
|
||||
|
||||
To call a cross-module functions use `ts_cm_functions-><function name>(args)`.
|
||||
|
||||
## License Validation ##
|
||||
|
||||
We want to keep all license verification code under the Timescale License;
|
||||
code under the Apache License is freely modifiable, and if we include our
|
||||
license-verification code there it may be possible to remove without violating
|
||||
the license (_using_ the module without a valid license would still be in
|
||||
violation).
|
||||
|
||||
Therefore we take a two stage approach to license validation:
|
||||
|
||||
1. In the Apache code we read the first character of the license to determine what type of license this is. This is enough to let us know if we need to load this module at all; ApacheOnly licenses can simply stop after this step.
|
||||
2. If the license requires it (Community and Enterprise), we load this module and use `tsl_license_update_check` to check that the license is valid. This function returns whether a given license-string is a valid license, and optionally returns a deserialized representation of the licenses capabilities.
|
||||
|
||||
## License Format ##
|
||||
|
||||
In a simplified form the BNF for a valid license key is
|
||||
|
||||
```ABNF
|
||||
LicenseKey = ApacheLicense / CommunityLicense / EnterpriseLicenseV1
|
||||
|
||||
ApacheLicense = "A" char*
|
||||
|
||||
CommunityLicense = "C" char*
|
||||
|
||||
EnterpriseLicense = "E" EnterpriseLicenseContents
|
||||
|
||||
EnterpriseLicenseContents = "1" <base64 encoded JSONLicenseInfo>
|
||||
```
|
||||
|
||||
where a `JSONLicenseInfo` describes the capabilities granted by the license.
|
||||
It is JSON corresponding to:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"id":"5D96CF28-87A6-48B5-AE8E-99CF851ABE0C", //uuid identifying the license
|
||||
"type":"trial", // either "trial" or "commercial"
|
||||
"start_time":"2018/10/01", //datetime at which the license was generated
|
||||
"end_time":"2018/10/31" //datetime at which the license expires
|
||||
}
|
||||
```
|
||||
|
||||
That is, an ApacheOnly license is an `'A'` followed by any string, the canonical
|
||||
version of this license is `"ApacheOnly"`; a Community License is `'C'` followed
|
||||
by any string, the canonical version being `'CommunityLicense'`; and an
|
||||
Enterprise License is `"E1"` followed by a base64 encoded JSON containing the
|
||||
capabilities the license enables.
|
@ -14,7 +14,6 @@
|
||||
#include "errors.h"
|
||||
#include "hypertable.h"
|
||||
#include "hypertable_cache.h"
|
||||
#include "license.h"
|
||||
#include "policy_utils.h"
|
||||
#include "utils.h"
|
||||
#include "jsonb_utils.h"
|
||||
|
@ -44,7 +44,6 @@
|
||||
#include "dimension_vector.h"
|
||||
#include "errors.h"
|
||||
#include "job.h"
|
||||
#include "license.h"
|
||||
#include "reorder.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include "bgw_policy/reorder_api.h"
|
||||
#include "errors.h"
|
||||
#include "hypertable.h"
|
||||
#include "license.h"
|
||||
#include "reorder.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "errors.h"
|
||||
#include "hypertable.h"
|
||||
#include "dimension.h"
|
||||
#include "license.h"
|
||||
#include "policy_utils.h"
|
||||
#include "utils.h"
|
||||
#include "jsonb_utils.h"
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "compat.h"
|
||||
#include "scanner.h"
|
||||
#include "scan_iterator.h"
|
||||
#include "license.h"
|
||||
#include "compression_chunk_size.h"
|
||||
|
||||
#define CHUNK_DML_BLOCKER_TRIGGER "chunk_dml_blocker"
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include "hypertable_cache.h"
|
||||
#include "hypertable_compression.h"
|
||||
#include "custom_type_cache.h"
|
||||
#include "license.h"
|
||||
#include "trigger.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "dimension.h"
|
||||
#include "errors.h"
|
||||
#include "hypertable.h"
|
||||
#include "license.h"
|
||||
#include "utils.h"
|
||||
#include "hypertable_cache.h"
|
||||
#include "chunk.h"
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "fdw/fdw.h"
|
||||
#include "hypertable.h"
|
||||
#include "license_guc.h"
|
||||
#include "license.h"
|
||||
#include "nodes/decompress_chunk/planner.h"
|
||||
#include "nodes/gapfill/gapfill.h"
|
||||
#include "partialize_finalize.h"
|
||||
@ -63,10 +62,6 @@ PG_MODULE_MAGIC;
|
||||
extern void PGDLLEXPORT _PG_init(void);
|
||||
extern void PGDLLEXPORT _PG_fini(void);
|
||||
|
||||
static void module_shutdown(void);
|
||||
static bool enterprise_enabled_internal(void);
|
||||
static bool check_tsl_loaded(void);
|
||||
|
||||
static void
|
||||
cache_syscache_invalidate(Datum arg, int cacheid, uint32 hashvalue)
|
||||
{
|
||||
@ -84,12 +79,6 @@ cache_syscache_invalidate(Datum arg, int cacheid, uint32 hashvalue)
|
||||
* Apache codebase.
|
||||
*/
|
||||
CrossModuleFunctions tsl_cm_functions = {
|
||||
.tsl_license_on_assign = tsl_license_on_assign,
|
||||
.enterprise_enabled_internal = enterprise_enabled_internal,
|
||||
.check_tsl_loaded = check_tsl_loaded,
|
||||
.license_end_time = license_end_time,
|
||||
.print_tsl_license_expiration_info_hook = license_print_expiration_info,
|
||||
.module_shutdown_hook = module_shutdown,
|
||||
.add_tsl_telemetry_info = tsl_telemetry_add_info,
|
||||
|
||||
.create_upper_paths_hook = tsl_create_upper_paths_hook,
|
||||
@ -213,40 +202,8 @@ ts_module_init(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BOOL(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently we disallow shutting down this submodule in a live session,
|
||||
* but if we did, this would be the function we'd use.
|
||||
*/
|
||||
static void
|
||||
module_shutdown(void)
|
||||
{
|
||||
_continuous_aggs_cache_inval_fini();
|
||||
|
||||
/*
|
||||
* Order of items should be strict reverse order of ts_module_init. Please
|
||||
* document any exceptions.
|
||||
*/
|
||||
_remote_dist_txn_fini();
|
||||
_remote_connection_cache_fini();
|
||||
_tsl_process_utility_fini();
|
||||
|
||||
ts_cm_functions = &ts_cm_functions_default;
|
||||
}
|
||||
|
||||
/* Informative functions */
|
||||
|
||||
static bool
|
||||
enterprise_enabled_internal(void)
|
||||
{
|
||||
return license_enterprise_enabled();
|
||||
}
|
||||
|
||||
static bool
|
||||
check_tsl_loaded(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
PGDLLEXPORT void
|
||||
_PG_init(void)
|
||||
{
|
||||
@ -255,7 +212,7 @@ _PG_init(void)
|
||||
* timescale library is loaded, after which we enable it from the loader.
|
||||
* In parallel workers the restore shared libraries function will load the
|
||||
* libraries itself, and we bypass the loader, so we need to ensure that
|
||||
* timescale is aware it can use the tsl if needed. It is always safe to
|
||||
* timescale is aware it can use the tsl if needed. It is always safe to
|
||||
* do this here, because if we reach this point, we must have already
|
||||
* loaded the tsl, so we no longer need to worry about its load order
|
||||
* relative to the other libraries.
|
||||
|
@ -1,443 +0,0 @@
|
||||
/*
|
||||
* This file and its contents are licensed under the Timescale License.
|
||||
* Please see the included NOTICE for copyright information and
|
||||
* LICENSE-TIMESCALE for a copy of the license.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A license is a type followed by the license contents, see Readme.module.md
|
||||
* for more detail.
|
||||
*/
|
||||
#include <postgres.h>
|
||||
#include <access/xact.h>
|
||||
#include <common/base64.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <utils/datetime.h>
|
||||
#include <utils/elog.h>
|
||||
#include <utils/jsonb.h>
|
||||
#include <utils/memutils.h>
|
||||
|
||||
#include <export.h>
|
||||
#include <license_guc.h>
|
||||
#include <jsonb_utils.h>
|
||||
|
||||
#include "license.h"
|
||||
|
||||
#define LICENSE_MAX_ID_LEN 40
|
||||
#define LICENSE_MAX_KIND_LEN 16
|
||||
|
||||
typedef struct LicenseInfo
|
||||
{
|
||||
/*
|
||||
* Leaving id and kind a strings for now since they're only used for
|
||||
* telemetry. If we start introspecting on one of them we should switch it
|
||||
* to a more structured type.
|
||||
*/
|
||||
char id[LICENSE_MAX_ID_LEN];
|
||||
char kind[LICENSE_MAX_KIND_LEN];
|
||||
TimestampTz start_time;
|
||||
TimestampTz end_time;
|
||||
bool enterprise_features_enabled;
|
||||
} LicenseInfo;
|
||||
|
||||
static bool license_deserialize_enterprise(char *license, LicenseInfo *license_out);
|
||||
static bool license_info_init_from_base64(char *license_key, LicenseInfo *out);
|
||||
static void license_info_init_from_jsonb(Jsonb *json_license, LicenseInfo *out);
|
||||
static bool validate_license_info(const LicenseInfo *license);
|
||||
|
||||
static LicenseInfo current_license = {
|
||||
.id = { 0 },
|
||||
.kind = { 0 },
|
||||
.end_time = DT_NOBEGIN,
|
||||
.enterprise_features_enabled = false,
|
||||
};
|
||||
|
||||
static const LicenseInfo no_license = {
|
||||
.id = "",
|
||||
.kind = { "" },
|
||||
.start_time = DT_NOBEGIN,
|
||||
.end_time = DT_NOBEGIN,
|
||||
.enterprise_features_enabled = false,
|
||||
};
|
||||
|
||||
static const LicenseInfo community_license = { .id = "",
|
||||
.kind = { "" },
|
||||
.start_time = DT_NOBEGIN,
|
||||
.end_time = DT_NOEND,
|
||||
.enterprise_features_enabled = false };
|
||||
|
||||
static bool printed_license_expiration_warning = false;
|
||||
|
||||
TS_FUNCTION_INFO_V1(tsl_license_update_check);
|
||||
|
||||
Datum
|
||||
tsl_license_update_check(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bool license_deserialized;
|
||||
char *license_key = NULL;
|
||||
LicenseInfo **guc_extra = NULL;
|
||||
LicenseInfo license_info = { { 0 } };
|
||||
|
||||
Assert(!PG_ARGISNULL(0));
|
||||
Assert(!PG_ARGISNULL(1));
|
||||
|
||||
license_key = PG_GETARG_CSTRING(0);
|
||||
guc_extra = (LicenseInfo **) PG_GETARG_POINTER(1);
|
||||
|
||||
license_deserialized = license_deserialize_enterprise(license_key, &license_info);
|
||||
if (guc_extra != NULL)
|
||||
{
|
||||
/*
|
||||
* According to the postgres guc documentation, string `extra`s MUST
|
||||
* be allocated with `malloc`. (postgres attempts to `free` unneeded
|
||||
* guc-extras with the system `free` upon transaction commit, and
|
||||
* there's no guarantee that any MemoryContext uses the correct
|
||||
* allocator.) However, there is a bug in Windows which causes heap
|
||||
* corruption if we try to do that. Instead we currently rerun this
|
||||
* function during the assign, with the assumption that since we
|
||||
* called the function during the check hook it cannot fail now.
|
||||
*/
|
||||
*guc_extra = malloc(sizeof(LicenseInfo));
|
||||
memcpy(*guc_extra, &license_info, sizeof(LicenseInfo));
|
||||
}
|
||||
|
||||
PG_RETURN_BOOL(license_deserialized && validate_license_info(&license_info));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return if a license is valid, optionally outputting the deserialized form.
|
||||
*/
|
||||
static bool
|
||||
license_deserialize_enterprise(char *license_key, LicenseInfo *license_out)
|
||||
{
|
||||
MemoryContext old_ctx;
|
||||
MemoryContext deserialize_ctx;
|
||||
LicenseInfo license_temp = { { 0 } };
|
||||
const LicenseInfo *license_info = NULL;
|
||||
size_t license_key_len = strlen(license_key);
|
||||
|
||||
if (license_key_len < 1)
|
||||
return false;
|
||||
|
||||
switch (TS_LICENSE_TYPE(license_key))
|
||||
{
|
||||
case LICENSE_TYPE_APACHE_ONLY:
|
||||
license_info = &no_license;
|
||||
break;
|
||||
case LICENSE_TYPE_COMMUNITY:
|
||||
license_info = &community_license;
|
||||
break;
|
||||
case LICENSE_TYPE_ENTERPRISE:
|
||||
if (license_key_len < 2)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Second byte of an enterprise license key is the version.
|
||||
* Hardcoding this for now since we only have one version. the
|
||||
* byte corresponding to '0' is reserved in case we need to extend
|
||||
* the length of the version number.
|
||||
*/
|
||||
if (license_key[1] != '1')
|
||||
return false;
|
||||
|
||||
/* create a memory context so all temporary alloctions will be freed
|
||||
* when this is called during initialization, it will not happen
|
||||
* automatically
|
||||
*/
|
||||
deserialize_ctx = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"license deserialize",
|
||||
ALLOCSET_SMALL_SIZES);
|
||||
old_ctx = MemoryContextSwitchTo(deserialize_ctx);
|
||||
if (license_info_init_from_base64(license_key + 2, &license_temp))
|
||||
license_info = &license_temp;
|
||||
|
||||
MemoryContextSwitchTo(old_ctx);
|
||||
MemoryContextDelete(deserialize_ctx);
|
||||
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (NULL == license_info)
|
||||
return false;
|
||||
|
||||
if (license_out != NULL)
|
||||
memmove(license_out, license_info, sizeof(*license_info));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_license_info(const LicenseInfo *license)
|
||||
{
|
||||
if (license->enterprise_features_enabled)
|
||||
{
|
||||
if (strcmp(license->kind, "trial") != 0 && strcmp(license->kind, "commercial") != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timestamp_cmp_internal(license->end_time, license->start_time) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
static char *base64_decode(char *license_key);
|
||||
|
||||
static bool
|
||||
license_info_init_from_base64(char *license_key, LicenseInfo *out)
|
||||
{
|
||||
char *expanded = base64_decode(license_key);
|
||||
|
||||
if (expanded == NULL)
|
||||
return false;
|
||||
|
||||
PG_TRY();
|
||||
{
|
||||
Datum json_key = DirectFunctionCall1(jsonb_in, CStringGetDatum(expanded));
|
||||
|
||||
license_info_init_from_jsonb((Jsonb *) DatumGetPointer(json_key), out);
|
||||
}
|
||||
PG_CATCH();
|
||||
{
|
||||
#ifdef TS_DEBUG
|
||||
EmitErrorReport();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
PG_END_TRY();
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *
|
||||
base64_decode(char *license_key)
|
||||
{
|
||||
int raw_len = strlen(license_key);
|
||||
int decoded_buffer_len = pg_b64_dec_len(raw_len) + 1;
|
||||
char *decoded = palloc(decoded_buffer_len);
|
||||
int decoded_len = pg_b64_decode(license_key, raw_len, decoded);
|
||||
|
||||
if (decoded_len < 0)
|
||||
return NULL;
|
||||
|
||||
Assert(decoded_len < decoded_buffer_len);
|
||||
decoded[decoded_len] = '\0';
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* JSON license decoding
|
||||
*/
|
||||
|
||||
#define ID_FIELD "id"
|
||||
#define KIND_FIELD "kind"
|
||||
#define START_TIME_FIELD "start_time"
|
||||
#define END_TIME_FIELD "end_time"
|
||||
#define FIELD_NOT_FOUND_ERRSTRING "invalid license key for TimescaleDB, could not find field \"%s\""
|
||||
|
||||
static char *json_get_id(Jsonb *license);
|
||||
static char *json_get_kind(Jsonb *license);
|
||||
static TimestampTz json_get_start_time(Jsonb *license);
|
||||
static TimestampTz json_get_end_time(Jsonb *license);
|
||||
static void
|
||||
license_info_init_from_jsonb(Jsonb *json_license, LicenseInfo *out)
|
||||
{
|
||||
char *id_str = json_get_id(json_license);
|
||||
|
||||
if (id_str == NULL)
|
||||
elog(ERROR, "missing id in license key");
|
||||
StrNCpy(out->id, id_str, sizeof(out->id));
|
||||
StrNCpy(out->kind, json_get_kind(json_license), sizeof(out->kind));
|
||||
out->start_time = json_get_start_time(json_license);
|
||||
out->end_time = json_get_end_time(json_license);
|
||||
out->enterprise_features_enabled = true;
|
||||
}
|
||||
|
||||
static char *
|
||||
json_get_id(Jsonb *license)
|
||||
{
|
||||
return ts_jsonb_get_str_field(license, ID_FIELD);
|
||||
}
|
||||
|
||||
static char *
|
||||
json_get_kind(Jsonb *license)
|
||||
{
|
||||
return ts_jsonb_get_str_field(license, KIND_FIELD);
|
||||
}
|
||||
|
||||
static TimestampTz
|
||||
json_get_start_time(Jsonb *license)
|
||||
{
|
||||
bool found = false;
|
||||
TimestampTz start_time = ts_jsonb_get_time_field(license, START_TIME_FIELD, &found);
|
||||
|
||||
if (!found)
|
||||
elog(ERRCODE_FEATURE_NOT_SUPPORTED, FIELD_NOT_FOUND_ERRSTRING, START_TIME_FIELD);
|
||||
return start_time;
|
||||
}
|
||||
|
||||
static TimestampTz
|
||||
json_get_end_time(Jsonb *license)
|
||||
{
|
||||
bool found = false;
|
||||
TimestampTz end_time = ts_jsonb_get_time_field(license, END_TIME_FIELD, &found);
|
||||
|
||||
if (!found)
|
||||
elog(ERRCODE_FEATURE_NOT_SUPPORTED, FIELD_NOT_FOUND_ERRSTRING, END_TIME_FIELD);
|
||||
return end_time;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*****************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
static bool license_info_is_expired(const LicenseInfo *license);
|
||||
|
||||
/*
|
||||
* We don't want to expose the LicenseInfo struct to the Apache code, so we use
|
||||
* this intermediate function to translate from the `void *` to `LicenseInfo *`.
|
||||
* There are some cases where the guc extra may not be set, so if this function
|
||||
* receives a NULL license, it will translate that to `no_license`
|
||||
*/
|
||||
void
|
||||
tsl_license_on_assign(const char *newval, const void *license)
|
||||
{
|
||||
/*
|
||||
* A NULL extra means that we're reverting to the top of the license guc
|
||||
* stack.
|
||||
*/
|
||||
if (license == NULL)
|
||||
{
|
||||
license_switch_to(&no_license);
|
||||
return;
|
||||
}
|
||||
|
||||
license_switch_to(license);
|
||||
}
|
||||
|
||||
void
|
||||
license_switch_to(const LicenseInfo *license)
|
||||
{
|
||||
Assert(license != NULL);
|
||||
current_license = *license;
|
||||
}
|
||||
|
||||
bool
|
||||
license_info_is_expired(const LicenseInfo *license)
|
||||
{
|
||||
TimestampTz current_time = GetCurrentTransactionStartTimestamp();
|
||||
|
||||
return timestamp_cmp_internal(license->end_time, current_time) < 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*****************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
/* Getters for current license */
|
||||
|
||||
bool
|
||||
license_is_expired()
|
||||
{
|
||||
return license_info_is_expired(¤t_license);
|
||||
}
|
||||
|
||||
bool
|
||||
license_enterprise_enabled(void)
|
||||
{
|
||||
return current_license.enterprise_features_enabled;
|
||||
}
|
||||
|
||||
char *
|
||||
license_kind_str(void)
|
||||
{
|
||||
return current_license.kind;
|
||||
}
|
||||
|
||||
char *
|
||||
license_id_str(void)
|
||||
{
|
||||
return current_license.id;
|
||||
}
|
||||
|
||||
TimestampTz
|
||||
license_start_time(void)
|
||||
{
|
||||
return current_license.start_time;
|
||||
}
|
||||
|
||||
TimestampTz
|
||||
license_end_time(void)
|
||||
{
|
||||
return current_license.end_time;
|
||||
}
|
||||
|
||||
void
|
||||
license_enforce_enterprise_enabled(void)
|
||||
{
|
||||
if (!license_enterprise_enabled())
|
||||
elog(ERROR, "cannot execute an enterprise function with an invalid enterprise license");
|
||||
}
|
||||
|
||||
void
|
||||
license_print_expiration_info(void)
|
||||
{
|
||||
if (!TIMESTAMP_NOT_FINITE(current_license.end_time) &&
|
||||
current_license.enterprise_features_enabled)
|
||||
{
|
||||
ereport(NOTICE,
|
||||
(errcode(ERRCODE_WARNING),
|
||||
errmsg("your Timescale Enterprise License expires on %s",
|
||||
DatumGetCString(
|
||||
DirectFunctionCall1(timestamptz_out, current_license.end_time)))));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
printed_license_expiration_warning = false;
|
||||
license_print_expiration_warning_if_needed();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
license_print_expiration_warning_if_needed(void)
|
||||
{
|
||||
if (printed_license_expiration_warning)
|
||||
return;
|
||||
|
||||
printed_license_expiration_warning = true;
|
||||
|
||||
if (license_is_expired())
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Timescale License expired"),
|
||||
errhint("Your license expired on %s. Renew your license to continue using "
|
||||
"enterprise features.",
|
||||
DatumGetCString(
|
||||
DirectFunctionCall1(timestamptz_out,
|
||||
TimestampTzGetDatum(current_license.end_time))))));
|
||||
else
|
||||
{
|
||||
Interval week = {
|
||||
.day = 7,
|
||||
};
|
||||
TimestampTz warn_after =
|
||||
DatumGetTimestampTz(DirectFunctionCall2(timestamptz_mi_interval,
|
||||
TimestampTzGetDatum(current_license.end_time),
|
||||
IntervalPGetDatum(&week)));
|
||||
|
||||
if (timestamp_cmp_internal(GetCurrentTransactionStartTimestamp(), warn_after) >= 0)
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_WARNING),
|
||||
errmsg("your Timescale Enterprise License expires on %s",
|
||||
DatumGetCString(DirectFunctionCall1(timestamptz_out,
|
||||
TimestampTzGetDatum(
|
||||
current_license.end_time))))));
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* This file and its contents are licensed under the Timescale License.
|
||||
* Please see the included NOTICE for copyright information and
|
||||
* LICENSE-TIMESCALE for a copy of the license.
|
||||
*/
|
||||
#ifndef TIMESCALEDB_TSL_LICENSE_H
|
||||
#define TIMESCALEDB_TSL_LICENSE_H
|
||||
|
||||
#include <postgres.h>
|
||||
#include <c.h>
|
||||
#include <export.h>
|
||||
#include <utils/jsonb.h>
|
||||
#include <utils/timestamp.h>
|
||||
|
||||
typedef struct LicenseInfo LicenseInfo;
|
||||
|
||||
extern PGDLLEXPORT Datum tsl_license_update_check(PG_FUNCTION_ARGS);
|
||||
extern void tsl_license_on_assign(const char *newval, const void *license);
|
||||
extern void license_switch_to(const LicenseInfo *license);
|
||||
|
||||
bool license_is_expired(void);
|
||||
bool license_enterprise_enabled(void);
|
||||
char *license_kind_str(void);
|
||||
char *license_id_str(void);
|
||||
TimestampTz license_start_time(void);
|
||||
TimestampTz license_end_time(void);
|
||||
void license_enforce_enterprise_enabled(void);
|
||||
void license_print_expiration_info(void);
|
||||
void license_print_expiration_warning_if_needed(void);
|
||||
|
||||
#endif /* TIMESCALEDB_TSL_LICENSE_H */
|
@ -7,7 +7,6 @@
|
||||
#include <fmgr.h>
|
||||
#include <utils/lsyscache.h>
|
||||
|
||||
#include "license.h"
|
||||
#include "time_bucket.h"
|
||||
#include "nodes/gapfill/gapfill.h"
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
#include <optimizer/optimizer.h>
|
||||
#endif
|
||||
|
||||
#include "license.h"
|
||||
#include "nodes/gapfill/gapfill.h"
|
||||
#include "nodes/gapfill/planner.h"
|
||||
#include "nodes/gapfill/exec.h"
|
||||
|
@ -62,7 +62,6 @@
|
||||
#include "chunk_index.h"
|
||||
#include "hypertable_cache.h"
|
||||
#include "indexing.h"
|
||||
#include "license.h"
|
||||
#include "reorder.h"
|
||||
|
||||
extern void timescale_reorder_rel(Oid tableOid, Oid indexOid, bool verbose, Oid wait_id,
|
||||
@ -126,8 +125,6 @@ tsl_move_chunk(PG_FUNCTION_ARGS)
|
||||
/* used for debugging purposes only see finish_heap_swaps */
|
||||
Oid wait_id = PG_NARGS() < 6 || PG_ARGISNULL(5) ? InvalidOid : PG_GETARG_OID(5);
|
||||
|
||||
license_print_expiration_warning_if_needed();
|
||||
|
||||
/*
|
||||
* Allow move in transactions for testing purposes only
|
||||
*/
|
||||
|
@ -8,18 +8,10 @@
|
||||
#include <utils/builtins.h>
|
||||
#include <jsonb_utils.h>
|
||||
#include "hypertable.h"
|
||||
#include "license.h"
|
||||
#include "telemetry/telemetry.h"
|
||||
#include "dist_util.h"
|
||||
#include "data_node.h"
|
||||
|
||||
#define LICENSE_EDITION_COMMUNITY "community"
|
||||
#define LICENSE_EDITION_ENTERPRISE "enterprise"
|
||||
#define LICENSE_KIND_KEY "kind"
|
||||
#define LICENSE_ID_KEY "id"
|
||||
#define LICENSE_START_TIME_KEY "start_time"
|
||||
#define LICENSE_END_TIME_KEY "end_time"
|
||||
|
||||
#define DISTRIBUTED_DB_KEY "distributed_db"
|
||||
#define DISTRIBUTED_MEMBER_KEY "distributed_member"
|
||||
#define NUM_DISTRIBUTED_HYPERTABLES_KEY "num_distributed_hypertables"
|
||||
@ -27,26 +19,6 @@
|
||||
#define NUM_REPLICATED_DISTRIBUTED_HYPERTABLES_KEY "num_replicated_distributed_hypertables"
|
||||
#define NUM_DATA_NODES_KEY "num_data_nodes"
|
||||
|
||||
static void
|
||||
tsl_telemetry_add_license_info(JsonbParseState *parse_state)
|
||||
{
|
||||
if (!license_enterprise_enabled())
|
||||
ts_jsonb_add_str(parse_state, REQ_LICENSE_EDITION, LICENSE_EDITION_COMMUNITY);
|
||||
else
|
||||
{
|
||||
char *start_time = DatumGetCString(
|
||||
DirectFunctionCall1(timestamptz_out, TimestampTzGetDatum(license_start_time())));
|
||||
char *end_time = DatumGetCString(
|
||||
DirectFunctionCall1(timestamptz_out, TimestampTzGetDatum(license_end_time())));
|
||||
|
||||
ts_jsonb_add_str(parse_state, REQ_LICENSE_EDITION, LICENSE_EDITION_ENTERPRISE);
|
||||
ts_jsonb_add_str(parse_state, LICENSE_KIND_KEY, license_kind_str());
|
||||
ts_jsonb_add_str(parse_state, LICENSE_ID_KEY, license_id_str());
|
||||
ts_jsonb_add_str(parse_state, LICENSE_START_TIME_KEY, start_time);
|
||||
ts_jsonb_add_str(parse_state, LICENSE_END_TIME_KEY, end_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tsl_telemetry_add_distributed_database_info(JsonbParseState *parse_state)
|
||||
{
|
||||
@ -81,27 +53,14 @@ tsl_telemetry_add_distributed_database_info(JsonbParseState *parse_state)
|
||||
void
|
||||
tsl_telemetry_add_info(JsonbParseState **parse_state)
|
||||
{
|
||||
JsonbValue license_key;
|
||||
JsonbValue distributed_db_key;
|
||||
|
||||
/* license */
|
||||
license_key.type = jbvString;
|
||||
license_key.val.string.val = REQ_LICENSE_INFO;
|
||||
license_key.val.string.len = strlen(REQ_LICENSE_INFO);
|
||||
pushJsonbValue(parse_state, WJB_KEY, &license_key);
|
||||
/* distributed_db */
|
||||
distributed_db_key.type = jbvString;
|
||||
distributed_db_key.val.string.val = DISTRIBUTED_DB_KEY;
|
||||
distributed_db_key.val.string.len = strlen(DISTRIBUTED_DB_KEY);
|
||||
pushJsonbValue(parse_state, WJB_KEY, &distributed_db_key);
|
||||
pushJsonbValue(parse_state, WJB_BEGIN_OBJECT, NULL);
|
||||
tsl_telemetry_add_license_info(*parse_state);
|
||||
tsl_telemetry_add_distributed_database_info(*parse_state);
|
||||
pushJsonbValue(parse_state, WJB_END_OBJECT, NULL);
|
||||
|
||||
{
|
||||
JsonbValue distributed_db_key;
|
||||
|
||||
/* distributed_db */
|
||||
distributed_db_key.type = jbvString;
|
||||
distributed_db_key.val.string.val = DISTRIBUTED_DB_KEY;
|
||||
distributed_db_key.val.string.len = strlen(DISTRIBUTED_DB_KEY);
|
||||
pushJsonbValue(parse_state, WJB_KEY, &distributed_db_key);
|
||||
pushJsonbValue(parse_state, WJB_BEGIN_OBJECT, NULL);
|
||||
tsl_telemetry_add_distributed_database_info(*parse_state);
|
||||
pushJsonbValue(parse_state, WJB_END_OBJECT, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
--
|
||||
-- Setup
|
||||
--
|
@ -1,6 +1,6 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
--------------------------------------------------------------------------
|
||||
-- show_chunks and drop_chunks functions on a compressed table
|
||||
-- (issue https://github.com/timescale/timescaledb/issues/1535)
|
@ -150,7 +150,6 @@ FROM pg_tables WHERE tablespace = 'tablespace1';
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
SELECT move_chunk(chunk=>:'COMPRESSED_CHUNK_NAME', destination_tablespace=>'tablespace1', index_destination_tablespace=>'tablespace1', reorder_index=>'_timescaledb_internal."compress_hyper_2_28_chunk__compressed_hypertable_2_b__ts_meta_s"');
|
||||
WARNING: Timescale License expired
|
||||
ERROR: cannot directly move internal compression data
|
||||
\set ON_ERROR_STOP 1
|
||||
-- ensure that both compressed and uncompressed chunks moved
|
||||
|
@ -263,7 +263,6 @@ RESET client_min_messages;
|
||||
CREATE TABLESPACE tablespace1 OWNER :ROLE_CLUSTER_SUPERUSER LOCATION :TEST_TABLESPACE1_PATH;
|
||||
\set ON_ERROR_STOP 0
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._dist_hyper_1_4_chunk', destination_tablespace=>'tablespace1', index_destination_tablespace=>'tablespace1', reorder_index=>'disttable_time_idx', verbose=>TRUE);
|
||||
WARNING: Timescale License expired
|
||||
ERROR: move_chunk() and reorder_chunk() cannot be used with distributed hypertables
|
||||
SELECT reorder_chunk('_timescaledb_internal._dist_hyper_1_4_chunk', verbose => TRUE);
|
||||
ERROR: move_chunk() and reorder_chunk() cannot be used with distributed hypertables
|
||||
|
16
tsl/test/expected/license.out
Normal file
16
tsl/test/expected/license.out
Normal file
@ -0,0 +1,16 @@
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
||||
SHOW timescaledb.license;
|
||||
timescaledb.license
|
||||
---------------------
|
||||
timescale
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
@ -217,7 +217,6 @@ SELECT * FROM test.show_indexes('_timescaledb_internal._hyper_1_2_chunk');
|
||||
\set ON_ERROR_STOP 0
|
||||
-- cannot move a chunk with no reorder index on first call
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>'tablespace1', index_destination_tablespace=>'tablespace1');
|
||||
WARNING: Timescale License expired
|
||||
ERROR: there is no previously clustered index for table "_hyper_1_2_chunk"
|
||||
-- cannot move a chunk without a destination tablespace set
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>NULL, index_destination_tablespace=>'tablespace1', reorder_index=>'_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx');
|
||||
@ -249,7 +248,6 @@ END;
|
||||
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
||||
-- must be hypertable owner to move a chunk
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>'tablespace2', index_destination_tablespace=>'tablespace2', reorder_index=>'_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx', verbose=>TRUE);
|
||||
WARNING: Timescale License expired
|
||||
ERROR: must be owner of hypertable "cluster_test"
|
||||
\set ON_ERROR_STOP 1
|
||||
-- grant create permissions on tablespace2 so we can use it later
|
||||
@ -257,7 +255,6 @@ GRANT CREATE ON TABLESPACE tablespace2 TO :ROLE_DEFAULT_PERM_USER;
|
||||
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
||||
-- move with chunk index for reorder
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>'tablespace1', index_destination_tablespace=>'tablespace1', reorder_index=>'_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx', verbose=>TRUE);
|
||||
WARNING: Timescale License expired
|
||||
INFO: reordering "_timescaledb_internal._hyper_1_2_chunk" using sequential scan and sort
|
||||
INFO: "_hyper_1_2_chunk": found 0 removable, 5 nonremovable row versions in 1 pages
|
||||
move_chunk
|
||||
|
@ -214,7 +214,6 @@ SELECT * FROM test.show_indexes('_timescaledb_internal._hyper_1_2_chunk');
|
||||
\set ON_ERROR_STOP 0
|
||||
-- cannot move a chunk with no reorder index on first call
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>'tablespace1', index_destination_tablespace=>'tablespace1');
|
||||
WARNING: Timescale License expired
|
||||
ERROR: there is no previously clustered index for table "_hyper_1_2_chunk"
|
||||
-- cannot move a chunk without a destination tablespace set
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>NULL, index_destination_tablespace=>'tablespace1', reorder_index=>'_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx');
|
||||
@ -246,7 +245,6 @@ END;
|
||||
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
|
||||
-- must be hypertable owner to move a chunk
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>'tablespace2', index_destination_tablespace=>'tablespace2', reorder_index=>'_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx', verbose=>TRUE);
|
||||
WARNING: Timescale License expired
|
||||
ERROR: must be owner of hypertable "cluster_test"
|
||||
\set ON_ERROR_STOP 1
|
||||
-- grant create permissions on tablespace2 so we can use it later
|
||||
@ -254,7 +252,6 @@ GRANT CREATE ON TABLESPACE tablespace2 TO :ROLE_DEFAULT_PERM_USER;
|
||||
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
|
||||
-- move with chunk index for reorder
|
||||
SELECT move_chunk(chunk=>'_timescaledb_internal._hyper_1_2_chunk', destination_tablespace=>'tablespace1', index_destination_tablespace=>'tablespace1', reorder_index=>'_timescaledb_internal._hyper_1_2_chunk_cluster_test_time_idx', verbose=>TRUE);
|
||||
WARNING: Timescale License expired
|
||||
INFO: reordering "_timescaledb_internal._hyper_1_2_chunk" using sequential scan and sort
|
||||
INFO: "_hyper_1_2_chunk": found 0 removable, 5 nonremovable row versions in 1 pages
|
||||
move_chunk
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
--telemetry tests that require a community license
|
||||
SELECT json_object_field(get_telemetry_report(always_display_report := true)::json,'num_continuous_aggs');
|
||||
json_object_field
|
@ -11,7 +11,7 @@ log_line_prefix='%u [%p] '
|
||||
|
||||
# This section adds additional options required by TSL
|
||||
# Note any changes here require updates to appveyor.yml
|
||||
timescaledb.license_key='E1eyJlbmRfdGltZSI6IjIwMTgtMTAtMDEgKzAwMDAiLCAic3RhcnRfdGltZSI6IjIwMTgtMDktMDEgKzAwMDAiLCAiaWQiOiI0OTBGQjI2MC1BMjkyLTRBRDktOUFBMi0wMzYwODM1NzkxQjgiLCAia2luZCI6InRyaWFsIn0K'
|
||||
timescaledb.license='timescale'
|
||||
log_line_prefix='%u [%p] %d '
|
||||
# PG12 changed the default rounding behavior of floating point
|
||||
# numbers. Setting extra_float_digits=0 retains the old behavior which
|
||||
|
@ -1,131 +0,0 @@
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select * from timescaledb_information.license;
|
||||
edition | expired | expiration_time
|
||||
------------+---------+------------------------------
|
||||
enterprise | t | Sun Sep 30 17:00:00 2018 PDT
|
||||
(1 row)
|
||||
|
||||
-- changing licenses requires superuser privleges
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.license_key='CommunityLicense';
|
||||
ERROR: permission denied to set parameter "timescaledb.license_key"
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
ERROR: must be superuser or a member of pg_read_all_settings to examine the license key
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 1
|
||||
\c :TEST_DBNAME :ROLE_SUPERUSER
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
current_license_key
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
E1eyJlbmRfdGltZSI6IjIwMTgtMTAtMDEgKzAwMDAiLCAic3RhcnRfdGltZSI6IjIwMTgtMDktMDEgKzAwMDAiLCAiaWQiOiI0OTBGQjI2MC1BMjkyLTRBRDktOUFBMi0wMzYwODM1NzkxQjgiLCAia2luZCI6InRyaWFsIn0K
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
ERROR: invalid value for parameter "timescaledb.license_key": "ApacheOnly"
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
current_license_key
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
E1eyJlbmRfdGltZSI6IjIwMTgtMTAtMDEgKzAwMDAiLCAic3RhcnRfdGltZSI6IjIwMTgtMDktMDEgKzAwMDAiLCAiaWQiOiI0OTBGQjI2MC1BMjkyLTRBRDktOUFBMi0wMzYwODM1NzkxQjgiLCAia2luZCI6InRyaWFsIn0K
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 1
|
||||
SET timescaledb.license_key='CommunityLicense';
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
current_license_key
|
||||
---------------------
|
||||
CommunityLicense
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
select * from timescaledb_information.license;
|
||||
edition | expired | expiration_time
|
||||
-----------+---------+-----------------
|
||||
community | f | infinity
|
||||
(1 row)
|
||||
|
||||
SET timescaledb.license_key=Default;
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
current_license_key
|
||||
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
E1eyJlbmRfdGltZSI6IjIwMTgtMTAtMDEgKzAwMDAiLCAic3RhcnRfdGltZSI6IjIwMTgtMDktMDEgKzAwMDAiLCAiaWQiOiI0OTBGQjI2MC1BMjkyLTRBRDktOUFBMi0wMzYwODM1NzkxQjgiLCAia2luZCI6InRyaWFsIn0K
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
tsl_loaded
|
||||
------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
enterprise_enabled
|
||||
--------------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
select * from timescaledb_information.license;
|
||||
edition | expired | expiration_time
|
||||
------------+---------+------------------------------
|
||||
enterprise | t | Sun Sep 30 17:00:00 2018 PDT
|
||||
(1 row)
|
||||
|
@ -2,7 +2,6 @@ set(TEST_FILES_SHARED
|
||||
constraint_exclusion_prepared.sql
|
||||
decompress_placeholdervar.sql
|
||||
ordered_append_join.sql
|
||||
license.sql
|
||||
)
|
||||
|
||||
set(TEST_TEMPLATES_SHARED
|
||||
|
@ -1,40 +0,0 @@
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
select * from timescaledb_information.license;
|
||||
|
||||
-- changing licenses requires superuser privleges
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.license_key='CommunityLicense';
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
\c :TEST_DBNAME :ROLE_SUPERUSER
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
SET timescaledb.license_key='ApacheOnly';
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
|
||||
SET timescaledb.license_key='CommunityLicense';
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
select * from timescaledb_information.license;
|
||||
|
||||
SET timescaledb.license_key=Default;
|
||||
SELECT _timescaledb_internal.current_license_key();
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
||||
SELECT _timescaledb_internal.enterprise_enabled();
|
||||
select * from timescaledb_information.license;
|
@ -15,8 +15,10 @@ set(TEST_FILES
|
||||
)
|
||||
|
||||
set(TEST_FILES_DEBUG
|
||||
bgw_db_scheduler.sql
|
||||
bgw_reorder_drop_chunks.sql
|
||||
compress_bgw_reorder_drop_chunks.sql
|
||||
chunk_utils_compression.sql
|
||||
compression_algos.sql
|
||||
compression_ddl.sql
|
||||
compression_errors.sql
|
||||
@ -55,9 +57,11 @@ set(TEST_FILES_DEBUG
|
||||
remote_txn_id.sql
|
||||
remote_txn_resolve.sql
|
||||
remote_txn.sql
|
||||
telemetry_community.sql
|
||||
telemetry_distributed.sql
|
||||
transparent_decompression_queries.sql
|
||||
tsl_tables.sql
|
||||
license.sql
|
||||
)
|
||||
|
||||
set(TEST_TEMPLATES
|
||||
@ -109,6 +113,7 @@ endif()
|
||||
# dist_views.sql sets some global information, so do not run it
|
||||
# in parallel
|
||||
set(SOLO_TESTS
|
||||
bgw_db_scheduler
|
||||
bgw_reorder_drop_chunks
|
||||
chunk_api
|
||||
compress_bgw_reorder_drop_chunks
|
||||
|
@ -1,6 +1,6 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
|
||||
--
|
||||
-- Setup
|
@ -1,6 +1,6 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
-- show_chunks and drop_chunks functions on a compressed table
|
8
tsl/test/sql/license.sql
Normal file
8
tsl/test/sql/license.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
|
||||
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER;
|
||||
|
||||
SHOW timescaledb.license;
|
||||
SELECT _timescaledb_internal.tsl_loaded();
|
@ -1,6 +1,6 @@
|
||||
-- This file and its contents are licensed under the Apache License 2.0.
|
||||
-- This file and its contents are licensed under the Timescale License.
|
||||
-- Please see the included NOTICE for copyright information and
|
||||
-- LICENSE-APACHE for a copy of the license.
|
||||
-- LICENSE-TIMESCALE for a copy of the license.
|
||||
|
||||
--telemetry tests that require a community license
|
||||
|
Loading…
x
Reference in New Issue
Block a user