From b9c17656e91fc488197db4a713314853b6945f07 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 9 Nov 2017 13:15:00 +0300 Subject: [PATCH] Fixed CORE-5657: Various UDF-related security vulnerabilities --- .../aix/classic/makeInstallImage.sh.in | 10 +- .../arch-specific/freebsd/install.sh.in | 13 +- .../linux/makeInstallImage.sh.in | 9 - .../arch-specific/netbsd/install.sh.in | 6 - .../win32/BuildExecutableInstall.bat | 17 - builds/install/misc/firebird.conf.in | 5 +- builds/posix/Makefile.in | 36 +- builds/posix/make.shared.variables | 9 +- builds/posix/postfix.darwin | 4 - doc/README.incompatibilities.3to4.txt | 32 + src/common/config/config.cpp | 8 +- src/extlib/UdfBackwardCompatibility.cpp | 348 ++++++++ src/extlib/UdfBackwardCompatibility.sql | 43 + src/extlib/fbudf/MSReadMe.txt | 37 - src/extlib/fbudf/fbudf.cpp | 835 ------------------ src/extlib/fbudf/fbudf.h | 94 -- src/extlib/fbudf/fbudf.sql | 206 ----- src/extlib/fbudf/fbudf.txt | 47 - src/extlib/fbudf/stdafx.cpp | 30 - src/extlib/fbudf/stdafx.h | 79 -- src/extlib/ib_udf.cpp | 462 ---------- src/extlib/ib_udf.h | 38 - src/extlib/ib_udf.sql | 692 --------------- src/extlib/ib_udf2.sql | 716 --------------- src/extlib/ib_util.cpp | 2 +- src/include/firebird/Message.h | 2 +- 26 files changed, 454 insertions(+), 3326 deletions(-) create mode 100644 doc/README.incompatibilities.3to4.txt create mode 100644 src/extlib/UdfBackwardCompatibility.cpp create mode 100644 src/extlib/UdfBackwardCompatibility.sql delete mode 100644 src/extlib/fbudf/MSReadMe.txt delete mode 100644 src/extlib/fbudf/fbudf.cpp delete mode 100644 src/extlib/fbudf/fbudf.h delete mode 100644 src/extlib/fbudf/fbudf.sql delete mode 100644 src/extlib/fbudf/fbudf.txt delete mode 100644 src/extlib/fbudf/stdafx.cpp delete mode 100644 src/extlib/fbudf/stdafx.h delete mode 100644 src/extlib/ib_udf.cpp delete mode 100644 src/extlib/ib_udf.h delete mode 100644 src/extlib/ib_udf.sql delete mode 100644 src/extlib/ib_udf2.sql diff --git a/builds/install/arch-specific/aix/classic/makeInstallImage.sh.in b/builds/install/arch-specific/aix/classic/makeInstallImage.sh.in index 8ab5c3ad78..51b55595ea 100644 --- a/builds/install/arch-specific/aix/classic/makeInstallImage.sh.in +++ b/builds/install/arch-specific/aix/classic/makeInstallImage.sh.in @@ -113,7 +113,7 @@ copyFiles() { mkdir $DestDir/UDF mkdir $DestDir/misc mkdir $DestDir/misc/upgrade - for i in ib_udf metadata security; do + for i in metadata security; do mkdir $DestDir/misc/upgrade/$i done @@ -184,16 +184,8 @@ copyFiles() { cp $BuiltFBDir/intl/libfbintl.so $DestDir/intl/fbintl - cp $BuiltFBDir/UDF/ib_udf.so $DestDir/UDF/ - cp $BuiltFBDir/UDF/fbudf.so $DestDir/UDF/ - # Note that the following items copy files from outside the build tree -# Copy the sql-declarations into the UDF-directory - cp $BuildRootDir/src/extlib/ib_udf.sql $DestDir/UDF/ - cp $BuildRootDir/src/extlib/ib_udf2.sql $DestDir/UDF/ - cp $BuildRootDir/src/extlib/fbudf/fbudf.sql $DestDir/UDF/ - # Copy various documentation ls $BuildRootDir/doc/*.pdf >/dev/null 2>&1 && cp $BuildRootDir/doc/*.pdf $DestDir/doc cp $BuildRootDir/doc/sql.extensions/README* $DestDir/doc/sql.extensions diff --git a/builds/install/arch-specific/freebsd/install.sh.in b/builds/install/arch-specific/freebsd/install.sh.in index ee9e752215..d566c7ee00 100755 --- a/builds/install/arch-specific/freebsd/install.sh.in +++ b/builds/install/arch-specific/freebsd/install.sh.in @@ -139,7 +139,7 @@ copyFiles() { makeDirs "@FB_BINDIR@ @FB_SBINDIR@ @FB_CONFDIR@ @FB_LIBDIR@ @FB_INCDIR@ @FB_DOCDIR@/sql.extensions @FB_UDFDIR@ @FB_SAMPLEDIR@ \ @FB_SAMPLEDBDIR@ @FB_HELPDIR@ @FB_INTLDIR@ @FB_MISCDIR@ @FB_SECDBDIR@ @FB_MSGDIR@ @FB_LOGDIR@ @FB_GUARDDIR@ @FB_PLUGDIR@" - for i in ib_udf metadata security; do + for i in metadata security; do makeDirs @FB_MISCDIR@/upgrade/$i done @@ -263,18 +263,7 @@ copyFiles() { chmod 0755 ${TargetDir}@FB_INTLDIR@/fbintl chmod 0644 ${TargetDir}@FB_INTLDIR@/fbintl.conf - #UDF - cp $BuiltFBDir/UDF/ib_udf.so ${TargetDir}@FB_UDFDIR@ - cp $BuiltFBDir/UDF/fbudf.so ${TargetDir}@FB_UDFDIR@ # Note that the following items copy files from outside the build tree. -# Copy the sql-declarations into the UDF-directory - cp $BuildRootDir/src/extlib/ib_udf.sql ${TargetDir}@FB_UDFDIR@ - cp $BuildRootDir/src/extlib/ib_udf2.sql ${TargetDir}@FB_UDFDIR@ - cp $BuildRootDir/src/extlib/fbudf/fbudf.sql ${TargetDir}@FB_UDFDIR@ - - chown root:wheel ${TargetDir}@FB_UDFDIR@/*.so ${TargetDir}@FB_UDFDIR@/*.sql - chmod 0755 ${TargetDir}@FB_UDFDIR@/*.so - chmod 0644 ${TargetDir}@FB_UDFDIR@/*.sql #doc cp $BuildRootDir/doc/README.* ${TargetDir}@FB_DOCDIR@ diff --git a/builds/install/arch-specific/linux/makeInstallImage.sh.in b/builds/install/arch-specific/linux/makeInstallImage.sh.in index 51895f9185..aede06a984 100644 --- a/builds/install/arch-specific/linux/makeInstallImage.sh.in +++ b/builds/install/arch-specific/linux/makeInstallImage.sh.in @@ -262,17 +262,8 @@ copyFiles() { chmod 0755 ${TargetDir}@FB_INTLDIR@/fbintl chmod 0644 ${TargetDir}@FB_INTLDIR@/fbintl.conf - #UDF - cp $BuiltFBDir/UDF/ib_udf.so ${TargetDir}@FB_UDFDIR@ - cp $BuiltFBDir/UDF/fbudf.so ${TargetDir}@FB_UDFDIR@ # Note that the following items copy files from outside the build tree. # Copy the sql-declarations into the UDF-directory - cp $BuildRootDir/src/extlib/ib_udf.sql ${TargetDir}@FB_UDFDIR@ - cp $BuildRootDir/src/extlib/ib_udf2.sql ${TargetDir}@FB_UDFDIR@ - cp $BuildRootDir/src/extlib/fbudf/fbudf.sql ${TargetDir}@FB_UDFDIR@ - - chmod 0755 ${TargetDir}@FB_UDFDIR@/*.so - chmod 0644 ${TargetDir}@FB_UDFDIR@/*.sql #doc cp $BuildRootDir/doc/*.pdf ${TargetDir}@FB_DOCDIR@ diff --git a/builds/install/arch-specific/netbsd/install.sh.in b/builds/install/arch-specific/netbsd/install.sh.in index 42c1a83e78..f820fef0b0 100644 --- a/builds/install/arch-specific/netbsd/install.sh.in +++ b/builds/install/arch-specific/netbsd/install.sh.in @@ -179,12 +179,6 @@ cp -Rf $BuiltFBDir/lib/libfbclient.so* $DestDir/lib || exit cp -f $BuiltFBDir/lib/libib_util.so $DestDir/lib/libib_util.so || exit cp $BuiltFBDir/intl/libfbintl.so $DestDir/intl/fbintl || exit -cp $BuiltFBDir/UDF/ib_udf.so $DestDir/UDF/ || exit -cp $BuiltFBDir/UDF/fbudf.so $DestDir/UDF/ || exit - -# Copy the sql-declarations into the UDF-directory -cp $BuildRootDir/src/extlib/ib_udf.sql $DestDir/UDF/ || exit -cp $BuildRootDir/src/extlib/fbudf/fbudf.sql $DestDir/UDF/ || exit # Note that this copies files from outside the build tree. echo Installing documentation diff --git a/builds/install/arch-specific/win32/BuildExecutableInstall.bat b/builds/install/arch-specific/win32/BuildExecutableInstall.bat index 27f3570ffe..829bb26159 100644 --- a/builds/install/arch-specific/win32/BuildExecutableInstall.bat +++ b/builds/install/arch-specific/win32/BuildExecutableInstall.bat @@ -287,23 +287,6 @@ if "%PROCESSOR_ARCHITECTURE%"=="x86" ( goto :EOF ) -@echo Copying udf library scripts... -for %%v in ( ib_udf.sql ib_udf2.sql ) do ( - @copy /Y %FB_ROOT_PATH%\src\extlib\%%v %FB_OUTPUT_DIR%\udf\%%v > nul - @if %ERRORLEVEL% GEQ 1 ( - call :ERROR copy /Y %FB_ROOT_PATH%\src\extlib\%%v %FB_OUTPUT_DIR%\udf\%%v failed with error %ERRORLEVEL%. - goto :EOF - ) -) - -for %%v in ( fbudf.sql fbudf.txt ) do ( - @copy /Y %FB_ROOT_PATH%\src\extlib\fbudf\%%v %FB_OUTPUT_DIR%\UDF\%%v > nul - @if %ERRORLEVEL% GEQ 1 ( - call :ERROR copy /Y %FB_ROOT_PATH%\src\extlib\fbudf\%%v %FB_OUTPUT_DIR%\UDF\%%v failed with error %ERRORLEVEL%. - goto :EOF - ) -) - :: Various upgrade scripts and docs mkdir %FB_OUTPUT_DIR%\misc\upgrade\security 2>nul @copy %FB_ROOT_PATH%\src\misc\upgrade\v3.0\security_* %FB_OUTPUT_DIR%\misc\upgrade\security > nul diff --git a/builds/install/misc/firebird.conf.in b/builds/install/misc/firebird.conf.in index 57e69a9acf..7a8422cb63 100644 --- a/builds/install/misc/firebird.conf.in +++ b/builds/install/misc/firebird.conf.in @@ -180,12 +180,11 @@ # NOTE: THE EXTERNAL FUNCTION ENGINE FEATURE COULD BE USED TO COMPROMISE # THE SERVER/HOST AS WELL AS DATABASE SECURITY!! # -# IT IS STRONGLY RECOMMENDED THAT THIS SETTING BE USED TO LIMIT -# EXTERNAL FUNCTION LOCATIONS! +# IT IS STRONGLY RECOMMENDED THAT THIS SETTING BE SET TO NONE! # # Type: string (special format) # -#UdfAccess = Restrict UDF +#UdfAccess = None # ---------------------------- diff --git a/builds/posix/Makefile.in b/builds/posix/Makefile.in index 70604e794d..bb9b43bc8c 100644 --- a/builds/posix/Makefile.in +++ b/builds/posix/Makefile.in @@ -396,27 +396,10 @@ msg.timestamp: $(MSG_FILES) $(TOUCH) $@ -.PHONY: udfs ibutil ibudf fbudf udfsupport +.PHONY: ibutil udfsupport udfsupport: $(MAKE) ibutil - $(MAKE) udfs - -udfs: ibudf fbudf - -UDF = $(FB_BUILD)/UDF -IBUDF_SO = $(UDF)/ib_udf.$(SHRLIB_EXT) -FBUDF_SO = $(UDF)/fbudf.$(SHRLIB_EXT) - -ibudf: $(IBUDF_SO) - -$(IBUDF_SO): $(IBUDF_Objects) - $(call LINK_UDF,ib_udf) -o $@ $^ $(LINK_UDF_LIBS) - -fbudf: $(FBUDF_SO) - -$(FBUDF_SO): $(FBUDF_Objects) $(COMMON_LIB) - $(call LINK_UDF,fbudf) -o $@ $^ $(LINK_UDF_LIBS) $(FIREBIRD_LIBRARY_LINK) ibutil: $(LIBIBUTIL_SO) @@ -425,7 +408,7 @@ $(LIBIBUTIL_SO): $(UTIL_Objects) #___________________________________________________________________________ -# most of utilities, including network server and UDFs +# most of utilities, including network server and UDF support # qli is not here cause it needs special database (help.fdb), therefore needs gbak # @@ -498,20 +481,22 @@ $(NBACKUP): $(NBACKUP_Objects) $(COMMON_LIB) # plugins - some of them are required to build examples, use separate entry for them # -.PHONY: udr legacy_user_management legacy_auth_server trace auth_debug +.PHONY: udr legacy_user_management legacy_auth_server trace auth_debug udf_compat UDR_PLUGIN = $(call makePluginName,udr_engine) LEGACY_USER_MANAGER = $(call makePluginName,Legacy_UserManager) LEGACY_AUTH_SERVER = $(call makePluginName,Legacy_Auth) SRP_USER_MANAGER = $(call makePluginName,Srp) FBTRACE = $(call makePluginName,fbtrace) AUTH_DEBUGGER = $(call makePluginName,Auth_Debug) +UDF_BACKWARD_COMPATIBILITY_BASENAME = $(LIB_PREFIX)udf_compat.$(SHRLIB_EXT) +UDF_BACKWARD_COMPATIBILITY = $(PLUGINS)/udr/$(UDF_BACKWARD_COMPATIBILITY_BASENAME) BUILD_DEBUG:= ifeq ($(TARGET),Debug) BUILD_DEBUG:=auth_debug endif -plugins: udr legacy_user_management legacy_auth_server srp_user_management trace $(BUILD_DEBUG) +plugins: udr legacy_user_management legacy_auth_server srp_user_management trace $(BUILD_DEBUG) udf_compat udr: $(UDR_PLUGIN) $(PLUGINS)/udr_engine.conf @@ -546,6 +531,15 @@ srp_user_management: $(SRP_USER_MANAGER) $(SRP_USER_MANAGER): $(SRP_USERS_MANAGE_Objects) $(COMMON_LIB) $(LINK_PLUGIN) $(call LIB_LINK_SONAME,$(notdir $@).0) -o $@ $^ $(LINK_PLUG_LIBS) $(FIREBIRD_LIBRARY_LINK) +udf_compat: $(UDF_BACKWARD_COMPATIBILITY) $(COMPAT_SQL) + +$(UDF_BACKWARD_COMPATIBILITY): $(COMPAT_Objects) + $(LIB_LINK) $(LIB_LINK_OPTIONS) $(call LIB_LINK_SONAME,$(UDF_BACKWARD_COMPATIBILITY_BASENAME)) \ + $(LINK_UDR_PLUGIN_SYMBOLS) $(LIB_PATH_OPTS) $(UNDEF_FLAGS) -o $@ $^ $(THR_LIBS) + +$(COMPAT_SQL): $(SRC_COMPAT_SQL) + cp $^ $@ + #___________________________________________________________________________ # codes - developers change them sometimes # diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables index 7ae11b4fef..df3982913e 100644 --- a/builds/posix/make.shared.variables +++ b/builds/posix/make.shared.variables @@ -196,11 +196,14 @@ AllObjects += $(UDRENG_Objects) # UDF support -IBUDF_Objects:= $(call makeObjects,extlib,ib_udf.cpp) -FBUDF_Objects:= $(call makeObjects,extlib/fbudf,fbudf.cpp) UTIL_Objects:= $(call makeObjects,extlib,ib_util.cpp) -AllObjects += $(IBUDF_Objects) $(FBUDF_Objects) $(UTIL_Objects) +# UDR backward compatible with distributed UDFs +COMPAT_Objects:= $(call makeObjects,extlib,UdfBackwardCompatibility.cpp) +SRC_COMPAT_SQL:= $(SRC_ROOT)/extlib/UdfBackwardCompatibility.sql +COMPAT_SQL:= $(PLUGINS)/udr/udf_compat.sql + +AllObjects += $(UTIL_Objects) $(COMPAT_Objects) # Regenerate error codes diff --git a/builds/posix/postfix.darwin b/builds/posix/postfix.darwin index 6c57844b17..35afe239dc 100644 --- a/builds/posix/postfix.darwin +++ b/builds/posix/postfix.darwin @@ -96,8 +96,6 @@ darwin_finish_cs_framework: chmod +x ../builds/install/arch-specific/darwin/changeMultiConnectMode cp ../builds/install/arch-specific/darwin/changeMultiConnectMode \ $(FB_FW)/Resources/bin/changeMultiConnectMode.sh - cp ../src/extlib/ib_udf.sql $(FB_FW)/Resources/English.lproj/var/UDF - cp ../src/extlib/fbudf/fbudf.sql $(FB_FW)/Resources/English.lproj/var/UDF cp ../builds/install/arch-specific/darwin/FrameworkInfo.plist \ $(FB_FW)/Resources/Info.plist cp ../builds/install/arch-specific/darwin/launchd.org.firebird.gds.plist \ @@ -165,8 +163,6 @@ darwin_finish_ss_framework: cp ../gen/firebird/bin/gdef $(FB_FW)/Resources/bin cp ../gen/firebird/bin/fbsvcmgr $(FB_FW)/Resources/bin cp ../gen/firebird/bin/fbtracemgr $(FB_FW)/Resources/bin - cp ../src/extlib/ib_udf.sql $(FB_FW)/Resources/English.lproj/var/UDF - cp ../src/extlib/fbudf/fbudf.sql $(FB_FW)/Resources/English.lproj/var/UDF cp ../builds/install/arch-specific/darwin/FrameworkInfo.plist \ $(FB_FW)/Resources/Info.plist cp ../builds/install/arch-specific/darwin/launchdss.org.firebird.gds.plist \ diff --git a/doc/README.incompatibilities.3to4.txt b/doc/README.incompatibilities.3to4.txt new file mode 100644 index 0000000000..417ec1b960 --- /dev/null +++ b/doc/README.incompatibilities.3to4.txt @@ -0,0 +1,32 @@ +******************************************************************************** + LIST OF KNOWN INCOMPATIBILITIES + between versions 3.0 and 4.0 +******************************************************************************** + +This document describes all the changes that make v4.0 incompatible in any way +as compared with the previous releases and hence could affect your databases and +applications. + +Please read the below descriptions carefully before upgrading your software to +the new Firebird version. + +Deprecating UDF +-------------------------- + + * Initial design of UDF always used to be security problem. The most dangerous + security holes when UDFs and external tables are used simultaneousky were + fixed in FB 1.5. But even after it incorrectly declared (using SQL statement + DECLARE EXTERNAL FUNCTION) UDF can easily cause various security issues like + server crash or execution of arbitrary code. Therefore UDFs are deprecated + in v.4. That means that UDFs can't be used with default configuration + (parameter "UdfAccess" set to "None") and all sample UDF libraries (ib_udf, + fbudf) are not distributed any more. Most of functions in that libraries + were replaced with builtin analogs in previous versions and therefore + already deprecated. A few remaining functions got safe replacement in UDR + library "udf_compat", namely div, frac, dow, sdow, getExactTimestampUTC and + isLeapYear. Users who still wish to use UDFs should set "UdfAccess" to + "Restrict ". If you never used to modify this parameter before + path-list is just UDF and resulting line in firebird.conf should be: + UdfAccess = Restrict UDF + Recommended long-term solution is replacing of UDF with UDR. + diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 1d2e228d7a..04abb3ba82 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -162,8 +162,8 @@ const Config::ConfigEntry Config::entries[MAX_CONFIG_KEY] = {TYPE_STRING, "RemoteBindAddress", (ConfigValue) 0}, {TYPE_STRING, "ExternalFileAccess", (ConfigValue) "None"}, // location(s) of external files for tables {TYPE_STRING, "DatabaseAccess", (ConfigValue) "Full"}, // location(s) of databases -#define UDF_DEFAULT_CONFIG_VALUE "Restrict UDF" - {TYPE_STRING, "UdfAccess", (ConfigValue) UDF_DEFAULT_CONFIG_VALUE}, // location(s) of UDFs +#define UDF_DEFAULT_RESTRICT_VALUE "Restrict UDF" // use it to substitute FB_UDFDIR value + {TYPE_STRING, "UdfAccess", (ConfigValue) "None"}, // location(s) of UDFs {TYPE_STRING, "TempDirectories", (ConfigValue) 0}, #ifdef DEV_BUILD {TYPE_BOOLEAN, "BugcheckAbort", (ConfigValue) true}, // whether to abort() engine when internal error is found @@ -595,8 +595,8 @@ const char *Config::getUdfAccess() } const char* v = (const char*) getDefaultConfig()->values[KEY_UDF_ACCESS]; - if (CASE_SENSITIVITY ? (! strcmp(v, UDF_DEFAULT_CONFIG_VALUE) && FB_UDFDIR[0]) : - (! fb_utils::stricmp(v, UDF_DEFAULT_CONFIG_VALUE) && FB_UDFDIR[0])) + if (CASE_SENSITIVITY ? (! strcmp(v, UDF_DEFAULT_RESTRICT_VALUE) && FB_UDFDIR[0]) : + (! fb_utils::stricmp(v, UDF_DEFAULT_RESTRICT_VALUE) && FB_UDFDIR[0])) { udfValue->printf("Restrict %s", FB_UDFDIR); value = udfValue->c_str(); diff --git a/src/extlib/UdfBackwardCompatibility.cpp b/src/extlib/UdfBackwardCompatibility.cpp new file mode 100644 index 0000000000..a7cb1b267d --- /dev/null +++ b/src/extlib/UdfBackwardCompatibility.cpp @@ -0,0 +1,348 @@ +/* + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Adriano dos Santos Fernandes + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2008 Adriano dos Santos Fernandes + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * Alex Peshkoff, 2017 + * Claudio Valderrama, 2001 + */ + +#define FB_UDR_STATUS_TYPE ::Firebird::ThrowStatusWrapper + +#include "firebird.h" + +#include +#include + +#include +#include +#include + +#ifdef HAVE_MATH_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#ifdef HAVE_SYS_TIMEB_H +# include +#endif + +#ifdef HAVE_LOCALE_H +#include +#endif + +#include + +using namespace Firebird; + + +//------------------------------------------------------------------------------ + +/*** +create function div ( + n1 integer, + n2 integer +) returns double precision + external name 'udf_compat!UC_div' + engine udr; +***/ +FB_UDR_BEGIN_FUNCTION(UC_div) + FB_UDR_MESSAGE(InMessage, + (FB_INTEGER, n1) + (FB_INTEGER, n2) + ); + + FB_UDR_MESSAGE(OutMessage, + (FB_DOUBLE, result) + ); + + FB_UDR_EXECUTE_FUNCTION + { + if (in->n1Null || in->n2Null) + { + out->resultNull = FB_TRUE; + out->result = 0; + } + else + { + out->resultNull = FB_FALSE; + if (in->n2) + out->result = div(in->n1, in->n2).quot; + else + out->result = std::numeric_limits::infinity(); + } + } +FB_UDR_END_FUNCTION + +/*** +create function frac ( + val double precision +) returns double precision + external name 'udf_compat!UC_frac' + engine udr; +***/ +FB_UDR_BEGIN_FUNCTION(UC_frac) + FB_UDR_MESSAGE(InMessage, + (FB_DOUBLE, val) + ); + + FB_UDR_MESSAGE(OutMessage, + (FB_DOUBLE, result) + ); + + FB_UDR_EXECUTE_FUNCTION + { + if (in->valNull) + { + out->resultNull = FB_TRUE; + out->result = 0; + } + else + { + out->resultNull = FB_FALSE; + out->result = in->val > 0 ? in->val - floor(in->val) : + in->val < 0 ? in->val - ceil(in->val) : 0; + } + } +FB_UDR_END_FUNCTION + + +namespace +{ + enum day_format {day_short, day_long}; + const FB_SIZE_T day_len[] = {4, 14}; + const char* day_fmtstr[] = {"%a", "%A"}; + + void decode_timestamp(IUtil* u, const FbTimestamp* from, tm* to, unsigned* fractions) + { + // decode firebird timestamp format + memset(to, 0, sizeof(tm)); + from->date.decode(u, (unsigned*)&to->tm_year, (unsigned*)&to->tm_mon, (unsigned*)&to->tm_mday); + to->tm_year -= 1900; + to->tm_mon--; + from->time.decode(u, (unsigned*)&to->tm_hour, (unsigned*)&to->tm_min, (unsigned*)&to->tm_sec, fractions); + + // set wday/yday + time_t tt = mktime(to); + localtime_r(&tt, to); + } + + template + void get_DOW(IUtil* u, const FbTimestamp* v, VC* rc, const day_format df) + { + // decode firebird timestamp format + tm times; + decode_timestamp(u, v, ×, NULL); + + const int dow = times.tm_wday; + if (dow >= 0 && dow <= 6) + { + FB_SIZE_T name_len = day_len[df]; + const char* name_fmt = day_fmtstr[df]; + // There should be a better way to do this than to alter the thread's locale. + if (!strcmp(setlocale(LC_TIME, NULL), "C")) + setlocale(LC_ALL, ""); + name_len = static_cast(strftime(rc->str, name_len, name_fmt, ×)); + if (name_len) + { + // There's no clarity in the docs whether '\0' is counted or not; be safe. + if (!rc->str[name_len - 1]) + --name_len; + rc->length = name_len; + return; + } + } + rc->length = df == day_long ? 5 : 3; + memcpy(rc->str, "ERROR", rc->length); + } + + void encode_timestamp(IUtil* u, const tm* from, const unsigned fractions, FbTimestamp* to) + { + tm times = *from; + + // decode firebird timestamp format + times.tm_year += 1900; + times.tm_mon++; + + to->date.encode(u, times.tm_year, times.tm_mon, times.tm_mday); + to->time.encode(u, times.tm_hour, times.tm_min, times.tm_sec, fractions); + } +} // anonymous namespace + +/*** +create function dow ( + val timestamp +) returns varchar(14) collate none + external name 'udf_compat!UC_dow' + engine udr; +***/ +FB_UDR_BEGIN_FUNCTION(UC_dow) + FB_UDR_MESSAGE(InMessage, + (FB_TIMESTAMP, val) + ); + + FB_UDR_MESSAGE(OutMessage, + (FB_VARCHAR(14), result) + ); + + FB_UDR_EXECUTE_FUNCTION + { + out->resultNull = in->valNull; + if (!out->resultNull) + get_DOW(master->getUtilInterface(), &in->val, &out->result, day_long); + } +FB_UDR_END_FUNCTION + +/*** +create function sdow ( + val timestamp +) returns varchar(4) collate none + external name 'udf_compat!UC_sdow' + engine udr; +***/ +FB_UDR_BEGIN_FUNCTION(UC_sdow) + FB_UDR_MESSAGE(InMessage, + (FB_TIMESTAMP, val) + ); + + FB_UDR_MESSAGE(OutMessage, + (FB_VARCHAR(4), result) + ); + + FB_UDR_EXECUTE_FUNCTION + { + out->resultNull = in->valNull; + if (!out->resultNull) + get_DOW(master->getUtilInterface(), &in->val, &out->result, day_short); + } +FB_UDR_END_FUNCTION + +/*** +create function getExactTimestampUTC + returns timestamp + external name 'udf_compat!UC_getExactTimestampUTC' + engine udr; +***/ +FB_UDR_BEGIN_FUNCTION(UC_getExactTimestampUTC) + FB_UDR_MESSAGE(OutMessage, + (FB_TIMESTAMP, result) + ); + + FB_UDR_EXECUTE_FUNCTION + { +#if defined(HAVE_GETTIMEOFDAY) + timeval tv; + GETTIMEOFDAY(&tv); + const time_t seconds = tv.tv_sec; + + tm timex; +#if defined(HAVE_GMTIME_R) + tm* times = gmtime_r(&seconds, &timex); +#else + timeMutex.enter(); + tm* times = gmtime(&seconds); + if (times) + { + // Copy to local variable before we exit the mutex. + timex = *times; + times = &timex; + } + timeMutex.leave(); +#endif // gmtime_r + + if (times) + { + encode_timestamp(master->getUtilInterface(), times, tv.tv_usec / 100, &out->result); + out->resultNull = false; + } + +#else // gettimeofday + + _timeb timebuffer; + _ftime(&timebuffer); + // gmtime uses thread local storage in NT, no need to lock threads. + // Of course, this facility is only available in multithreaded builds. + tm* times = gmtime(&timebuffer.time); + + if (times) + { + encode_timestamp(master->getUtilInterface(), times, timebuffer.millitm * 10, &out->result); + out->resultNull = false; + } + +#endif + + else + { + out->resultNull = true; + out->result.date.value = 0; + out->result.time.value = 0; + } + } +FB_UDR_END_FUNCTION + +/*** +create function isLeapYear ( + val timestamp +) returns boolean + external name 'udf_compat!UC_isLeapYear' + engine udr; +***/ +FB_UDR_BEGIN_FUNCTION(UC_isLeapYear) + FB_UDR_MESSAGE(InMessage, + (FB_TIMESTAMP, val) + ); + + FB_UDR_MESSAGE(OutMessage, + (FB_BOOLEAN, result) + ); + + FB_UDR_EXECUTE_FUNCTION + { + if (in->valNull) + { + out->result = FB_FALSE; + out->resultNull = true; + return; + } + const int ly = in->val.date.getYear(master->getUtilInterface()); + out->result = ((ly % 4 == 0 && ly % 100 != 0) || ly % 400 == 0) ? FB_TRUE : FB_FALSE; + out->resultNull = false; + } +FB_UDR_END_FUNCTION + +//------------------------------------------------------------------------------ + + +// This should be used in only one of the UDR library files. +// Build must export firebird_udr_plugin function. +FB_UDR_IMPLEMENT_ENTRY_POINT diff --git a/src/extlib/UdfBackwardCompatibility.sql b/src/extlib/UdfBackwardCompatibility.sql new file mode 100644 index 0000000000..2e55943c1e --- /dev/null +++ b/src/extlib/UdfBackwardCompatibility.sql @@ -0,0 +1,43 @@ +-- Create functions in current DB +create function div ( + n1 integer, + n2 integer +) returns double precision + external name 'udf_compat!UC_div' + engine udr; + +create function frac ( + val double precision +) returns double precision + external name 'udf_compat!UC_frac' + engine udr; + +create function dow ( + val timestamp +) returns varchar(14) collate none + external name 'udf_compat!UC_dow' + engine udr; + +create function sdow ( + val timestamp +) returns varchar(4) collate none + external name 'udf_compat!UC_sdow' + engine udr; + +create function getExactTimestampUTC + returns timestamp + external name 'udf_compat!UC_getExactTimestampUTC' + engine udr; + +create function isLeapYear ( + val timestamp +) returns boolean + external name 'udf_compat!UC_isLeapYear' + engine udr; + +-- Run minimum test +select 25, 3, div(25, 3) from rdb$database; +select pi(), frac(pi()) from rdb$database; +select current_date, dow(current_date), sdow(current_date) from rdb$database; +select current_timestamp, getExactTimestampUTC() from rdb$database; +select current_date, isLeapYear(current_date) from rdb$database; diff --git a/src/extlib/fbudf/MSReadMe.txt b/src/extlib/fbudf/MSReadMe.txt deleted file mode 100644 index 353397049e..0000000000 --- a/src/extlib/fbudf/MSReadMe.txt +++ /dev/null @@ -1,37 +0,0 @@ -======================================================================== - DYNAMIC LINK LIBRARY : fbudf -======================================================================== - - -AppWizard has created this fbudf DLL for you. - -This file contains a summary of what you will find in each of the files that -make up your fbudf application. - -fbudf.dsp - This file (the project file) contains information at the project level and - is used to build a single project or subproject. Other users can share the - project (.dsp) file, but they should export the makefiles locally. - -fbudf.cpp - This is the main DLL source file. - -fbudf.h - This file contains your DLL exports. - -///////////////////////////////////////////////////////////////////////////// -Other standard files: - -StdAfx.h, StdAfx.cpp - These files are used to build a precompiled header (PCH) file - named fbudf.pch and a precompiled types file named StdAfx.obj. - - -///////////////////////////////////////////////////////////////////////////// -Other notes: - -AppWizard uses "TODO:" to indicate parts of the source code you -should add to or customize. - - -///////////////////////////////////////////////////////////////////////////// diff --git a/src/extlib/fbudf/fbudf.cpp b/src/extlib/fbudf/fbudf.cpp deleted file mode 100644 index d3a5a9604f..0000000000 --- a/src/extlib/fbudf/fbudf.cpp +++ /dev/null @@ -1,835 +0,0 @@ -/* - * - * The contents of this file are subject to the Initial - * Developer's Public License Version 1.0 (the "License"); - * you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * http://www.ibphoenix.com/idpl.html. - * - * Software distributed under the License is distributed on - * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See the License for the specific - * language governing rights and limitations under the License. - * - * - * The Original Code was created by Claudio Valderrama C. for IBPhoenix. - * The development of the Original Code was sponsored by Craig Leonardi. - * - * Copyright (c) 2001 IBPhoenix - * All Rights Reserved. - * - * 2002.01.07 Claudio Valderrama: change the impolite way truncate and round work, - * make null handling more consistent and add dpower(x,y). - * Beware the SQL declaration for those functions has changed. - * 2002.01.20 Claudio Valderrama: addMonth should work with negative values, too. - * 2003.10.26: Made some values const and other minor changes. - * 2004.09.29 Claudio Valderrama: fix numeric overflow in addHour reported by - * "jssahdra" . Since all add