Skip to content

Commit

Permalink
Load hypercore handler eagerly
Browse files Browse the repository at this point in the history
If a crossmodule wrapper function is called as part of a query on a
fresh backend, it will generate a license error even if the license is
set correctly. This happens because the module load happens very late
(in `post_parse_analyze_hook`) but the function call is quite early.

This is fixed by eagerly loading the hypercore handler.
  • Loading branch information
mkindahl committed Feb 14, 2025
1 parent 5a936d5 commit 9d2866b
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .unreleased/pr_7711
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixes: #7711 License error when using hypercore handler
Thanks: @jflambert for reporting an bug on license error show in autovacuum
20 changes: 19 additions & 1 deletion src/cross_module_fn.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,24 @@ process_cagg_try_repair(PG_FUNCTION_ARGS)
pg_unreachable();
}

/*
* Ensure that the TSL library is loaded before trying to use the handler.
*
* As for the functions above, the TSL library might not be loaded when this
* function is called, so we try to load this function, but fall back on the
* Apache error message if not possible.
*/
static Datum
process_hypercore_handler(PG_FUNCTION_ARGS)
{
ts_license_enable_module_loading();
if (ts_cm_functions->hypercore_handler != process_hypercore_handler)
return ts_cm_functions->hypercore_handler(fcinfo);

error_no_default_fn_pg_community(fcinfo);
pg_unreachable();
}

static DDLResult
process_cagg_viewstmt_default(Node *stmt, const char *query_string, void *pstmt,
WithClauseResult *with_clause_options)
Expand Down Expand Up @@ -395,7 +413,7 @@ TSDLLEXPORT CrossModuleFunctions ts_cm_functions_default = {
.dictionary_compressor_finish = error_no_default_fn_pg_community,
.array_compressor_append = error_no_default_fn_pg_community,
.array_compressor_finish = error_no_default_fn_pg_community,
.hypercore_handler = error_no_default_fn_pg_community,
.hypercore_handler = process_hypercore_handler,
.hypercore_proxy_handler = error_pg_community_hypercore_proxy_handler,
.is_compressed_tid = error_no_default_fn_pg_community,

Expand Down
44 changes: 44 additions & 0 deletions tsl/test/expected/hypercore.out
Original file line number Diff line number Diff line change
Expand Up @@ -784,3 +784,47 @@ select test_hypercore(:'rescan_chunk');

(1 row)

-- Simple test that loading the hypercore handler works on a fresh
-- backend.
--
-- Since TSL library is loaded late (after a query has been executed),
-- this can generate a license error if an attempt is made to use a
-- crossmodule function inside a query, and the hypertable handler is
-- used through GetTableAmRoutine().
--
-- To test this, we create a hypercore table, start a fresh
-- connection, and run a query on it.
\c :TEST_DBNAME :ROLE_SUPERUSER
create table crossmodule_test(
time timestamptz unique,
location int
);
select create_hypertable('crossmodule_test', by_range('time', '1d'::interval));
NOTICE: adding not-null constraint to column "time"
create_hypertable
-------------------
(7,t)
(1 row)

select setseed(1);
setseed
---------

(1 row)

insert into crossmodule_test select t, ceil(random()*10)
from generate_series('2022-06-01'::timestamptz, '2022-06-10'::timestamptz, '1h') t;
alter table crossmodule_test
set access method hypercore,
set (timescaledb.compress_orderby = 'time');
WARNING: there was some uncertainty picking the default segment by for the hypertable: You do not have any indexes on columns that can be used for segment_by and thus we are not using segment_by for compression. Please make sure you are not missing any indexes
NOTICE: default segment by for hypertable "crossmodule_test" is set to ""
WARNING: there was some uncertainty picking the default segment by for the hypertable: You do not have any indexes on columns that can be used for segment_by and thus we are not using segment_by for compression. Please make sure you are not missing any indexes
NOTICE: default segment by for hypertable "crossmodule_test" is set to ""
\c :TEST_DBNAME :ROLE_SUPERUSER
select count(*) from crossmodule_test;
count
-------
217
(1 row)

31 changes: 31 additions & 0 deletions tsl/test/sql/hypercore.sql
Original file line number Diff line number Diff line change
Expand Up @@ -427,3 +427,34 @@ returns void as :TSL_MODULE_PATHNAME, 'ts_test_hypercore' language c;
set role :ROLE_DEFAULT_PERM_USER;

select test_hypercore(:'rescan_chunk');

-- Simple test that loading the hypercore handler works on a fresh
-- backend.
--
-- Since TSL library is loaded late (after a query has been executed),
-- this can generate a license error if an attempt is made to use a
-- crossmodule function inside a query, and the hypertable handler is
-- used through GetTableAmRoutine().
--
-- To test this, we create a hypercore table, start a fresh
-- connection, and run a query on it.
\c :TEST_DBNAME :ROLE_SUPERUSER
create table crossmodule_test(
time timestamptz unique,
location int
);

select create_hypertable('crossmodule_test', by_range('time', '1d'::interval));

select setseed(1);

insert into crossmodule_test select t, ceil(random()*10)
from generate_series('2022-06-01'::timestamptz, '2022-06-10'::timestamptz, '1h') t;

alter table crossmodule_test
set access method hypercore,
set (timescaledb.compress_orderby = 'time');

\c :TEST_DBNAME :ROLE_SUPERUSER
select count(*) from crossmodule_test;

0 comments on commit 9d2866b

Please sign in to comment.