8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 20:43:03 +01:00
firebird-mirror/src/jrd/ext.cpp

541 lines
14 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: ext.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: External file access
*
* The contents of this file are subject to the Interbase 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.Inprise.com/IPL.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 Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
2008-12-05 02:20:14 +01:00
* 26-Sept-2001 Paul Beach - Windows External File Directory Config. Parameter
2002-06-30 11:58:20 +02:00
*
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
*
* 2001.08.07 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, second attempt
2002-10-30 07:40:58 +01:00
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2004-04-29 00:36:29 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <errno.h>
#include <string.h>
2010-05-22 04:19:01 +02:00
#include <sys/stat.h>
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
#include "../jrd/req.h"
#include "../jrd/val.h"
#include "../jrd/exe.h"
#include "../jrd/rse.h"
#include "../jrd/ext.h"
#include "../jrd/tra.h"
#include "gen/iberror.h"
#include "../jrd/cmp_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/err_proto.h"
#include "../jrd/ext_proto.h"
2010-10-12 10:02:57 +02:00
#include "../yvalve/gds_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/vio_proto.h"
#include "../common/config/config.h"
#include "../common/config/dir_list.h"
2010-10-12 10:02:57 +02:00
#include "../common/os/path_utils.h"
#include "../common/classes/init.h"
2001-05-23 15:26:42 +02:00
#if defined _MSC_VER && _MSC_VER < 1400
// NS: in VS2003 these only work with static CRT
extern "C" {
int __cdecl _fseeki64(FILE*, __int64, int);
__int64 __cdecl _ftelli64(FILE*);
}
#endif
#ifdef WIN_NT
#define FTELL64 _ftelli64
#define FSEEK64 _fseeki64
#else
#define FTELL64 ftello
#define FSEEK64 fseeko
#endif
using namespace Firebird;
2010-03-01 03:14:36 +01:00
namespace Jrd
{
class ExternalFileDirectoryList : public DirectoryList
{
private:
2010-03-01 03:14:36 +01:00
const PathName getConfigString() const
2008-05-10 05:44:57 +02:00
{
2010-03-01 03:14:36 +01:00
return PathName(config->getExternalFileAccess());
}
2010-03-01 03:14:36 +01:00
public:
2010-03-07 05:30:35 +01:00
explicit ExternalFileDirectoryList(const Database* dbb)
: DirectoryList(*dbb->dbb_permanent), config(dbb->dbb_config)
{
initialize();
}
2010-03-01 03:14:36 +01:00
static void create(Database* dbb)
{
if (!dbb->dbb_external_file_directory_list)
{
2010-03-01 03:14:36 +01:00
dbb->dbb_external_file_directory_list =
FB_NEW(*dbb->dbb_permanent) ExternalFileDirectoryList(dbb);
}
}
2010-03-01 03:14:36 +01:00
private:
2010-03-01 03:14:36 +01:00
RefPtr<Config> config;
};
}
using namespace Jrd;
namespace {
#ifdef WIN_NT
static const char* const FOPEN_TYPE = "a+b";
#else
static const char* const FOPEN_TYPE = "a+";
#endif
static const char* const FOPEN_READ_ONLY = "rb";
2008-12-05 02:20:14 +01:00
FILE *ext_fopen(Database* dbb, ExternalFile* ext_file)
{
const char* file_name = ext_file->ext_filename;
ExternalFileDirectoryList::create(dbb);
if (!dbb->dbb_external_file_directory_list->isPathInList(file_name))
2008-05-10 05:44:57 +02:00
{
2008-12-05 02:20:14 +01:00
ERR_post(Arg::Gds(isc_conf_access_denied) << Arg::Str("external file") <<
Arg::Str(file_name));
2008-05-10 05:44:57 +02:00
}
// If the database is updateable, then try opening the external files in
// RW mode. If the DB is ReadOnly, then open the external files only in
// ReadOnly mode, thus being consistent.
if (!(dbb->dbb_flags & DBB_read_only))
ext_file->ext_ifi = fopen(file_name, FOPEN_TYPE);
if (!ext_file->ext_ifi)
{
2008-12-05 02:20:14 +01:00
// could not open the file as read write attempt as read only
if (!(ext_file->ext_ifi = fopen(file_name, FOPEN_READ_ONLY)))
{
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fopen") << Arg::Str(file_name) <<
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
}
else {
ext_file->ext_flags |= EXT_readonly;
}
}
return ext_file->ext_ifi;
}
} // namespace
2001-05-23 15:26:42 +02:00
double EXT_cardinality(thread_db* tdbb, jrd_rel* relation)
{
/**************************************
*
* E X T _ c a r d i n a l i t y
*
**************************************
*
* Functional description
* Return cardinality for the external file.
*
**************************************/
ExternalFile* const file = relation->rel_file;
fb_assert(file);
bool must_close = false;
if (!file->ext_ifi)
{
ext_fopen(tdbb->getDatabase(), file);
must_close = true;
}
FB_UINT64 file_size = 0;
#ifdef WIN_NT
struct __stat64 statistics;
if (!_fstat64(_fileno(file->ext_ifi), &statistics))
#else
struct stat statistics;
2010-05-22 04:19:01 +02:00
if (!fstat(fileno(file->ext_ifi), &statistics))
#endif
{
file_size = statistics.st_size;
}
if (must_close)
{
fclose(file->ext_ifi);
file->ext_ifi = NULL;
}
const Format* const format = MET_current(tdbb, relation);
fb_assert(format && format->fmt_length);
2010-05-22 04:00:45 +02:00
const USHORT offset = (USHORT)(IPTR) format->fmt_desc[0].dsc_address;
const ULONG record_length = format->fmt_length - offset;
return (double) file_size / record_length;
}
2009-04-28 15:08:04 +02:00
void EXT_erase(record_param*, jrd_tra*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ e r a s e
*
**************************************
*
* Functional description
* Update an external file.
*
**************************************/
ERR_post(Arg::Gds(isc_ext_file_delete));
2001-05-23 15:26:42 +02:00
}
// Third param is unused.
2009-04-28 15:08:04 +02:00
ExternalFile* EXT_file(jrd_rel* relation, const TEXT* file_name) //, bid* description)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ f i l e
*
**************************************
*
* Functional description
* Create a file block for external file access.
*
**************************************/
Database* dbb = GET_DBB();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2009-08-22 14:16:47 +02:00
// if we already have a external file associated with this relation just
// return the file structure
2001-05-23 15:26:42 +02:00
if (relation->rel_file) {
EXT_fini(relation, false);
2001-05-23 15:26:42 +02:00
}
2002-06-30 11:58:20 +02:00
#ifdef WIN_NT
2009-08-22 14:16:47 +02:00
// Default number of file handles stdio.h on Windows is 512, use this
// call to increase and set to the maximum
2002-06-30 11:58:20 +02:00
_setmaxstdio(2048);
#endif
// If file_name has no path part, expand it in ExternalFilesPath.
2010-03-01 03:14:36 +01:00
PathName path, name;
PathUtils::splitLastComponent(path, name, file_name);
if (path.isEmpty())
2009-08-23 09:31:48 +02:00
{
// path component not present in file_name
ExternalFileDirectoryList::create(dbb);
2010-03-01 03:14:36 +01:00
if (!(dbb->dbb_external_file_directory_list->expandFileName(path, name)))
{
2010-03-01 03:14:36 +01:00
dbb->dbb_external_file_directory_list->defaultName(path, name);
}
2010-03-01 03:14:36 +01:00
file_name = path.c_str();
}
2008-12-20 09:12:19 +01:00
ExternalFile* file = FB_NEW_RPT(*dbb->dbb_permanent, (strlen(file_name) + 1)) ExternalFile();
2003-12-31 06:36:12 +01:00
relation->rel_file = file;
strcpy(file->ext_filename, file_name);
2001-05-23 15:26:42 +02:00
file->ext_flags = 0;
file->ext_ifi = NULL;
2002-06-30 11:58:20 +02:00
2001-05-23 15:26:42 +02:00
return file;
}
void EXT_fini(jrd_rel* relation, bool close_only)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ f i n i
*
**************************************
*
* Functional description
* Close the file associated with a relation.
*
**************************************/
2009-08-23 09:31:48 +02:00
if (relation->rel_file)
{
ExternalFile* file = relation->rel_file;
2001-05-23 15:26:42 +02:00
if (file->ext_ifi)
{
fclose(file->ext_ifi);
file->ext_ifi = NULL;
}
2008-12-05 02:20:14 +01:00
// before zeroing out the rel_file we need to deallocate the memory
if (!close_only)
{
delete file;
relation->rel_file = NULL;
}
2001-05-23 15:26:42 +02:00
}
}
2010-10-12 10:02:57 +02:00
bool EXT_get(thread_db* tdbb, record_param* rpb, FB_UINT64& position)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ g e t
*
**************************************
*
* Functional description
* Get a record from an external file.
*
**************************************/
jrd_rel* const relation = rpb->rpb_relation;
ExternalFile* const file = relation->rel_file;
fb_assert(file->ext_ifi);
Record* const record = rpb->rpb_record;
const Format* const format = record->rec_format;
2001-05-23 15:26:42 +02:00
const SSHORT offset = (SSHORT) (IPTR) format->fmt_desc[0].dsc_address;
2003-12-31 06:36:12 +01:00
UCHAR* p = record->rec_data + offset;
2008-05-10 05:44:57 +02:00
const ULONG l = record->rec_length - offset;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// hvlad: fseek will flush file buffer and degrade performance, so don't
// call it if it is not necessary. Note that we must flush file buffer if we
// do read after write
2008-12-05 02:20:14 +01:00
if (file->ext_ifi == NULL ||
((FTELL64(file->ext_ifi) != position || !(file->ext_flags & EXT_last_read)) &&
(FSEEK64(file->ext_ifi, position, SEEK_SET) != 0)) )
2003-12-31 06:36:12 +01:00
{
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fseek") << Arg::Str(file->ext_filename) <<
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
if (!fread(p, l, 1, file->ext_ifi))
return false;
position += l;
2001-05-23 15:26:42 +02:00
file->ext_flags |= EXT_last_read;
file->ext_flags &= ~EXT_last_write;
2009-08-22 14:16:47 +02:00
// Loop thru fields setting missing fields to either blanks/zeros or the missing value
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
dsc desc;
2004-03-30 06:10:52 +02:00
Format::fmt_desc_const_iterator desc_ptr = format->fmt_desc.begin();
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
SSHORT i = 0;
for (vec<jrd_fld*>::iterator itr = relation->rel_fields->begin();
2008-12-20 09:12:19 +01:00
i < format->fmt_count; ++i, ++itr, ++desc_ptr)
2003-12-31 06:36:12 +01:00
{
const jrd_fld* field = *itr;
2001-05-23 15:26:42 +02:00
SET_NULL(record, i);
2008-12-05 02:20:14 +01:00
if (!desc_ptr->dsc_length || !field)
2001-05-23 15:26:42 +02:00
continue;
const Literal* literal = (Literal*) field->fld_missing_value;
2009-08-23 09:31:48 +02:00
if (literal)
{
2001-05-23 15:26:42 +02:00
desc = *desc_ptr;
desc.dsc_address = record->rec_data + (IPTR) desc.dsc_address;
2001-05-23 15:26:42 +02:00
if (!MOV_compare(&literal->lit_desc, &desc))
continue;
}
CLEAR_NULL(record, i);
}
return true;
2001-05-23 15:26:42 +02:00
}
2009-04-28 15:08:04 +02:00
void EXT_modify(record_param* /*old_rpb*/, record_param* /*new_rpb*/, jrd_tra* /*transaction*/)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ m o d i f y
*
**************************************
*
* Functional description
* Update an external file.
*
**************************************/
ERR_post(Arg::Gds(isc_ext_file_modify));
2001-05-23 15:26:42 +02:00
}
void EXT_open(Database* dbb, ExternalFile* file)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ o p e n
*
**************************************
*
* Functional description
* Open a record stream for an external file.
*
**************************************/
if (!file->ext_ifi) {
ext_fopen(dbb, file);
}
2001-05-23 15:26:42 +02:00
}
2009-04-28 15:08:04 +02:00
void EXT_store(thread_db* tdbb, record_param* rpb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ s t o r e
*
**************************************
*
* Functional description
* Update an external file.
*
**************************************/
2003-12-31 06:36:12 +01:00
jrd_rel* relation = rpb->rpb_relation;
ExternalFile* file = relation->rel_file;
Record* record = rpb->rpb_record;
2004-03-30 06:10:52 +02:00
const Format* format = record->rec_format;
2001-05-23 15:26:42 +02:00
if (!file->ext_ifi) {
ext_fopen(tdbb->getDatabase(), file);
}
2009-08-22 14:16:47 +02:00
// Loop thru fields setting missing fields to either blanks/zeros or the missing value
2001-05-23 15:26:42 +02:00
2009-08-22 14:16:47 +02:00
// check if file is read only if read only then post error we cannot write to this file
2009-08-23 09:31:48 +02:00
if (file->ext_flags & EXT_readonly)
{
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
2009-08-22 14:16:47 +02:00
// Distinguish error message for a ReadOnly database
2001-05-23 15:26:42 +02:00
if (dbb->dbb_flags & DBB_read_only)
ERR_post(Arg::Gds(isc_read_only_database));
2009-08-23 09:31:48 +02:00
else
{
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("insert") << Arg::Str(file->ext_filename) <<
Arg::Gds(isc_io_write_err) <<
Arg::Gds(isc_ext_readonly_err));
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
}
2003-12-31 06:36:12 +01:00
dsc desc;
vec<jrd_fld*>::iterator field_ptr = relation->rel_fields->begin();
2004-03-30 06:10:52 +02:00
Format::fmt_desc_const_iterator desc_ptr = format->fmt_desc.begin();
2001-05-23 15:26:42 +02:00
2006-02-10 04:28:43 +01:00
for (USHORT i = 0; i < format->fmt_count; ++i, ++field_ptr, ++desc_ptr)
2001-12-24 03:51:06 +01:00
{
const jrd_fld* field = *field_ptr;
2008-12-20 09:12:19 +01:00
if (field && !field->fld_computation && desc_ptr->dsc_length && TEST_NULL(record, i))
2001-12-24 03:51:06 +01:00
{
UCHAR* p = record->rec_data + (IPTR) desc_ptr->dsc_address;
Literal* literal = (Literal*) field->fld_missing_value;
2009-08-23 09:31:48 +02:00
if (literal)
{
2001-05-23 15:26:42 +02:00
desc = *desc_ptr;
desc.dsc_address = p;
MOV_move(tdbb, &literal->lit_desc, &desc);
2001-05-23 15:26:42 +02:00
}
2009-08-23 09:31:48 +02:00
else
{
2003-12-31 06:36:12 +01:00
const UCHAR pad = (desc_ptr->dsc_dtype == dtype_text) ? ' ' : 0;
memset(p, pad, desc_ptr->dsc_length);
2001-05-23 15:26:42 +02:00
}
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
const USHORT offset = (USHORT) (IPTR) format->fmt_desc[0].dsc_address;
2003-12-31 06:36:12 +01:00
const UCHAR* p = record->rec_data + offset;
2008-05-10 05:44:57 +02:00
const ULONG l = record->rec_length - offset;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
// hvlad: fseek will flush file buffer and degrade performance, so don't
// call it if it is not necessary. Note that we must flush file buffer if we
// do write after read
2008-12-05 02:20:14 +01:00
if (file->ext_ifi == NULL ||
2009-06-06 20:28:29 +02:00
(!(file->ext_flags & EXT_last_write) && FSEEK64(file->ext_ifi, (SINT64) 0, SEEK_END) != 0) )
2003-12-31 06:36:12 +01:00
{
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fseek") << Arg::Str(file->ext_filename) <<
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
2003-12-31 06:36:12 +01:00
}
if (!fwrite(p, l, 1, file->ext_ifi))
{
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_io_error) << Arg::Str("fwrite") << Arg::Str(file->ext_filename) <<
Arg::Gds(isc_io_open_err) << SYS_ERR(errno));
}
// fflush(file->ext_ifi);
file->ext_flags |= EXT_last_write;
file->ext_flags &= ~EXT_last_read;
2001-05-23 15:26:42 +02:00
}
void EXT_tra_attach(ExternalFile* file, jrd_tra*)
{
/**************************************
*
* E X T _ t r a _ a t t a c h
*
**************************************
*
* Functional description
* Transaction going to use external table.
* Increment transactions use count.
*
**************************************/
file->ext_tra_cnt++;
}
void EXT_tra_detach(ExternalFile* file, jrd_tra*)
{
/**************************************
*
* E X T _ t r a _ d e t a c h
*
**************************************
*
* Functional description
2009-06-06 20:13:57 +02:00
* Transaction used external table is finished.
* Decrement transactions use count and close
* external file if count is zero.
*
**************************************/
file->ext_tra_cnt--;
if (!file->ext_tra_cnt && file->ext_ifi)
{
fclose(file->ext_ifi);
file->ext_ifi = NULL;
}
}