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

635 lines
16 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>
#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"
#include "../jrd/gds_proto.h"
#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"
#include "../jrd/os/path_utils.h"
#include "../common/classes/init.h"
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
#define FTELL64 _ftelli64
#define FSEEK64 _fseeki64
#else
#define FTELL64 ftello
#define FSEEK64 fseeko
#endif
using namespace Jrd;
using namespace Firebird;
2003-04-14 09:23:14 +02:00
namespace {
#ifdef WIN_NT
static const char* FOPEN_TYPE = "a+b";
#else
static const char* FOPEN_TYPE = "a+";
#endif
2009-06-05 12:24:39 +02:00
static const char* FOPEN_READ_ONLY = "rb";
FILE *ext_fopen(Database* dbb, ExternalFile* ext_file);
2001-05-23 15:26:42 +02:00
class ExternalFileDirectoryList : public Firebird::DirectoryList
{
private:
2008-05-10 05:44:57 +02:00
const Firebird::PathName getConfigString() const
{
return Firebird::PathName(Config::getExternalFileAccess());
}
public:
2008-03-11 03:05:09 +01:00
explicit ExternalFileDirectoryList(MemoryPool& p)
: DirectoryList(p)
{
initialize();
}
};
Firebird::InitInstance<ExternalFileDirectoryList> iExternalFileDirectoryList;
2008-12-05 02:20:14 +01:00
FILE *ext_fopen(Database* dbb, ExternalFile* ext_file)
{
const char* file_name = ext_file->ext_filename;
if (!iExternalFileDirectoryList().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
2009-04-28 15:08:04 +02:00
void EXT_close(RecordSource*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ c l o s e
*
**************************************
*
* Functional description
* Close a record stream for an external file.
*
**************************************/
}
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);
/* if we already have a external file associated with this relation just
* return the file structure */
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
2008-12-05 02:20:14 +01:00
/* Default number of file handles stdio.h on Windows is 512, use this
2002-06-30 11:58:20 +02:00
call to increase and set to the maximum */
_setmaxstdio(2048);
#endif
// If file_name has no path part, expand it in ExternalFilesPath.
Firebird::PathName Path, Name;
PathUtils::splitLastComponent(Path, Name, file_name);
if (Path.length() == 0) { // path component not present in file_name
if (!(iExternalFileDirectoryList().expandFileName(Path, Name)))
{
iExternalFileDirectoryList().defaultName(Path, Name);
}
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.
*
**************************************/
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
}
}
bool EXT_get(thread_db* tdbb, RecordSource* rsb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ g e t
*
**************************************
*
* Functional description
* Get a record from an external file.
*
**************************************/
2003-12-31 06:36:12 +01:00
jrd_rel* relation = rsb->rsb_relation;
ExternalFile* file = relation->rel_file;
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
if (request->req_flags & req_abort)
return false;
2001-05-23 15:26:42 +02:00
fb_assert(file->ext_ifi);
record_param* rpb = &request->req_rpb[rsb->rsb_stream];
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
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) != rpb->rpb_ext_pos || !(file->ext_flags & EXT_last_read)) &&
2009-06-06 20:28:29 +02:00
(FSEEK64(file->ext_ifi, rpb->rpb_ext_pos, 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;
rpb->rpb_ext_pos += l;
2001-05-23 15:26:42 +02:00
file->ext_flags |= EXT_last_read;
file->ext_flags &= ~EXT_last_write;
2001-05-23 15:26:42 +02:00
/* Loop thru fields setting missing fields to either blanks/zeros
or the missing value */
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;
2003-12-31 06:36:12 +01: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(thread_db* tdbb, RecordSource* rsb)
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.
*
**************************************/
2003-12-31 06:36:12 +01:00
jrd_rel* relation = rsb->rsb_relation;
ExternalFile* file = relation->rel_file;
jrd_req* request = tdbb->getRequest();
record_param* rpb = &request->req_rpb[rsb->rsb_stream];
2001-05-23 15:26:42 +02:00
if (!file->ext_ifi) {
ext_fopen(tdbb->getDatabase(), file);
}
2004-03-30 06:10:52 +02:00
const Format* format;
Record* record = rpb->rpb_record;
2003-12-31 06:36:12 +01:00
if (!record || !(format = record->rec_format)) {
2001-05-23 15:26:42 +02:00
format = MET_current(tdbb, relation);
VIO_record(tdbb, rpb, format, request->req_pool);
}
rpb->rpb_ext_pos = 0;
}
2009-04-28 15:08:04 +02:00
// Only extvms.cpp needs the third param.
RecordSource* EXT_optimize(OptimizerBlk* opt, SSHORT stream) //, jrd_nod** sort_ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ o p t i m i z e
*
**************************************
*
* Functional description
* Compile and optimize a record selection expression into a
* set of record source blocks (rsb's).
*
**************************************/
/* all these are un refrenced due to the code commented below
2004-01-13 10:52:19 +01:00
jrd_nod* node, inversion;
OptimizerBlk::opt_repeat *tail, *opt_end;
2001-05-23 15:26:42 +02:00
SSHORT i, size;
*/
thread_db* tdbb = JRD_get_thread_data();
2001-05-23 15:26:42 +02:00
CompilerScratch* csb = opt->opt_csb;
CompilerScratch::csb_repeat* csb_tail = &csb->csb_rpt[stream];
2003-12-31 06:36:12 +01:00
jrd_rel* relation = csb_tail->csb_relation;
2001-05-23 15:26:42 +02:00
/* Time to find inversions. For each index on the relation
match all unused booleans against the index looking for upper
and lower bounds that can be computed by the index. When
all unused conjunctions are exhausted, see if there is enough
information for an index retrieval. If so, build up and
inversion component of the boolean. */
/*
2008-12-20 09:12:19 +01:00
inversion = NULL;
opt_end = opt->opt_rpt + opt->opt_count;
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
if (opt->opt_count)
for (i = 0; i < csb_tail->csb_indices; i++)
{
clear_bounds (opt, idx);
for (tail = opt->opt_rpt; tail < opt_end; tail++)
{
node = tail->opt_conjunct;
if (!(tail->opt_flags & opt_used) && OPT_computable(csb, node, -1))
match (opt, stream, node, idx);
if (node->nod_type == nod_starts)
compose (&inversion, make_starts (opt, node, stream, idx), nod_bit_and);
}
compose (&inversion, make_index (opt, relation, idx), nod_bit_and);
idx = idx->idx_rpt + idx->idx_count;
}
2001-05-23 15:26:42 +02:00
*/
2004-08-21 11:29:46 +02:00
RecordSource* rsb = FB_NEW_RPT(*tdbb->getDefaultPool(), 0) RecordSource;
rsb->rsb_type = rsb_ext_sequential;
rsb->rsb_stream = stream;
rsb->rsb_relation = relation;
rsb->rsb_impure = CMP_impure(csb, sizeof(irsb));
2001-05-23 15:26:42 +02:00
return rsb;
2001-05-23 15:26:42 +02:00
}
2009-04-28 15:08:04 +02:00
void EXT_ready(jrd_rel*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ r e a d y
*
**************************************
*
* Functional description
* Open an external file.
*
**************************************/
}
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);
}
2001-05-23 15:26:42 +02:00
/* Loop thru fields setting missing fields to either blanks/zeros
or the missing value */
/* check if file is read only if read only then
2001-05-23 15:26:42 +02:00
post error we cannot write to this file */
if (file->ext_flags & EXT_readonly) {
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
/* Distinguish error message for a ReadOnly database */
if (dbb->dbb_flags & DBB_read_only)
ERR_post(Arg::Gds(isc_read_only_database));
2003-12-31 06:36:12 +01: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;
2003-12-31 06:36:12 +01: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
}
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
}
2009-04-28 15:08:04 +02:00
void EXT_trans_commit(jrd_tra*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ t r a n s _ c o m m i t
*
**************************************
*
* Functional description
* Checkin at transaction commit time.
*
**************************************/
}
2009-04-28 15:08:04 +02:00
void EXT_trans_prepare(jrd_tra*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ t r a n s _ p r e p a r e
*
**************************************
*
* Functional description
* Checkin at transaction prepare time.
*
**************************************/
}
2009-04-28 15:08:04 +02:00
void EXT_trans_rollback(jrd_tra*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ t r a n s _ r o l l b a c k
*
**************************************
*
* Functional description
* Checkin at transaction rollback time.
*
**************************************/
}
2009-04-28 15:08:04 +02:00
void EXT_trans_start(jrd_tra*)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X T _ t r a n s _ s t a r t
*
**************************************
*
* Functional description
* Checkin at start transaction time.
*
**************************************/
}
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;
}
}