Skip to content

Commit

Permalink
Fix float to varchar/char casting and coversion (babelfish-for-postgr…
Browse files Browse the repository at this point in the history
…esql#3413)

We were using CoerceViaIO path before to cast float to varchar/char , added a new cast which will always display only upto 6 decimal digits, and rounding results when more decimal digits are present
Fix Formatting when using to_char function inside CONVERT function for float->string . Also we were not compute the
number of decimal digits to be shown properly in case the value before decimal is 0.

Issues Resolved: BABEL-5459
Signed-off-by: Nirmit Shah <[email protected]>
  • Loading branch information
shah-nirmit authored and “manisha-deshpande” committed Jan 28, 2025
1 parent 2371a05 commit 673d497
Show file tree
Hide file tree
Showing 64 changed files with 2,048 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ RETURNS sys.BPCHAR
AS 'babelfishpg_common', 'fixeddecimal2bpchar'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE OR REPLACE FUNCTION sys.float82varchar(pg_catalog.float8, integer, BOOLEAN)
RETURNS sys.VARCHAR
AS 'babelfishpg_common', 'float82varchar'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE OR REPLACE FUNCTION sys.float82bpchar(pg_catalog.float8, integer, BOOLEAN)
RETURNS sys.BPCHAR
AS 'babelfishpg_common', 'float82bpchar'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

DO $$
DECLARE
exception_message text;
Expand Down Expand Up @@ -103,6 +113,22 @@ CREATE OPERATOR sys.% (
END IF;
END $$;

DO $$
DECLARE
exception_message text;
BEGIN
CREATE CAST (pg_catalog.float8 AS sys.VARCHAR)
WITH FUNCTION sys.float82varchar(pg_catalog.float8, integer, BOOLEAN) AS IMPLICIT;

CREATE CAST (pg_catalog.float8 AS sys.BPCHAR)
WITH FUNCTION sys.float82bpchar(pg_catalog.float8, integer, BOOLEAN) AS IMPLICIT;
EXCEPTION WHEN duplicate_object THEN
GET STACKED DIAGNOSTICS
exception_message = MESSAGE_TEXT;
RAISE WARNING '%', exception_message;
END;
$$;

CREATE OR REPLACE FUNCTION sys.moneylarger(sys.MONEY, sys.MONEY)
RETURNS sys.MONEY
AS 'babelfishpg_money', 'fixeddecimallarger'
Expand Down
16 changes: 16 additions & 0 deletions contrib/babelfishpg_common/sql/varchar.sql
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,19 @@ CREATE OR REPLACE AGGREGATE sys.min(sys.NVARCHAR)
combinefunc = sys.nvarchar_smaller,
parallel = safe
);

CREATE OR REPLACE FUNCTION sys.float82varchar(pg_catalog.float8, integer, BOOLEAN)
RETURNS sys.VARCHAR
AS 'babelfishpg_common', 'float82varchar'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE OR REPLACE FUNCTION sys.float82bpchar(pg_catalog.float8, integer, BOOLEAN)
RETURNS sys.BPCHAR
AS 'babelfishpg_common', 'float82bpchar'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE CAST (pg_catalog.float8 AS sys.VARCHAR)
WITH FUNCTION sys.float82varchar(pg_catalog.float8, integer, BOOLEAN) AS IMPLICIT;

CREATE CAST (pg_catalog.float8 AS sys.BPCHAR)
WITH FUNCTION sys.float82bpchar(pg_catalog.float8, integer, BOOLEAN) AS IMPLICIT;
102 changes: 102 additions & 0 deletions contrib/babelfishpg_common/src/varchar.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "access/hash.h"
#include "collation.h"
#include "common/shortest_dec.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "encoding/encoding.h"
Expand Down Expand Up @@ -562,6 +563,8 @@ PG_FUNCTION_INFO_V1(varchar2money);
PG_FUNCTION_INFO_V1(varchar2numeric);
PG_FUNCTION_INFO_V1(fixeddecimal2varchar);
PG_FUNCTION_INFO_V1(fixeddecimal2bpchar);
PG_FUNCTION_INFO_V1(float82varchar);
PG_FUNCTION_INFO_V1(float82bpchar);

/*****************************************************************************
* varchar - varchar(n)
Expand Down Expand Up @@ -1188,6 +1191,105 @@ fixeddecimal2bpchar(PG_FUNCTION_ARGS)
#undef FIXEDDECIMAL_2_VARCHAR_MULTIPLIER
#undef FIXEDDECIMAL_2_VARCHAR_SCALE

Datum
float82varchar(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
int32 typmod = PG_GETARG_INT32(1);
/* When No Typmod is defined Default Length is 30 */
int maxlen = (typmod == -1) ? 30 : (typmod - VARHDRSZ);
Datum res;
/* 32 length as double_to_shortest_decimal_buf always returns string with length less that 30*/
char *ascii = (char *) palloc0(32);

/* round to 6 decimal digits */
if (unlikely(isinf(num)|| isnan(num)))
{
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("Error Converting Float Value to String.")));
}
else
{
num = round(num * 1000000.0) / 1000000.0;
}

