From 5d498a6d7449d44a9278909dd10530ef0a19de29 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Sun, 18 Sep 2011 00:21:12 +0000 Subject: Imported from /home/lorry/working-area/delta_itzam-tarball/libitzam-6.0.4.tar.gz. --- src/itzam_data.c | 1573 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1573 insertions(+) create mode 100644 src/itzam_data.c (limited to 'src/itzam_data.c') diff --git a/src/itzam_data.c b/src/itzam_data.c new file mode 100644 index 0000000..b4b31d8 --- /dev/null +++ b/src/itzam_data.c @@ -0,0 +1,1573 @@ +/* + Itzam/C (version 6.0) is an embedded database engine written in Standard C. + + Copyright 2011 Scott Robert Ladd. All rights reserved. + + Older versions of Itzam/C are: + Copyright 2002, 2004, 2006, 2008 Scott Robert Ladd. All rights reserved. + + Ancestral code, from Java and C++ books by the author, is: + Copyright 1992, 1994, 1996, 2001 Scott Robert Ladd. All rights reserved. + + Itzam/C is user-supported open source software. It's continued development is dependent on + financial support from the community. You can provide funding by visiting the Itzam/C + website at: + + http://www.coyotegulch.com + + You may license Itzam/C in one of two fashions: + + 1) Simplified BSD License (FreeBSD License) + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY SCOTT ROBERT LADD ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SCOTT ROBERT LADD OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those of the + authors and should not be interpreted as representing official policies, either expressed + or implied, of Scott Robert Ladd. + + 2) Closed-Source Proprietary License + + If your project is a closed-source or proprietary project, the Simplified BSD License may + not be appropriate or desirable. In such cases, contact the Itzam copyright holder to + arrange your purchase of an appropriate license. + + The author can be contacted at: + + scott.ladd@coyotegulch.com + scott.ladd@gmail.com + http:www.coyotegulch.com +*/ + +#include "itzam.h" + +#include +#include +#include +#include +#include + +static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; + +/*----------------------------------------------------------------------------- + * deleted record list management + */ +static itzam_state read_dellist(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + + if (datafile->m_shared->m_header.m_dellist_ref != ITZAM_NULL_REF) + { + if (-1 != itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref + sizeof(itzam_record_header),ITZAM_SEEK_BEGIN)) + { + /* read the header + */ + if (itzam_file_read(datafile->m_file,&datafile->m_dellist_header,sizeof(itzam_dellist_header))) + { + itzam_int size = sizeof(itzam_dellist_entry) * datafile->m_dellist_header.m_table_size; + + if (datafile->m_dellist != NULL) + free(datafile->m_dellist); + + datafile->m_dellist = (itzam_dellist_entry *)malloc(size); + + if (datafile->m_dellist != NULL) + { + if (itzam_file_read(datafile->m_file,datafile->m_dellist,size)) + result = ITZAM_OKAY; + } + else + datafile->m_error_handler("read_dellist",ITZAM_ERROR_MALLOC); + } + } + + if (result != ITZAM_OKAY) + datafile->m_error_handler("read_dellist",ITZAM_ERROR_DELLIST_NOT_READ); + } + else + datafile->m_dellist = NULL; + + return result; +} + +static itzam_state write_dellist(itzam_datafile * datafile, itzam_bool has_grown) +{ + itzam_int size = sizeof(itzam_dellist_entry) * datafile->m_dellist_header.m_table_size; + itzam_state result = ITZAM_FAILED; + itzam_record_header header; + + if (has_grown) + { + /* remove old record if there was one + */ + if (datafile->m_shared->m_header.m_dellist_ref != ITZAM_NULL_REF) + { + if (-1 != itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref,ITZAM_SEEK_BEGIN)) + { + /* read the header + */ + if (itzam_file_read(datafile->m_file,&header,sizeof(header))) + { + /* change record header; make this record the head of the deleted list + */ + header.m_flags &= ~ITZAM_RECORD_IN_USE; + header.m_flags &= ~ITZAM_RECORD_DELLIST; + + /* move to beginning of record header again and rewrite it + */ + itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref,ITZAM_SEEK_BEGIN); + + if (itzam_file_write(datafile->m_file,&header,sizeof(header))) + result = ITZAM_OKAY; + } + } + } + + /* explicitly append; we can't use write because it might try to change the + * deleted list while we're saving it + */ + if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_END)) + { + datafile->m_shared->m_header.m_dellist_ref = itzam_file_tell(datafile->m_file); + + if (datafile->m_shared->m_header.m_dellist_ref > 0) + { + /* write new record header + */ + itzam_record_header rec_head; + + rec_head.m_signature = ITZAM_RECORD_SIGNATURE; + rec_head.m_flags = ITZAM_RECORD_IN_USE | ITZAM_RECORD_DELLIST; + rec_head.m_length = sizeof(itzam_dellist_header) + size; + rec_head.m_rec_len = rec_head.m_length; + + if (itzam_file_write(datafile->m_file,&rec_head,sizeof(itzam_record_header))) + { + /* write the list header + */ + if (itzam_file_write(datafile->m_file,&datafile->m_dellist_header,sizeof(itzam_dellist_header))) + { + /* write the list + */ + if (itzam_file_write(datafile->m_file,datafile->m_dellist,size)) + { + /* update the header with the pointer to the new deleted record list + */ + if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_BEGIN)) + { + if (itzam_file_write(datafile->m_file,&datafile->m_shared->m_header,sizeof(itzam_datafile_header))) + result = ITZAM_OKAY; + } + } + } + } + } + } + } + else + { + if (-1 != itzam_file_seek(datafile->m_file,datafile->m_shared->m_header.m_dellist_ref + sizeof(itzam_record_header),ITZAM_SEEK_BEGIN)) + { + /* write the header + */ + if (itzam_file_write(datafile->m_file,&datafile->m_dellist_header,sizeof(itzam_dellist_header))) + { + /* write the list + */ + if (itzam_file_write(datafile->m_file,datafile->m_dellist,size)) + result = ITZAM_OKAY; + } + } + } + + if (result != ITZAM_OKAY) + datafile->m_error_handler("write_dellist",ITZAM_ERROR_DELLIST_NOT_WRITTEN); + + return result; +} + +/*----------------------------------------------------------------------------- + * utilities + */ +static char * get_tranfile_name(const char * filename) +{ + static const char * tran_name_mask = "%s.itzamtran"; + char * result = (char *)malloc(strlen(tran_name_mask) + strlen(filename) + 1); + sprintf(result,tran_name_mask,filename); + return result; +} + +itzam_bool itzam_datafile_exists(const char * filename) +{ + struct stat info; + return (0 == stat(filename, &info)); +} + +static char * get_shared_name(const char * mask, const char * filename) +{ + char * result = (char *)malloc(strlen(mask) + strlen(filename) + 1); + + char * norm = strdup(filename); + char * c = norm; + + while (*c) + { + if (!isalnum(*c)) + *c = '_'; + else + if (isalpha(*c)) + *c = tolower(*c); + + ++c; + } + + sprintf(result, mask, norm); + + free(norm); + + return result; +} + +#if defined(ITZAM_UNIX) +static const char * shared_mask = "/%s_ItzamSharedDatafile"; +#else +static const char * shared_mask = "Global\\%s_ItzamSharedDatafile"; +static const char * mutex_mask = "Global\\%s_ItzamMutex"; +#endif + +/*----------------------------------------------------------------------------- + * datafile functions + */ +itzam_state itzam_datafile_create(itzam_datafile * datafile, const char * filename) +{ + itzam_datafile_header header; +#if defined(ITZAM_UNIX) + pthread_mutexattr_t attr; +#else + char * mutex_name; +#endif + itzam_state result = ITZAM_FAILED; + itzam_bool creator; + + pthread_mutex_lock(&global_mutex); + + /* verify arguments before proceeding + */ + if (datafile != NULL) + { + /* fill header + */ + header.m_signature = ITZAM_DATAFILE_SIGNATURE; + header.m_version = ITZAM_DATAFILE_VERSION; + header.m_dellist_ref = ITZAM_NULL_REF; + header.m_schema_ref = ITZAM_NULL_REF; + header.m_index_list_ref = ITZAM_NULL_REF; + header.m_transaction_tail = ITZAM_NULL_REF; + + /* fill remaining datafile members + */ +#if defined(ITZAM_UNIX) + datafile->m_file = -1; + datafile->m_shmem = -1; +#else + datafile->m_file = NULL; + datafile->m_shmem = NULL; +#endif + datafile->m_filename = strdup(filename); + datafile->m_dellist = NULL; + datafile->m_tran_file = NULL; + datafile->m_tran_replacing = itzam_false; + datafile->m_shared = NULL; + datafile->m_is_open = itzam_false; + datafile->m_file_locked = itzam_false; + datafile->m_in_transaction = itzam_false; + datafile->m_error_handler = default_error_handler; + +#if defined(ITZAM_UNIX) + memset(&datafile->m_file_lock,0,sizeof(struct flock)); +#endif + + /* generate transaction file name + */ + datafile->m_tran_file_name = get_tranfile_name(filename);; + + /* generate shared memory for header + */ + datafile->m_shmem_name = get_shared_name(shared_mask, filename); + + /* create OS-level file + */ + datafile->m_file = itzam_file_create(filename); + + if (ITZAM_GOOD_FILE(datafile->m_file)) + { + /* write header + */ + if (itzam_file_write(datafile->m_file,&header,sizeof(itzam_datafile_header))) + { + itzam_file_commit(datafile->m_file); + + /* generate shared memory and fill it + */ + datafile->m_shmem = itzam_shmem_obtain(datafile->m_shmem_name, sizeof(itzam_datafile_shared), &creator); + datafile->m_shared = (itzam_datafile_shared *)itzam_shmem_getptr(datafile->m_shmem, sizeof(itzam_datafile_shared)); + datafile->m_shared->m_count = 1; + + /* obtain mutex + */ +#if defined(ITZAM_UNIX) + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&datafile->m_shared->m_mutex, &attr); +#else + mutex_name = get_shared_name(mutex_mask,filename); + + datafile->m_mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, (LPCSTR)name); + + if (datafile->m_mutex == NULL) + datafile->m_mutex = CreateMutex(NULL, FALSE, (LPCSTR)name); + + free(mutex_name); +#endif + datafile->m_read_only = itzam_false; /* can't be read only durign creation */ + memcpy(&datafile->m_shared->m_header, &header, sizeof(itzam_datafile_header)); + + result = ITZAM_OKAY; + } + else + default_error_handler("itzam_datafile_create",ITZAM_ERROR_WRITE_FAILED); + } + else + default_error_handler("itzam_datafile_create",ITZAM_ERROR_FILE_CREATE); + + datafile->m_is_open = (result == ITZAM_OKAY); + } + else + default_error_handler("itzam_datafile_create",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + pthread_mutex_unlock(&global_mutex); + + return result; +} + +itzam_state itzam_datafile_open(itzam_datafile * datafile, + const char * filename, + itzam_bool recover, + itzam_bool read_only) +{ + itzam_bool have_header = itzam_false; + itzam_bool creator = itzam_false; +#if defined(ITZAM_UNIX) + pthread_mutexattr_t attr; +#else + char * mutex_name; +#endif + itzam_state result = ITZAM_FAILED; + + pthread_mutex_lock(&global_mutex); + + /* verify arguments before proceeding + */ + if (datafile != NULL) + { + /* set default error handler + */ + datafile->m_error_handler = default_error_handler; + datafile->m_tran_file = NULL; + datafile->m_tran_replacing = itzam_false; + datafile->m_file_locked = itzam_false; + datafile->m_is_open = itzam_false; + datafile->m_dellist = NULL; + datafile->m_in_transaction = itzam_false; + +#if defined(ITZAM_UNIX) + memset(&datafile->m_file_lock,0,sizeof(struct flock)); +#endif + + /* assumes that datafile is a clean structure (i.e., it isn't open already) + */ + datafile->m_file = itzam_file_open(filename); + + if (ITZAM_GOOD_FILE(datafile->m_file)) + { + datafile->m_is_open = itzam_true; + + //itzam_datafile_file_lock(datafile); + + /* get tranfile name + */ + datafile->m_tran_file_name = get_tranfile_name(filename);; + + /* get shared memory + */ + datafile->m_shmem_name = get_shared_name(shared_mask, filename); + datafile->m_shmem = itzam_shmem_obtain(datafile->m_shmem_name, sizeof(itzam_datafile_shared), &creator); + datafile->m_shared = (itzam_datafile_shared *)itzam_shmem_getptr(datafile->m_shmem, sizeof(itzam_datafile_shared)); + +#if defined(ITZAM_UNIX) + if (creator) + { + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&datafile->m_shared->m_mutex, &attr); + } +#else + mutex_name = get_shared_name(mutex_mask,filename); + + datafile->m_mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, (LPCSTR)name); + + if (datafile->m_mutex == NULL) + datafile->m_mutex = CreateMutex(NULL, FALSE, (LPCSTR)name); + + free(mutex_name); +#endif + + datafile->m_read_only = read_only; + + if (creator) + datafile->m_shared->m_count = 1; + else + datafile->m_shared->m_count += 1; + + /* read the header + */ + if (creator) // (datafile->m_shared->m_header.m_signature != ITZAM_DATAFILE_SIGNATURE) && (creator)) + have_header = itzam_file_read(datafile->m_file, &datafile->m_shared->m_header, sizeof(itzam_datafile_header)); + else + have_header = itzam_true; + + if (have_header) + { + /* verify signature and version + */ + if (datafile->m_shared->m_header.m_signature == ITZAM_DATAFILE_SIGNATURE) + { + if (datafile->m_shared->m_header.m_version == ITZAM_DATAFILE_VERSION) + { + /* read deleted list, if any + */ + if (datafile->m_shared->m_header.m_dellist_ref != ITZAM_NULL_REF) + result = read_dellist(datafile); + else + result = ITZAM_OKAY; + + /* if we have a dangling transaction, roll it back + */ + if (recover && (datafile->m_shared->m_header.m_transaction_tail != ITZAM_NULL_REF)) + itzam_datafile_transaction_rollback(datafile); + } + else + datafile->m_error_handler("itzam_datafile_open",ITZAM_ERROR_VERSION); + } + else + datafile->m_error_handler("itzam_datafile_open",ITZAM_ERROR_SIGNATURE); + } + else + datafile->m_error_handler("itzam_datafile_open",ITZAM_ERROR_OPEN_FAILED); + } + + /* can size_t hold the types of references used by this file? + */ + if (result == ITZAM_OKAY) + { + size_t refbytes = (int)((datafile->m_shared->m_header.m_version & 0xFF000000) >> 24) / 8; + + if (REF_BYTES != refbytes) + result = ITZAM_REF_SIZE_MISMATCH; + } + + //itzam_datafile_file_unlock(datafile); + } + else + default_error_handler("itzam_datafile_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + pthread_mutex_unlock(&global_mutex); + + return result; +} + +itzam_state itzam_datafile_close(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + itzam_bool last_owner = itzam_false; + + pthread_mutex_lock(&global_mutex); + + if (datafile != NULL) + { + itzam_datafile_mutex_lock(datafile); + datafile->m_shared->m_count -= 1; + itzam_datafile_mutex_unlock(datafile); + + last_owner = (datafile->m_shared->m_count <= 0) ? itzam_true : itzam_false; + + if (last_owner) + #if defined(ITZAM_UNIX) + pthread_mutex_destroy(&datafile->m_shared->m_mutex); + #else + CloseHandle(datafile->m_mutex); + #endif + + if (datafile->m_dellist != NULL) + { + free(datafile->m_dellist); + datafile->m_dellist = NULL; + } + + free(datafile->m_tran_file_name); + + itzam_shmem_freeptr(datafile->m_shared, sizeof(itzam_datafile_shared)); + + if (last_owner) + itzam_shmem_close(datafile->m_shmem, datafile->m_shmem_name); + + free(datafile->m_shmem_name); + + if (itzam_file_close(datafile->m_file)) + { + datafile->m_is_open = itzam_false; + result = ITZAM_OKAY; + } + else + datafile->m_error_handler("itzam_datafile_close",ITZAM_ERROR_CLOSE_FAILED); + } + else + default_error_handler("itzam_datafile_close",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + pthread_mutex_unlock(&global_mutex); + + return result; +} + +void itzam_datafile_mutex_lock(itzam_datafile * datafile) +{ +#if defined(ITZAM_UNIX) + pthread_mutex_lock(&datafile->m_shared->m_mutex); +#else + WaitForSingleObject(datafile->m_mutex, INFINITE); +#endif +} + +void itzam_datafile_mutex_unlock(itzam_datafile * datafile) +{ +#if defined(ITZAM_UNIX) + pthread_mutex_unlock(&datafile->m_shared->m_mutex); +#else + ReleaseMutex(datafile->m_mutex); +#endif +} + +itzam_bool itzam_datafile_file_lock(itzam_datafile * datafile) +{ + itzam_bool result = itzam_false; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + if (datafile->m_file_locked) + result = itzam_true; + else + { +#if defined(ITZAM_UNIX) + memset(&datafile->m_file_lock,0,sizeof(struct flock)); + datafile->m_file_lock.l_type = F_WRLCK; + result = (itzam_bool)fcntl(datafile->m_file,F_SETLKW,&datafile->m_file_lock); +#else + result = (itzam_bool)LockFile(datafile->m_file, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF); +#endif + } + + datafile->m_file_locked = result; + } + else + { + default_error_handler("itzam_datafile_file_lock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + } + + return result; +} + +itzam_bool itzam_datafile_file_unlock(itzam_datafile * datafile) +{ + itzam_bool result = itzam_false; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + if (datafile->m_file_locked) + { +#if defined(ITZAM_UNIX) + fsync(datafile->m_file); + datafile->m_file_lock.l_type = F_UNLCK; + result = (itzam_bool)fcntl(datafile->m_file,F_SETLKW,&datafile->m_file_lock); +#else + result = (itzam_bool)UnlockFile(datafile->m_file, 0, 0, 0xFFFFFFFF, 0xFFFFFFFF); +#endif + } + else + result = itzam_true; /* file wasn't locked, so it *is* unlocked... */ + + if (result == itzam_true) + datafile->m_file_locked = itzam_false; + } + else + default_error_handler("itzam_datafile_file_unlock",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_bool itzam_datafile_is_open(itzam_datafile * datafile) +{ + itzam_bool result = itzam_false; + + if (datafile != NULL) + { + itzam_datafile_mutex_lock(datafile); + result = datafile->m_is_open; + itzam_datafile_mutex_unlock(datafile); + } + else + { + default_error_handler("itzam_datafile_is_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + } + + return result; +} + +float itzam_datafile_get_version(itzam_datafile * datafile) +{ + float result = 0.0F; + + if (datafile != NULL) + { + itzam_datafile_mutex_lock(datafile); + + result = (double)((datafile->m_shared->m_header.m_version & 0x00FF0000) >> 16) + + ((datafile->m_shared->m_header.m_version & 0x0000FF00) >> 8) / 100.0 + + ((datafile->m_shared->m_header.m_version & 0x000000FF)) / 10000.0; + + itzam_datafile_mutex_unlock(datafile); + } + else + { + default_error_handler("itzam_datafile_get_version",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + } + + return result; +} + +void itzam_datafile_set_error_handler(itzam_datafile * datafile, + itzam_error_handler * error_handler) +{ + if (datafile != NULL) + { + itzam_datafile_mutex_lock(datafile); + + if (error_handler == NULL) + datafile->m_error_handler = default_error_handler; + else + datafile->m_error_handler = error_handler; + + itzam_datafile_mutex_unlock(datafile); + } + else + { + default_error_handler("itzam_datafile_set_error_handler",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + } +} + +itzam_ref itzam_datafile_tell(itzam_datafile * datafile) +{ + itzam_ref where = -1; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + where = (itzam_ref)itzam_file_tell(datafile->m_file); + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_tell",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return where; +} + +itzam_state itzam_datafile_seek(itzam_datafile * datafile, itzam_ref pos) +{ + itzam_state result = ITZAM_FAILED; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + /* seek to requested position + */ + if (-1 != itzam_file_seek(datafile->m_file,(long)pos,ITZAM_SEEK_BEGIN)) + result = ITZAM_OKAY; + else + datafile->m_error_handler("itzam_datafile_seek",ITZAM_ERROR_SEEK_FAILED); + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_seek",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_state itzam_datafile_rewind(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + /* seek to byte after first deleted marker + */ + if (-1 != itzam_file_seek(datafile->m_file,(long)sizeof(itzam_datafile_header),ITZAM_SEEK_BEGIN)) + result = ITZAM_OKAY; + else + datafile->m_error_handler("itzam_datafile_rewind",ITZAM_ERROR_SEEK_FAILED); + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_rewind",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +/* This function should NEVER be called by user code; it is an internal function used by indexes. + * It assumes that a returned deleted record will be used by the calling function. + */ +itzam_ref itzam_datafile_get_next_open(itzam_datafile * datafile, itzam_int length) +{ + itzam_int n; + itzam_ref where = ITZAM_NULL_REF; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + if (ITZAM_OKAY == read_dellist(datafile)) + { + for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n) + { + if (datafile->m_dellist[n].m_where != ITZAM_NULL_REF) + { + if (datafile->m_dellist[n].m_length == length) + { + /* save the location of the deleted record we're replacing + */ + where = datafile->m_dellist[n].m_where; + + /* remove this entry from the table + */ + datafile->m_dellist[n].m_where = ITZAM_NULL_REF; + datafile->m_dellist[n].m_length = 0; + write_dellist(datafile,itzam_false); + break; + } + } + } + } + + if (where == ITZAM_NULL_REF) + { + /* no deleted records, so append + */ + if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_END)) + { + where = itzam_file_tell(datafile->m_file); + + if (where < 0) + where = ITZAM_NULL_REF; + } + else + where = ITZAM_NULL_REF; + } + } + else + default_error_handler("itzam_datafile_get_next_open",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return where; +} + +/* internal function to add an operation to the transaction list + */ +static itzam_bool add_tran_op(itzam_datafile * datafile, itzam_int where, itzam_record_header * header, const void * data, itzam_op_type op_type) +{ + itzam_bool result = itzam_false; + itzam_op_header op_header; + itzam_int op_where; + + if (datafile->m_in_transaction) + { + /* initialize operation header and copy data in + */ + op_header.m_type = op_type; + op_header.m_where = where; + op_header.m_prev_tran = datafile->m_shared->m_header.m_transaction_tail; + memcpy(&op_header.m_record_header,header,sizeof(itzam_record_header)); + op_header.m_record_header.m_flags |= ITZAM_RECORD_TRAN_RECORD; + op_header.m_record_where = ITZAM_NULL_REF; + + /* turn off transactions for a moment + */ + datafile->m_in_transaction = itzam_false; + + /* write op data to the file + */ + if (op_type != ITZAM_TRAN_OP_WRITE) + { + op_header.m_record_where = itzam_datafile_get_next_open(datafile->m_tran_file,op_header.m_record_header.m_length); + itzam_datafile_write_flags(datafile->m_tran_file,data,op_header.m_record_header.m_length,op_header.m_record_where,ITZAM_RECORD_TRAN_RECORD); + } + + /* write op header + */ + op_where = itzam_datafile_get_next_open(datafile->m_tran_file,sizeof(itzam_op_header)); + itzam_datafile_write_flags(datafile->m_tran_file,&op_header,sizeof(op_header),op_where,ITZAM_RECORD_TRAN_HEADER); + + /* turn transactions on again + */ + datafile->m_in_transaction = itzam_true; + + /* rewrite datafile header + */ + datafile->m_shared->m_header.m_transaction_tail = op_where; + + if (-1 != itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_BEGIN)) + { + if (itzam_file_write(datafile->m_file,&datafile->m_shared->m_header,sizeof(itzam_datafile_header))) + result = itzam_true; + } + } + + return result; +} + +itzam_ref itzam_datafile_write_flags(itzam_datafile * datafile, const void * data, itzam_int length, itzam_ref where, int32_t flags) +{ + itzam_record_header rec_header; + + /* make sure the arguments make sense + */ + if ((datafile != NULL) && (data != NULL) && (length > 0) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + if (datafile->m_read_only) + return ITZAM_NULL_REF; + else + { + /* if we aren't told where to put the record, find a place + */ + if (where == ITZAM_NULL_REF) + where = itzam_datafile_get_next_open(datafile,length); + else + { + /* are we in a transaction? + */ + if (datafile->m_in_transaction) + { + /* read the old record rec_header + */ + if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN)) + { + if (itzam_file_read(datafile->m_file,&rec_header,sizeof(itzam_record_header))) + { + /* if the record is active, then we need to save it + */ + if (rec_header.m_flags & ITZAM_RECORD_IN_USE) + { + /* create a temporary buffer + */ + void * op_data = malloc(rec_header.m_rec_len); + + /* read data + */ + if (op_data != NULL) + { + /* now save record information + */ + if (itzam_file_read(datafile->m_file,op_data,rec_header.m_length)) + { + add_tran_op(datafile,where,&rec_header,op_data,ITZAM_TRAN_OP_REMOVE); + free(op_data); + } + else + datafile->m_error_handler("itzam_datafile_write_flags (1)",ITZAM_ERROR_READ_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_write_flags (2)",ITZAM_ERROR_MALLOC); + } + } + } + } + } + + rec_header.m_signature = ITZAM_RECORD_SIGNATURE; + rec_header.m_flags = ITZAM_RECORD_IN_USE | flags; + rec_header.m_length = 0; + rec_header.m_rec_len = 0; + + /* do we have a good place to write? + */ + if (where != ITZAM_NULL_REF) + { + /* modify record rec_header + */ + rec_header.m_rec_len = length; + rec_header.m_length = length; + + /* ITZAM_SEEK to beginning of new record + */ + if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN)) + { + /* write the record rec_header + */ + if (itzam_file_write(datafile->m_file,&rec_header,sizeof(rec_header))) + { + /* write the record + */ + if (!itzam_file_write(datafile->m_file,data,length)) + { + datafile->m_error_handler("itzam_datafile_write_flags (4)",ITZAM_ERROR_WRITE_FAILED); + where = ITZAM_NULL_REF; + } + } + else + { + datafile->m_error_handler("itzam_datafile_write_flags (5)", ITZAM_ERROR_WRITE_FAILED); + where = ITZAM_NULL_REF; + } + } + else + { + datafile->m_error_handler("itzam_datafile_write_flags (6)",ITZAM_ERROR_SEEK_FAILED); + where = ITZAM_NULL_REF; + } + + if ((where != ITZAM_NULL_REF) && (datafile->m_in_transaction)) + add_tran_op(datafile,where,&rec_header,data,ITZAM_TRAN_OP_WRITE); + } + } + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_write_flags (7)",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return where; +} + +itzam_ref itzam_datafile_write(itzam_datafile * datafile, const void * data, itzam_int length, itzam_ref where) +{ + return itzam_datafile_write_flags(datafile, data, length, where, 0); +} + +// dangerous! this function could overwrite critical file data structures +itzam_state itzam_datafile_overwrite(itzam_datafile * datafile, const void * data, itzam_int length, itzam_ref where, itzam_int offset) +{ + itzam_state result = ITZAM_FAILED; + itzam_record_header rec_header; + + if ((datafile != NULL) && (data != NULL) && (length > 0) && (datafile->m_is_open) && (where != ITZAM_NULL_REF)) + { + if (datafile->m_read_only) + { + datafile->m_error_handler("itzam_datafile_write_flags", ITZAM_ERROR_READ_ONLY); + return ITZAM_FAILED; + } + + itzam_datafile_mutex_lock(datafile); + + /* read header file + */ + if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN)) + { + if (!itzam_file_read(datafile->m_file,&rec_header,sizeof(itzam_record_header))) + datafile->m_error_handler("itzam_datafile_explicit_write (1)",ITZAM_ERROR_READ_FAILED); + + if ((0 == (rec_header.m_flags & ITZAM_RECORD_IN_USE)) || (rec_header.m_signature != ITZAM_RECORD_SIGNATURE)) + datafile->m_error_handler("itzam_datafile_explicit_write (2)",ITZAM_ERROR_INVALID_RECORD); + } + + /* make sure we fit inside the record + */ + if (rec_header.m_length < (length + offset)) + return ITZAM_OVERWRITE_TOO_LONG; + + /* are we in a transaction? if so, save the rec we're changing + */ + if (datafile->m_in_transaction) + { + /* create a temporary buffer + */ + void * op_data = malloc(rec_header.m_rec_len); + + /* read data + */ + if (op_data != NULL) + { + /* now save record information + */ + if (itzam_file_read(datafile->m_file,op_data,rec_header.m_length)) + { + add_tran_op(datafile, where, &rec_header, op_data, ITZAM_TRAN_OP_OVERWRITE); + free(op_data); + } + else + datafile->m_error_handler("itzam_datafile_explicit_write (2)",ITZAM_ERROR_READ_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_explicit_write (3)",ITZAM_ERROR_MALLOC); + } + + /* modify record at given offset + */ + if (-1 != itzam_file_seek(datafile->m_file, where + sizeof(itzam_record_header) + offset, ITZAM_SEEK_BEGIN)) + { + if (!itzam_file_write(datafile->m_file,data,length)) + { + datafile->m_error_handler("itzam_datafile_explicit_write (4)",ITZAM_ERROR_WRITE_FAILED); + where = ITZAM_NULL_REF; + } + } + else + { + datafile->m_error_handler("itzam_datafile_explicit_write (5)",ITZAM_ERROR_SEEK_FAILED); + where = ITZAM_NULL_REF; + } + + itzam_datafile_mutex_unlock(datafile); + + result = ITZAM_OKAY; + } + else + default_error_handler("itzam_datafile_write_flags (5)",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_state itzam_datafile_read(itzam_datafile * datafile, void * record, itzam_int max_length) +{ + itzam_state result = ITZAM_FAILED; + itzam_record_header header = { 0, 0, 0, 0 }; + itzam_bool error = itzam_false; + + if ((datafile != NULL) && (record != NULL) && (max_length > 0) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + while (!error) + { + /* read the record header + */ + if (itzam_file_read(datafile->m_file,&header,sizeof(itzam_record_header))) + { + /* if the record is active, we can read it + */ + if ((header.m_flags & ITZAM_RECORD_IN_USE) && !(header.m_flags & ITZAM_RECORD_DELLIST)) + break; + + /* if the record signature is invalid, we have an error + */ + if (header.m_signature != ITZAM_RECORD_SIGNATURE) + { + datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_INVALID_RECORD); + error = itzam_true; + } + + /* move to the next record + */ + if (-1 == itzam_file_seek(datafile->m_file,header.m_length,ITZAM_SEEK_CURRENT)) + { + datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_SEEK_FAILED); + error = itzam_true; + } + } + else + { + datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_READ_FAILED); + error = itzam_true; + } + } + + /* only read actual data if no errors + */ + if (!error) + { + itzam_int read_len = (max_length < header.m_rec_len) ? max_length : header.m_rec_len; + + if (itzam_file_read(datafile->m_file, record, read_len)) + { + /* skip any "padding" between record size and record buffer length + */ + if (read_len < header.m_length) + { + if (-1 == itzam_file_seek(datafile->m_file,header.m_length - read_len,ITZAM_SEEK_CURRENT)) + { + datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_SEEK_FAILED); + error = itzam_true; + } + } + + result = ITZAM_OKAY; + } + else + { + datafile->m_error_handler("itzam_datafile_read",ITZAM_ERROR_READ_FAILED); + error = itzam_true; + } + } + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_read",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_state itzam_datafile_read_alloc(itzam_datafile * datafile, void ** data, itzam_int * length) +{ + itzam_state result = ITZAM_FAILED; + itzam_record_header header; + + if ((datafile != NULL) && (data != NULL) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + if (itzam_file_read(datafile->m_file,&header,sizeof(header))) + { + /* if the record is active, we can read it + */ + if (header.m_flags & ITZAM_RECORD_IN_USE) + { + /* create a temporary buffer + */ + if (length != NULL) + *length = header.m_rec_len; + + *data = malloc(header.m_rec_len); + + /* read data + */ + if (*data != NULL) + { + if (itzam_file_read(datafile->m_file,*data,header.m_length)) + { + /* skip any "padding" between record size and record buffer length + */ + if (header.m_rec_len < header.m_length) + itzam_file_seek(datafile->m_file,header.m_length - header.m_rec_len,ITZAM_SEEK_CURRENT); + + result = ITZAM_OKAY; + } + else + datafile->m_error_handler("1) itzam_datafile_read_alloc",ITZAM_ERROR_READ_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_read_alloc",ITZAM_ERROR_MALLOC); + } + else + datafile->m_error_handler("2) itzam_datafile_read_alloc",ITZAM_ERROR_READ_FAILED); + } + else + datafile->m_error_handler("3) itzam_datafile_read_alloc",ITZAM_ERROR_READ_FAILED); + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_read_alloc",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_state itzam_datafile_remove(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + int n; + itzam_ref where; + + if ((datafile != NULL) && (datafile->m_is_open)) + { + itzam_datafile_mutex_lock(datafile); + + if (datafile->m_read_only) + result = ITZAM_READ_ONLY; + else + { + /* get our position + */ + where = itzam_file_tell(datafile->m_file); + + /* make sure we're not in the file header + */ + if (where >= sizeof(itzam_ref)) + { + itzam_record_header header; + + /* read the header + */ + if (itzam_file_read(datafile->m_file,&header,sizeof(header))) + { + /* only delete if it isn't already deleted + */ + if (header.m_flags & ITZAM_RECORD_IN_USE) + { + /* save this record if we're in a transaction + */ + if (datafile->m_in_transaction) + { + /** + * create a temporary buffer + */ + void * op_data = malloc(header.m_rec_len); + + /* read data + */ + if (op_data != NULL) + { + /* now save record information + */ + if (itzam_file_read(datafile->m_file,op_data,header.m_length)) + { + add_tran_op(datafile,where,&header,op_data,ITZAM_TRAN_OP_REMOVE); + free(op_data); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_READ_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_MALLOC); + } + + /* change record header; make this record the head of the deleted list + */ + header.m_flags &= ~ITZAM_RECORD_IN_USE; + + /* move to beginning of record header + */ + if (-1 != itzam_file_seek(datafile->m_file,where,ITZAM_SEEK_BEGIN)) + { + /* write revised record header + */ + if (itzam_file_write(datafile->m_file,&header,sizeof(header))) + { + /* update deleted list + */ + if (ITZAM_FAILED == read_dellist(datafile)) + { + datafile->m_dellist_header.m_table_size = ITZAM_DELLIST_BLOCK_SIZE; + datafile->m_dellist = (itzam_dellist_entry *)malloc(sizeof(itzam_dellist_entry) * datafile->m_dellist_header.m_table_size); + + if (datafile->m_dellist != NULL) + { + datafile->m_dellist[0].m_where = where; + datafile->m_dellist[0].m_length = header.m_length; + + for (n = 1; n < datafile->m_dellist_header.m_table_size; ++n) + { + datafile->m_dellist[n].m_where = ITZAM_NULL_REF; + datafile->m_dellist[n].m_length = 0; + } + + result = write_dellist(datafile,itzam_true); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_MALLOC); + } + else + { + for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n) + { + if (datafile->m_dellist[n].m_where == ITZAM_NULL_REF) + { + datafile->m_dellist[n].m_where = where; + datafile->m_dellist[n].m_length = header.m_length; + result = write_dellist(datafile,itzam_false); + break; + } + } + + /* if the entry didn't fit, we have to expand it + */ + if (n == datafile->m_dellist_header.m_table_size) + { + /* create new table + */ + itzam_int newsize = datafile->m_dellist_header.m_table_size + ITZAM_DELLIST_BLOCK_SIZE; + itzam_dellist_entry * newlist = (itzam_dellist_entry *)malloc(sizeof(itzam_dellist_entry) * newsize); + + if (newlist != NULL) + { + /* copy old entries; could (?should) use memcpy here I suppose + */ + for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n) + { + newlist[n].m_where = datafile->m_dellist[n].m_where; + newlist[n].m_length = datafile->m_dellist[n].m_length; + } + + /* add the newly-deleted record + */ + newlist[n].m_where = where; + newlist[n].m_length = header.m_length; + + /* add the old deleted list record + */ + newlist[n+1].m_where = datafile->m_shared->m_header.m_dellist_ref; + newlist[n+1].m_length = datafile->m_dellist_header.m_table_size; + + /* initialize remaining unused new entries + */ + for (n += 2 ; n < newsize; ++n) + { + newlist[n].m_where = ITZAM_NULL_REF; + newlist[n].m_length = 0; + } + + /* free space used by old list + */ + free(datafile->m_dellist); + + /* exchange new list for old + */ + datafile->m_dellist = newlist; + datafile->m_dellist_header.m_table_size = newsize; + + /* write it + */ + result = write_dellist(datafile,itzam_true); + } + else + datafile->m_error_handler("itzam_datafile_read_alloc",ITZAM_ERROR_MALLOC); + } + } + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_WRITE_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_SEEK_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_DUPE_REMOVE); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_READ_FAILED); + } + else + datafile->m_error_handler("itzam_datafile_remove",ITZAM_ERROR_TELL_FAILED); + } + + itzam_datafile_mutex_unlock(datafile); + } + else + default_error_handler("itzam_datafile_remove",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_state itzam_datafile_transaction_start(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + + /* only start a transaction is one isn't already in progress + */ + if ((datafile != NULL) && (datafile->m_is_open) && (!datafile->m_in_transaction)) + { + if (datafile->m_read_only) + { + datafile->m_error_handler("itzam_datafile_transactions_start", ITZAM_ERROR_READ_ONLY); + return ITZAM_READ_ONLY; + } + + itzam_datafile_mutex_lock(datafile); + + datafile->m_tran_file = (itzam_datafile *)malloc(sizeof(itzam_datafile)); + + if (datafile->m_tran_file != NULL) + { + /* create a datafile to contain the transaction operations + */ + result = itzam_datafile_create(datafile->m_tran_file,datafile->m_tran_file_name); + + if (ITZAM_OKAY == result) + datafile->m_in_transaction = itzam_true; + else + default_error_handler("itzam_datafile_transaction_start",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + } + } + else + default_error_handler("itzam_datafile_transaction_start",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +static void transaction_cleanup(itzam_datafile * datafile, itzam_bool rollback) +{ + itzam_op_header * op_header; + itzam_int dont_care; + void * op_record; + itzam_int data_len; + itzam_int n; + itzam_bool dummy; + + if (rollback) + { + /* start at the beginning + */ + itzam_ref op_where = datafile->m_shared->m_header.m_transaction_tail; + + /* while we have something to process + */ + while (op_where != ITZAM_NULL_REF) + { + /* read the operation header + */ + itzam_datafile_seek(datafile->m_tran_file,op_where); + + /* read the rolled-back record + */ + itzam_datafile_read_alloc(datafile->m_tran_file,(void **)(void*)&op_header,&dont_care); + + /* act upon the op_record.... + */ + switch (op_header->m_type) + { + /* remove a record that was written + */ + case ITZAM_TRAN_OP_WRITE: + itzam_datafile_seek(datafile,op_header->m_where); + itzam_datafile_remove(datafile); + break; + + /* replace a record that was removed + */ + case ITZAM_TRAN_OP_REMOVE: + itzam_datafile_seek(datafile->m_tran_file,op_header->m_record_where); + itzam_datafile_read_alloc(datafile->m_tran_file,(void **)&op_record,&data_len); + itzam_datafile_write(datafile,op_record,op_header->m_record_header.m_length,op_header->m_where); + free(op_record); + + if (ITZAM_OKAY == read_dellist(datafile)) + { + for (n = 0; n < datafile->m_dellist_header.m_table_size; ++n) + { + if (datafile->m_dellist[n].m_where == op_header->m_where) + { + /* remove this entry from the table + */ + datafile->m_dellist[n].m_where = ITZAM_NULL_REF; + datafile->m_dellist[n].m_length = 0; + break; + } + } + } + + write_dellist(datafile,itzam_false); + + break; + + /* restore a record that was over-written + */ + case ITZAM_TRAN_OP_OVERWRITE: + itzam_datafile_seek(datafile->m_tran_file,op_header->m_record_where); + itzam_datafile_read_alloc(datafile->m_tran_file,(void **)&op_record,&data_len); + itzam_datafile_write(datafile, op_record, op_header->m_record_header.m_length, op_header->m_where); + free(op_record); + + break; + } + + /* save next operation + */ + op_where = op_header->m_prev_tran; + + /* release memory + */ + free(op_header); + } + } + + /* update the header + */ + datafile->m_shared->m_header.m_transaction_tail = ITZAM_NULL_REF; + itzam_file_seek(datafile->m_file,0,ITZAM_SEEK_BEGIN); + dummy = itzam_file_write(datafile->m_file,&datafile->m_shared->m_header,sizeof(itzam_datafile_header)); + + /* close and remove transaction file + */ + itzam_datafile_close(datafile->m_tran_file); + itzam_file_remove(datafile->m_tran_file_name); + free(datafile->m_tran_file); + datafile->m_tran_file = NULL; + + itzam_datafile_mutex_unlock(datafile); +} + +itzam_state itzam_datafile_transaction_commit(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + + /* only commit if a transaction is active + */ + if ((datafile != NULL) && (datafile->m_is_open) && (datafile->m_in_transaction)) + { + /* we're no longer in a transaction + */ + datafile->m_in_transaction = itzam_false; + + /* clean up transactioon data; it's no longer needed + */ + transaction_cleanup(datafile,itzam_false); + result = ITZAM_OKAY; + } + else + default_error_handler("itzam_datafile_transaction_commit",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} + +itzam_state itzam_datafile_transaction_rollback(itzam_datafile * datafile) +{ + itzam_state result = ITZAM_FAILED; + + /* only rollback if a transaction is active + */ + if ((datafile != NULL) && (datafile->m_is_open) && (datafile->m_in_transaction)) + { + /* we're no longer in a transaction + */ + datafile->m_in_transaction = itzam_false; + + /* clean up transaction data; it's no longer needed + */ + transaction_cleanup(datafile,itzam_true); + result = ITZAM_OKAY; + } + else + default_error_handler("itzam_datafile_transaction_rollback",ITZAM_ERROR_INVALID_DATAFILE_OBJECT); + + return result; +} -- cgit v1.2.1