Skip to content

Commit

Permalink
Implemented CAST from VARBINARY to DATETIME in Babelfish
Browse files Browse the repository at this point in the history
Signed-off-by: yashneet vinayak <[email protected]>
  • Loading branch information
yashneet vinayak committed Feb 3, 2025
1 parent f60d3c0 commit defe74d
Show file tree
Hide file tree
Showing 8 changed files with 727 additions and 0 deletions.
7 changes: 7 additions & 0 deletions contrib/babelfishpg_common/sql/datetime.sql
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,13 @@ LANGUAGE C VOLATILE STRICT PARALLEL SAFE;
CREATE CAST (TIMESTAMP AS DATETIME)
WITH FUNCTION sys.timestamp2datetime(TIMESTAMP) AS ASSIGNMENT;

CREATE OR REPLACE FUNCTION sys.varbinary2datetime(sys.BBF_VARBINARY)
RETURNS DATETIME
AS 'babelfishpg_common', 'varbinary_datetime'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE CAST (sys.BBF_VARBINARY AS DATETIME)
WITH FUNCTION sys.varbinary2datetime(sys.BBF_VARBINARY) AS ASSIGNMENT;

CREATE OR REPLACE FUNCTION sys.timestamptz2datetime(TIMESTAMPTZ)
RETURNS DATETIME
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,13 @@ CREATE OR REPLACE AGGREGATE sys.max(sys.money) (
PARALLEL = SAFE
);

CREATE OR REPLACE FUNCTION sys.varbinary2datetime(sys.BBF_VARBINARY)
RETURNS DATETIME
AS 'babelfishpg_common', 'varbinary_datetime'
LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;

CREATE CAST (sys.BBF_VARBINARY AS DATETIME)
WITH FUNCTION sys.varbinary2datetime(sys.BBF_VARBINARY) AS ASSIGNMENT;

-- Reset search_path to not affect any subsequent scripts
SELECT set_config('search_path', trim(leading 'sys, ' from current_setting('search_path')), false);
64 changes: 64 additions & 0 deletions contrib/babelfishpg_common/src/datetime.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ PG_FUNCTION_INFO_V1(datetime_recv);
PG_FUNCTION_INFO_V1(date_datetime);
PG_FUNCTION_INFO_V1(time_datetime);
PG_FUNCTION_INFO_V1(timestamp_datetime);
PG_FUNCTION_INFO_V1(varbinary_datetime);
PG_FUNCTION_INFO_V1(timestamptz_datetime);
PG_FUNCTION_INFO_V1(datetime_varchar);
PG_FUNCTION_INFO_V1(varchar_datetime);
Expand Down Expand Up @@ -764,6 +765,69 @@ timestamp_datetime(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMP(result);
}

/*
* varbinary_datetime()
* Convert varbinary to datetime
*/
Datum
varbinary_datetime(PG_FUNCTION_ARGS)
{
bytea *arg = PG_GETARG_BYTEA_PP(0);
int32 size = VARSIZE_ANY_EXHDR(arg);
int32 days;
int32 time_part;
int64 ms_value;
int64 usecs;
Timestamp result;
unsigned char *data = (unsigned char *)VARDATA_ANY(arg);
unsigned char *buffer;

/* TSQL datetime is 8 bytes */
if (size != sizeof(int64) && size != sizeof(int32))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid binary size for datetime conversion")));

buffer = (unsigned char *)palloc(size);
memcpy(buffer, data, size);

/* Extract days and time parts from the binary data */
if (size == sizeof(int32))
{
days = 0;
time_part = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
}
else
{
days = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
time_part = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
}

/* Convert time_part to microseconds */
ms_value = ((int64)time_part * 10LL) / 3LL;
usecs = ms_value * 1000;

if (days < 0)
{
/* Handle pre-1900 dates */
int64 day_value = (int64) (((int64) days) & ((int64) 0xFFFFFFFF));
int64 total_usecs = (day_value - 0xFFFF2E46LL) * USECS_PER_DAY + usecs;
result = MIN_DATETIME + total_usecs;
}
else
{
/* Handle post-1900 dates */
int64 total_usecs = days * USECS_PER_DAY + usecs;
result = TSQL_DEFAULT_DATETIME + total_usecs;
}

pfree(buffer);

CheckDatetimeRange(result, fcinfo->context);

PG_RETURN_TIMESTAMP(result);
}

/* timestamptz_datetime()
* Convert timestamptz to datetime
*/
Expand Down
2 changes: 2 additions & 0 deletions contrib/babelfishpg_common/src/datetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#define MIN_DATETIME INT64CONST(-7794489600000000)
/* upper bond: 9999-12-31 23:59:29.999 */
#define END_DATETIME INT64CONST(252455615999999000)
/* TSQL default datetime: 1900-01-01 00:00:00.000 */
#define TSQL_DEFAULT_DATETIME INT64CONST(-3155673600000000)

extern Timestamp initializeToDefaultDatetime(void);
/** Utility function to calculate days from '1900-01-01 00:00:00' */
Expand Down
30 changes: 30 additions & 0 deletions test/JDBC/expected/babel_datetime-vu-prepare.out
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,33 @@ INSERT INTO babel_datetime_vu_prepare_testing VALUES('2020-03-14')
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0x0000B022)
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0x0000B02200EF28C0)
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0x00008EE700C5C100)
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0xFFFF2E4600000000)
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0x0000B02200EF28C1)
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0x002D247F018B81FF)
go
~~ROW COUNT: 1~~

INSERT INTO babel_datetime_vu_prepare_testing VALUES(0x002D247F018B8200)
go
~~ERROR (Code: 517)~~

~~ERROR (Message: data out of range for datetime)~~

Loading

0 comments on commit defe74d

Please sign in to comment.