/* * 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): ______________________________________. * * Paul Reeves, 2023 * */ #include "MyFirstUDRKit.h" #include #include #include "UdrCppTemplates.h" #include #include #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; // First, let's start with a function from the UdfBackwardCompatibility // library and add some additional comments. // We will also use self-docmenting variable names // ////////////////////////////////////////////////// /*** * create function div ( * anumerator bigint, * adenominator bigint * ) returns bigint * external name 'MyFirstUDRKit!MFK_div!Divide anumerator by adenominator' * engine udr; ***/ FB_UDR_BEGIN_FUNCTION(MFK_div) // Divide integer anumerator 1 by integer adenominator using the stdlib // Note this differs from the example that shifts with firebird as it returns // a bigint, not a double precision //BEGIN FB_UDR_MESSAGE(InMessage, (FB_BIGINT, anumerator) (FB_BIGINT, adenominator) ); FB_UDR_MESSAGE(OutMessage, (FB_BIGINT, result) ); FB_UDR_EXECUTE_FUNCTION { if (in->anumeratorNull || in->adenominatorNull) { out->resultNull = FB_TRUE; out->result = 0; } else { out->resultNull = FB_FALSE; if (in->adenominator) { out->result = std::div(in->anumerator, in->adenominator).quot; } else { out->result = int( std::numeric_limits::infinity() ); ISC_STATUS_ARRAY StatusVector = {isc_arg_gds, isc_arith_except, isc_arg_gds, isc_exception_integer_divide_by_zero, isc_arg_end}; FbException::check(isc_exception_integer_divide_by_zero, status, StatusVector); } } } FB_UDR_END_FUNCTION //END /*** DDL create function flagged ( flags integer, flag integer ) returns integer external name 'MyFirstUDRKit!MFK_flagged!Check if flag is set in flags. flag is zero-based.' engine udr; ***/ FB_UDR_BEGIN_FUNCTION (MFK_flagged) //BEGIN FB_UDR_MESSAGE(InMessage, (FB_INTEGER, flags) (FB_INTEGER, flag) ); FB_UDR_MESSAGE(OutMessage, (FB_INTEGER, result) ); FB_UDR_EXECUTE_FUNCTION { if ( in->flagsNull != 0 ) { out->resultNull = FB_TRUE; out->result = 0; } else { out->resultNull = FB_FALSE; ISC_UINT64 i = ( 1ULL << in->flag ); out->result = ( in->flags & i ) ? 1 : 0; } } FB_UDR_END_FUNCTION //END /*** DDL * create or alter function LoadBlobFromFile ( * afilename varchar(8191), * ablob BLOB * ) returns bigint * external name 'MyFirstUDRKit!MFKLoadBlobFromFile!Load file and save to Blob' * engine udr; ***/ FB_UDR_BEGIN_FUNCTION (MFK_LoadBlobFromFile) //BEGIN FB_UDR_MESSAGE(InMessage, (FB_VARCHAR(8191), afilename) ); FB_UDR_MESSAGE(OutMessage, (FB_BLOB, ablob) ); AutoRelease att; AutoRelease tra; AutoRelease blob; FB_UDR_EXECUTE_FUNCTION { // Test Input if (in->afilenameNull != 0) { out->ablobNull = FB_TRUE; return; } std::ifstream FileReader; FileReader.open( in->afilename.str , std::ifstream::binary ); if (! FileReader.is_open()) { out->result = -2; return; } try { att.reset(context->getAttachment(status)); tra.reset(context->getTransaction(status)); blob.reset(att->openBlob(status, tra, &in->ablob, 0, nullptr)); if (blob == nullptr) { out->result = -1; return; } std::vector Buffer (MaxSegmentSize, 0); std::streamsize DataSize; while ( FileReader.good() ) { FileReader.read( Buffer.data(), Buffer.size() ); DataSize = FileReader.gcount(); // fail seems to be here... // Probably need to declare buffer differently. blob->putSegment( status, DataSize, Buffer.data() ); out->result += DataSize; // Perhaps test for badbit here? } if (FileReader.bad()) { // Something went wrong // What to do? out->resultNull = FB_TRUE; // What do we do with the partially written blob? Is cancel enough? blob->cancel( status ); // Should we reset result to 0? } blob->close( status ); blob->release(); Buffer.clear(); } catch (...) { // This generates an unrecognised C++ exception, which is insufficient. throw std::runtime_error("Error writing stream to BLOB."); } FileReader.close(); out->result = 1; } FB_UDR_END_FUNCTION //END /*** create or alter function SaveBlobToFile ( afilename varchar(8191), ablob BLOB ) returns bigint external name 'MyFirstUDRKit!MFK_SaveBlobToFile!Save blob to file' engine udr; ***/ FB_UDR_BEGIN_FUNCTION(MFK_SaveBlobToFile) //BEGIN FB_UDR_MESSAGE(InMessage, (FB_CHAR(8191), afilename) (FB_BLOB, ablob) ); FB_UDR_MESSAGE(OutMessage, (FB_BIGINT, result) ); AutoRelease att; AutoRelease tra; AutoRelease Blob; FB_UDR_EXECUTE_FUNCTION { // Test Input if (in->afilenameNull != 0 || in->ablobNull != 0 ) { out->resultNull = FB_TRUE; out->result = 0; } /* * DOC NOTE - We do not test for existence of the file here! */ std::fstream File ( in->afilename.str , std::fstream::out | std::fstream::binary | std::fstream::app ); if (! File.is_open()) { out->result = -2; return; } Blob.reset(att->openBlob(status, tra, &in->ablob, 0, nullptr)); if (Blob == nullptr) { out->result = -1; return; } std::vector Buffer (MaxSegmentSize, 0); unsigned BytesRead = 0; for (bool Eof = false; !Eof; ) { switch (Blob->getSegment( status, MaxSegmentSize, Buffer.data(), &BytesRead)) { case IStatus::RESULT_OK: case IStatus::RESULT_SEGMENT: { File.write( Buffer.data(), Buffer.size() ); out->result += BytesRead; continue; } default: { Blob->close( status ); // will close interface Blob.release(); Eof = true; break; } } } Buffer.clear(); File.close(); } FB_UDR_END_FUNCTION //END /*** DDL create function BillDate ( d integer, date ) returns date external name 'my_first_udr_kit!MFK_BillDate!What does BillDate do?' engine udr; ***/ /* FB_UDR_BEGIN_FUNCTION(MFK_BillDate) FB_UDR_MESSAGE(InMessage, (FB_INTEGER, d) (FB_INTEGER, date) ); FB_UDR_MESSAGE(OutMessage, (FB_DATE, result) ); FB_UDR_END_FUNCTION */