double_to_shortest_decimal_buf(num, ascii);

/* Check if the number fits within the specified length */
if (maxlen > 0)
{
size_t str_len = strlen(ascii);
if (str_len > maxlen)
{
ereport(ERROR,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("There is insufficient result space to convert a float value to varchar/nvarchar.")));
}
}

res = DirectFunctionCall3(varcharin,
CStringGetDatum(ascii),
ObjectIdGetDatum(0),
Int32GetDatum(typmod));

PG_RETURN_DATUM(res);

}

Datum
float82bpchar(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
int32 typmod = PG_GETARG_INT32(1);
/* When No Typmod is defined Default Length is 30 */
int maxlen = (typmod == -1) ? 30 : (typmod - VARHDRSZ);
Datum res;
/* 32 length as double_to_shortest_decimal_buf always returns string with length less that 30*/
char *ascii = (char *) palloc0(32);
char *buf_padded;
int str_len = -1;

/* Handle special cases */
if (unlikely(isinf(num)|| isnan(num)))
{
ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("Error Converting Float Value to String.")));
}
else
{
num = round(num * 1000000.0) / 1000000.0;
}

double_to_shortest_decimal_buf(num, ascii);

/* Check if the number fits within the specified length */
if (maxlen > 0)
{
str_len = strlen(ascii);
if (str_len > maxlen)
{
ereport(ERROR,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("There is insufficient result space to convert a float value to char/nchar.")));
}
}

/* Left pad float value with the spaces */
buf_padded = (char *) palloc0(maxlen + 1);
memset(buf_padded, ' ', maxlen - str_len);
memcpy(buf_padded + maxlen - str_len, ascii, str_len);

res = DirectFunctionCall3(bpcharin,
CStringGetDatum(buf_padded),
ObjectIdGetDatum(0),
Int32GetDatum(typmod));

PG_RETURN_DATUM(res);

}

/*****************************************************************************
* bpchar - char() *
*****************************************************************************/
Expand Down
12 changes: 7 additions & 5 deletions contrib/babelfishpg_tsql/sql/sys_function_helpers.sql
Original file line number Diff line number Diff line change
Expand Up @@ -10975,13 +10975,15 @@ BEGIN
END IF;
IF (v_floatval >= 999999.5) THEN
v_format := '9D99999EEEE';
v_result := to_char(v_sign * ceiling(v_floatval), v_format);
v_result := to_char(v_sign::NUMERIC * ceiling(v_floatval), v_format);
v_result := to_char(substring(v_result, 1, 8)::NUMERIC, 'FM9D99999')::NUMERIC::TEXT || substring(v_result, 9);
ELSE
if (6 - v_integral_digits < v_decimal_digits) THEN
v_decimal_digits := 6 - v_integral_digits;
END IF;
v_format := (pow(10, v_integral_digits)-1)::TEXT || 'D';
IF (6 - v_integral_digits < v_decimal_digits) AND (trunc(abs(v_floatval)) != 0) THEN
v_decimal_digits := 6 - v_integral_digits;
ELSIF (6 - v_integral_digits < v_decimal_digits) THEN
v_decimal_digits := 6;
END IF;
v_format := (pow(10, v_integral_digits)-10)::TEXT || 'D';
IF (v_decimal_digits > 0) THEN
v_format := v_format || (pow(10, v_decimal_digits)-1)::TEXT;
END IF;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5486,6 +5486,79 @@ BEGIN
END;
$$;

