New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid having to cast time arg for cagg policy #2387
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,45 @@ subtract_interval_from_now(Oid timetype, const Interval *interval) | |
return res; | ||
} | ||
|
||
Datum | ||
ts_time_datum_convert_arg(Datum arg, Oid *argtype, Oid timetype) | ||
{ | ||
Oid type = *argtype; | ||
|
||
if (!OidIsValid(type) || type == UNKNOWNOID) | ||
{ | ||
Oid infuncid = InvalidOid; | ||
Oid typeioparam; | ||
|
||
type = timetype; | ||
getTypeInputInfo(type, &infuncid, &typeioparam); | ||
|
||
switch (get_func_nargs(infuncid)) | ||
{ | ||
case 1: | ||
/* Functions that take one input argument, e.g., the Date function */ | ||
arg = OidFunctionCall1(infuncid, arg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I correct that this is a reuse of variable |
||
break; | ||
case 3: | ||
/* Timestamp functions take three input arguments */ | ||
arg = OidFunctionCall3(infuncid, | ||
arg, | ||
ObjectIdGetDatum(InvalidOid), | ||
Int32GetDatum(-1)); | ||
break; | ||
default: | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("invalid time argument"), | ||
errhint("Time argument requires an explicit cast."))); | ||
Comment on lines
+71
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to test this case? Codecov complains that it's not covered. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think most of those cases already covered by continuous agg refresh function tests, which are reusing the same functionality. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess Codecov would not complain if it is covered ;) I searched that such error is never expected in our tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this case is actually possible right now, it was made more like a precaution in case new time functions will arrive in future versions I believe |
||
} | ||
|
||
*argtype = type; | ||
} | ||
|
||
return arg; | ||
} | ||
|
||
/* | ||
* Get the internal time value from a pseudo-type function argument. | ||
* | ||
|
@@ -75,36 +114,9 @@ subtract_interval_from_now(Oid timetype, const Interval *interval) | |
int64 | ||
ts_time_value_from_arg(Datum arg, Oid argtype, Oid timetype) | ||
{ | ||
if (!OidIsValid(argtype) || argtype == UNKNOWNOID) | ||
{ | ||
/* No explicit cast was done by the user. Try to convert the argument | ||
* to the time type used by the continuous aggregate. */ | ||
Oid infuncid = InvalidOid; | ||
Oid typeioparam; | ||
|
||
argtype = timetype; | ||
getTypeInputInfo(argtype, &infuncid, &typeioparam); | ||
|
||
switch (get_func_nargs(infuncid)) | ||
{ | ||
case 1: | ||
/* Functions that take one input argument, e.g., the Date function */ | ||
arg = OidFunctionCall1(infuncid, arg); | ||
break; | ||
case 3: | ||
/* Timestamp functions take three input arguments */ | ||
arg = OidFunctionCall3(infuncid, | ||
arg, | ||
ObjectIdGetDatum(InvalidOid), | ||
Int32GetDatum(-1)); | ||
break; | ||
default: | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("invalid time argument"), | ||
errhint("Time argument requires an explicit cast."))); | ||
} | ||
} | ||
/* If no explicit cast was done by the user, try to convert the argument | ||
* to the time type used by the continuous aggregate. */ | ||
arg = ts_time_datum_convert_arg(arg, &argtype, timetype); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment here, since |
||
|
||
if (argtype == INTERVALOID) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,16 +6,18 @@ | |
|
||
#include <postgres.h> | ||
#include <miscadmin.h> | ||
#include <parser/parse_coerce.h> | ||
|
||
#include <jsonb_utils.h> | ||
#include <miscadmin.h> | ||
#include <utils/builtins.h> | ||
#include "bgw_policy/continuous_aggregate_api.h" | ||
#include "bgw_policy/job.h" | ||
#include "bgw/job.h" | ||
#include "continuous_agg.h" | ||
#include "continuous_aggs/materialize.h" | ||
#include "dimension.h" | ||
#include "hypertable_cache.h" | ||
#include "time_utils.h" | ||
#include "policy_utils.h" | ||
|
||
#define POLICY_REFRESH_CAGG_PROC_NAME "policy_refresh_continuous_aggregate" | ||
|
@@ -143,26 +145,35 @@ json_add_dim_interval_value(JsonbParseState *parse_state, const char *json_label | |
} | ||
} | ||
|
||
static void | ||
check_valid_interval(Oid dim_type, Oid interval_type, const char *str_msg) | ||
static Datum | ||
convert_interval_arg(Oid dim_type, Datum interval, Oid *interval_type, const char *str_msg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this conversion useful for other policies as well (e.g., retention)? I figured this would be in time_utils or policy_utils for use by all policies. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I thought maybe to do that in another PR related to the retention policy. |
||
{ | ||
if (IS_INTEGER_TYPE(dim_type)) | ||
{ | ||
if (interval_type != dim_type) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("invalid parameter value for %s", str_msg), | ||
errhint("Use time interval of type %s with the continuous aggregate.", | ||
format_type_be(dim_type)))); | ||
} | ||
else if (IS_TIMESTAMP_TYPE(dim_type) && (interval_type != INTERVALOID)) | ||
Oid convert_to = dim_type; | ||
|
||
if (*interval_type != convert_to) | ||
{ | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("invalid parameter value for %s", str_msg), | ||
errhint("Use time interval with a continuous aggregate using timestamp-based time " | ||
"bucket."))); | ||
if (IS_TIMESTAMP_TYPE(dim_type)) | ||
convert_to = INTERVALOID; | ||
|
||
if (!can_coerce_type(1, interval_type, &convert_to, COERCION_IMPLICIT)) | ||
{ | ||
if (IS_INTEGER_TYPE(dim_type)) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("invalid parameter value for %s", str_msg), | ||
errhint("Use time interval of type %s with the continuous aggregate.", | ||
format_type_be(dim_type)))); | ||
else if (IS_TIMESTAMP_TYPE(dim_type)) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("invalid parameter value for %s", str_msg), | ||
errhint("Use time interval with a continuous aggregate using " | ||
"timestamp-based time " | ||
"bucket."))); | ||
} | ||
} | ||
|
||
return ts_time_datum_convert_arg(interval, interval_type, convert_to); | ||
} | ||
|
||
Datum | ||
|
@@ -182,40 +193,49 @@ policy_refresh_cagg_add(PG_FUNCTION_ARGS) | |
Oid cagg_oid, owner_id; | ||
List *jobs; | ||
bool if_not_exists, start_isnull, end_isnull; | ||
if (PG_ARGISNULL(3)) | ||
{ | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("cannot use NULL schedule interval"))); | ||
} | ||
|
||
/* Verify that the owner can create a background worker */ | ||
cagg_oid = PG_GETARG_OID(0); | ||
start_interval = PG_GETARG_DATUM(1); | ||
end_interval = PG_GETARG_DATUM(2); | ||
start_isnull = PG_ARGISNULL(1); | ||
end_isnull = PG_ARGISNULL(2); | ||
start_interval_type = get_fn_expr_argtype(fcinfo->flinfo, 1); | ||
end_interval_type = get_fn_expr_argtype(fcinfo->flinfo, 2); | ||
refresh_interval = *PG_GETARG_INTERVAL_P(3); | ||
if_not_exists = PG_GETARG_BOOL(4); | ||
owner_id = ts_cagg_permissions_check(cagg_oid, GetUserId()); | ||
ts_bgw_job_validate_job_owner(owner_id); | ||
|
||
cagg = ts_continuous_agg_find_by_relid(cagg_oid); | ||
if (!cagg) | ||
{ | ||
ereport(ERROR, | ||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||
errmsg("\"%s\" is not a continuous aggregate", get_rel_name(cagg_oid)))); | ||
} | ||
|
||
mat_htid = cagg->data.mat_hypertable_id; | ||
/* Verify that the owner can create a background worker */ | ||
owner_id = ts_cagg_permissions_check(cagg_oid, GetUserId()); | ||
ts_bgw_job_validate_job_owner(owner_id); | ||
|
||
hcache = ts_hypertable_cache_pin(); | ||
mat_htid = cagg->data.mat_hypertable_id; | ||
mat_ht = ts_hypertable_cache_get_entry_by_id(hcache, mat_htid); | ||
dim = hyperspace_get_open_dimension(mat_ht->space, 0); | ||
dim_type = ts_dimension_get_partition_type(dim); | ||
ts_cache_release(hcache); | ||
|
||
/* Try to convert the argument to the time type used by the | ||
* continuous aggregate */ | ||
start_interval = PG_GETARG_DATUM(1); | ||
end_interval = PG_GETARG_DATUM(2); | ||
start_isnull = PG_ARGISNULL(1); | ||
end_isnull = PG_ARGISNULL(2); | ||
start_interval_type = get_fn_expr_argtype(fcinfo->flinfo, 1); | ||
end_interval_type = get_fn_expr_argtype(fcinfo->flinfo, 2); | ||
|
||
if (!start_isnull) | ||
start_interval = | ||
convert_interval_arg(dim_type, start_interval, &start_interval_type, "start_interval"); | ||
|
||
if (!end_isnull) | ||
end_interval = | ||
convert_interval_arg(dim_type, end_interval, &end_interval_type, "end_interval"); | ||
|
||
if (PG_ARGISNULL(3)) | ||
ereport(ERROR, | ||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
errmsg("cannot use NULL schedule interval"))); | ||
refresh_interval = *PG_GETARG_INTERVAL_P(3); | ||
if_not_exists = PG_GETARG_BOOL(4); | ||
|
||
/* Make sure there is only 1 refresh policy on the cagg */ | ||
jobs = ts_bgw_job_find_by_proc_and_hypertable_id(POLICY_REFRESH_CAGG_PROC_NAME, | ||
INTERNAL_SCHEMA_NAME, | ||
|
@@ -265,11 +285,6 @@ policy_refresh_cagg_add(PG_FUNCTION_ARGS) | |
namestrcpy(&proc_schema, INTERNAL_SCHEMA_NAME); | ||
namestrcpy(&owner, GetUserNameFromId(owner_id, false)); | ||
|
||
if (!start_isnull) | ||
check_valid_interval(dim_type, start_interval_type, "start_interval"); | ||
if (!end_isnull) | ||
check_valid_interval(dim_type, end_interval_type, "end_interval"); | ||
|
||
JsonbParseState *parse_state = NULL; | ||
pushJsonbValue(&parse_state, WJB_BEGIN_OBJECT, NULL); | ||
ts_jsonb_add_int32(parse_state, CONFIG_KEY_MAT_HYPERTABLE_ID, mat_htid); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is worth do document this function as it is part of internal API, so it is clear what to expect from it and how to use it. Currently I cannot guess from the function name what to expect from it.