Skip to content
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

Fix issue with truncation of result of arithmetic computations to correct scale when result is of numeric datatype #3455

Open
wants to merge 34 commits into
base: BABEL_5_X_DEV
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
48d2f25
Added BBF hook to truncate numeric result to correct scale
Feb 5, 2025
7871c8a
Added handling for aggstar in resolve_numeric_typmod_from_exp
Feb 5, 2025
644db6f
restrict usage of resolve_numeric_typmod_from_exp on numeric datatype…
Feb 5, 2025
1130c35
refactored bbf_trunc_numeric_result hook
Feb 5, 2025
e230004
update BABEL-CASE_EXPR expected files
Feb 6, 2025
879284a
Added some sanity checks
Feb 6, 2025
865b5ca
fixed issue with computation of precision and scale for non-exact num…
Feb 7, 2025
9ffa41f
Updated expected output
Feb 7, 2025
d252416
Added UDT related tests
Feb 10, 2025
600befe
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 10, 2025
5e470f2
update BABEL-5467 expected output
Feb 11, 2025
06a6812
added before files for money_aggregate test
Feb 11, 2025
a76dc8c
Refactored code and added comments
Feb 12, 2025
839fa2c
renamed hooks
Feb 14, 2025
a3b0534
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 14, 2025
185e310
removed datatype check from resolve_numeric_typmod_from_exp function
Feb 14, 2025
8292ede
code refactor
Feb 14, 2025
a8f3b79
updated hook pltsql_trunc_numeric_result_hook to pass the plan
Feb 17, 2025
7215d7c
Added hook pltsql_ExecInitResultTypeTL_hook to generate correct tuple…
Feb 18, 2025
548de00
remove plan == NULL checks from resolve_numeric_typmod_from_exp function
Feb 18, 2025
b5da16d
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 18, 2025
0429503
removed redundant plan check in resolve_numeric_typmod_from_exp
Feb 18, 2025
0e5b248
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 21, 2025
c63d5d8
code refactor
Feb 25, 2025
0a153d6
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 27, 2025
0609fe6
renamed hooks and moved checks around hooks inside hooks
Feb 27, 2025
4846a36
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 27, 2025
23e9596
fixed indentation and added some sanity checks
Feb 27, 2025
551079a
fixed indentation
Feb 27, 2025
67164ea
fixed indentation
Feb 27, 2025
86d9945
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Feb 28, 2025
9cbd6f5
Added more tests
Mar 6, 2025
e675487
Updated function trunc_numeric_result to adjust_numeric_result, where…
Mar 9, 2025
701bae5
Merge branch 'BABEL_5_X_DEV' into BABEL-5467
Mar 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 223 additions & 60 deletions contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contrib/babelfishpg_tds/src/include/tds_response.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ extern void TDSStatementExceptionCallback(PLtsql_execstate *estate, PLtsql_stmt
bool terminate_batch);
extern void SendColumnMetadata(TupleDesc typeinfo, List *targetlist, int16 *formats);
extern bool GetTdsEstateErrorData(int *number, int *severity, int *state);
extern int32 resolve_numeric_typmod_from_exp(Plan *plan, Node *expr);
extern int32 resolve_numeric_typmod_from_exp(Plan *plan, Node *expr, bool *found);

#endif /* TDS_H */
164 changes: 164 additions & 0 deletions contrib/babelfishpg_tsql/src/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ static void pltsql_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, ui
static void pltsql_ExecutorFinish(QueryDesc *queryDesc);
static void pltsql_ExecutorEnd(QueryDesc *queryDesc);
static bool pltsql_bbfViewHasInsteadofTrigger(Relation view, CmdType event);
static Datum adjust_numeric_result(Plan *plan, Node *expr, Datum result, bool result_isnull, Oid result_type, int32 result_typmod);
static void pltsql_ExecUpdateResultTypeTL(PlanState *planstate, TupleDesc desc);

static bool plsql_TriggerRecursiveCheck(ResultRelInfo *resultRelInfo);
static bool bbf_check_rowcount_hook(int es_processed);
Expand Down Expand Up @@ -232,6 +234,7 @@ static Oid set_param_collation(Param *param);
static Oid default_collation_for_builtin_type(Type typ, bool handle_text);
static char* pltsql_get_object_identity_event_trigger(ObjectAddress *addr);
static const char *remove_db_name_in_schema(const char *schema_name, const char *object_type);
static int32 pltsql_exprTypmod(Plan *plan, Node *expr);