CREATE OR REPLACE FUNCTION sys.babelfish_try_conv_float_to_string(IN p_datatype TEXT,
IN p_floatval FLOAT,
IN p_style NUMERIC DEFAULT 0)
RETURNS TEXT
AS
$BODY$
DECLARE
v_style SMALLINT;
v_format VARCHAR COLLATE "C";
v_floatval NUMERIC := abs(p_floatval);
v_digits SMALLINT;
v_integral_digits SMALLINT;
v_decimal_digits SMALLINT;
v_sign SMALLINT := sign(p_floatval);
v_result TEXT;
v_res_length SMALLINT;
MASK_REGEXP CONSTANT VARCHAR COLLATE "C" := '^\s*(?:character varying)\s*\(\s*(\d+|MAX)\s*\)\s*$';
BEGIN
v_style := floor(p_style)::SMALLINT;
IF (v_style = 0) THEN
v_digits := length(v_floatval::NUMERIC::TEXT);
v_decimal_digits := scale(v_floatval);
IF (v_decimal_digits > 0) THEN
v_integral_digits := v_digits - v_decimal_digits - 1;
ELSE
v_integral_digits := v_digits;
END IF;
IF (v_floatval >= 999999.5) THEN
v_format := '9D99999EEEE';
v_result := to_char(v_sign::NUMERIC * ceiling(v_floatval), v_format);
v_result := to_char(substring(v_result, 1, 8)::NUMERIC, 'FM9D99999')::NUMERIC::TEXT || substring(v_result, 9);
ELSE
IF (6 - v_integral_digits < v_decimal_digits) AND (trunc(abs(v_floatval)) != 0) THEN
v_decimal_digits := 6 - v_integral_digits;
ELSIF (6 - v_integral_digits < v_decimal_digits) THEN
v_decimal_digits := 6;
END IF;
v_format := (pow(10, v_integral_digits)-10)::TEXT || 'D';
IF (v_decimal_digits > 0) THEN
v_format := v_format || (pow(10, v_decimal_digits)-1)::TEXT;
END IF;
v_result := to_char(p_floatval, v_format);
END IF;
ELSIF (v_style = 1) THEN
v_format := '9D9999999EEEE';
v_result := to_char(p_floatval, v_format);
ELSIF (v_style = 2) THEN
v_format := '9D999999999999999EEEE';
v_result := to_char(p_floatval, v_format);
ELSIF (v_style = 3) THEN
v_format := '9D9999999999999999EEEE';
v_result := to_char(p_floatval, v_format);
ELSE
RAISE invalid_parameter_value;
END IF;

v_res_length := substring(p_datatype COLLATE "C", MASK_REGEXP)::SMALLINT;
IF v_res_length IS NULL THEN
RETURN v_result;
ELSE
RETURN rpad(v_result, v_res_length, ' ');
END IF;
EXCEPTION
WHEN invalid_parameter_value THEN
RAISE USING MESSAGE := pg_catalog.format('%s is not a valid style number when converting from FLOAT to a character string.', v_style),
DETAIL := 'Use of incorrect "style" parameter value during conversion process.',
HINT := 'Change "style" parameter to the proper value and try again.';
END;
$BODY$
LANGUAGE plpgsql
STABLE
RETURNS NULL ON NULL INPUT;

