mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-17 00:35:55 +01:00
836 lines
19 KiB
C++
836 lines
19 KiB
C++
/*
|
|
*
|
|
* 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" <joga.singh at inventum.cc>. Since all add<time> functions are
|
|
* wrappers around addTenthMSec, only two lines needed to be fixed.
|
|
*/
|
|
|
|
|
|
// fbudf.cpp : Defines the entry point for the DLL application.
|
|
//
|
|
|
|
/*
|
|
CVC: The MS way of doing things puts the includes in stdafx. I expect
|
|
that you can continue this trend without problems on other compilers
|
|
or platforms. Since I conditioned the Windows-related includes, it should
|
|
be easy to add needed headers to stdafx.h after a makefile is built.
|
|
*/
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
|
#ifndef FBUDF_EXPORTS
|
|
#define FBUDF_EXPORTS
|
|
#endif
|
|
|
|
#include "fbudf.h"
|
|
#include "../common/classes/NoThrowTimeStamp.h"
|
|
|
|
#if defined(HAVE_GETTIMEOFDAY) && (!(defined (HAVE_LOCALTIME_R) && defined (HAVE_GMTIME_R)))
|
|
#define NEED_TIME_MUTEX
|
|
#include "../common/classes/locks.h"
|
|
#endif
|
|
|
|
|
|
//Original code for this library was written by Claudio Valderrama
|
|
// on July 2001 for IBPhoenix.
|
|
|
|
// Define this symbol if you want truncate and round to be symmetric wr to zero.
|
|
//#define SYMMETRIC_MATH
|
|
|
|
#if defined (_WIN32)
|
|
/*
|
|
BOOL APIENTRY DllMain( HANDLE ,//hModule,
|
|
DWORD ul_reason_for_call,
|
|
LPVOID //lpReserved
|
|
)
|
|
{
|
|
switch (ul_reason_for_call)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
case DLL_THREAD_ATTACH:
|
|
case DLL_THREAD_DETACH:
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
|
|
// To do: go from C++ native types to abstract FB types.
|
|
|
|
|
|
#ifdef DEV_BUILD
|
|
// This function shouldn't be defined in production.
|
|
FBUDF_API paramdsc* testreflect(paramdsc* rc)
|
|
{
|
|
rc->dsc_address = 0;
|
|
rc->dsc_flags = 1 | 4; //DSC_null | DSC_nullable;
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
|
|
namespace internal
|
|
{
|
|
typedef ISC_USHORT fb_len;
|
|
|
|
const long seconds_in_day = 86400;
|
|
const long tenthmsec_in_day = seconds_in_day * ISC_TIME_SECONDS_PRECISION;
|
|
const int varchar_indicator_size = sizeof(ISC_USHORT);
|
|
const int max_varchar_size = 65535 - varchar_indicator_size; // in theory
|
|
|
|
|
|
// This definition comes from jrd\val.h and is used in helper
|
|
// functions {get/set}_varchar_len defined below.
|
|
struct vvary
|
|
{
|
|
fb_len vary_length;
|
|
ISC_UCHAR vary_string[max_varchar_size];
|
|
};
|
|
|
|
/*
|
|
inline fb_len get_varchar_len(const char* vchar)
|
|
{
|
|
return reinterpret_cast<const vvary*>(vchar)->vary_length;
|
|
}
|
|
*/
|
|
|
|
inline fb_len get_varchar_len(const ISC_UCHAR* vchar)
|
|
{
|
|
return reinterpret_cast<const vvary*>(vchar)->vary_length;
|
|
}
|
|
|
|
inline void set_varchar_len(char* vchar, const fb_len len)
|
|
{
|
|
reinterpret_cast<vvary*>(vchar)->vary_length = len;
|
|
}
|
|
|
|
inline void set_varchar_len(ISC_UCHAR* vchar, const fb_len len)
|
|
{
|
|
reinterpret_cast<vvary*>(vchar)->vary_length = len;
|
|
}
|
|
|
|
bool isnull(const paramdsc* v)
|
|
{
|
|
return !v || !v->dsc_address || (v->dsc_flags & DSC_null);
|
|
}
|
|
|
|
paramdsc* setnull(paramdsc* v)
|
|
{
|
|
if (v)
|
|
v->dsc_flags |= DSC_null;
|
|
return v;
|
|
}
|
|
|
|
int get_int_type(const paramdsc* v, ISC_INT64& rc)
|
|
{
|
|
int s = -1;
|
|
switch (v->dsc_dtype)
|
|
{
|
|
case dtype_short:
|
|
rc = *reinterpret_cast<ISC_SHORT*>(v->dsc_address);
|
|
s = sizeof(ISC_SHORT);
|
|
break;
|
|
case dtype_long:
|
|
rc = *reinterpret_cast<ISC_LONG*>(v->dsc_address);
|
|
s = sizeof(ISC_LONG);
|
|
break;
|
|
case dtype_int64:
|
|
rc = *reinterpret_cast<ISC_INT64*>(v->dsc_address);
|
|
s = sizeof(ISC_INT64);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void set_int_type(paramdsc* v, const ISC_INT64 iv)
|
|
{
|
|
switch (v->dsc_dtype)
|
|
{
|
|
case dtype_short:
|
|
*reinterpret_cast<ISC_SHORT*>(v->dsc_address) = static_cast<ISC_SHORT>(iv);
|
|
break;
|
|
case dtype_long:
|
|
*reinterpret_cast<ISC_LONG*>(v->dsc_address) = static_cast<ISC_LONG>(iv);
|
|
break;
|
|
case dtype_int64:
|
|
*reinterpret_cast<ISC_INT64*>(v->dsc_address) = iv;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int get_double_type(const paramdsc* v, double& rc)
|
|
{
|
|
int s = -1;
|
|
switch (v->dsc_dtype)
|
|
{
|
|
case dtype_real:
|
|
rc = static_cast<double>(*reinterpret_cast<float*>(v->dsc_address));
|
|
s = sizeof(float);
|
|
break;
|
|
case dtype_double:
|
|
rc = *reinterpret_cast<double*>(v->dsc_address);
|
|
s = sizeof(double);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
void set_double_type(paramdsc* v, const double iv)
|
|
{
|
|
switch (v->dsc_dtype)
|
|
{
|
|
case dtype_real:
|
|
*reinterpret_cast<float*>(v->dsc_address) = static_cast<float>(iv);
|
|
break;
|
|
case dtype_double:
|
|
*reinterpret_cast<double*>(v->dsc_address) = iv;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int get_any_string_type(const paramdsc* v, ISC_UCHAR*& text)
|
|
{
|
|
int len = v->dsc_length;
|
|
switch (v->dsc_dtype)
|
|
{
|
|
case dtype_text:
|
|
text = v->dsc_address;
|
|
break;
|
|
case dtype_cstring:
|
|
text = v->dsc_address;
|
|
if (len && text)
|
|
{
|
|
const ISC_UCHAR* p = text; //strlen(v->dsc_address);
|
|
while (*p)
|
|
++p; // couldn't use strlen!
|
|
if (p - text < len)
|
|
len = p - text;
|
|
}
|
|
break;
|
|
case dtype_varying:
|
|
len -= varchar_indicator_size;
|
|
text = reinterpret_cast<vvary*>(v->dsc_address)->vary_string;
|
|
{
|
|
const int x = get_varchar_len(v->dsc_address);
|
|
if (x < len)
|
|
len = x;
|
|
}
|
|
break;
|
|
default:
|
|
len = -1;
|
|
break;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
void set_any_string_type(paramdsc* v, const int len0, ISC_UCHAR* text = 0)
|
|
{
|
|
fb_len len = static_cast<fb_len>(len0);
|
|
switch (v->dsc_dtype)
|
|
{
|
|
case dtype_text:
|
|
v->dsc_length = len;
|
|
if (text)
|
|
memcpy(v->dsc_address, text, len);
|
|
else
|
|
memset(v->dsc_address, ' ', len);
|
|
break;
|
|
case dtype_cstring:
|
|
v->dsc_length = len;
|
|
if (text)
|
|
memcpy(v->dsc_address, text, len);
|
|
else
|
|
v->dsc_length = len = 0;
|
|
v->dsc_address[len] = 0;
|
|
break;
|
|
case dtype_varying:
|
|
if (!text)
|
|
len = 0;
|
|
else if (len > max_varchar_size)
|
|
len = max_varchar_size;
|
|
v->dsc_length = len + static_cast<fb_len>(varchar_indicator_size);
|
|
set_varchar_len(v->dsc_address, len);
|
|
if (text)
|
|
memcpy(v->dsc_address + varchar_indicator_size, text, len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int get_scaled_double(const paramdsc* v, double& rc)
|
|
{
|
|
ISC_INT64 iv;
|
|
int rct = get_int_type(v, iv);
|
|
if (rct < 0)
|
|
rct = get_double_type(v, rc);
|
|
else
|
|
{
|
|
rc = static_cast<double>(iv);
|
|
int scale = v->dsc_scale;
|
|
for (; scale < 0; ++scale)
|
|
rc /= 10;
|
|
for (; scale > 0; --scale)
|
|
rc *= 10;
|
|
}
|
|
return rct;
|
|
}
|
|
} // namespace internal
|
|
|
|
|
|
// BEGIN DEPRECATED FUNCTIONS.
|
|
FBUDF_API paramdsc* idNvl(paramdsc* v, paramdsc* v2)
|
|
{
|
|
if (!internal::isnull(v))
|
|
return v;
|
|
return v2;
|
|
}
|
|
|
|
FBUDF_API void sNvl(const paramdsc* v, const paramdsc* v2, paramdsc* rc)
|
|
{
|
|
if (!internal::isnull(v))
|
|
{
|
|
ISC_UCHAR* sv = 0;
|
|
const int len = internal::get_any_string_type(v, sv);
|
|
if (len < 0)
|
|
internal::setnull(rc);
|
|
else
|
|
internal::set_any_string_type(rc, len, sv);
|
|
return;
|
|
}
|
|
if (!internal::isnull(v2))
|
|
{
|
|
ISC_UCHAR* sv2 = 0;
|
|
const int len = internal::get_any_string_type(v2, sv2);
|
|
if (len < 0)
|
|
internal::setnull(rc);
|
|
else
|
|
internal::set_any_string_type(rc, len, sv2);
|
|
return;
|
|
}
|
|
internal::setnull(rc);
|
|
}
|
|
|
|
|
|
FBUDF_API paramdsc* iNullIf(paramdsc* v, paramdsc* v2)
|
|
{
|
|
if (internal::isnull(v) || internal::isnull(v2))
|
|
return 0;
|
|
ISC_INT64 iv, iv2;
|
|
const int rc = internal::get_int_type(v, iv);
|
|
const int rc2 = internal::get_int_type(v2, iv2);
|
|
if (rc < 0 || rc2 < 0)
|
|
return v;
|
|
if (iv == iv2 && v->dsc_scale == v2->dsc_scale)
|
|
return 0;
|
|
return v;
|
|
}
|
|
|
|
FBUDF_API paramdsc* dNullIf(paramdsc* v, paramdsc* v2)
|
|
{
|
|
if (internal::isnull(v) || internal::isnull(v2))
|
|
return 0;
|
|
double iv, iv2;
|
|
const int rc = internal::get_double_type(v, iv);
|
|
const int rc2 = internal::get_double_type(v2, iv2);
|
|
if (rc < 0 || rc2 < 0)
|
|
return v;
|
|
if (iv == iv2) // && v->dsc_scale == v2->dsc_scale) double w/o scale
|
|
return 0;
|
|
return v;
|
|
}
|
|
|
|
FBUDF_API void sNullIf(const paramdsc* v, const paramdsc* v2, paramdsc* rc)
|
|
{
|
|
if (internal::isnull(v) || internal::isnull(v2))
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
ISC_UCHAR* sv;
|
|
const int len = internal::get_any_string_type(v, sv);
|
|
ISC_UCHAR* sv2;
|
|
const int len2 = internal::get_any_string_type(v2, sv2);
|
|
if (len < 0 || len2 < 0) // good luck with the result, we can't do more.
|
|
return;
|
|
if (len == len2 && (!len || !memcmp(sv, sv2, len)) &&
|
|
(v->dsc_sub_type == v2->dsc_sub_type || // ttype
|
|
!v->dsc_sub_type || !v2->dsc_sub_type)) // tyype
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
internal::set_any_string_type(rc, len, sv);
|
|
return;
|
|
}
|
|
// END DEPRECATED FUNCTIONS.
|
|
|
|
namespace internal
|
|
{
|
|
void decode_timestamp(const GDS_TIMESTAMP* date, tm* times_arg) throw()
|
|
{
|
|
Firebird::NoThrowTimeStamp::decode_timestamp(*date, times_arg);
|
|
}
|
|
|
|
void encode_timestamp(const tm* times_arg, GDS_TIMESTAMP* date) throw()
|
|
{
|
|
*date = Firebird::NoThrowTimeStamp::encode_timestamp(times_arg);
|
|
}
|
|
|
|
enum day_format {day_short, day_long};
|
|
const fb_len day_len[] = {4, 14};
|
|
const char* day_fmtstr[] = {"%a", "%A"};
|
|
|
|
void get_DOW(const ISC_TIMESTAMP* v, paramvary* rc, const day_format df)
|
|
{
|
|
tm times;
|
|
decode_timestamp(v, ×);
|
|
const int dow = times.tm_wday;
|
|
if (dow >= 0 && dow <= 6)
|
|
{
|
|
fb_len 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, "");
|
|
char* const target = reinterpret_cast<char*>(rc->vary_string);
|
|
name_len = static_cast<fb_len>(strftime(target, name_len, name_fmt, ×));
|
|
if (name_len)
|
|
{
|
|
// There's no clarity in the docs whether '\0' is counted or not; be safe.
|
|
if (!target[name_len - 1])
|
|
--name_len;
|
|
rc->vary_length = name_len;
|
|
return;
|
|
}
|
|
}
|
|
rc->vary_length = 5;
|
|
memcpy(rc->vary_string, "ERROR", 5);
|
|
}
|
|
} // namespace internal
|
|
|
|
FBUDF_API void DOW(const ISC_TIMESTAMP* v, paramvary* rc)
|
|
{
|
|
internal::get_DOW(v, rc, internal::day_long);
|
|
}
|
|
|
|
FBUDF_API void SDOW(const ISC_TIMESTAMP* v, paramvary* rc)
|
|
{
|
|
internal::get_DOW(v, rc, internal::day_short);
|
|
}
|
|
|
|
FBUDF_API void right(const paramdsc* v, const ISC_SHORT& rl, paramdsc* rc)
|
|
{
|
|
if (internal::isnull(v))
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
ISC_UCHAR* text = 0;
|
|
int len = internal::get_any_string_type(v, text);
|
|
const int diff = len - rl;
|
|
if (rl < len)
|
|
len = rl;
|
|
if (len < 0)
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
if (diff > 0)
|
|
text += diff;
|
|
internal::set_any_string_type(rc, len, text);
|
|
return;
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addDay(ISC_TIMESTAMP* v, const ISC_LONG& ndays)
|
|
{
|
|
v->timestamp_date += ndays;
|
|
return v;
|
|
}
|
|
|
|
FBUDF_API void addDay2(const ISC_TIMESTAMP* v0, const ISC_LONG& ndays, ISC_TIMESTAMP* v)
|
|
{
|
|
*v = *v0;
|
|
v->timestamp_date += ndays;
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addWeek(ISC_TIMESTAMP* v, const ISC_LONG& nweeks)
|
|
{
|
|
v->timestamp_date += nweeks * 7;
|
|
return v;
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addMonth(ISC_TIMESTAMP* v, const ISC_LONG& nmonths)
|
|
{
|
|
tm times;
|
|
internal::decode_timestamp(v, ×);
|
|
const int y = nmonths / 12, m = nmonths % 12;
|
|
times.tm_year += y;
|
|
if ((times.tm_mon += m) > 11)
|
|
{
|
|
++times.tm_year;
|
|
times.tm_mon -= 12;
|
|
}
|
|
else if (times.tm_mon < 0)
|
|
{
|
|
--times.tm_year;
|
|
times.tm_mon += 12;
|
|
}
|
|
const int ly = times.tm_year + 1900;
|
|
const bool leap = (ly % 4 == 0 && ly % 100 != 0) || ly % 400 == 0;
|
|
const int md[] = {31, (leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
if (times.tm_mday > md[times.tm_mon])
|
|
times.tm_mday = md[times.tm_mon];
|
|
internal::encode_timestamp(×, v);
|
|
return v;
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addYear(ISC_TIMESTAMP* v, const ISC_LONG& nyears)
|
|
{
|
|
tm times;
|
|
internal::decode_timestamp(v, ×);
|
|
times.tm_year += nyears;
|
|
internal::encode_timestamp(×, v);
|
|
return v;
|
|
}
|
|
|
|
namespace internal
|
|
{
|
|
ISC_TIMESTAMP* addTenthMSec(ISC_TIMESTAMP* v, SINT64 tenthmilliseconds, int multiplier)
|
|
{
|
|
const SINT64 full = tenthmilliseconds * multiplier;
|
|
const long days = full / tenthmsec_in_day, secs = full % tenthmsec_in_day;
|
|
v->timestamp_date += days;
|
|
// Time portion is unsigned, so we avoid unsigned rolling over negative values
|
|
// that only produce a new unsigned number with the wrong result.
|
|
if (secs < 0 && static_cast<ISC_ULONG>(-secs) > v->timestamp_time)
|
|
{
|
|
--v->timestamp_date;
|
|
v->timestamp_time += tenthmsec_in_day + secs;
|
|
}
|
|
else if ((v->timestamp_time += secs) >= static_cast<ISC_ULONG>(tenthmsec_in_day))
|
|
{
|
|
++v->timestamp_date;
|
|
v->timestamp_time -= tenthmsec_in_day;
|
|
}
|
|
return v;
|
|
}
|
|
} // namespace internal
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addMilliSecond(ISC_TIMESTAMP* v, const ISC_LONG& nmseconds)
|
|
{
|
|
return internal::addTenthMSec(v, nmseconds, ISC_TIME_SECONDS_PRECISION / 1000);
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addSecond(ISC_TIMESTAMP* v, const ISC_LONG& nseconds)
|
|
{
|
|
return internal::addTenthMSec(v, nseconds, ISC_TIME_SECONDS_PRECISION);
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addMinute(ISC_TIMESTAMP* v, const ISC_LONG& nminutes)
|
|
{
|
|
return internal::addTenthMSec(v, nminutes, 60 * ISC_TIME_SECONDS_PRECISION);
|
|
}
|
|
|
|
FBUDF_API ISC_TIMESTAMP* addHour(ISC_TIMESTAMP* v, const ISC_LONG& nhours)
|
|
{
|
|
return internal::addTenthMSec(v, nhours, 3600 * ISC_TIME_SECONDS_PRECISION);
|
|
}
|
|
|
|
#ifdef NEED_TIME_MUTEX
|
|
Firebird::Mutex timeMutex;
|
|
#endif
|
|
|
|
FBUDF_API void getExactTimestamp(ISC_TIMESTAMP* rc)
|
|
{
|
|
#if defined(HAVE_GETTIMEOFDAY)
|
|
timeval tv;
|
|
GETTIMEOFDAY(&tv);
|
|
const time_t seconds = tv.tv_sec;
|
|
|
|
tm timex;
|
|
#if defined(HAVE_LOCALTIME_R)
|
|
tm* times = localtime_r(&seconds, &timex);
|
|
#else
|
|
timeMutex.enter();
|
|
tm* times = localtime(&seconds);
|
|
if (times)
|
|
{
|
|
// Copy to local variable before we exit the mutex.
|
|
timex = *times;
|
|
times = &timex;
|
|
}
|
|
timeMutex.leave();
|
|
#endif // localtime_r
|
|
|
|
if (times)
|
|
{
|
|
internal::encode_timestamp(times, rc);
|
|
rc->timestamp_time += tv.tv_usec / 100;
|
|
}
|
|
else
|
|
rc->timestamp_date = rc->timestamp_time = 0;
|
|
|
|
#else // gettimeofday
|
|
_timeb timebuffer;
|
|
_ftime(&timebuffer);
|
|
// localtime uses thread local storage in NT, no need to lock threads.
|
|
// Of course, this facility is only available in multithreaded builds.
|
|
tm* times = localtime(&timebuffer.time);
|
|
if (times)
|
|
{
|
|
internal::encode_timestamp(times, rc);
|
|
rc->timestamp_time += timebuffer.millitm * 10;
|
|
}
|
|
else
|
|
rc->timestamp_date = rc->timestamp_time = 0;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
FBUDF_API void getExactTimestampUTC(ISC_TIMESTAMP* rc)
|
|
{
|
|
#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)
|
|
{
|
|
internal::encode_timestamp(times, rc);
|
|
rc->timestamp_time += tv.tv_usec / 100;
|
|
}
|
|
else
|
|
rc->timestamp_date = rc->timestamp_time = 0;
|
|
|
|
#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)
|
|
{
|
|
internal::encode_timestamp(times, rc);
|
|
rc->timestamp_time += timebuffer.millitm * 10;
|
|
}
|
|
else
|
|
rc->timestamp_date = rc->timestamp_time = 0;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
FBUDF_API ISC_LONG isLeapYear(const ISC_TIMESTAMP* v)
|
|
{
|
|
tm times;
|
|
internal::decode_timestamp(v, ×);
|
|
const int ly = times.tm_year + 1900;
|
|
return (ly % 4 == 0 && ly % 100 != 0) || ly % 400 == 0;
|
|
}
|
|
|
|
FBUDF_API void fbtruncate(const paramdsc* v, paramdsc* rc)
|
|
{
|
|
if (internal::isnull(v))
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
ISC_INT64 iv;
|
|
const int rct = internal::get_int_type(v, iv);
|
|
if (rct < 0 || v->dsc_scale > 0)
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
if (!v->dsc_scale /*|| !v->dsc_sub_type*/) //second test won't work with ODS9
|
|
{
|
|
internal::set_int_type(rc, iv);
|
|
rc->dsc_scale = 0;
|
|
return;
|
|
}
|
|
|
|
// truncate(0.9) => 0
|
|
// truncate(-0.9) => -1
|
|
// truncate(-0.9) => 0 ### SYMMETRIC_MATH defined.
|
|
int scale = v->dsc_scale;
|
|
#if defined(SYMMETRIC_MATH)
|
|
while (scale++ < 0)
|
|
iv /= 10;
|
|
#else
|
|
const bool isNeg = iv < 0;
|
|
bool gt = false;
|
|
while (scale++ < 0)
|
|
{
|
|
if (iv % 10)
|
|
gt = true;
|
|
iv /= 10;
|
|
}
|
|
if (gt)
|
|
{
|
|
if (isNeg)
|
|
--iv;
|
|
}
|
|
#endif
|
|
internal::set_int_type(rc, iv);
|
|
rc->dsc_scale = 0;
|
|
return;
|
|
}
|
|
|
|
FBUDF_API void fbround(const paramdsc* v, paramdsc* rc)
|
|
{
|
|
if (internal::isnull(v))
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
ISC_INT64 iv;
|
|
const int rct = internal::get_int_type(v, iv);
|
|
if (rct < 0 || v->dsc_scale > 0)
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
if (!v->dsc_scale /*|| !v->dsc_sub_type*/) //second test won't work with ODS9
|
|
{
|
|
internal::set_int_type(rc, iv);
|
|
rc->dsc_scale = 0;
|
|
return;
|
|
}
|
|
|
|
// round(0.3) => 0 ### round(0.5) => 1
|
|
// round(-0.3) => 0 ### round(-0.5) => 0
|
|
// round(-0.3) => 0 ### round(-0.5) => -1 ### SYMMETRIC_MATH defined.
|
|
const bool isNeg = iv < 0;
|
|
int scale = v->dsc_scale;
|
|
bool gt = false, check_more = false;
|
|
while (scale++ < 0)
|
|
{
|
|
if (!scale)
|
|
{
|
|
int dig;
|
|
if (iv == MIN_SINT64)
|
|
dig = -(iv + 10) % 10;
|
|
else
|
|
dig = static_cast<int>(iv >= 0 ? iv % 10 : -iv % 10);
|
|
|
|
#if defined(SYMMETRIC_MATH)
|
|
if (dig >= 5)
|
|
gt = true;
|
|
#else
|
|
if (!isNeg)
|
|
{
|
|
if (dig >= 5)
|
|
gt = true;
|
|
}
|
|
else
|
|
{
|
|
if (dig > 5 || (dig == 5 && check_more))
|
|
gt = true;
|
|
}
|
|
#endif
|
|
}
|
|
else if (isNeg && !check_more)
|
|
{
|
|
if (iv % 10 != 0)
|
|
check_more = true;
|
|
}
|
|
iv /= 10;
|
|
}
|
|
if (gt)
|
|
{
|
|
if (isNeg)
|
|
--iv;
|
|
else
|
|
++iv;
|
|
}
|
|
internal::set_int_type(rc, iv);
|
|
rc->dsc_scale = 0;
|
|
return;
|
|
}
|
|
|
|
FBUDF_API void power(const paramdsc* v, const paramdsc* v2, paramdsc* rc)
|
|
{
|
|
if (internal::isnull(v) || internal::isnull(v2))
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
double d, d2;
|
|
const int rct = internal::get_scaled_double(v, d);
|
|
const int rct2 = internal::get_scaled_double(v2, d2);
|
|
|
|
// If we cause a div by zero, SS shuts down in response.
|
|
// The doc I read says 0^0 will produce 1, so it's not tested below.
|
|
if (rct < 0 || rct2 < 0 || (!d && d2 < 0))
|
|
{
|
|
internal::setnull(rc);
|
|
return;
|
|
}
|
|
|
|
internal::set_double_type(rc, pow(d, d2));
|
|
rc->dsc_scale = 0;
|
|
return;
|
|
}
|
|
|
|
FBUDF_API void string2blob(const paramdsc* v, blobcallback* outblob)
|
|
{
|
|
if (internal::isnull(v))
|
|
{
|
|
outblob->blob_handle = 0; // hint for the engine, null blob.
|
|
return;
|
|
}
|
|
ISC_UCHAR* text = 0;
|
|
const int len = internal::get_any_string_type(v, text);
|
|
if (len < 0 && outblob)
|
|
outblob->blob_handle = 0; // hint for the engine, null blob.
|
|
if (!outblob || !outblob->blob_handle)
|
|
return;
|
|
outblob->blob_put_segment(outblob->blob_handle, text, len);
|
|
return;
|
|
}
|
|
|