/***************************************************
* Temp Table Related Declarations + Hooks
Expand Down Expand Up @@ -278,10 +281,13 @@ static pltsql_is_local_only_inval_msg_hook_type prev_pltsql_is_local_only_inval_
static pltsql_get_tsql_enr_from_oid_hook_type prev_pltsql_get_tsql_enr_from_oid_hook = NULL;
static inherit_view_constraints_from_table_hook_type prev_inherit_view_constraints_from_table = NULL;
static bbfViewHasInsteadofTrigger_hook_type prev_bbfViewHasInsteadofTrigger_hook = NULL;
static adjust_numeric_result_hook_type prev_adjust_numeric_result_hook = NULL;
static ExecUpdateResultTypeTL_hook_type prev_ExecUpdateResultTypeTL_hook = NULL;
static detect_numeric_overflow_hook_type prev_detect_numeric_overflow_hook = NULL;
static match_pltsql_func_call_hook_type prev_match_pltsql_func_call_hook = NULL;
static insert_pltsql_function_defaults_hook_type prev_insert_pltsql_function_defaults_hook = NULL;
static replace_pltsql_function_defaults_hook_type prev_replace_pltsql_function_defaults_hook = NULL;
static exprTypmod_hook_type prev_exprTypmod_hook = NULL;
static print_pltsql_function_arguments_hook_type prev_print_pltsql_function_arguments_hook = NULL;
static planner_hook_type prev_planner_hook = NULL;
static transform_check_constraint_expr_hook_type prev_transform_check_constraint_expr_hook = NULL;
Expand Down Expand Up @@ -421,6 +427,12 @@ InstallExtendedHooks(void)
prev_bbfViewHasInsteadofTrigger_hook = bbfViewHasInsteadofTrigger_hook;
bbfViewHasInsteadofTrigger_hook = pltsql_bbfViewHasInsteadofTrigger;

prev_adjust_numeric_result_hook = adjust_numeric_result_hook;
adjust_numeric_result_hook = adjust_numeric_result;

prev_ExecUpdateResultTypeTL_hook = ExecUpdateResultTypeTL_hook;
ExecUpdateResultTypeTL_hook = pltsql_ExecUpdateResultTypeTL;

prev_detect_numeric_overflow_hook = detect_numeric_overflow_hook;
detect_numeric_overflow_hook = pltsql_detect_numeric_overflow;

Expand All @@ -433,6 +445,9 @@ InstallExtendedHooks(void)
prev_replace_pltsql_function_defaults_hook = replace_pltsql_function_defaults_hook;
replace_pltsql_function_defaults_hook = replace_pltsql_function_defaults;

prev_exprTypmod_hook = exprTypmod_hook;
exprTypmod_hook = pltsql_exprTypmod;

prev_print_pltsql_function_arguments_hook = print_pltsql_function_arguments_hook;
print_pltsql_function_arguments_hook = print_pltsql_function_arguments;

Expand Down Expand Up @@ -578,10 +593,13 @@ UninstallExtendedHooks(void)
GetNewTempOidWithIndex_hook = prev_GetNewTempOidWithIndex_hook;
inherit_view_constraints_from_table_hook = prev_inherit_view_constraints_from_table;
bbfViewHasInsteadofTrigger_hook = prev_bbfViewHasInsteadofTrigger_hook;
adjust_numeric_result_hook = prev_adjust_numeric_result_hook;
ExecUpdateResultTypeTL_hook = prev_ExecUpdateResultTypeTL_hook;
detect_numeric_overflow_hook = prev_detect_numeric_overflow_hook;
match_pltsql_func_call_hook = prev_match_pltsql_func_call_hook;
insert_pltsql_function_defaults_hook = prev_insert_pltsql_function_defaults_hook;
replace_pltsql_function_defaults_hook = prev_replace_pltsql_function_defaults_hook;
exprTypmod_hook = prev_exprTypmod_hook;
print_pltsql_function_arguments_hook = prev_print_pltsql_function_arguments_hook;
planner_hook = prev_planner_hook;
transform_check_constraint_expr_hook = prev_transform_check_constraint_expr_hook;
Expand Down Expand Up @@ -1199,6 +1217,66 @@ pltsql_bbfViewHasInsteadofTrigger(Relation view, CmdType event)
return false;
}

/*
* get_func_id_from_exp
* For expressions T_FuncExpr, T_OpExpr, T_Aggref returns its function Oid
*/
static Oid
get_func_id_from_exp(Node *expr)
{
if (expr == NULL)
return InvalidOid;

switch(nodeTag(expr))
{
case T_FuncExpr:
return ((FuncExpr *) expr)->funcid;
case T_OpExpr:
return ((OpExpr *) expr)->opfuncid;
case T_Aggref:
return ((Aggref *) expr)->aggfnoid;
default:
return InvalidOid;
}
}