-- Drops the temporary procedure used by the upgrade script.
-- Please have this be one of the last statements executed in this upgrade script.
DROP PROCEDURE sys.babelfish_drop_deprecated_object(varchar, varchar, varchar);
Expand Down
4 changes: 0 additions & 4 deletions contrib/babelfishpg_tsql/src/pltsql_coerce.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,6 @@ tsql_cast_raw_info_t tsql_cast_raw_infos[] =
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "float4", "sys", "varchar", NULL, 'i', 'i'},
/* float8 -> string via I/O */
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "float8", "pg_catalog", "text", NULL, 'i', 'i'},
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "float8", "pg_catalog", "bpchar", NULL, 'i', 'i'},
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "float8", "sys", "bpchar", NULL, 'i', 'i'},
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "float8", "pg_catalog", "varchar", NULL, 'i', 'i'},
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "float8", "sys", "varchar", NULL, 'i', 'i'},
/* numeric -> string via I/O */
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "numeric", "pg_catalog", "text", NULL, 'i', 'i'},
{TSQL_CAST_WITHOUT_FUNC_ENTRY, "pg_catalog", "numeric", "pg_catalog", "bpchar", NULL, 'i', 'i'},
Expand Down
6 changes: 3 additions & 3 deletions test/JDBC/expected/BABEL-1193.out
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,9 @@ SELECT * FROM t15;
GO
~~START~~
char#!#char#!#char#!#char#!#char#!#char#!#char#!#char
12 #!#4553 #!#123456 #!#12.345 #!#2344.456 #!#12.34 #!# 456.33#!# 1123.68
-12 #!#-1234 #!#-123456 #!#-12.345 #!#-2344.456 #!#12.34 #!# -456.33#!# -1123.68
0 #!#0 #!#0 #!#0 #!#0 #!#0.00 #!# 0.00#!# 0.00
12 #!#4553 #!#123456 #!#12.345 #!# 2344.456#!#12.34 #!# 456.33#!# 1123.68
-12 #!#-1234 #!#-123456 #!#-12.345 #!# -2344.456#!#12.34 #!# -456.33#!# -1123.68
0 #!#0 #!#0 #!#0 #!# 0#!#0.00 #!# 0.00#!# 0.00
~~END~~


Expand Down
2 changes: 1 addition & 1 deletion test/JDBC/expected/BABEL-889-vu-verify.out
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ select cast(cast(cast('3.1415926' as float) as sql_variant) as varchar);
go
~~START~~
varchar
3.1415926
3.141593
~~END~~

-- real
Expand Down
2 changes: 1 addition & 1 deletion test/JDBC/expected/BABEL-889.out
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ select cast(cast(cast('3.1415926' as float) as sql_variant) as varchar);
go
~~START~~
varchar
3.1415926
3.141593
~~END~~

-- real
Expand Down
2 changes: 1 addition & 1 deletion test/JDBC/expected/babel_function.out
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ select TRY_CONVERT(varchar(30), CAST('11234561231231.234' AS float), 0);
GO
~~START~~
varchar
<NULL>
1.12346e+13
~~END~~

select TRY_CONVERT(varchar(30), CAST('11234561231231.234'AS float), 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ select cast(cast(cast('3.1415926' as float) as sql_variant) as varchar);
go
~~START~~
varchar
3.1415926
3.141593
~~END~~

-- real
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ select cast(cast(cast('3.1415926' as float) as sql_variant) as varchar);
go
~~START~~
varchar
3.1415926
3.141593
~~END~~

-- real
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ select TRY_CONVERT(varchar(30), CAST('11234561231231.234' AS float), 0);
GO
~~START~~
varchar
<NULL>
1.12346e+13
~~END~~

select TRY_CONVERT(varchar(30), CAST('11234561231231.234'AS float), 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ select cast(cast(cast('3.1415926' as float) as sql_variant) as varchar);
go
~~START~~
varchar
3.1415926
3.141593
~~END~~

-- real
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ select cast(cast(cast('3.1415926' as float) as sql_variant) as varchar);
go
~~START~~
varchar
3.1415926
3.141593
~~END~~

-- real
Expand Down
17 changes: 17 additions & 0 deletions test/JDBC/expected/test_conv_float_to_varchar_char-vu-cleanup.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
DROP TABLE TestResults
DROP VIEW float_char_v1;
DROP VIEW float_char_v2;
DROP FUNCTION float_char_f1;
DROP FUNCTION float_char_f2;
DROP PROCEDURE float_char_p1;
DROP TABLE float_char_t1;
DROP TABLE float_char_t2;
DROP TABLE TestResults_1
DROP VIEW float_varchar_v1;
DROP VIEW float_varchar_v2;
DROP FUNCTION float_varchar_f1;
DROP FUNCTION float_varchar_f2;
DROP PROCEDURE float_varchar_p1;
DROP TABLE float_varchar_t1;
DROP TABLE float_varchar_t2;
GO
Loading

0 comments on commit 673d497

Please sign in to comment.