diff --git a/contrib/dolphin/expected/test_interval_expr.out b/contrib/dolphin/expected/test_interval_expr.out index 0550037ad2ad518e9b62858c2076debf4fcc0d2c..0ddbc970b94cc58dcb5545043e29900220c09612 100644 --- a/contrib/dolphin/expected/test_interval_expr.out +++ b/contrib/dolphin/expected/test_interval_expr.out @@ -862,6 +862,109 @@ select '2024-06-18 18:30:00' - interval 1 + c1 second_microsecond from t1; select '2024-06-18 18:30:00' - interval 1 + c1 second to microsecond from t1; ERROR: interval second to microsecond is not supported +-- 函数 +set dolphin.cmpt_version=8.0; +select date_add(time'38:59:58', interval '5 4' year_month); + date_add +--------------------- +--?.* +(1 row) + +select date_add(time'38:59:58', interval '5 4' day_minute); + date_add +--------------------- +--?.* +(1 row) + +select date_add(time'38:59:58', interval '5:4' day_second); + date_add +--------------------- +--?.* +(1 row) + +select date_add(time'38:59:58', interval '5:4' day_hour); + date_add +--------------------- +--?.* +(1 row) + +select date_add(time'38:59:58', interval '5:4' hour_minute); + date_add +---------- + 44:03:58 +(1 row) + +select date_add(time'38:59:58', interval '5 4' hour_second); + date_add +---------- + 39:05:02 +(1 row) + +select date_add(time'38:59:58', interval '5 4' minute_second); + date_add +---------- + 39:05:02 +(1 row) + +select date_add(time'38:59:58', interval '5 4' week); +WARNING: invalid input syntax for type numeric: "5 4" +CONTEXT: referenced column: date_add + date_add +--------------------- +--?.* +(1 row) + +select date_sub(time'38:59:58', interval '5 4' year_month); + date_sub +--------------------- +--?.* +(1 row) + +select date_sub(time'38:59:58', interval '5 4' day_minute); + date_sub +--------------------- +--?.* +(1 row) + +select date_sub(time'38:59:58', interval '5:4' day_second); + date_sub +--------------------- +--?.* +(1 row) + +select date_sub(time'38:59:58', interval '5:4' day_hour); + date_sub +--------------------- +--?.* +(1 row) + +select date_sub(time'38:59:58', interval '5:4' hour_minute); + date_sub +---------- + 33:55:58 +(1 row) + +select date_sub(time'38:59:58', interval '5 4' hour_second); + date_sub +---------- + 38:54:54 +(1 row) + +select date_sub(time'38:59:58', interval '5 4' minute_second); + date_sub +---------- + 38:54:54 +(1 row) + +select date_sub(time'38:59:58', interval '5 4' week); +WARNING: invalid input syntax for type numeric: "5 4" +CONTEXT: referenced column: date_sub + date_sub +--------------------- +--?.* +(1 row) + +set dolphin.cmpt_version=5.7; -- 全类型兼容 create table all_types_table ( `int1` tinyint, diff --git a/contrib/dolphin/include/plugin_parser/kwlist.h b/contrib/dolphin/include/plugin_parser/kwlist.h index 3a11ff4980f5409a58d9bd9ce9a4a2cfc04abb90..92f7afb5bb4e37010a9062a7cabfa94ffe23be20 100644 --- a/contrib/dolphin/include/plugin_parser/kwlist.h +++ b/contrib/dolphin/include/plugin_parser/kwlist.h @@ -237,8 +237,12 @@ PG_KEYWORD("datanode", DATANODE, UNRESERVED_KEYWORD) PG_KEYWORD("datanodes", DATANODES, UNRESERVED_KEYWORD) PG_KEYWORD("datatype_cl", DATATYPE_CL, UNRESERVED_KEYWORD) PG_KEYWORD("date", DATE_P, COL_NAME_KEYWORD) +#ifdef DOLPHIN +PG_KEYWORD("date_add", DATE_ADD_P, UNRESERVED_KEYWORD) +#endif PG_KEYWORD("date_format", DATE_FORMAT_P, UNRESERVED_KEYWORD) #ifdef DOLPHIN +PG_KEYWORD("date_sub", DATE_SUB_P, UNRESERVED_KEYWORD) PG_KEYWORD("datetime", DATETIME, COL_NAME_KEYWORD) #endif PG_KEYWORD("day", DAY_P, UNRESERVED_KEYWORD) diff --git a/contrib/dolphin/plugin_parser/gram.y b/contrib/dolphin/plugin_parser/gram.y index c8a6a2e10e44acdf03ddc9d61150a320b5022d0c..08ea49c32abba37546fd5f4556eb6656083d3add 100644 --- a/contrib/dolphin/plugin_parser/gram.y +++ b/contrib/dolphin/plugin_parser/gram.y @@ -559,6 +559,7 @@ static inline List* MakeNotLikeOpList(); static inline Node* MakeSubLinkWithOp(SubLinkType subType, Node* testExpr, char* op, Node* subSelect, int location); /* null is the minimum value in sortby */ static inline SortByNulls GetNullOrderRule(SortByDir sortBy, SortByNulls nullRule); +static bool GreaterThanHour (List* int_type); %} %define api.pure @@ -1233,7 +1234,7 @@ static inline SortByNulls GetNullOrderRule(SortByDir sortBy, SortByNulls nullRul CURRENT_TIME CURTIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CURSOR_NAME CYCLE NOW_FUNC SHRINK - DATA_P DATABASE DATABASES DATAFILE DATANODE DATANODES DATATYPE_CL DATE_P DATETIME DATE_FORMAT_P DAY_P DAY_HOUR_P DAY_MICROSECOND_P DAY_MINUTE_P DAY_SECOND_P DAYOFMONTH DAYOFWEEK DAYOFYEAR DBCOMPATIBILITY_P DB_B_FORMAT DB_B_JSOBJ DEALLOCATE DEC DECIMAL_P DECLARE DECODE DEFAULT DEFAULTS + DATA_P DATABASE DATABASES DATAFILE DATANODE DATANODES DATATYPE_CL DATE_P DATETIME DATE_ADD_P DATE_FORMAT_P DATE_SUB_P DAY_P DAY_HOUR_P DAY_MICROSECOND_P DAY_MINUTE_P DAY_SECOND_P DAYOFMONTH DAYOFWEEK DAYOFYEAR DBCOMPATIBILITY_P DB_B_FORMAT DB_B_JSOBJ DEALLOCATE DEC DECIMAL_P DECLARE DECODE DEFAULT DEFAULTS DEFERRABLE DEFERRED DEFINER DELAYED DELAY_KEY_WRITE DELETE_P DELIMITER DELIMITERS DELTA DELTAMERGE DESC DESCRIBE DETERMINISTIC DISK DIV /* PGXC_BEGIN */ DIAGNOSTICS DICTIONARY DIRECT DIRECTORY DISABLE_P DISCARD DISTINCT DISTINCTROW DISTRIBUTE DISTRIBUTION DO DOCUMENT_P DOMAIN_P DOUBLE_P DUAL_P @@ -36981,6 +36982,60 @@ func_expr_common_subexpr: n->call_func = false; $$ = (Node *)n; } + | DATE_ADD_P '(' a_expr ',' INTERVAL a_expr interval_unit ')' + { + bool isGreatThanHour = GreaterThanHour($7); + FuncCall *n1 = makeNode(FuncCall); + n1->funcname = SystemFuncName("any2interval"); + n1->args = lcons($6, $7); + n1->agg_order = NIL; + n1->agg_star = FALSE; + n1->agg_distinct = FALSE; + n1->func_variadic = FALSE; + n1->over = NULL; + n1->location = @5; + n1->call_func = false; + + FuncCall *n2 = makeNode(FuncCall); + n2->funcname = SystemFuncName("date_add"); + n2->args = list_make3($3, (Node *)n1, makeBoolAConst(isGreatThanHour, @7)); + n2->agg_order = NIL; + n2->agg_star = FALSE; + n2->agg_distinct = FALSE; + n2->func_variadic = FALSE; + n2->over = NULL; + n2->location = @1; + n2->call_func = false; + + $$ = (Node *)n2; + } + | DATE_SUB_P '(' a_expr ',' INTERVAL a_expr interval_unit ')' + { + bool isGreatThanHour = GreaterThanHour($7); + FuncCall *n1 = makeNode(FuncCall); + n1->funcname = SystemFuncName("any2interval"); + n1->args = lcons($6, $7); + n1->agg_order = NIL; + n1->agg_star = FALSE; + n1->agg_distinct = FALSE; + n1->func_variadic = FALSE; + n1->over = NULL; + n1->location = @5; + n1->call_func = false; + + FuncCall *n2 = makeNode(FuncCall); + n2->funcname = SystemFuncName("date_sub"); + n2->args = list_make3($3, (Node *)n1, makeBoolAConst(isGreatThanHour, @7)); + n2->agg_order = NIL; + n2->agg_star = FALSE; + n2->agg_distinct = FALSE; + n2->func_variadic = FALSE; + n2->over = NULL; + n2->location = @1; + n2->call_func = false; + + $$ = (Node *)n2; + } ; @@ -40031,6 +40086,8 @@ alias_name_col_name_keyword: COALESCE | CAST | DATE_P %prec IDENT + | DATE_ADD_P + | DATE_SUB_P | EXTRACT | SYSDATE | WEIGHT_STRING @@ -43709,6 +43766,16 @@ static char* IdentResolveToChar(DolphinIdent* ident, core_yyscan_t yyscanner) return ident->str; } } + +static bool GreaterThanHour (List* argsList) { + A_Const *n = (A_Const *) linitial(argsList); + if ((n->val.val.ival & (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH) | INTERVAL_MASK(QUARTER) + | INTERVAL_MASK(WEEK) | INTERVAL_MASK(DAY))) != 0) { + return true; + } else { + return false; + } +} /* * Must undefine this stuff before including scan.c, since it has different * definitions for these macros. diff --git a/contrib/dolphin/plugin_utils/adt/date.cpp b/contrib/dolphin/plugin_utils/adt/date.cpp index 6290b1327f99cd56f253112a894264fbe4e38585..6feacd2783d29c0c83afec11de6739cd5740b8b7 100644 --- a/contrib/dolphin/plugin_utils/adt/date.cpp +++ b/contrib/dolphin/plugin_utils/adt/date.cpp @@ -317,6 +317,8 @@ extern "C" DLL_PUBLIC Datum date_add_datetime_interval(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1_PUBLIC(date_add_timestamp_interval); extern "C" DLL_PUBLIC Datum date_add_timestamp_interval(PG_FUNCTION_ARGS); + +extern Datum DirectFunctionCall0(PGFunction func); #endif /* common code for timetypmodin and timetztypmodin */ static int32 anytime_typmodin(bool istz, ArrayType* ta) @@ -7886,6 +7888,57 @@ Datum dolphin_datenot(PG_FUNCTION_ARGS) j2date(date + POSTGRES_EPOCH_JDATE, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); PG_RETURN_UINT64(~((uint64)date2int(tm))); } + +PG_FUNCTION_INFO_V1_PUBLIC(date_add_explicit); +extern "C" DLL_PUBLIC Datum date_add_explicit(PG_FUNCTION_ARGS); +/** + * date_add(time, INTERVAL expr UNIT) +*/ +Datum date_add_explicit(PG_FUNCTION_ARGS) +{ + int errlevel = (!fcinfo->can_ignore && SQL_MODE_STRICT() ? ERROR : WARNING); + TimeADT time = PG_GETARG_TIMEADT(0); + Interval *span = PG_GETARG_INTERVAL_P(1); + bool isGreaterThanHour = PG_GETARG_BOOL(2); + int cmpt_version = GetSessionContext()->cmpt_version; + if (cmpt_version == MYSQL_VERSION_5_7) { + if (span->month != 0) { + ereport(errlevel, (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), errmsg("time field value out of range"))); + PG_RETURN_NULL(); + } + } else if (cmpt_version == MYSQL_VERSION_8_0) { + if (span->month != 0 || span->day != 0 || isGreaterThanHour) { + // convert time to datetime, then call adddate_datetime_interval_t(datetime, interval) + bool isRetNull = false; + Datum timetxt = DirectFunctionCall1(time_text, time); + Datum curdt = DirectFunctionCall0(curdate); + Datum curdatetime = DirectFunctionCall1(date_timestamp, curdt); + Datum curdatetimetxt = DirectFunctionCall1(timestamp_text, curdatetime); + Datum datetime = DirectFunctionCall2(addtime_text, curdatetimetxt, timetxt); + + Datum result = DirectCall2(&isRetNull, adddate_datetime_interval_t, + InvalidOid, datetime, PG_GETARG_DATUM(1)); + if (isRetNull) { + PG_RETURN_NULL(); + } else { + return result; + } + } + } + + TimeADT time2 = span->time; +#ifdef HAVE_INT64_TIMESTAMP + time2 += (span->day * HOURS_PER_DAY * MINS_PER_HOUR * SECS_PER_MINUTE * USECS_PER_SEC); +#else + time2 += (span->day * HOURS_PER_DAY * MINS_PER_HOUR * SECS_PER_MINUTE); +#endif + time += time2; + if (time >= -B_FORMAT_TIME_MAX_VALUE && time <= B_FORMAT_TIME_MAX_VALUE) { + return DirectFunctionCall1(time_text, time); + } + ereport(errlevel, (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), errmsg("time field value out of range"))); + PG_RETURN_NULL(); +} #endif #endif diff --git a/contrib/dolphin/plugin_utils/fmgr/fmgr.cpp b/contrib/dolphin/plugin_utils/fmgr/fmgr.cpp index 1613560bf6a97b441e0e69e487a5f57ebb32e596..f9610c9efae40b528c4ebe9622fa52815c02575e 100644 --- a/contrib/dolphin/plugin_utils/fmgr/fmgr.cpp +++ b/contrib/dolphin/plugin_utils/fmgr/fmgr.cpp @@ -3212,5 +3212,23 @@ Datum DirectCall2WithNullArg(bool* isRetNull, PGFunction func, Oid collation, Da return result; } +Datum DirectFunctionCall0(PGFunction func) +{ + FunctionCallInfoData fcinfo; + Datum result; + + InitFunctionCallInfoData(fcinfo, NULL, 1, InvalidOid, NULL, NULL); + + result = (*func)(&fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (fcinfo.isnull) { + ereport(ERROR, (errmodule(MOD_EXECUTOR), errcode(ERRCODE_UNEXPECTED_NULL_VALUE), + errmsg("function returned NULL"))); + } + + return result; +} + #endif diff --git a/contrib/dolphin/rollback_script/dolphin--4.0--3.0.sql b/contrib/dolphin/rollback_script/dolphin--4.0--3.0.sql index 3bebab88802145248bf74e1db26e15a1aaae0278..5fd60eb3e1ea49691d339ef7c5e2025bbb231be1 100644 --- a/contrib/dolphin/rollback_script/dolphin--4.0--3.0.sql +++ b/contrib/dolphin/rollback_script/dolphin--4.0--3.0.sql @@ -586,6 +586,21 @@ DROP FUNCTION IF EXISTS pg_catalog.date_add (timestamp without time zone, interv CREATE OR REPLACE FUNCTION pg_catalog.date_add (time, interval) RETURNS time AS $$ SELECT pg_catalog.adddate($1, $2) $$ LANGUAGE SQL; CREATE OR REPLACE FUNCTION pg_catalog.date_sub (time, interval) RETURNS time AS $$ SELECT pg_catalog.adddate($1, -$2) $$ LANGUAGE SQL; +DROP FUNCTION IF EXISTS pg_catalog.date_add(time, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_add(timestamp without time zone, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_add(timestamptz, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_add(text, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_add(numeric, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_add(year, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_add(date, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(time, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(timestamp without time zone, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(timestamptz, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(text, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(numeric, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(year, interval, boolean); +DROP FUNCTION IF EXISTS pg_catalog.date_sub(date, interval, boolean); + DROP FUNCTION IF EXISTS pg_catalog.any2interval(anyelement, integer); DROP FUNCTION IF EXISTS pg_catalog.any2interval(anyelement, integer, integer); diff --git a/contrib/dolphin/sql/test_interval_expr.sql b/contrib/dolphin/sql/test_interval_expr.sql index 690d185995505b64a3b77f490fcf846c2f40d197..883ac2342e859b8c68ebcd5d1a3483f4cfc9d0e2 100644 --- a/contrib/dolphin/sql/test_interval_expr.sql +++ b/contrib/dolphin/sql/test_interval_expr.sql @@ -209,6 +209,25 @@ select '2024-06-18 18:30:00' - interval 1 + c1 minute to microsecond from t1; select '2024-06-18 18:30:00' - interval 1 + c1 second_microsecond from t1; select '2024-06-18 18:30:00' - interval 1 + c1 second to microsecond from t1; +-- 函数 +set dolphin.cmpt_version=8.0; +select date_add(time'38:59:58', interval '5 4' year_month); +select date_add(time'38:59:58', interval '5 4' day_minute); +select date_add(time'38:59:58', interval '5:4' day_second); +select date_add(time'38:59:58', interval '5:4' day_hour); +select date_add(time'38:59:58', interval '5:4' hour_minute); +select date_add(time'38:59:58', interval '5 4' hour_second); +select date_add(time'38:59:58', interval '5 4' minute_second); +select date_add(time'38:59:58', interval '5 4' week); +select date_sub(time'38:59:58', interval '5 4' year_month); +select date_sub(time'38:59:58', interval '5 4' day_minute); +select date_sub(time'38:59:58', interval '5:4' day_second); +select date_sub(time'38:59:58', interval '5:4' day_hour); +select date_sub(time'38:59:58', interval '5:4' hour_minute); +select date_sub(time'38:59:58', interval '5 4' hour_second); +select date_sub(time'38:59:58', interval '5 4' minute_second); +select date_sub(time'38:59:58', interval '5 4' week); +set dolphin.cmpt_version=5.7; -- 全类型兼容 create table all_types_table ( `int1` tinyint, diff --git a/contrib/dolphin/upgrade_script/dolphin--3.0--4.0.sql b/contrib/dolphin/upgrade_script/dolphin--3.0--4.0.sql index 3aef693ce34f3d49ba4b9e8983c354843fe2928b..f0a62425657977e7d51fdc345d0ce14ef253e014 100644 --- a/contrib/dolphin/upgrade_script/dolphin--3.0--4.0.sql +++ b/contrib/dolphin/upgrade_script/dolphin--3.0--4.0.sql @@ -763,6 +763,21 @@ BEGIN END $$; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(time, interval, boolean) RETURNS text LANGUAGE C STABLE STRICT as '$libdir/dolphin', 'date_add_explicit'; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(timestamp without time zone, interval, boolean) RETURNS timestamp without time zone AS $$ SELECT pg_catalog.date_add($1, $2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(timestamptz, interval, boolean) RETURNS timestamptz AS $$ SELECT pg_catalog.date_add($1, $2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(text, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, $2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(numeric, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, $2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(year, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, $2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_add(date, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, $2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(time, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, -$2, $3) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(timestamp without time zone, interval, boolean) RETURNS timestamp without time zone AS $$ SELECT pg_catalog.date_add($1, -$2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(timestamptz, interval, boolean) RETURNS timestamptz AS $$ SELECT pg_catalog.date_add($1, -$2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(text, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, -$2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(numeric, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, -$2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(year, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, -$2) $$ LANGUAGE SQL; +CREATE OR REPLACE FUNCTION pg_catalog.date_sub(date, interval, boolean) RETURNS text AS $$ SELECT pg_catalog.date_add($1, -$2) $$ LANGUAGE SQL; + create or replace function pg_catalog.any2interval(anyelement, integer) returns interval LANGUAGE C IMMUTABLE STRICT as '$libdir/dolphin','any2interval'; create or replace function pg_catalog.any2interval(anyelement, integer, integer) returns interval LANGUAGE C IMMUTABLE STRICT as '$libdir/dolphin','any2interval';