/*
* adjust_numeric_result
* truncates/rounds the result value to the correct scale based on result_typmod.
* for result_typmod = -1, computes the result_typmod using pltsql_exprTypmod function.
*/
static Datum
adjust_numeric_result(Plan *plan, Node *expr, Datum result, bool result_isnull, Oid result_type, int32 result_typmod)
{
int32 scale;
Oid func_id;

if (sql_dialect != SQL_DIALECT_TSQL || result_isnull)
return result;

if (!OidIsValid(result_type))
result_type = exprType(expr);

if (result && OidIsValid(result_type) && getBaseType(result_type) == NUMERICOID)
{
if (expr != NULL && result_typmod == -1)
result_typmod = pltsql_exprTypmod(plan, expr);

if (result_typmod != -1)
{
scale = (result_typmod - VARHDRSZ) & 0xffff;
func_id = get_func_id_from_exp(expr);

if (func_id == F_NUMERIC_DIV || func_id == F_AVG_NUMERIC)
return DirectFunctionCall2(numeric_trunc, result, Int32GetDatum(scale));
else
return DirectFunctionCall2(numeric_round, result, Int32GetDatum(scale));
}
}

return result;
}

/*
* Wrapper function that calls the initilization function.
* Calls the pre function call hook on the procname
Expand Down Expand Up @@ -6028,6 +6106,92 @@ remove_db_name_in_schema(const char *object_name, const char *object_type)
return (const char *)pstrdup(object_name + prefix_len);
}

/*
* pltsql_exprTypmod -
* returns the type-specific modifier of the expression's result type,
* if it can be determined, else we return -1.
*/
static int32
pltsql_exprTypmod(Plan *plan, Node *expr)
{
int32 result_typmod = -1;
uint8_t scale;
uint8_t precision;
Oid expr_type;

if (sql_dialect != SQL_DIALECT_TSQL || expr == NULL)
return -1;

expr_type = exprType(expr);

if (!OidIsValid(expr_type))
return -1;

if (getBaseType(expr_type) == NUMERICOID)
{
/*
* use get_numeric_typmod_from_exp function to get the typmod
* from the expression node, when the expression type is numeric.
*/
if (*pltsql_protocol_plugin_ptr && (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp)
result_typmod = (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp(plan, expr, NULL);
if (result_typmod != -1)
{
/*
* If we are unable to get correct precision and scale for overflow cases
* then return -1
*/
scale = (result_typmod - VARHDRSZ) & 0xffff;
precision = ((result_typmod - VARHDRSZ) >> 16) & 0xffff;
if (precision > TDS_NUMERIC_MAX_PRECISION)
{
if (!(precision - scale > 32 && scale > 6) && !(precision - scale <= TDS_NUMERIC_MAX_PRECISION))
return -1;
}
}
}
return result_typmod;
}

/*
* pltsql_ExecUpdateResultTypeTL
*
* Update typmod of all the entries of a previously initialized tuple descriptor,
* using pltsql_exprTypmod when attribute type is NUMERIC, DECIMAL and any UDT on NUMERIC, DECIMAL.
*/
void
pltsql_ExecUpdateResultTypeTL(PlanState *planstate, TupleDesc desc)
{
ListCell *l;
int cur_resno = 1;
List *targetList;

/*
* sanity checks
*/
if (sql_dialect != SQL_DIALECT_TSQL ||
!PointerIsValid(desc) ||
!PointerIsValid(planstate) ||
!PointerIsValid(planstate->plan) ||
!PointerIsValid(planstate->plan->targetlist))
return;

targetList = planstate->plan->targetlist;

foreach(l, targetList)
{
TargetEntry *tle = lfirst(l);
Form_pg_attribute attr = TupleDescAttr(desc, cur_resno - 1);

if (getBaseType(attr->atttypid) == NUMERICOID)
{
attr->atttypmod = pltsql_exprTypmod(planstate->plan, (Node *) tle->expr);
}

cur_resno++;
}
}

/* Check if current connection is a tds connection */
static bool
is_bbf_tds_connection(void)
Expand Down
2 changes: 1 addition & 1 deletion contrib/babelfishpg_tsql/src/pltsql.h
Original file line number Diff line number Diff line change
Expand Up @@ -1800,7 +1800,7 @@ typedef struct PLtsql_protocol_plugin
bool (*get_reset_tds_connection_flag) ();
void (*get_tvp_typename_typeschemaname) (char *proc_name, char *target_arg_name,
char **tvp_type_name, char **tvp_type_schema_name);
int32 (*get_numeric_typmod_from_exp) (Plan *plan, Node *expr);
int32 (*get_numeric_typmod_from_exp) (Plan *plan, Node *expr, bool *found);
/* Session level GUCs */
bool quoted_identifier;
bool arithabort;
Expand Down
2 changes: 1 addition & 1 deletion contrib/babelfishpg_tsql/src/pltsql_coerce.c
Original file line number Diff line number Diff line change
Expand Up @@ -2088,7 +2088,7 @@ tsql_select_common_typmod_hook(ParseState *pstate, List *exprs, Oid common_type)
type = getBaseTypeAndTypmod(type, &typmod);

if (typmod == -1 && (*pltsql_protocol_plugin_ptr))
typmod = (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp(NULL, expr);
typmod = (*pltsql_protocol_plugin_ptr)->get_numeric_typmod_from_exp(NULL, expr, NULL);

if (typmod == -1 || getBaseType(type) != NUMERICOID)
continue;
Expand Down
4 changes: 2 additions & 2 deletions test/JDBC/expected/BABEL-1000.out
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,14 @@ SELECT * FROM babel_1000_test8(1)
GO
~~START~~
numeric
2.06000000
2.06
~~END~~

SELECT * FROM babel_1000_test8(12.345678)
GO
~~START~~
numeric
13.06000000
13.06
~~END~~

-- overflow, expect error
Expand Down
2 changes: 1 addition & 1 deletion test/JDBC/expected/BABEL-1164.out
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ SELECT * FROM t2;
GO
~~START~~
int#!#int#!#numeric#!#numeric
1#!#1#!#2#!#2.00000000
1#!#1#!#2#!#2
~~END~~


Expand Down
19 changes: 10 additions & 9 deletions test/JDBC/expected/BABEL-3066-vu-verify.out
Original file line number Diff line number Diff line change
Expand Up @@ -504,15 +504,15 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) - CAST(CAST(12465781
GO
~~START~~
numeric
0
0E-15
~~END~~


SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) - CAST(CAST(12465781.4679213254 as real) as numeric(38,10));
GO
~~START~~
numeric
0
0E-10
~~END~~


Expand Down Expand Up @@ -540,9 +540,10 @@ GO

SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) * CAST(CAST(12465781.4679213254 as real) as numeric(38,15));
GO
~~ERROR (Code: 33557097)~~

~~ERROR (Message: Arithmetic overflow error for data type numeric.)~~
~~START~~
numeric
155395695939961.000000
~~END~~


SELECT CAST(CAST(CAST(CAST(12465781.46792 as real) as numeric(38,10)) * CAST(CAST(12465781.4679213254 as real) as numeric(38,15)) as real) as numeric(38,6));
Expand All @@ -557,15 +558,15 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) * CAST(CAST(12465781
GO
~~START~~
numeric
155395695939961.00000000000000000000
155395695939961.000000
~~END~~


SELECT CAST(CAST(12465781.46792 as real) as numeric(38,0)) / CAST(CAST(12465781.4679213254 as real) as numeric(38,0));
GO
~~START~~
numeric
1.00000000000000000000
1.000000
~~END~~


Expand All @@ -587,14 +588,14 @@ SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) / CAST(CAST(12465781
GO
~~START~~
numeric
1.00000000000000000000
1.000000
~~END~~


SELECT CAST(CAST(12465781.46792 as real) as numeric(38,10)) / CAST(CAST(12465781.4679213254 as real) as numeric(38,10));
GO
~~START~~
numeric
1.00000000000000000000
1.000000
~~END~~

4 changes: 2 additions & 2 deletions test/JDBC/expected/BABEL-381.out
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ select 2.0/1.5;
go
~~START~~
numeric
1.3333333333333333
1.333333
~~END~~


select 2.0, 2.0/1.5, 1.0/1.5;
go
~~START~~
numeric#!#numeric#!#numeric
2.0#!#1.3333333333333333#!#0.66666666666666666667
2.0#!#1.333333#!#0.666666
~~END~~

Loading
Loading