libxflaim-5.1.969/0000755000175000017500000000000010511001742015235 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/VERSION0000644000175000017500000000000710511001742016302 0ustar ahodgkinsonahodgkinson5.1.969libxflaim-5.1.969/SVNRevision.9690000644000175000017500000000000110511001742017662 0ustar ahodgkinsonahodgkinson libxflaim-5.1.969/Makefile0000644000175000017500000027205610511001742016711 0ustar ahodgkinsonahodgkinson#------------------------------------------------------------------------- # Desc: GNU makefile for XFLAIM library and utilities # Tabs: 3 # # Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public # License as published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, contact Novell, Inc. # # To contact Novell about this file by physical or electronic mail, # you may find current contact information at www.novell.com # # $Id: Makefile 3136 2006-01-25 12:19:01 -0700 (Wed, 25 Jan 2006) dsanders $ #------------------------------------------------------------------------- ############################################################################# # # Sample Usage: # # make clean debug all # ############################################################################# # -- Include -- -include config.in # -- Project -- project_name = xflaim project_display_name = XFLAIM project_brief_desc = An extensible, flexible, adaptable, embeddable XML database engine # -- Maintainers -- ahodgkinson_info = Andrew Hodgkinson (Sr. Software Engineer) dsanders_info = Daniel Sanders (Sr. Software Engineer) # -- Versions -- major_version = 5 minor_version = 1 version = $(major_version).$(minor_version).$(svn_revision) # libtool versions are updated according to the following rules: # # 1. Start with a version of 0.0.0 for a library # 2. Update the version number immediately before a public release of the software # 3. If the library source code has changed at all since the last update, increment revision # 4. If any interfaces have been added, removed, or changed since the last update, increment current # 5. If any interfaces have been added since the last public release, increment age # 6. If any interfaces have been removed since the last public release, set age to 0 so_current = 3 so_revision = 2 so_age = 0 shared_lib_version = package_release_num = 1 # -- Paths initializations -- install_prefix = /usr # -- RPM, SPEC file names package_proj_name = lib$(project_name) package_proj_name_and_ver = $(package_proj_name)-$(version) # -- Determine if we are only cleaning -- util_targets = checkdb rebuild view sample xshell test_targets = basictest binarytest colldeftest dictchangetest dictdeftest \ dirtyexittest1 dirtyexittest2 domnodetest enctest importtest \ indexdeftest indextest1 indextest2 indextest3 \ namespacetest metaphonetest regressiontest rfltest sortkeytest \ sortkeytest2 xpathtest1 xpathtest2 all_targets = java csharp rpms install libs all allutils test $(util_targets) $(test_targets) found_targets = $(foreach target,$(MAKECMDGOALS),$(if $(findstring $(target),$(all_targets)),$(target),)) ifneq (,$(findstring clean,$(MAKECMDGOALS))) do_clean = 1 ifeq ($(if $(findstring 0,$(words $(found_targets))),1,0),0) $(error Cannot specify other targets with clean target) endif else do_clean = 0 endif # -- Target variables -- target_build_type = usenativecc = yes target_os_family = target_processor_family = target_word_size = requested_word_size = win_target = unix_target = netware_target = submake_targets = netware_ring_0_target = sparc_generic = debian_arch = unknown # -- Enable command echoing -- ifneq (,$(findstring verbose,$(MAKECMDGOALS))) submake_targets += verbose ec = else ec = @ endif # -- Determine the host operating system -- ifndef host_os_family ifneq (,$(findstring WIN,$(OS))) host_os_family = win endif endif ifndef host_os_family ifneq (,$(findstring Win,$(OS))) host_os_family = win endif endif ifndef host_os_family ifeq (,$(OSTYPE)) ifneq (,$(RPM_OS)) OSTYPE = $(RPM_OS) endif endif ifeq (,$(OSTYPE)) OSTYPE := $(shell uname -s) endif endif ifndef host_os_family ifneq (,$(findstring Linux,$(OSTYPE))) host_os_family = linux endif endif ifndef host_os_family ifneq (,$(findstring linux,$(OSTYPE))) host_os_family = linux endif endif ifndef host_os_family ifneq (,$(findstring solaris,$(OSTYPE))) host_os_family = solaris endif endif ifndef host_os_family ifneq (,$(findstring SunOS,$(OSTYPE))) host_os_family = solaris endif endif ifndef host_os_family ifneq (,$(findstring darwin,$(OSTYPE))) host_os_family = osx endif endif ifndef host_os_family ifneq (,$(findstring Darwin,$(OSTYPE))) host_os_family = osx endif endif ifndef host_os_family ifneq (,$(findstring aix,$(OSTYPE))) host_os_family = aix endif endif ifndef host_os_family ifneq (,$(findstring hpux,$(OSTYPE))) host_os_family = hpux endif endif ifndef host_os_family ifneq (,$(findstring HP-UX,$(OSTYPE))) host_os_family = hpux endif endif ifndef host_os_family $(error Host operating system could not be determined. You may need to export OSTYPE from the environment.) endif # -- Target build type -- ifndef target_build_type ifneq (,$(findstring debug,$(MAKECMDGOALS))) submake_targets += debug target_build_type = debug endif endif ifndef target_build_type ifneq (,$(findstring release,$(MAKECMDGOALS))) submake_targets += release target_build_type = release endif endif ifndef target_build_type target_build_type = release endif # -- Use non-native (i.e., gcc) compiler on Solaris, etc. ifneq (,$(findstring usegcc,$(MAKECMDGOALS))) submake_targets += usegcc usenativecc = no endif # -- Override platform default word size? -- ifneq (,$(findstring 64bit,$(MAKECMDGOALS))) submake_targets += 64bit requested_word_size = 64 endif ifneq (,$(findstring 32bit,$(MAKECMDGOALS))) submake_targets += 32bit requested_word_size = 32 endif # -- Target operating system -- ifndef target_os_family ifeq ($(host_os_family),linux) unix_target = yes target_os_family = linux endif endif ifndef target_os_family ifeq ($(host_os_family),solaris) unix_target = yes target_os_family = solaris endif endif ifndef target_os_family ifeq ($(host_os_family),osx) unix_target = yes target_os_family = osx endif endif ifndef target_os_family ifeq ($(host_os_family),aix) unix_target = yes target_os_family = aix endif endif ifndef target_os_family ifeq ($(host_os_family),hpux) unix_target = yes target_os_family = hpux endif endif ifneq (,$(findstring nlm,$(MAKECMDGOALS))) submake_targets += nlm netware_target = yes target_os_family = netware host_os_family = win ifneq (,$(findstring ring0,$(MAKECMDGOALS))) submake_targets += ring0 netware_ring_0_target = yes endif endif ifndef target_os_family ifeq ($(host_os_family),win) win_target = yes target_os_family = win endif endif ifndef target_os_family $(error Target operating system could not be determined) endif # -- Host word size and processor -- host_native_word_size = host_processor_family = host_supported_word_sizes = ifneq (,$(PROCESSOR_ARCHITECTURE)) HOSTTYPE = $(PROCESSOR_ARCHITECTURE) endif ifeq (,$(HOSTTYPE)) ifneq (,$(RPM_ARCH)) HOSTTYPE = $(RPM_ARCH) endif endif ifeq (,$(HOSTTYPE)) ifneq ($(host_os_family),hpux) ifneq ($(host_os_family),linux) HOSTTYPE := $(shell uname -p) endif endif ifeq (,$(HOSTTYPE)) HOSTTYPE := $(shell uname -m) else ifneq (,$(findstring nvalid,$(HOSTTYPE))) HOSTTYPE := $(shell uname -m) else ifneq (,$(findstring unknown,$(HOSTTYPE))) HOSTTYPE := $(shell uname -m) endif endif endif endif ifeq (,$(HOSTTYPE)) $(error HOSTTYPE environment variable has not been set) endif ifndef host_native_word_size ifneq (,$(findstring x86_64,$(HOSTTYPE))) host_processor_family = x86 host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring x86,$(HOSTTYPE))) host_processor_family = x86 host_native_word_size = 32 host_supported_word_sizes = 32 endif endif ifndef host_native_word_size ifneq (,$(findstring 86,$(HOSTTYPE))) host_processor_family = x86 host_native_word_size = 32 host_supported_word_sizes = 32 endif endif ifndef host_native_word_size ifneq (,$(findstring ia64,$(HOSTTYPE))) host_processor_family = ia64 host_native_word_size = 64 host_supported_word_sizes = 64 endif endif ifndef host_native_word_size ifneq (,$(findstring s390x,$(HOSTTYPE))) host_processor_family = s390 host_native_word_size = 64 host_supported_word_sizes = 31 64 endif endif ifndef host_native_word_size ifneq (,$(findstring s390,$(HOSTTYPE))) host_processor_family = s390 host_native_word_size = 31 host_supported_word_sizes = 31 endif endif ifndef host_native_word_size ifneq (,$(findstring ppc64,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring ppc,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 32 host_supported_word_sizes = 32 endif endif ifndef host_native_word_size ifneq (,$(findstring sparc,$(HOSTTYPE))) host_processor_family = sparc host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring powerpc,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 32 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring Power,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 32 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring rs6000,$(HOSTTYPE))) host_processor_family = powerpc host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring hppa,$(HOSTTYPE))) host_processor_family = hppa host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size ifneq (,$(findstring 9000,$(HOSTTYPE))) host_processor_family = hppa host_native_word_size = 64 host_supported_word_sizes = 32 64 endif endif ifndef host_native_word_size $(error Unable to determine host word size. $(HOSTTYPE)) endif # -- Target word size and processor -- ifneq (,$(findstring nlm,$(MAKECMDGOALS))) target_processor_family = x86 target_word_size = 32 target_supported_word_sizes = 32 else target_processor_family = $(host_processor_family) target_word_size = $(host_native_word_size) target_supported_word_sizes = $(host_supported_word_sizes) endif ifdef requested_word_size ifneq (,$(findstring $(requested_word_size),$(target_supported_word_sizes))) target_word_size = $(requested_word_size) else $(error Unsupported target word size) endif endif # -- Debian architecture -- ifeq ($(target_os_family),linux) ifeq ($(target_processor_family),x86) ifeq ($(target_word_size),64) debian_arch = amd64 else debian_arch = i386 endif endif ifeq ($(target_processor_family),sparc) debian_arch = sparc endif ifeq ($(target_processor_family),powerpc) debian_arch = powerpc endif endif # -- Other targets and options -- ifneq (,$(findstring sparcgeneric,$(MAKECMDGOALS))) sparc_generic = yes endif # -- Helper functions -- define normpath $(strip $(subst \,/,$(1))) endef ifeq (win,$(host_os_family)) define hostpath $(strip $(subst /,\,$(1))) endef else define hostpath $(strip $(1)) endef endif ifeq (win,$(host_os_family)) define ppath $(strip $(subst \,\\,$(subst /,\,$(1)))) endef else define ppath $(strip $(1)) endef endif ifeq (win,$(host_os_family)) define create_archive -$(ec)$(call rmcmd,$(2)) $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) a -ttar -r $(call hostpath,$(2)).tar $(call hostpath,$(3))" $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) a -tgzip -r $(call hostpath,$(2)).tar.gz $(call hostpath,$(2)).tar" $(ec)cmd /C "cd $(call hostpath,$(1)) && del $(call hostpath,$(2)).tar" endef define extract_archive $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) x -y $(call hostpath,$(2)).tar.gz $(ec)cmd /C "cd $(call hostpath,$(1)) && $(call hostpath,$(tooldir)/7z) x -y $(call hostpath,$(2)).tar endef else define create_archive -$(ec)$(call rmcmd,$(2)) $(ec)tar cf $(2).tar -C $(1) $(3) $(ec)gzip -f $(2).tar $(ec)chmod 775 $(2).tar.gz endef define extract_archive $(ec)gunzip -f $(strip $(1))/$(2).tar.gz $(ec)tar xvf $(strip $(1))/$(2).tar -C $(1) endef endif # Platform-specific commands, directories, etc. ifeq ($(host_os_family),win) allprereqs = $(call hostpath,$+) copycmd = copy /Y $(call hostpath,$(1)) $(call hostpath,$(2)) 1>NUL dircopycmd = xcopy /Y /E /V /I $(call hostpath,$(1)) $(call hostpath,$(2)) rmcmd = if exist $(call hostpath,$(1)) del /Q $(call hostpath,$(1)) 1>NUL rmdircmd = if exist $(call hostpath,$(1)) rmdir /q /s $(call hostpath,$(1)) 1>NUL mkdircmd = -if not exist $(call hostpath,$(1)) mkdir $(call hostpath,$(1)) runtest = cmd /C "cd $(call hostpath,$(test_dir)) && $(1) -d" topdir := $(call normpath,$(shell chdir)) else allprereqs = $+ copycmd = cp -f $(1) $(2) dircopycmd = cp -rf $(1) $(2) rmcmd = rm -f $(1) rmdircmd = rm -rf $(1) mkdircmd = mkdir -p $(1) runtest = sh -c "cd $(test_dir); ./$(1) -d; exit" topdir := $(shell pwd) endif # If this is an un-tar'd or un-zipped source package, the tools directory # will be subordinate to the top directory. Otherwise, it will be # a sibling to the top directory - which is how it is set up in the # subversion repository. ifeq "$(wildcard $(topdir)/tools*)" "" tooldir := $(dir $(topdir))tools/$(host_os_family) else tooldir := $(topdir)/tools/$(host_os_family) endif # -- Utility variables -- em := sp := $(em) $(em) percent := \045 dollar := \044 question := \077 asterisk := \052 dash := \055 backslash := \134 double_quote := \042 # -- printf -- ifdef unix_target gprintf = printf else gprintf = $(call hostpath,$(tooldir)/printf.exe) endif # Determine the toolkit directory ifeq "$(wildcard $(topdir)/ftk)" "" ftk_dir := $(dir $(topdir))ftk else ftk_dir := $(topdir)/ftk endif ftk_src_dir = $(ftk_dir)/src # -- Subversion Revision -- calc_svn_revision = ignore_local_mods = ifneq (,$(findstring ignore-local-mods,$(MAKECMDGOALS))) submake_targets += ignore-local-mods ignore_local_mods = 1 endif ifneq (,$(findstring ilm,$(MAKECMDGOALS))) submake_targets += ilm ignore_local_mods = 1 endif ifdef ignore_local_mods local_mods_ok = 1 else local_mods_ok = endif ifneq (,$(findstring dist,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring rpm,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring ubuntusrc,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring docs,$(MAKECMDGOALS))) calc_svn_revision = 1 ifndef ignore_local_mods local_mods_ok = endif endif ifneq (,$(findstring java,$(MAKECMDGOALS))) calc_svn_revision = 1 local_mods_ok = 1 endif ifneq (,$(findstring xedit,$(MAKECMDGOALS))) calc_svn_revision = 1 local_mods_ok = 1 endif ifneq (,$(findstring changelog,$(MAKECMDGOALS))) calc_svn_revision = 1 # Get the info for this directory ifndef svn_user $(error Must define svn_user= in environment or as a parameter) endif ifndef svn_rev $(error Must define svn_rev= in environment or as a parameter) endif svnrevs = $(subst :, ,$(svn_rev)) svn_low_rev = $(word 1,$(svnrevs)) svn_high_rev = $(word 2,$(svnrevs)) svnurl0 := $(shell svn info) svnurl1 = $(subst URL: ,URL:,$(svnurl0)) svnurl2 = $(filter URL:%,$(svnurl1)) svnurl3 = $(subst URL:,,$(svnurl2)) svnurl = $(subst ://,://$(svn_user)@,$(svnurl3)) endif ifdef calc_svn_revision # Get the info for all files. ifndef local_mods_ok srevision := $(shell svnversion . -n) ifneq (,$(findstring M,$(srevision))) $(error Local modifications found - please check in before making distro) endif ifneq (,$(findstring :,$(srevision))) $(error Mixed revisions in repository - please update before making distro) endif srevision := $(shell svnversion $(ftk_dir) -n) ifneq (,$(findstring M,$(srevision))) $(error Local modifications found - please check in before making distro) endif ifneq (,$(findstring :,$(srevision))) $(error Mixed revisions in repository - please update before making distro) endif endif numdigits = $(words $(subst 9,9 ,$(subst 8,8 ,$(subst 7,7 ,\ $(subst 6,6 ,$(subst 5,5 ,$(subst 4,4 ,$(subst 3,3 ,\ $(subst 2,2 ,$(subst 1,1 ,$(subst 0,0 ,$(1)))))))))))) revision0 := $(shell svn info -R . $(ftk_dir)) revision1 = $(subst Last Changed Rev: ,LastChangedRev:,$(revision0)) revision2 = $(filter LastChangedRev:%,$(revision1)) revision3 = $(subst LastChangedRev:,,$(revision2)) revision4 = $(sort $(revision3)) revision5 = $(foreach num,$(revision4),$(call numdigits,$(num)):$(num)) revision6 = $(sort $(revision5)) revision7 = $(word $(words $(revision6)),$(revision6)) svn_revision = $(word 2,$(subst :, ,$(revision7))) else ifeq "$(wildcard SVNRevision.*)" "" svn_revision = 0 else svn_revision = $(word 2,$(subst ., ,$(wildcard SVNRevision.*))) endif endif ifeq "$(svn_high_rev)" "" svn_high_rev = $(svn_revision) endif # Files and Directories ifeq ($(target_word_size),64) ifeq ($(target_os_family),linux) lib_dir_name = lib64 endif endif ifndef lib_dir_name lib_dir_name = lib endif ifndef rpm_build_root ifneq (,$(DESTDIR)) rpm_build_root = $(DESTDIR) else rpm_build_root = endif endif lib_install_dir = $(rpm_build_root)$(install_prefix)/$(lib_dir_name) include_install_dir = $(rpm_build_root)$(install_prefix)/include pkgconfig_install_dir = $(lib_install_dir)/pkgconfig build_output_dir = $(topdir)/build doxygen_output_dir = $(build_output_dir)/docs javadoc_output_dir = $(build_output_dir)/javadocs target_path = $(build_output_dir)/$(target_os_family)-$(target_processor_family)-$(target_word_size)/$(target_build_type) package_dir = $(target_path)/package spec_dir = $(package_dir)/SPECS spec_file = $(spec_dir)/$(package_proj_name).spec package_sources_dir = $(package_dir)/SOURCES package_bin_dir = $(package_dir)/BIN package_build_dir = $(package_dir)/BUILD package_rpms_dir = $(package_dir)/RPMS package_srpms_dir = $(package_dir)/SRPMS pkgconfig_file_name = $(package_proj_name).pc pkgconfig_file = $(package_dir)/$(pkgconfig_file_name) package_debian_dir = $(package_dir)/DEBIAN debian_stage_dir = $(package_dir)/debian_stage debian_pkginfo_dir = $(debian_stage_dir)/DEBIAN package_ubuntu_dir = $(package_dir)/UBUNTU package_version_ubuntu = $(version)-0ubuntu1 package_distro_ubuntu = edgy ubuntu_stage_dir = $(package_dir)/ubuntu_stage ubuntu_pkginfo_dir = $(ubuntu_stage_dir)/DEBIAN package_stage_parent_dir = $(package_dir)/stage package_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver) package_bin_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver)/$(target_os_family)-$(target_processor_family)-$(target_word_size)/$(target_build_type) package_lib_stage_dir = $(package_bin_stage_dir)/lib package_shared_lib_stage_dir = $(package_lib_stage_dir)/shared package_static_lib_stage_dir = $(package_lib_stage_dir)/static package_util_stage_dir = $(package_bin_stage_dir)/util package_inc_stage_dir = $(package_stage_parent_dir)/$(package_proj_name_and_ver)/include src_package_dir = $(package_sources_dir) bin_package_dir = $(package_bin_dir) src_package_base_name = $(package_proj_name_and_ver) bin_package_base_name = $(package_proj_name_and_ver)-$(target_os_family)-$(target_processor_family)-$(target_word_size)-bin src_package_name=$(src_package_base_name).tar.gz bin_package_name=$(bin_package_base_name).tar.gz rpm_name = $(package_proj_name_and_ver)-$(package_release_num).$(HOSTTYPE).rpm srpm_name = $(package_proj_name_and_ver)-$(package_release_num).src.rpm develrpm_name = $(package_proj_name)-devel-$(version)-$(package_release_num).$(HOSTTYPE).rpm java_output_dir = $(target_path)/java java_class_output_dir = $(target_path)/java/classes csharp_output_dir = $(target_path)/csharp cstest_output_dir = $(target_path)/csharp/cstest ifdef JAVA_HOME jhome := $(subst \,/,$(JAVA_HOME)) endif inc_dirs = src util $(ftk_src_dir) ifdef jhome jni_src_dir = java/jni inc_dirs += $(jni_src_dir) endif xflaim_jni_jar = $(java_output_dir)/xflaimjni.jar xflaim_jar_manifest = $(java_output_dir)/xflaimjni.mf xflaim_csharp_assembly = $(csharp_output_dir)/xflaim_csharp.dll xflaim_csharp_assembly_xml = $(csharp_output_dir)/xflaim_csharp.xml cstest_exe = $(cstest_output_dir)/cstest.exe util_dir = $(target_path)/util test_dir = $(target_path)/test sample_dir = $(target_path)/sample csharp_dir = $(target_path)/csharp lib_dir = $(target_path)/$(lib_dir_name) shared_lib_dir = $(lib_dir)/shared static_lib_dir = $(lib_dir)/static util_obj_dir = $(util_dir)/obj test_obj_dir = $(test_dir)/obj sample_obj_dir = $(sample_dir)/obj lib_obj_dir = $(lib_dir)/obj csharp_obj_dir = $(csharp_dir)/obj doxyfile = $(doxygen_output_dir)/Doxyfile # -- Tools -- libr = exe_linker = shared_linker = compiler = # Compiler definitions and flags ccflags = clrflags = noclrflags = cs_flags = /nologo /warn:4 /warnaserror+ cs_defines = ifeq ($(target_build_type),debug) cs_flags += /debug+ /debug:full /define:FLM_DEBUG else cs_flags += /optimize+ endif ccdefs = ifeq ($(target_word_size),64) ccdefs += FLM_64BIT endif ############################################################################## # Win settings ############################################################################## ifdef win_target exe_suffix = .exe obj_suffix = .obj lib_prefix = static_lib_suffix = .lib shared_lib_suffix = .dll libr = lib.exe exe_linker = link.exe shared_linker = link.exe compiler = cl.exe csharp_compiler := csc.exe # Compiler defines and flags ccflags += /nologo /c /GF /GR /J /MD /W4 /WX /Zi /Zp1 ccdefs += _CRT_SECURE_NO_DEPRECATE ccdefs += WIN32_LEAN_AND_MEAN ccdefs += WIN32_EXTRA_LEAN ifeq ($(target_build_type),debug) ccflags += /Ob1 /Od /RTC1 /Wp64 noclrflags += /RTC1 ccdefs += FLM_DEBUG else ccflags += /O2 endif # Linker switches shared_link_flags = \ /DLL \ /DEBUG /PDB:$(call hostpath,$(@:.dll=.pdb)) \ /map:$(call hostpath,$(@:.dll=.map)) \ /INCREMENTAL:NO \ /NOLOGO \ /OUT:$(call hostpath,$@) exe_link_flags = \ /DEBUG /PDB:$(call hostpath,$(@:.exe=.pdb)) \ /map:$(call hostpath,$(@:.exe=.map)) \ /INCREMENTAL:NO \ /FIXED:NO \ /NOLOGO \ /OUT:$(call hostpath,$@) # Java ifdef jhome inc_dirs += $(subst $(sp),@,"$(jhome)/include") inc_dirs += $(subst $(sp),@,"$(jhome)/include/win32") endif # Libraries that our various components need to link against lib_link_libs = imagehlp.lib user32.lib rpcrt4.lib wsock32.lib advapi32.lib exe_link_libs = $(lib_link_libs) # Convert the list of defines into a proper set of command-line params ifdef ccdefs ccdefine = $(foreach def,$(strip $(ccdefs)),/D$(def)) endif # Same thing for the include dirs ccinclude = $(foreach inc_dir,$(strip $(inc_dirs)),/I$(subst @,$(sp),$(call hostpath,$(inc_dir)))) # Concatenate everything into the ccflags variable ccflags += $(ccdefine) $(ccinclude) endif ############################################################################## # Linux/Unix settings ############################################################################## ifdef unix_target ifneq ($(so_age),0) shared_lib_version = $(so_current).$(so_revision).$(so_age) else ifneq ($(so_revision),0) shared_lib_version = $(so_current).$(so_revision) else shared_lib_version = $(so_current) endif endif exe_suffix = obj_suffix = .o lib_prefix = lib static_lib_suffix = .a shared_lib_suffix = .so.$(shared_lib_version) compiler = g++ exe_linker = g++ shared_linker = g++ ifeq ($(target_os_family),osx) libr = libtool else libr = ar endif gcc_optimization_flags = \ -O \ -foptimize-sibling-calls \ -fstrength-reduce -fcse-follow-jumps \ -fcse-skip-blocks \ -frerun-cse-after-loop \ -frerun-loop-opt \ -fgcse \ -fgcse-lm \ -fgcse-sm \ -fdelete-null-pointer-checks \ -fexpensive-optimizations \ -fregmove \ -fsched-interblock \ -fsched-spec \ -fcaller-saves \ -fpeephole2 \ -freorder-blocks \ -freorder-functions \ -falign-functions \ -falign-jumps \ -falign-loops \ -falign-labels \ -fcrossjumping ifeq ($(usenativecc),yes) ifeq ($(target_os_family),solaris) compiler = CC exe_linker = CC shared_linker = CC compiler_version := $(shell $(compiler) -V 2>&1) ifneq (,$(findstring Sun C++,$(compiler_version))) sun_studio_compiler = yes endif endif endif ifeq ($(usenativecc),yes) ifeq ($(target_os_family),aix) compiler = xlC_r exe_linker = xlC_r shared_linker = xlC_r endif endif ifeq ($(usenativecc),yes) ifeq ($(target_os_family),hpux) compiler = aCC exe_linker = aCC shared_linker = aCC endif endif # Compiler defines and flags ifeq ($(compiler),g++) ccflags += -Wall -Werror -fPIC ifneq ($(target_processor_family),ia64) ccflags += -m$(target_word_size) endif endif ifeq ($(target_os_family),linux) csharp_compiler := mcs cs_defines = /d:mono # Must support 64 bit file sizes - even for 32 bit builds. ccdefs += N_PLAT_UNIX _LARGEFILE64_SOURCE _FILE_OFFSET_BITS=64 ifdef jhome inc_dirs += $(subst $(sp),@,"$(jhome)/include") inc_dirs += $(subst $(sp),@,"$(jhome)/include/linux") endif ifeq ($(target_build_type),release) ccflags += $(gcc_optimization_flags) endif endif ifeq ($(target_os_family),solaris) ifdef jhome inc_dirs += $(subst $(sp),@,"$(jhome)/include") inc_dirs += $(subst $(sp),@,"$(jhome)/include/solaris") endif ifeq ($(usenativecc),yes) ccflags += -KPIC ifeq ($(target_build_type),release) ccflags += -xO3 endif ifeq ($(sun_studio_compiler),yes) ccflags += -errwarn=%all -errtags -erroff=hidef,inllargeuse,doubunder endif ifeq ($(target_word_size),64) ccflags += -xarch=generic64 else # Must support 64 bit file sizes - even for 32 bit builds. ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 ifdef sparc_generic ccflags += -xarch=generic ccdefs += FLM_SPARC_GENERIC else ccflags += -xarch=v8plus endif endif endif endif ifeq ($(target_os_family),aix) ifeq ($(usenativecc),yes) ccflags += -qthreaded -qstrict ifeq ($(target_word_size),64) ccflags += -q64 else # Must support 64 bit file sizes - even for 32 bit builds. ccflags += -q32 ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 endif endif endif ifeq ($(target_os_family),hpux) ifeq ($(usenativecc),yes) # Disable "Placement operator delete invocation is not yet # implemented" warning ccflags += +W930 ifeq ($(target_word_size),64) ccflags += +DD64 else # Must support 64 bit file sizes - even for 32 bit builds. ccdefs += _LARGEFILE_SOURCE _FILE_OFFSET_BITS=64 endif endif endif ifeq ($(target_os_family),osx) ccdefs += OSX ifeq ($(target_build_type),release) ccflags += $(gcc_optimization_flags) endif inc_dirs += /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Headers endif ccdefs += _REENTRANT ifeq ($(target_build_type),debug) ccdefs += FLM_DEBUG ccflags += -g endif # Convert the list of defines into a proper set of command-line params ifdef ccdefs ccdefine = $(foreach def,$(strip $(ccdefs)),-D$(def)) endif # Same thing for the include dirs ccinclude = $(foreach inc_dir,$(strip $(inc_dirs)),-I$(subst @,$(sp),$(inc_dir))) # Concatenate everything into the ccflags variable ccflags += $(ccdefine) $(ccinclude) # Linker switches shared_link_flags = link_flags = -o $@ libr_flags = ifeq ($(compiler),g++) ifneq ($(target_processor_family),ia64) shared_link_flags += -m$(target_word_size) link_flags += -m$(target_word_size) endif endif lib_link_libs = -lpthread exe_link_libs = -lpthread ifeq ($(target_os_family),linux) lib_link_libs += -lrt -lstdc++ -ldl -lncurses exe_link_libs += -lrt -lstdc++ -ldl -lncurses shared_link_flags += -shared -Wl,-Bsymbolic -fpic \ -Wl,-soname,$(@F) -o $@ endif ifeq ($(target_os_family),solaris) link_flags += -R /usr/lib/lwp shared_link_flags += -G -pic -o $@ ifeq ($(usenativecc),yes) ifeq ($(target_word_size),64) link_flags += -xarch=generic64 shared_link_flags += -xarch=generic64 else link_flags += -xarch=v8plus endif endif lib_link_libs += -lm -lc -ldl -lsocket -lnsl -lrt -lcurses exe_link_libs += -lm -lc -ldl -lsocket -lnsl -lrt -lcurses endif ifeq ($(target_os_family),aix) ifeq ($(target_word_size),64) link_flags += -q64 libr_flags = -X64 else link_flags += -q32 libr_flags = -X32 endif lib_link_libs += -lm -lc -lcurses exe_link_libs += -lm -lc -lcurses endif ifeq ($(target_os_family),hpux) ifeq ($(target_word_size),64) link_flags += +DD64 endif lib_link_libs += -lm -lc -lrt -lcurses exe_link_libs += -lm -lc -lrt -lcurses endif ifeq ($(target_os_family),osx) shared_lib_suffix = -$(major_version).$(so_current).dylib lib_link_libs += -lstdc++ -ldl -lncurses exe_link_libs += -lstdc++ -ldl -lncurses shared_link_flags += -dynamiclib shared_link_flags += -current_version $(major_version).$(so_current).$(so_revision) shared_link_flags += -compatibility_version $(major_version).$(so_current).0 shared_link_flags += -o $@ endif exe_link_flags = $(link_flags) endif ############################################################################## # NetWare settings ############################################################################## ifdef netware_target exe_suffix = .nlm obj_suffix = .obj lib_prefix = static_lib_suffix = .lib shared_lib_suffix = .nlm ifdef WATCOM wc_dir = $(WATCOM) endif ifdef watcom wc_dir = $(watcom) endif ifndef wc_dir wc_dir = $(WC_DIR) endif ifndef wc_dir $(error Watcom compiler could not be found. Please define wc_dir) endif wc_dir := $(call normpath,$(wc_dir)) ifndef netware_ring_0_target ifndef ndk_dir $(error Netware SDK could not be found. Please define ndk_dir) endif ndk_dir := $(call normpath,$(ndk_dir)) endif libr = "$(call normpath,$(strip $(wc_dir)))/binnt/wlib.exe" exe_linker = "$(call normpath,$(strip $(wc_dir)))/binnt/wlink.exe" shared_linker = "$(call normpath,$(strip $(wc_dir)))/binnt/wlink.exe" compiler = "$(call normpath,$(wc_dir))/binnt/wpp386.exe" ifneq ($(target_build_type),release) ccdefs += FLM_DEBUG endif ifdef netware_ring_0_target ccdefs += FLM_RING_ZERO_NLM else ccdefs += FLM_LIBC_NLM endif ccflags += /ez /6s /w4 /za /zp1 /zq /zm /s /ei /of+ /we /bt=NETWARE ifeq ($(target_build_type),release) ccflags += /oair else ccflags += /hc endif libflags += /b /q /p=256 link_flags = /m /l /v /s inc_dirs += $(ndk_dir)/libc/include \ $(ndk_dir)/libc/include/winsock export include = $(foreach inc_dir,$(strip $(inc_dirs)),$(call hostpath,$(inc_dir));) export INCLUDE = $(include) export wpp386 = /d$(subst $(sp), /d,$(strip $(ccdefs))) $(ccflags) export wcc386 = /d$(subst $(sp), /d,$(strip $(ccdefs))) $(ccflags) define make_ring_0_lis_file_cmd $(ec)$(gprintf) "option verbose\n" > $(4) $(ec)$(gprintf) "option stack=32k\n" >> $(4) $(ec)$(gprintf) "option nod\n" >> $(4) $(ec)$(gprintf) "option map\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option screenname 'NONE'\n" >> $(4) $(ec)$(gprintf) "option threadname '$(2)'\n" >> $(4) $(ec)$(gprintf) "option start = f_nlmEntryPoint\n" >> $(4) $(ec)$(gprintf) "option exit = f_nlmExitPoint\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option xdcdata=nlm.xdc\n" >> $(4) $(ec)$(gprintf) "option pseudopreemption\n" >> $(4) $(ec)$(gprintf) "debug all debug novell\n" >> $(4) $(ec)$(gprintf) "form novell nlm '$(2)'\n" >> $(4) $(ec)$(gprintf) "name $(call ppath,$(1)/$(2)$(exe_suffix))\n" >> $(4) $(ec)$(gprintf) "file $(subst $(sp),\nfile ,$(call ppath,$(3)))\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(xflaim_static_lib))\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(ctorarst)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(dtorarst)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(undefed)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(undefmbd)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(pure_err)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stablcl)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabact)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabactv)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabmod)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(prwdata)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(moddtorr)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/plib3s.lib)(stabadt)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)(i8d)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)(i8m)\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(wc_dir)/lib386/netware/clib3s.lib)(i8s)\n" >> $(4) $(ec)$(gprintf) "alias __wcpp_4_fatal_runtime_error_=f_fatalRuntimeError\n" >> $(4) endef define make_libc_lis_file_cmd $(ec)$(gprintf) "option verbose\n" > $(4) $(ec)$(gprintf) "option stack=32k\n" >> $(4) $(ec)$(gprintf) "option nod\n" >> $(4) $(ec)$(gprintf) "option map\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option screenname 'NONE'\n" >> $(4) $(ec)$(gprintf) "option threadname '$(2)'\n" >> $(4) $(ec)$(gprintf) "option start = _LibCPrelude\n" >> $(4) $(ec)$(gprintf) "option exit = _LibCPostlude\n" >> $(4) $(ec)$(gprintf) "option nodefaultlibs\n" >> $(4) $(ec)$(gprintf) "option xdcdata=nlm.xdc\n" >> $(4) $(ec)$(gprintf) "option pseudopreemption\n" >> $(4) $(ec)$(gprintf) "debug all debug novell\n" >> $(4) $(ec)$(gprintf) "form novell nlm '$(2)'\n" >> $(4) $(ec)$(gprintf) "name $(call ppath,$(1)/$(2)$(exe_suffix))\n" >> $(4) $(ec)$(gprintf) "file $(subst $(sp),\nfile ,$(call ppath,$(3)))\n" >> $(4) $(ec)$(gprintf) "file $(call ppath,$(ndk_dir)/libc/imports/libcpre.obj)\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(xflaim_static_lib))\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(wc_dir)/lib386/plib3s.lib)\n" >> $(4) $(ec)$(gprintf) "library $(call ppath,$(wc_dir)/lib386/netware/libc3s.lib)\n" >> $(4) endef define make_ring_0_imp_file_cmd $(ec)$(gprintf) "import __WSAFDIsSet\n" > $(1) $(ec)$(gprintf) "import ActivateScreen\n" >> $(1) $(ec)$(gprintf) "import Alloc\n" >> $(1) $(ec)$(gprintf) "import AllocateResourceTag\n" >> $(1) $(ec)$(gprintf) "import atomic_dec\n" >> $(1) $(ec)$(gprintf) "import atomic_inc\n" >> $(1) $(ec)$(gprintf) "import atomic_xchg\n" >> $(1) $(ec)$(gprintf) "import BitTest\n" >> $(1) $(ec)$(gprintf) "import CEvaluateExpression\n" >> $(1) $(ec)$(gprintf) "import CFindLoadModuleHandle\n" >> $(1) $(ec)$(gprintf) "import CheckKeyStatus\n" >> $(1) $(ec)$(gprintf) "import ClearScreen\n" >> $(1) $(ec)$(gprintf) "import CloseFile\n" >> $(1) $(ec)$(gprintf) "import CloseScreen\n" >> $(1) $(ec)$(gprintf) "import CMovB\n" >> $(1) $(ec)$(gprintf) "import CMoveFast\n" >> $(1) $(ec)$(gprintf) "import ConvertPathString\n" >> $(1) $(ec)$(gprintf) "import ConvertSecondsToTicks\n" >> $(1) $(ec)$(gprintf) "import ConvertTicksToSeconds\n" >> $(1) $(ec)$(gprintf) "import CpuCurrentProcessor\n" >> $(1) $(ec)$(gprintf) "import CreateDirectory\n" >> $(1) $(ec)$(gprintf) "import CreateFile\n" >> $(1) $(ec)$(gprintf) "import CSetD\n" >> $(1) $(ec)$(gprintf) "import DebuggerSymbolList\n" >> $(1) $(ec)$(gprintf) "import DeleteDirectory\n" >> $(1) $(ec)$(gprintf) "import DirectorySearch\n" >> $(1) $(ec)$(gprintf) "import DirectReadFile\n" >> $(1) $(ec)$(gprintf) "import DirectReadFile\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFile\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFile\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFileNoWait\n" >> $(1) $(ec)$(gprintf) "import DirectWriteFileNoWait\n" >> $(1) $(ec)$(gprintf) "import DisableInputCursor\n" >> $(1) $(ec)$(gprintf) "import DisplayScreenTextWithAttribute\n" >> $(1) $(ec)$(gprintf) "import DOSFirstByteBitMap\n" >> $(1) $(ec)$(gprintf) "import EnableInputCursor\n" >> $(1) $(ec)$(gprintf) "import EnterDebugger\n" >> $(1) $(ec)$(gprintf) "import EraseFile\n" >> $(1) $(ec)$(gprintf) "import ExpandFileInContiguousBlocks\n" >> $(1) $(ec)$(gprintf) "import ExpandFileInContiguousBlocks\n" >> $(1) $(ec)$(gprintf) "import ExportPublicSymbol\n" >> $(1) $(ec)$(gprintf) "import FindAndLoadNLM\n" >> $(1) $(ec)$(gprintf) "import Free\n" >> $(1) $(ec)$(gprintf) "import FreeLimboVolumeSpace\n" >> $(1) $(ec)$(gprintf) "import FreeLimboVolumeSpace\n" >> $(1) $(ec)$(gprintf) "import GetCacheBufferSize\n" >> $(1) $(ec)$(gprintf) "import GetClosestSymbol\n" >> $(1) $(ec)$(gprintf) "import GetCurrentClock\n" >> $(1) $(ec)$(gprintf) "import GetCurrentNumberOfCacheBuffers\n" >> $(1) $(ec)$(gprintf) "import GetCurrentTime\n" >> $(1) $(ec)$(gprintf) "import GetEntryFromPathStringBase\n" >> $(1) $(ec)$(gprintf) "import GetEntryFromPathStringBase\n" >> $(1) $(ec)$(gprintf) "import GetFileSize\n" >> $(1) $(ec)$(gprintf) "import GetKey\n" >> $(1) $(ec)$(gprintf) "import GetNLMAllocMemoryCounts\n" >> $(1) $(ec)$(gprintf) "import GetOriginalNumberOfCacheBuffers\n" >> $(1) $(ec)$(gprintf) "import GetProductMajorVersionNumber\n" >> $(1) $(ec)$(gprintf) "import GetRunningProcess\n" >> $(1) $(ec)$(gprintf) "import GetScreenSize\n" >> $(1) $(ec)$(gprintf) "import GetSyncClockFields\n" >> $(1) $(ec)$(gprintf) "import GetSystemConsoleScreen\n" >> $(1) $(ec)$(gprintf) "import ImportPublicSymbol\n" >> $(1) $(ec)$(gprintf) "import kCreateThread\n" >> $(1) $(ec)$(gprintf) "import kCurrentThread\n" >> $(1) $(ec)$(gprintf) "import kDelayThread\n" >> $(1) $(ec)$(gprintf) "import kDestroyThread\n" >> $(1) $(ec)$(gprintf) "import kExitThread\n" >> $(1) $(ec)$(gprintf) "import kGetThreadName\n" >> $(1) $(ec)$(gprintf) "import kGetThreadName\n" >> $(1) $(ec)$(gprintf) "import KillMe\n" >> $(1) $(ec)$(gprintf) "import kMutexAlloc\n" >> $(1) $(ec)$(gprintf) "import kMutexFree\n" >> $(1) $(ec)$(gprintf) "import kMutexLock\n" >> $(1) $(ec)$(gprintf) "import kMutexUnlock\n" >> $(1) $(ec)$(gprintf) "import kReturnCurrentProcessorID\n" >> $(1) $(ec)$(gprintf) "import kScheduleThread\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreAlloc\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreExamineCount\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreFree\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreSignal\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreTimedWait\n" >> $(1) $(ec)$(gprintf) "import kSemaphoreWait\n" >> $(1) $(ec)$(gprintf) "import kSetThreadLoadHandle\n" >> $(1) $(ec)$(gprintf) "import kSetThreadName\n" >> $(1) $(ec)$(gprintf) "import kYieldIfTimeSliceUp\n" >> $(1) $(ec)$(gprintf) "import kYieldThread\n" >> $(1) $(ec)$(gprintf) "import LoadModule\n" >> $(1) $(ec)$(gprintf) "import LoadRules\n" >> $(1) $(ec)$(gprintf) "import MapFileHandleToFCB\n" >> $(1) $(ec)$(gprintf) "import MapPathToDirectoryNumber\n" >> $(1) $(ec)$(gprintf) "import MapPathToDirectoryNumber\n" >> $(1) $(ec)$(gprintf) "import MapVolumeNameToNumber\n" >> $(1) $(ec)$(gprintf) "import ModifyDirectoryEntry\n" >> $(1) $(ec)$(gprintf) "import ModifyDirectoryEntry\n" >> $(1) $(ec)$(gprintf) "import MountVolume\n" >> $(1) $(ec)$(gprintf) "import NDSCreateStreamFile\n" >> $(1) $(ec)$(gprintf) "import NDSDeleteStreamFile\n" >> $(1) $(ec)$(gprintf) "import NDSOpenStreamFile\n" >> $(1) $(ec)$(gprintf) "import NWLocalToUnicode\n" >> $(1) $(ec)$(gprintf) "import NWUnicodeToLocal\n" >> $(1) $(ec)$(gprintf) "import OpenFile\n" >> $(1) $(ec)$(gprintf) "import OpenScreen\n" >> $(1) $(ec)$(gprintf) "import OutputToScreen\n" >> $(1) $(ec)$(gprintf) "import PositionInputCursor\n" >> $(1) $(ec)$(gprintf) "import PositionOutputCursor\n" >> $(1) $(ec)$(gprintf) "import ReadFile\n" >> $(1) $(ec)$(gprintf) "import RenameEntry\n" >> $(1) $(ec)$(gprintf) "import RestartServer\n" >> $(1) $(ec)$(gprintf) "import ReturnResourceTag\n" >> $(1) $(ec)$(gprintf) "import ReturnVolumeMappingInformation\n" >> $(1) $(ec)$(gprintf) "import ReturnVolumeMappingInformation\n" >> $(1) $(ec)$(gprintf) "import RevokeFileHandleRights\n" >> $(1) $(ec)$(gprintf) "import SetCursorStyle\n" >> $(1) $(ec)$(gprintf) "import SetFileSize\n" >> $(1) $(ec)$(gprintf) "import SetFileSize\n" >> $(1) $(ec)$(gprintf) "import SGUIDCreate\n" >> $(1) $(ec)$(gprintf) "import SizeOfAllocBlock\n" >> $(1) $(ec)$(gprintf) "import SwitchToDirectFileMode\n" >> $(1) $(ec)$(gprintf) "import SwitchToDirectFileMode\n" >> $(1) $(ec)$(gprintf) "import UngetKey\n" >> $(1) $(ec)$(gprintf) "import UnImportPublicSymbol\n" >> $(1) $(ec)$(gprintf) "import UnloadRules\n" >> $(1) $(ec)$(gprintf) "import VMGetDirectoryEntry\n" >> $(1) $(ec)$(gprintf) "import WriteFile\n" >> $(1) $(ec)$(gprintf) "import WS2_32_bind\n" >> $(1) $(ec)$(gprintf) "import WS2_32_closesocket\n" >> $(1) $(ec)$(gprintf) "import WS2_32_gethostbyaddr\n" >> $(1) $(ec)$(gprintf) "import WS2_32_gethostbyname\n" >> $(1) $(ec)$(gprintf) "import WS2_32_gethostname\n" >> $(1) $(ec)$(gprintf) "import WS2_32_htonl\n" >> $(1) $(ec)$(gprintf) "import WS2_32_htons\n" >> $(1) $(ec)$(gprintf) "import WS2_32_inet_addr\n" >> $(1) $(ec)$(gprintf) "import WS2_32_inet_ntoa\n" >> $(1) $(ec)$(gprintf) "import WS2_32_listen\n" >> $(1) $(ec)$(gprintf) "import WS2_32_recv\n" >> $(1) $(ec)$(gprintf) "import WS2_32_select\n" >> $(1) $(ec)$(gprintf) "import WS2_32_send\n" >> $(1) $(ec)$(gprintf) "import WS2_32_setsockopt\n" >> $(1) $(ec)$(gprintf) "import WS2_32_shutdown\n" >> $(1) $(ec)$(gprintf) "import WS2_32_socket\n" >> $(1) $(ec)$(gprintf) "import WSAAccept\n" >> $(1) $(ec)$(gprintf) "import WSACleanup\n" >> $(1) $(ec)$(gprintf) "import WSAConnect\n" >> $(1) $(ec)$(gprintf) "import WSAGetLastError\n" >> $(1) $(ec)$(gprintf) "import WSAStartup\n" >> $(1) endef define make_libc_imp_file_cmd $(ec)$(gprintf) "import CurrentProcess\n" > $(1) $(ec)$(gprintf) "import @$(call ppath,$(ndk_dir)/libc/imports/libc.imp)\n" >> $(1) $(ec)$(gprintf) "import @$(call ppath,$(ndk_dir)/libc/imports/netware.imp)\n" >> $(1) $(ec)$(gprintf) "import @$(call ppath,$(ndk_dir)/libc/imports/ws2nlm.imp)\n" >> $(1) endef define flm_exe_link_cmd $(call $(if $(netware_ring_0_target),make_ring_0_imp_file_cmd,make_libc_imp_file_cmd),$(call hostpath,$(1)/$(2).imp)) $(call $(if $(netware_ring_0_target),make_ring_0_lis_file_cmd,make_libc_lis_file_cmd),$(1),$(2),$(3),$(call hostpath,$(1)/$(2).lis)) $(ec)$(call hostpath,$(exe_linker)) @$(call hostpath,$(1)/$(2).lis) @$(call hostpath,$(1)/$(2).imp) $(ec)$(call rmcmd,$(target_path)/$(1).lis) $(ec)$(call rmcmd,$(target_path)/$(1).imp) endef endif # -- File lists -- xflaim_src = \ $(patsubst src/%.cpp,%.cpp,$(wildcard src/*.cpp)) ftk_src = \ $(patsubst $(ftk_src_dir)/%.cpp,%.cpp,$(wildcard $(ftk_src_dir)/*.cpp)) xflaim_jni_src = \ $(patsubst java/jni/%.cpp,%.cpp,$(wildcard java/jni/*.cpp)) xflaim_csharp_src = \ $(patsubst csharp/xflaim/%.cpp,%.cpp,$(wildcard csharp/xflaim/*.cpp)) util_common_src = \ flm_dlst.cpp \ flm_lutl.cpp \ sharutil.cpp checkdb_src = \ checkdb.cpp \ $(util_common_src) rebuild_src = \ rebuild.cpp \ $(util_common_src) view_src = \ view.cpp \ viewblk.cpp \ viewdisp.cpp \ viewedit.cpp \ viewhdr.cpp \ viewlfil.cpp \ viewmenu.cpp \ viewsrch.cpp \ $(util_common_src) sample_src = \ sample.cpp \ $(util_common_src) xshell_src = \ fdomedt.cpp \ xshell.cpp \ fshell.cpp \ $(util_common_src) ut_basictest_src = \ flmunittest.cpp \ basictestsrv.cpp ut_binarytest_src = \ flmunittest.cpp \ binarytest.cpp ut_colldeftest_src = \ flmunittest.cpp \ colldeftestsrv.cpp ut_dictchangetest_src = \ flmunittest.cpp \ dictchangetest.cpp ut_dictdeftest_src = \ flmunittest.cpp \ dictdeftestsrv.cpp ut_dirtyexittest1_src = \ flmunittest.cpp \ dirtyexittest1srv.cpp ut_dirtyexittest2_src = \ flmunittest.cpp \ dirtyexittest2srv.cpp ut_domnodetest_src = \ flmunittest.cpp \ domnodetestsrv.cpp ut_enctest_src = \ flmunittest.cpp \ enctestsrv.cpp ut_importtest_src = \ flmunittest.cpp \ importtestsrv.cpp ut_indexdeftest_src = \ flmunittest.cpp \ indexdeftestsrv.cpp ut_indextest1_src = \ flmunittest.cpp \ indextest1srv.cpp ut_indextest2_src = \ flmunittest.cpp \ indextest2srv.cpp ut_indextest3_src = \ flmunittest.cpp \ indextest3.cpp ut_metaphonetest_src = \ flmunittest.cpp \ metaphonetestsrv.cpp ut_namespacetest_src = \ flmunittest.cpp \ namespacetestsrv.cpp ut_regressiontest_src = \ flmunittest.cpp \ regressiontest.cpp ut_rfltest_src = \ flmunittest.cpp \ rfltestsrv.cpp ut_sortkeytest_src = \ flmunittest.cpp \ sortkeytest.cpp ut_sortkeytest2_src = \ flmunittest.cpp \ sortkeytest2.cpp ut_xpathtest1_src = \ flmunittest.cpp \ xpathtest1srv.cpp ut_xpathtest2_src = \ flmunittest.cpp \ xpathtest2srv.cpp # -- XFLAIM library -- ftk_obj = $(patsubst %.cpp,$(lib_obj_dir)/%$(obj_suffix),$(ftk_src)) xflaim_obj = $(patsubst %.cpp,$(lib_obj_dir)/%$(obj_suffix),$(xflaim_src)) xflaim_csharp_obj = $(patsubst %.cpp,$(csharp_obj_dir)/%$(obj_suffix),$(xflaim_csharp_src)) xflaim_static_lib = $(static_lib_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) ifndef netware_target xflaim_shared_lib = $(shared_lib_dir)/$(lib_prefix)$(project_name)$(shared_lib_suffix) xflaim_shared_imp_lib = $(shared_lib_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) endif ifdef jhome xflaim_jni_obj = $(patsubst %.cpp,$(lib_obj_dir)/%$(obj_suffix),$(xflaim_jni_src)) endif # -- Unit tests -- ut_basictest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_basictest_src)) ut_binarytest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_binarytest_src)) ut_colldeftest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_colldeftest_src)) ut_dictchangetest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_dictchangetest_src)) ut_dictdeftest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_dictdeftest_src)) ut_dirtyexittest1_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_dirtyexittest1_src)) ut_dirtyexittest2_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_dirtyexittest2_src)) ut_domnodetest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_domnodetest_src)) ut_enctest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_enctest_src)) ut_importtest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_importtest_src)) ut_indexdeftest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_indexdeftest_src)) ut_indextest1_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_indextest1_src)) ut_indextest2_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_indextest2_src)) ut_indextest3_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_indextest3_src)) ut_metaphonetest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_metaphonetest_src)) ut_namespacetest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_namespacetest_src)) ut_regressiontest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_regressiontest_src)) ut_rfltest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_rfltest_src)) ut_sortkeytest_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_sortkeytest_src)) ut_sortkeytest2_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_sortkeytest2_src)) ut_xpathtest1_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_xpathtest1_src)) ut_xpathtest2_obj = $(patsubst %.cpp,$(test_obj_dir)/%$(obj_suffix),$(ut_xpathtest2_src)) # -- Java -- jni_java_src = $(wildcard java/xflaim/*.java) xedit_java_src = $(wildcard java/util/xedit/*.java) xedit_java_exe = $(java_class_output_dir)/xedit/XEdit.class # -- CSharp -- csharp_src = $(wildcard csharp/xflaim/*.cs) cstest_src = $(wildcard csharp/cstest/*.cs) # -- Utilities -- checkdb_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(checkdb_src)) checkdb_exe = $(util_dir)/checkdb$(exe_suffix) rebuild_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(rebuild_src)) rebuild_exe = $(util_dir)/rebuild$(exe_suffix) view_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(view_src)) view_exe = $(util_dir)/view$(exe_suffix) xshell_obj = $(patsubst %.cpp,$(util_obj_dir)/%$(obj_suffix),$(xshell_src)) xshell_exe = $(util_dir)/xshell$(exe_suffix) sample_obj = $(patsubst %.cpp,$(sample_obj_dir)/%$(obj_suffix),$(sample_src)) sample_exe = $(sample_dir)/sample$(exe_suffix) # -- Make system pattern search paths -- vpath %.cpp src util sample java/jni csharp/xflaim $(ftk_src_dir) vpath %.java jni/java/xflaim vpath %.cs csharp/xflaim csharp/cstest # -- Default target -- .PHONY : libs libs: status clean dircheck $(xflaim_static_lib) $(xflaim_shared_lib) # -- JNI (Java) -- ifdef jhome java_manifest: dircheck $(ec)$(gprintf) "Creating manifest for jar file ...\n" $(ec)$(gprintf) "Manifest-version: 1.0\n\n" > $(xflaim_jar_manifest) $(ec)$(gprintf) "Name: $(project_name)\n" >> $(xflaim_jar_manifest) $(ec)$(gprintf) "Specification-Title: \"XFLAIM Java Classes\"\n" >> $(xflaim_jar_manifest) $(ec)$(gprintf) "Specification-Version: \"$(major_version).$(minor_version)\"\n" >> $(xflaim_jar_manifest) $(ec)$(gprintf) "Specification-Vendor: \"Novell, Inc.\"\n" >> $(xflaim_jar_manifest) $(ec)$(gprintf) "Package-Title: \"xflaim\"\n" >> $(xflaim_jar_manifest) $(ec)$(gprintf) "Package-Version: \"$(version)\"\n" >> $(xflaim_jar_manifest) $(ec)$(gprintf) "Package-Vendor: \"Novell, Inc.\"\n" >> $(xflaim_jar_manifest) $(xflaim_jni_jar) : $(jni_java_src) java_manifest $(ec)$(gprintf) "Compiling JNI java sources ...\n" $(ec)$(call hostpath,$(jhome)/bin/javac) -g -d $(java_class_output_dir) $(call hostpath,$(jni_java_src)) $(ec)$(gprintf) "Creating JNI jar file ...\n" $(ec)$(call rmcmd,$(xflaim_jni_jar)) $(ec)$(call hostpath,$(jhome)/bin/jar) cfm $(call hostpath,$(xflaim_jni_jar)) \ $(call hostpath,$(xflaim_jar_manifest)) \ -C $(call hostpath,$(java_class_output_dir)) $(project_name) $(ec)$(gprintf) "Creating JNI header files ...\n" $(ec)$(call hostpath,$(jhome)/bin/javah) -d $(jni_src_dir) \ -classpath $(call hostpath,$(xflaim_jni_jar)) \ $(foreach jclass,$(notdir $(basename $(jni_java_src))),xflaim.$(jclass)) endif ifdef jhome $(xedit_java_exe): $(xedit_java_src) $(xflaim_jni_jar) $(ec)$(gprintf) "Compiling XEDIT java sources ...\n" $(ec)$(call hostpath,$(jhome)/bin/javac) -g -classpath $(call hostpath,$(xflaim_jni_jar)) \ -d $(call hostpath,$(java_class_output_dir)) $(call hostpath,$(xedit_java_src)) endif # -- CSharp -- ifdef csharp_compiler $(xflaim_csharp_assembly) : $(csharp_src) $(ec)$(gprintf) "Compiling CSharp sources ...\n" $(ec)$(csharp_compiler) /target:library /out:$(call hostpath,$(xflaim_csharp_assembly)) \ /doc:$(call hostpath,$(xflaim_csharp_assembly_xml)) $(cs_defines) $(cs_flags)\ $(call hostpath,$(csharp_src)) endif ifdef csharp_compiler $(cstest_exe) : $(cstest_src) $(xflaim_csharp_assembly) $(ec)$(gprintf) "Compiling CSTest sources ...\n" $(ec)$(csharp_compiler) /out:$(call hostpath,$(cstest_exe)) $(call hostpath,$(cstest_src)) \ /reference:$(call hostpath,$(xflaim_csharp_assembly)) $(cs_defines) $(cs_flags) endif # -- *.cpp -> *$(obj_suffix) -- ifdef win_target define cpp_to_obj_template $$($(1)_obj_dir)/%$$(obj_suffix) : %.cpp $$(ec)$$(compiler) $$(ccflags) $$(noclrflags) /Fo$$(call hostpath,$$@) $$(call hostpath,$$<) endef define cpp_to_clr_obj_template $$($(1)_obj_dir)/%$$(obj_suffix) : %.cpp $$(ec)$$(compiler) $$(ccflags) /clr /AI$$(call hostpath,$$(csharp_output_dir)) /Fo$$(call hostpath,$$@) $$(call hostpath,$$<) endef endif ifdef unix_target define cpp_to_obj_template $$($(1)_obj_dir)/%$$(obj_suffix) : %.cpp $$(ec)$$(gprintf) "$$<\n" $$(ec)$$(compiler) $$(ccflags) -c $$< -o $$@ endef define cpp_to_clr_obj_template $$($(1)_obj_dir)/%$$(obj_suffix) : %.cpp $$(ec)$$(gprintf) "$$<\n" $$(ec)$$(compiler) $$(ccflags) -c $$< -o $$@ endef endif ifdef netware_target define cpp_to_obj_template $$($(1)_obj_dir)/%$(obj_suffix) : %.cpp $$(ec)$$(gprintf) "$$(notdir $$(strip $$@))\n" $$(ec)$$(call hostpath,$$(compiler)) $$(call hostpath,$$<) /fo=$$(call hostpath,$$@) endef define cpp_to_clr_obj_template $$($(1)_obj_dir)/%$(obj_suffix) : %.cpp $$(ec)$$(gprintf) "$$(notdir $$(strip $$@))\n" $$(ec)$$(call hostpath,$$(compiler)) $$(call hostpath,$$<) /fo=$$(call hostpath,$$@) endef endif $(foreach tmpl,lib csharp util sample test,$(eval $(call cpp_to_obj_template,$(tmpl)))) #$(foreach tmpl,csharp,$(eval $(call cpp_to_clr_obj_template,$(tmpl)))) # -- xflaim.lib and libxflaim.a -- $(xflaim_static_lib) : $(xflaim_obj) $(ftk_obj) $(ec)$(gprintf) "Building $@ ...\n" ifdef win_target $(ec)$(libr) /NOLOGO $(call hostpath,$+) /OUT:$(call hostpath,$@) endif ifdef unix_target $(ec)rm -f $@ ifeq ($(target_os_family),osx) $(ec)$(libr) -static -o $@ $+ else $(ec)$(libr) $(libr_flags) -rcs $@ $+ endif endif ifdef netware_target $(ec)dir /s/b $(call hostpath,$(lib_obj_dir)/*$(obj_suffix)) > $(call hostpath,$(static_lib_dir)/xflmlib.lis) $(ec)$(call hostpath,$(libr)) $(libflags) $(call hostpath,$(xflaim_static_lib)) @$(call hostpath,$(static_lib_dir)/xflmlib.lis) endif # -- xflaim.dll and libxflaim.so -- $(xflaim_shared_lib) : $(xflaim_obj) $(ftk_obj) $(xflaim_jni_obj) $(xflaim_csharp_obj) $(ec)$(gprintf) "Building $@ ...\n" ifdef win_target $(ec)$(shared_linker) $(call hostpath,$+) $(shared_link_flags) $(lib_link_libs) endif ifdef unix_target $(ec)rm -f $@ $(ec)$(shared_linker) $+ $(shared_link_flags) $(lib_link_libs) endif # -- xflaim.lib import library -- $(xflaim_shared_imp_lib) : $(xflaim_shared_lib) $(ec)$(gprintf) "Building $@ ...\n" # -- Utility link command -- ifndef flm_exe_link_cmd define flm_exe_link_cmd $(ec)$(exe_linker) $(exe_link_flags) $(allprereqs) $(exe_link_libs) endef endif # -- checkdb -- .PHONY : checkdb checkdb: status clean dircheck libs $(checkdb_exe) $(checkdb_exe): $(checkdb_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),checkdb,$(checkdb_obj)) # -- rebuild -- .PHONY : rebuild rebuild: status clean dircheck libs $(rebuild_exe) $(rebuild_exe): $(rebuild_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),rebuild,$(rebuild_obj)) # -- view -- .PHONY : view view: status clean dircheck libs $(view_exe) $(view_exe): $(view_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),view,$(view_obj)) # -- sample -- .PHONY : sample sample: status clean dircheck libs $(sample_exe) $(sample_exe): $(sample_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(sample_dir),sample,$(sample_obj)) $(ec)$(call copycmd,sample/xmlfiles/*.xml,$(sample_dir)) # -- xshell -- .PHONY : xshell xshell: status clean dircheck libs $(xshell_exe) $(xshell_exe): $(xshell_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(util_dir),xshell,$(xshell_obj)) # -- basictest -- .PHONY : basictest basictest: status clean dircheck $(test_dir)/basictest$(exe_suffix) $(test_dir)/basictest$(exe_suffix): $(ut_basictest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),basictest,$(ut_basictest_obj)) # -- binarytest -- .PHONY : binarytest binarytest: status clean dircheck $(test_dir)/binarytest$(exe_suffix) $(test_dir)/binarytest$(exe_suffix): $(ut_binarytest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),binarytest,$(ut_binarytest_obj)) # -- colldeftest -- .PHONY : colldeftest colldeftest: status clean dircheck $(test_dir)/colldeftest$(exe_suffix) $(test_dir)/colldeftest$(exe_suffix): $(ut_colldeftest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),colldeftest,$(ut_colldeftest_obj)) # -- dictchangetest -- .PHONY : dictchangetest dictchangetest: status clean dircheck $(test_dir)/dictchangetest$(exe_suffix) $(test_dir)/dictchangetest$(exe_suffix): $(ut_dictchangetest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),dictchangetest,$(ut_dictchangetest_obj)) # -- dictdeftest -- .PHONY : dictdeftest dictdeftest: status clean dircheck $(test_dir)/dictdeftest$(exe_suffix) $(test_dir)/dictdeftest$(exe_suffix): $(ut_dictdeftest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),dictdeftest,$(ut_dictdeftest_obj)) # -- dirtyexittest1 -- .PHONY : dirtyexittest1 dirtyexittest1: status clean dircheck $(test_dir)/dirtyexittest1$(exe_suffix) $(test_dir)/dirtyexittest1$(exe_suffix): $(ut_dirtyexittest1_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),dirtyexittest1,$(ut_dirtyexittest1_obj)) # -- dirtyexittest2 -- .PHONY : dirtyexittest2 dirtyexittest2: status clean dircheck $(test_dir)/dirtyexittest2$(exe_suffix) $(test_dir)/dirtyexittest2$(exe_suffix): $(ut_dirtyexittest2_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),dirtyexittest2,$(ut_dirtyexittest2_obj)) # -- domnodetest -- .PHONY : domnodetest domnodetest: status clean dircheck $(test_dir)/domnodetest$(exe_suffix) $(test_dir)/domnodetest$(exe_suffix): $(ut_domnodetest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),domnodetest,$(ut_domnodetest_obj)) # -- enctest -- .PHONY : enctest enctest: status clean dircheck $(test_dir)/enctest$(exe_suffix) $(test_dir)/enctest$(exe_suffix): $(ut_enctest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),enctest,$(ut_enctest_obj)) # -- importtest -- .PHONY : importtest importtest: status clean dircheck $(test_dir)/importtest$(exe_suffix) $(test_dir)/importtest$(exe_suffix): $(ut_importtest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),importtest,$(ut_importtest_obj)) # -- indexdeftest -- .PHONY : indexdeftest indexdeftest: status clean dircheck $(test_dir)/indexdeftest$(exe_suffix) $(test_dir)/indexdeftest$(exe_suffix): $(ut_indexdeftest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),indexdeftest,$(ut_indexdeftest_obj)) # -- indextest1 -- .PHONY : indextest1 indextest1: status clean dircheck $(test_dir)/indextest1$(exe_suffix) $(test_dir)/indextest1$(exe_suffix): $(ut_indextest1_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),indextest1,$(ut_indextest1_obj)) # -- indextest2 -- .PHONY : indextest2 indextest2: status clean dircheck $(test_dir)/indextest2$(exe_suffix) $(test_dir)/indextest2$(exe_suffix): $(ut_indextest2_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),indextest2,$(ut_indextest2_obj)) # -- indextest3 -- .PHONY : indextest3 indextest3: status clean dircheck $(test_dir)/indextest3$(exe_suffix) $(test_dir)/indextest3$(exe_suffix): $(ut_indextest3_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),indextest3,$(ut_indextest3_obj)) # -- metaphonetest -- .PHONY : metaphonetest metaphonetest: status clean dircheck $(test_dir)/metaphonetest$(exe_suffix) $(test_dir)/metaphonetest$(exe_suffix): $(ut_metaphonetest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),metaphonetest,$(ut_metaphonetest_obj)) # -- namespacetest -- .PHONY : namespacetest namespacetest: status clean dircheck $(test_dir)/namespacetest$(exe_suffix) $(test_dir)/namespacetest$(exe_suffix): $(ut_namespacetest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),namespacetest,$(ut_namespacetest_obj)) # -- regressiontest -- .PHONY : regressiontest regressiontest: status clean dircheck $(test_dir)/regressiontest$(exe_suffix) $(test_dir)/regressiontest$(exe_suffix): $(ut_regressiontest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),regressiontest,$(ut_regressiontest_obj)) # -- rfltest -- .PHONY : rfltest rfltest: status clean dircheck $(test_dir)/rfltest$(exe_suffix) $(test_dir)/rfltest$(exe_suffix): $(ut_rfltest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),rfltest,$(ut_rfltest_obj)) # -- sortkeytest -- .PHONY : sortkeytest sortkeytest: status clean dircheck $(test_dir)/sortkeytest$(exe_suffix) $(test_dir)/sortkeytest$(exe_suffix): $(ut_sortkeytest_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),sortkeytest,$(ut_sortkeytest_obj)) # -- sortkeytest2 -- .PHONY : sortkeytest2 sortkeytest2: status clean dircheck $(test_dir)/sortkeytest2$(exe_suffix) $(test_dir)/sortkeytest2$(exe_suffix): $(ut_sortkeytest2_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),sortkeytest2,$(ut_sortkeytest2_obj)) # -- xpathtest1 -- .PHONY : xpathtest1 xpathtest1: status clean dircheck $(test_dir)/xpathtest1$(exe_suffix) $(test_dir)/xpathtest1$(exe_suffix): $(ut_xpathtest1_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),xpathtest1,$(ut_xpathtest1_obj)) # -- xpathtest2 -- .PHONY : xpathtest2 xpathtest2: status clean dircheck $(test_dir)/xpathtest2$(exe_suffix) $(test_dir)/xpathtest2$(exe_suffix): $(ut_xpathtest2_obj) $(xflaim_static_lib) $(ec)$(gprintf) "Linking $@ ...\n" $(call flm_exe_link_cmd,$(test_dir),xpathtest2,$(ut_xpathtest2_obj)) # -- java (JNI jar file) -- .PHONY : java java: status clean dircheck $(xflaim_jni_jar) $(xflaim_shared_lib) .PHONY : xedit xedit: java $(xedit_java_exe) # -- csharp .PHONY : csharp csharp: status clean dircheck $(xflaim_csharp_assembly) $(xflaim_shared_lib) .PHONY : cstest cstest: csharp $(cstest_exe) # -- version -- define make_version_files $(ec)$(gprintf) "$(version)" > $(1)/VERSION $(ec)$(gprintf) " " > $(1)/SVNRevision.$(svn_revision) $(ec)$(gprintf) "Version files created.\n" endef # -- srcdist -- .PHONY : srcdist srcdist: status clean dircheck docs spec ifeq "$(svn_revision)" "0" $(error SVN revision cannot be $(svn_revision)) else $(ec)$(gprintf) "Creating source package (SVN revision $(svn_revision)) ...\n" endif -$(ec)$(call rmdircmd,$(package_stage_dir)) $(ec)$(call mkdircmd,$(package_stage_dir)) $(ec)$(call make_version_files,$(package_stage_dir)) $(ec)$(call copycmd,Makefile,$(package_stage_dir)) $(ec)$(call copycmd,COPYING,$(package_stage_dir)) $(ec)$(call copycmd,COPYRIGHT,$(package_stage_dir)) $(ec)$(call dircopycmd,docs,$(package_stage_dir)/docs) $(ec)$(call dircopycmd,src,$(package_stage_dir)/src) $(ec)$(call copycmd,$(ftk_src_dir)/ftk.h,$(package_stage_dir)/src/xflaimtk.h) $(ec)$(call dircopycmd,util,$(package_stage_dir)/util) $(ec)$(call dircopycmd,debian,$(package_stage_dir)/debian) $(ec)$(call dircopycmd,sample,$(package_stage_dir)/sample) $(ec)$(call dircopycmd,java,$(package_stage_dir)/java) $(ec)$(call dircopycmd,$(doxygen_output_dir),$(package_stage_dir)/docs) ifdef jhome $(ec)$(call dircopycmd,$(javadoc_output_dir),$(package_stage_dir)/javadocs) endif $(ec)$(call dircopycmd,$(dir $(topdir))tools,$(package_stage_dir)/tools) $(ec)$(call mkdircmd,$(package_stage_dir)/ftk) $(ec)$(call dircopycmd,$(dir $(topdir))ftk/src,$(package_stage_dir)/ftk/src) ifneq ($(host_os_family),win) -$(ec)rm -rf `find $(package_stage_dir) -name .svn` endif ifeq ($(host_os_family),win) $(ec)$(call copycmd,make.exe,$(package_stage_dir)) endif $(ec)$(call create_archive,$(package_stage_parent_dir), \ $(src_package_dir)/$(src_package_base_name), \ $(package_proj_name_and_ver)) $(ec)$(call rmdircmd,$(package_stage_parent_dir)) $(ec)$(gprintf) "Source package created.\n" # -- bindist -- .PHONY : bindist bindist: status clean dircheck all binpackage $(ec)$(gprintf) "" # -- binpackage -- .PHONY : binpackage binpackage: status ifeq "$(svn_revision)" "0" $(error SVN revision cannot be $(svn_revision)) else $(ec)$(gprintf) "Creating binary package (SVN revision $(svn_revision)) ...\n" endif -$(ec)$(call rmdircmd,$(package_stage_dir)) $(ec)$(call mkdircmd,$(package_stage_dir)) $(ec)$(call mkdircmd,$(package_inc_stage_dir)) $(ec)$(call mkdircmd,$(package_shared_lib_stage_dir)) $(ec)$(call mkdircmd,$(package_static_lib_stage_dir)) $(ec)$(call mkdircmd,$(package_util_stage_dir)) $(ec)$(call make_version_files,$(package_stage_dir)) $(ec)$(call copycmd,COPYING,$(package_stage_dir)) $(ec)$(call copycmd,COPYRIGHT,$(package_stage_dir)) $(ec)$(call copycmd,src/xflaim.h,$(package_inc_stage_dir)) $(ec)$(call copycmd,$(ftk_src_dir)/ftk.h,$(package_inc_stage_dir)/xflaimtk.h) $(ec)$(call copycmd,$(xflaim_static_lib),$(package_static_lib_stage_dir)) ifdef xflaim_shared_lib $(ec)$(call copycmd,$(xflaim_shared_lib),$(package_shared_lib_stage_dir)) endif ifdef win_target $(ec)$(call copycmd,$(xflaim_shared_imp_lib),$(package_shared_lib_stage_dir)) endif $(ec)$(call copycmd,$(checkdb_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(rebuild_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(view_exe),$(package_util_stage_dir)) $(ec)$(call copycmd,$(xshell_exe),$(package_util_stage_dir)) $(ec)$(call create_archive,$(package_stage_parent_dir), \ $(bin_package_dir)/$(bin_package_base_name), \ $(package_proj_name_and_ver)) $(ec)$(call rmdircmd,$(package_stage_parent_dir)) $(ec)$(gprintf) "Binary package created.\n" # -- dist -- .PHONY : dist dist: status clean dircheck srcdist ifeq "$(svn_revision)" "0" $(error SVN revision cannot be $(svn_revision)) else $(ec)$(gprintf) "Creating distribution (SVN revision $(svn_revision)) ...\n" endif $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(package_dir)) $(ec)$(call extract_archive,$(package_dir),$(src_package_base_name)) $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) clean $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) $(submake_targets) all $(ec)$(MAKE) -C $(package_dir)/$(package_proj_name_and_ver) $(submake_targets) binpackage package_dir="$(package_dir)" $(ec)$(call rmdircmd,$(package_dir)/$(package_proj_name_and_ver)) $(ec)$(call rmcmd,$(package_dir)/$(src_package_name)) $(ec)$(gprintf) "Distribution created.\n" # -- Change log -- .PHONY : changelog changelog: $(ec)$(gprintf) "Creating change log for SVN revisions $(svn_low_rev)-$(svn_high_rev) ...\n" $(ec)$(gprintf) "Using SVN URL $(svnurl) ...\n" $(ec)svn log $(svnurl) -v -r $(svn_low_rev):$(svn_high_rev) > $(package_sources_dir)/$(package_proj_name_and_ver).tar.log $(ec)$(gprintf) "Change log created.\n" # -- install -- .PHONY : install install: libs pkgconfig ifneq ($(host_os_family),win) $(ec)$(gprintf) "Installing ...\n" mkdir -p $(lib_install_dir)/pkgconfig mkdir -p $(include_install_dir) install --mode=644 $(xflaim_shared_lib) $(lib_install_dir) install --mode=644 $(xflaim_static_lib) $(lib_install_dir) install --mode=644 $(pkgconfig_file) $(pkgconfig_install_dir) install --mode=644 src/xflaim.h $(include_install_dir) install --mode=644 $(ftk_src_dir)/ftk.h $(include_install_dir)/xflaimtk.h ifneq ($(so_age),0) ifneq ($(so_revision),0) cd $(lib_install_dir); ln -fs $(lib_prefix)$(project_name).so.$(so_current).$(so_revision).$(so_age) $(lib_prefix)$(project_name).so.$(so_current).$(so_revision) endif endif ifneq ($(so_revision),0) cd $(lib_install_dir); ln -fs $(lib_prefix)$(project_name).so.$(so_current).$(so_revision) $(lib_prefix)$(project_name).so.$(so_current) endif cd $(lib_install_dir); ln -fs $(lib_prefix)$(project_name).so.$(so_current) $(lib_prefix)$(project_name).so -ldconfig $(lib_install_dir) $(ec)$(gprintf) "Installation complete.\n" endif # -- uninstall -- .PHONY : uninstall uninstall: ifneq ($(host_os_family),win) $(ec)$(gprintf) "Uninstalling ...\n" -rm -rf $(lib_install_dir)/$(lib_prefix)$(project_name)*$(shared_lib_suffix)* -rm -rf $(lib_install_dir)/$(lib_prefix)$(project_name)$(static_lib_suffix) -rm -rf $(pkgconfig_install_dir)/$(pkgconfig_file_name) -rm -rf $(include_install_dir)/xflaim.h -rm -rf $(include_install_dir)/xflaimtk.h $(ec)$(gprintf) "Uninstalled.\n" endif # -- spec file -- .PHONY : spec spec: dircheck $(ec)$(gprintf) "Creating spec file ...\n" $(ec)$(gprintf) "Name: $(package_proj_name)\n" > $(spec_file) $(ec)$(gprintf) "$(percent)define prefix $(install_prefix)\n" >> $(spec_file) $(ec)$(gprintf) "BuildRequires: gcc-c++ libstdc++ libstdc++-devel java2-devel-packages\n" >> $(spec_file) $(ec)$(gprintf) "Summary: $(project_brief_desc)\n" >> $(spec_file) $(ec)$(gprintf) "URL: http://forge.novell.com/modules/xfmod/project/$(question)flaim\n" >> $(spec_file) $(ec)$(gprintf) "Version: $(version)\n" >> $(spec_file) $(ec)$(gprintf) "Release: $(package_release_num)\n" >> $(spec_file) $(ec)$(gprintf) "License: GPL\n" >> $(spec_file) $(ec)$(gprintf) "Vendor: Novell, Inc.\n" >> $(spec_file) $(ec)$(gprintf) "Group: Development/Libraries/C and C++\n" >> $(spec_file) $(ec)$(gprintf) "Source: $(package_proj_name_and_ver).tar.gz\n" >> $(spec_file) $(ec)$(gprintf) "BuildRoot: $(percent){_tmppath}/$(percent){name}-$(percent){version}-build\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)description\n" >> $(spec_file) $(ec)$(gprintf) "XFLAIM is an embeddable cross-platform XML database engine that\n" >> $(spec_file) $(ec)$(gprintf) "provides a rich, powerful, easy-to-use feature set. It has proven to be\n" >> $(spec_file) $(ec)$(gprintf) "highly scalable, reliable, and robust. It is available on a wide\n" >> $(spec_file) $(ec)$(gprintf) "variety of 32 bit and 64 bit platforms.\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "Authors:\n" >> $(spec_file) $(ec)$(gprintf) "$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)\n" >> $(spec_file) $(ec)$(gprintf) " $(dsanders_info)\n" >> $(spec_file) $(ec)$(gprintf) " $(ahodgkinson_info)\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)package devel\n" >> $(spec_file) $(ec)$(gprintf) "Summary: XFLAIM static library and header file\n" >> $(spec_file) $(ec)$(gprintf) "Group: Development/Libraries/C and C++\n" >> $(spec_file) $(ec)$(gprintf) "Provides: $(package_proj_name)-devel\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)description devel\n" >> $(spec_file) $(ec)$(gprintf) "XFLAIM is an embeddable cross-platform XML database engine that\n" >> $(spec_file) $(ec)$(gprintf) "provides a rich, powerful, easy-to-use feature set. It has proven to be\n" >> $(spec_file) $(ec)$(gprintf) "highly scalable, reliable, and robust. It is available on a wide\n" >> $(spec_file) $(ec)$(gprintf) "variety of 32 bit and 64 bit platforms.\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "Authors:\n" >> $(spec_file) $(ec)$(gprintf) "$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)$(dash)\n" >> $(spec_file) $(ec)$(gprintf) " Daniel Sanders\n" >> $(spec_file) $(ec)$(gprintf) " Andrew Hodgkinson\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)prep\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)setup -q\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)build\n" >> $(spec_file) $(ec)$(gprintf) "$(MAKE) lib_dir_name=$(percent){_lib} $(submake_targets) libs\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)install\n" >> $(spec_file) $(ec)$(gprintf) "$(MAKE) rpm_build_root=$(dollar)RPM_BUILD_ROOT install_prefix=$(percent){prefix} lib_dir_name=$(percent){_lib} $(submake_targets) install\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)clean\n" >> $(spec_file) $(ec)$(gprintf) "rm -rf $(dollar)RPM_BUILD_ROOT\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)files\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)defattr(-,root,root)\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)doc COPYING COPYRIGHT VERSION\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/$(lib_prefix)$(project_name)$(asterisk)$(shared_lib_suffix)$(asterisk)\n" >> $(spec_file) $(ec)$(gprintf) "\n" >> $(spec_file) $(ec)$(gprintf) "$(percent)files devel\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/$(lib_prefix)$(project_name)$(static_lib_suffix)\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/$(percent){_lib}/pkgconfig/$(pkgconfig_file_name)\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/include/xflaim.h\n" >> $(spec_file) $(ec)$(gprintf) "$(percent){prefix}/include/xflaimtk.h\n" >> $(spec_file) $(ec)$(gprintf) "Created spec file.\n" # -- PKG-CONFIG -- .PHONY : pkgconfig pkgconfig: dircheck $(ec)$(gprintf) "prefix=$(install_prefix)\n" > $(pkgconfig_file) $(ec)$(gprintf) "exec_prefix=$(dollar){prefix}\n" >> $(pkgconfig_file) $(ec)$(gprintf) "libdir=$(dollar){exec_prefix}/$(lib_dir_name)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "includedir=$(dollar){prefix}/include\n\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Name: $(package_proj_name)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Description: $(project_brief_desc)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Version: $(version)\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Libs: $(lib_link_libs) -lxflaim -L$(dollar){libdir}\n" >> $(pkgconfig_file) $(ec)$(gprintf) "Cflags: -I$(dollar){includedir}\n" >> $(pkgconfig_file) # -- SRCRPM -- .PHONY : srcrpm srcrpm: srcdist spec $(ec)$(gprintf) "Creating source RPM ...\n" $(ec)rpmbuild --define="_topdir $(package_dir)" --quiet --nodeps -bs $(spec_file) $(ec)$(gprintf) "Source RPM created.\n" # -- RPMS -- .PHONY : rpms rpms: spec srcdist $(ec)$(gprintf) "Creating source and binary RPMs ...\n" $(ec)rpmbuild --define="_topdir $(package_dir)" --quiet --nodeps -ba $(spec_file) $(ec)find $(package_dir) -name *.rpm | xargs chmod 775 $(ec)$(gprintf) "Source and binary RPMs created.\n" # -- Ubuntu Source Package -- .PHONY : ubuntusrc ubuntusrc: srcdist $(ec)$(gprintf) "Creating Ubuntu source package ...\n" -$(ec)$(call rmdircmd,$(ubuntu_stage_dir)) $(ec)$(call mkdircmd,$(ubuntu_stage_dir)) $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(ubuntu_stage_dir)/$(src_package_base_name).orig.tar.gz) $(ec)$(call extract_archive,$(ubuntu_stage_dir),$(src_package_base_name).orig) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/$(src_package_base_name).orig.tar) $(ec)$(call copycmd,$(ubuntu_stage_dir)/$(package_proj_name_and_ver)/COPYRIGHT,$(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/copyright) $(ec)$(call copycmd,$(src_package_dir)/$(src_package_name),$(ubuntu_stage_dir)/$(package_proj_name)_$(version).orig.tar.gz) $(ec)$(gprintf) "Creating Ubuntu changelog file ...\n" $(ec)$(gprintf) "$(package_proj_name) ($(package_version_ubuntu)) $(package_distro_ubuntu); urgency=low\n\n" > $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)$(gprintf) " * Package update for Ubuntu.\n\n" >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)$(gprintf) " -- $(ahodgkinson_info) " >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)822-date >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)$(gprintf) "\n" >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)cat ChangeLog.ubuntu >> $(ubuntu_stage_dir)/$(package_proj_name_and_ver)/debian/changelog $(ec)cd $(ubuntu_stage_dir)/$(package_proj_name_and_ver); dpkg-buildpackage -S -sa -rfakeroot $(ec)$(gprintf) "Checking Ubuntu package ...\n" $(ec)lintian -i $(ubuntu_stage_dir)/$(package_proj_name)_$(package_version_ubuntu).dsc $(ec)linda -i $(ubuntu_stage_dir)/$(package_proj_name)_$(package_version_ubuntu).dsc $(ec)$(gprintf) "Moving packages to UBUNTU directory ...\n" $(ec)$(call copycmd,$(ubuntu_stage_dir)/*.dsc,$(package_ubuntu_dir)) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/*.dsc) $(ec)$(call copycmd,$(ubuntu_stage_dir)/*.gz,$(package_ubuntu_dir)) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/*.gz) $(ec)$(call copycmd,$(ubuntu_stage_dir)/*.changes,$(package_ubuntu_dir)) $(ec)$(call rmcmd,$(ubuntu_stage_dir)/*.changes) $(ec)$(gprintf) "Removing temporary files ...\n" $(ec)$(call rmdircmd,$(ubuntu_stage_dir)) $(ec)$(gprintf) "Done.\n" # -- Documentation -- .PHONY : docs docs: status clean dircheck doxyfile $(ec)$(gprintf) "Creating documentation ...\n" $(ec)doxygen $(doxyfile) ifdef jhome $(ec)$(gprintf) "Creating JAVADOC documentation ...\n" $(ec)$(call hostpath,$(jhome)/bin/javadoc) -sourcepath java -d $(call hostpath,$(javadoc_output_dir)) $(project_name) endif $(ec)$(gprintf) "Documentation created.\n" # -- misc. targets -- .PHONY : dircheck dircheck: $(ec)$(call mkdircmd,$(util_obj_dir)) $(ec)$(call mkdircmd,$(test_obj_dir)) $(ec)$(call mkdircmd,$(sample_obj_dir)) $(ec)$(call mkdircmd,$(lib_obj_dir)) $(ec)$(call mkdircmd,$(csharp_obj_dir)) $(ec)$(call mkdircmd,$(java_output_dir)) $(ec)$(call mkdircmd,$(java_class_output_dir)) $(ec)$(call mkdircmd,$(csharp_output_dir)) $(ec)$(call mkdircmd,$(cstest_output_dir)) $(ec)$(call mkdircmd,$(doxygen_output_dir)) $(ec)$(call mkdircmd,$(javadoc_output_dir)) $(ec)$(call mkdircmd,$(util_dir)) $(ec)$(call mkdircmd,$(test_dir)) $(ec)$(call mkdircmd,$(sample_dir)) $(ec)$(call mkdircmd,$(static_lib_dir)) $(ec)$(call mkdircmd,$(shared_lib_dir)) $(ec)$(call mkdircmd,$(package_dir)) $(ec)$(call mkdircmd,$(spec_dir)) $(ec)$(call mkdircmd,$(package_sources_dir)) $(ec)$(call mkdircmd,$(package_bin_dir)) $(ec)$(call mkdircmd,$(package_build_dir)) $(ec)$(call mkdircmd,$(package_rpms_dir)) $(ec)$(call mkdircmd,$(package_srpms_dir)) $(ec)$(call mkdircmd,$(package_debian_dir)) $(ec)$(call mkdircmd,$(package_ubuntu_dir)) # -- phony targets -- .PHONY : all all: libs allutils $(ec)$(gprintf) "" .PHONY : allutils allutils: status dircheck libs $(util_targets) $(ec)$(gprintf) "" .PHONY : test test: status dircheck $(xflaim_static_lib) $(test_targets) ifndef netware_target $(ec)$(call copycmd,util/xmlfiles/*.xml,$(test_dir)) $(ec)$(call runtest,basictest) $(ec)$(call runtest,binarytest) $(ec)$(call runtest,colldeftest) $(ec)$(call runtest,dictchangetest) $(ec)$(call runtest,dictdeftest) $(ec)$(call runtest,dirtyexittest1) $(ec)$(call runtest,dirtyexittest2) $(ec)$(call runtest,domnodetest) $(ec)$(call runtest,enctest) $(ec)$(call runtest,importtest) $(ec)$(call runtest,indexdeftest) $(ec)$(call runtest,indextest1) $(ec)$(call runtest,indextest2) $(ec)$(call runtest,indextest3) $(ec)$(call runtest,namespacetest) $(ec)$(call runtest,metaphonetest) $(ec)$(call runtest,regressiontest) $(ec)$(call runtest,rfltest) $(ec)$(call runtest,sortkeytest) $(ec)$(call runtest,sortkeytest2) $(ec)$(call runtest,xpathtest1) $(ec)$(call runtest,xpathtest2) endif .PHONY : debug debug: $(ec)$(gprintf) "" .PHONY : release release: $(ec)$(gprintf) "" .PHONY : usegcc usegcc: $(ec)$(gprintf) "" .PHONY : 32bit 32bit: $(ec)$(gprintf) "" .PHONY : 64bit 64bit: $(ec)$(gprintf) "" .PHONY : win win: $(ec)$(gprintf) "" .PHONY : linux linux: $(ec)$(gprintf) "" .PHONY : solaris solaris: $(ec)$(gprintf) "" .PHONY : sparcgeneric sparcgeneric: $(ec)$(gprintf) "" .PHONY : osx osx: $(ec)$(gprintf) "" .PHONY : nlm nlm: $(ec)$(gprintf) "" .PHONY : ring0 ring0: $(ec)$(gprintf) "" .PHONY : verbose verbose: $(ec)$(gprintf) "" .PHONY : check check: $(ec)$(gprintf) "" .PHONY : TAGS TAGS: $(ec)$(gprintf) "" .PHONY : info info: $(ec)$(gprintf) "" .PHONY : ignore-local-mods ignore-local-mods: $(ec)$(gprintf) "" .PHONY : ilm ilm: $(ec)$(gprintf) "" .PHONY : installcheck installcheck: $(ec)$(gprintf) "" .PHONY : clean clean: ifeq ($(do_clean),1) $(ec)$(gprintf) "\n" $(ec)$(gprintf) "Cleaning $(target_path) ...\n" -$(ec)$(call rmdircmd,$(target_path)) -$(ec)$(call rmcmd,*.pch) $(ec)$(gprintf) "\n" endif .PHONY : distclean -$(ec)$(call rmcmd,*.pch) .PHONY : mostlyclean mostlyclean : clean $(ec)$(gprintf) "" .PHONY : maintainer-clean maintainer-clean: -$(ec)$(call rmdircmd,$(build_output_dir)) -$(ec)$(call rmcmd,*.pch) .PHONY : status status: $(ec)$(gprintf) "===============================================================================\n" $(ec)$(gprintf) "SVN Revision.................... $(svn_revision)\n" $(ec)$(gprintf) "Host Operating System Family.... $(host_os_family)\n" $(ec)$(gprintf) "Top Directory................... $(call ppath,$(topdir))\n" $(ec)$(gprintf) "Target Operating System Family.. $(target_os_family)\n" $(ec)$(gprintf) "Target Processor Family......... $(target_processor_family)\n" $(ec)$(gprintf) "Target Word Size................ $(target_word_size)\n" $(ec)$(gprintf) "Target Build Type............... $(target_build_type)\n" $(ec)$(gprintf) "Target Path..................... $(call ppath,$(target_path))\n" $(ec)$(gprintf) "Toolkit Path.................... $(call ppath,$(ftk_dir))\n" $(ec)$(gprintf) "Install Prefix.................. $(call ppath,$(install_prefix))\n" $(ec)$(gprintf) "Compiler........................ $(call ppath,$(compiler))\n" $(ec)$(gprintf) "Librarian....................... $(call ppath,$(libr))\n" $(ec)$(gprintf) "JAVA HOME....................... $(call ppath,$(jhome))\n" $(ec)$(gprintf) "Defines......................... $(strip $(ccdefs))\n" $(ec)$(gprintf) "===============================================================================\n" .PHONY : doxyfile doxyfile: dircheck $(ec)$(gprintf) "PROJECT_NAME = \"$(project_display_name)\"\n" > $(doxyfile) $(ec)$(gprintf) "PROJECT_NUMBER = \"$(version)\"\n" >> $(doxyfile) $(ec)$(gprintf) "OUTPUT_DIRECTORY = $(doxygen_output_dir)\n" >> $(doxyfile) $(ec)$(gprintf) "CREATE_SUBDIRS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "OUTPUT_LANGUAGE = English\n" >> $(doxyfile) $(ec)$(gprintf) "USE_WINDOWS_ENCODING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "BRIEF_MEMBER_DESC = YES\n" >> $(doxyfile) $(ec)$(gprintf) "REPEAT_BRIEF = YES\n" >> $(doxyfile) $(ec)$(gprintf) "ABBREVIATE_BRIEF = \"The $(dollar)name class\" $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " \"The $(dollar)name widget\" $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " \"The $(dollar)name file\" $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " is $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " provides $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " specifies $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " contains $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " represents $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " a $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " an $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " the\n" >> $(doxyfile) $(ec)$(gprintf) "ALWAYS_DETAILED_SEC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INLINE_INHERITED_MEMB = NO\n" >> $(doxyfile) $(ec)$(gprintf) "FULL_PATH_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "STRIP_FROM_PATH = \"\"\n" >> $(doxyfile) $(ec)$(gprintf) "STRIP_FROM_INC_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "SHORT_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "JAVADOC_AUTOBRIEF = YES\n" >> $(doxyfile) $(ec)$(gprintf) "MULTILINE_CPP_IS_BRIEF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DETAILS_AT_TOP = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INHERIT_DOCS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SEPARATE_MEMBER_PAGES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "TAB_SIZE = 3\n" >> $(doxyfile) $(ec)$(gprintf) "ALIASES = \n" >> $(doxyfile) $(ec)$(gprintf) "OPTIMIZE_OUTPUT_FOR_C = NO\n" >> $(doxyfile) $(ec)$(gprintf) "OPTIMIZE_OUTPUT_JAVA = NO\n" >> $(doxyfile) $(ec)$(gprintf) "BUILTIN_STL_SUPPORT = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DISTRIBUTE_GROUP_DOC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SUBGROUPING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_ALL = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_PRIVATE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_STATIC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_LOCAL_CLASSES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRACT_LOCAL_METHODS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_UNDOC_MEMBERS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_UNDOC_CLASSES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_FRIEND_COMPOUNDS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_IN_BODY_DOCS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INTERNAL_DOCS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "CASE_SENSE_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_SCOPE_NAMES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SHOW_INCLUDE_FILES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "INLINE_INFO = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SORT_MEMBER_DOCS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SORT_BRIEF_DOCS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SORT_BY_SCOPE_NAME = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TODOLIST = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TESTLIST = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_BUGLIST = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_DEPRECATEDLIST= YES\n" >> $(doxyfile) $(ec)$(gprintf) "ENABLED_SECTIONS = \n" >> $(doxyfile) $(ec)$(gprintf) "MAX_INITIALIZER_LINES = 30\n" >> $(doxyfile) $(ec)$(gprintf) "SHOW_USED_FILES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SHOW_DIRECTORIES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "FILE_VERSION_FILTER = \n" >> $(doxyfile) $(ec)$(gprintf) "QUIET = NO\n" >> $(doxyfile) $(ec)$(gprintf) "WARNINGS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_IF_UNDOCUMENTED = YES\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_IF_DOC_ERROR = YES\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_NO_PARAMDOC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_FORMAT = \"$(dollar)file:$(dollar)line: $(dollar)text\"\n" >> $(doxyfile) $(ec)$(gprintf) "WARN_LOGFILE = \n" >> $(doxyfile) $(ec)$(gprintf) "INPUT = src/xflaim.h $(backslash)\n" >> $(doxyfile) $(ec)$(gprintf) " $(ftk_src_dir)/ftk.h\n" >> $(doxyfile) $(ec)$(gprintf) "FILE_PATTERNS = *.h\n" >> $(doxyfile) $(ec)$(gprintf) "RECURSIVE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXCLUDE = \n" >> $(doxyfile) $(ec)$(gprintf) "EXCLUDE_SYMLINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXCLUDE_PATTERNS = \n" >> $(doxyfile) $(ec)$(gprintf) "EXAMPLE_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "EXAMPLE_PATTERNS = *\n" >> $(doxyfile) $(ec)$(gprintf) "EXAMPLE_RECURSIVE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "IMAGE_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "INPUT_FILTER = \n" >> $(doxyfile) $(ec)$(gprintf) "FILTER_PATTERNS = \n" >> $(doxyfile) $(ec)$(gprintf) "FILTER_SOURCE_FILES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SOURCE_BROWSER = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INLINE_SOURCES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "STRIP_CODE_COMMENTS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "REFERENCED_BY_RELATION = NO\n" >> $(doxyfile) $(ec)$(gprintf) "REFERENCES_RELATION = NO\n" >> $(doxyfile) $(ec)$(gprintf) "USE_HTAGS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "VERBATIM_HEADERS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "ALPHABETICAL_INDEX = YES\n" >> $(doxyfile) $(ec)$(gprintf) "COLS_IN_ALPHA_INDEX = 5\n" >> $(doxyfile) $(ec)$(gprintf) "IGNORE_PREFIX = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_HTML = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HTML_OUTPUT = html\n" >> $(doxyfile) $(ec)$(gprintf) "HTML_FILE_EXTENSION = .html\n" >> $(doxyfile) $(ec)$(gprintf) "HTML_HEADER = \n" >> $(doxyfile) $(ec)$(gprintf) "HTML_FOOTER = \n" >> $(doxyfile) $(ec)$(gprintf) "HTML_STYLESHEET = \n" >> $(doxyfile) $(ec)$(gprintf) "HTML_ALIGN_MEMBERS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_HTMLHELP = NO\n" >> $(doxyfile) $(ec)$(gprintf) "CHM_FILE = \n" >> $(doxyfile) $(ec)$(gprintf) "HHC_LOCATION = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_CHI = NO\n" >> $(doxyfile) $(ec)$(gprintf) "BINARY_TOC = NO\n" >> $(doxyfile) $(ec)$(gprintf) "TOC_EXPAND = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DISABLE_INDEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "ENUM_VALUES_PER_LINE = 4\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TREEVIEW = YES\n" >> $(doxyfile) $(ec)$(gprintf) "TREEVIEW_WIDTH = 250\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_LATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_OUTPUT = latex\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_CMD_NAME = latex\n" >> $(doxyfile) $(ec)$(gprintf) "MAKEINDEX_CMD_NAME = makeindex\n" >> $(doxyfile) $(ec)$(gprintf) "COMPACT_LATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "PAPER_TYPE = a4wide\n" >> $(doxyfile) $(ec)$(gprintf) "EXTRA_PACKAGES = \n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_HEADER = \n" >> $(doxyfile) $(ec)$(gprintf) "PDF_HYPERLINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "USE_PDFLATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_BATCHMODE = NO\n" >> $(doxyfile) $(ec)$(gprintf) "LATEX_HIDE_INDICES = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_RTF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "RTF_OUTPUT = rtf\n" >> $(doxyfile) $(ec)$(gprintf) "COMPACT_RTF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "RTF_HYPERLINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "RTF_STYLESHEET_FILE = \n" >> $(doxyfile) $(ec)$(gprintf) "RTF_EXTENSIONS_FILE = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_MAN = NO\n" >> $(doxyfile) $(ec)$(gprintf) "MAN_OUTPUT = man\n" >> $(doxyfile) $(ec)$(gprintf) "MAN_EXTENSION = .3\n" >> $(doxyfile) $(ec)$(gprintf) "MAN_LINKS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_XML = NO\n" >> $(doxyfile) $(ec)$(gprintf) "XML_OUTPUT = xml\n" >> $(doxyfile) $(ec)$(gprintf) "XML_SCHEMA = \n" >> $(doxyfile) $(ec)$(gprintf) "XML_DTD = \n" >> $(doxyfile) $(ec)$(gprintf) "XML_PROGRAMLISTING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_AUTOGEN_DEF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_PERLMOD = NO\n" >> $(doxyfile) $(ec)$(gprintf) "PERLMOD_LATEX = NO\n" >> $(doxyfile) $(ec)$(gprintf) "PERLMOD_PRETTY = YES\n" >> $(doxyfile) $(ec)$(gprintf) "PERLMOD_MAKEVAR_PREFIX = \n" >> $(doxyfile) $(ec)$(gprintf) "ENABLE_PREPROCESSING = YES\n" >> $(doxyfile) $(ec)$(gprintf) "MACRO_EXPANSION = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXPAND_ONLY_PREDEF = NO\n" >> $(doxyfile) $(ec)$(gprintf) "SEARCH_INCLUDES = YES\n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDE_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDE_FILE_PATTERNS = \n" >> $(doxyfile) $(ec)$(gprintf) "PREDEFINED = \n" >> $(doxyfile) $(ec)$(gprintf) "EXPAND_AS_DEFINED = \n" >> $(doxyfile) $(ec)$(gprintf) "SKIP_FUNCTION_MACROS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "TAGFILES = \n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_TAGFILE = \n" >> $(doxyfile) $(ec)$(gprintf) "ALLEXTERNALS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "EXTERNAL_GROUPS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "PERL_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "CLASS_DIAGRAMS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HIDE_UNDOC_RELATIONS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "HAVE_DOT = NO\n" >> $(doxyfile) $(ec)$(gprintf) "CLASS_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "COLLABORATION_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "GROUP_GRAPHS = YES\n" >> $(doxyfile) $(ec)$(gprintf) "UML_LOOK = NO\n" >> $(doxyfile) $(ec)$(gprintf) "TEMPLATE_RELATIONS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDE_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "INCLUDED_BY_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "CALL_GRAPH = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GRAPHICAL_HIERARCHY = YES\n" >> $(doxyfile) $(ec)$(gprintf) "DIRECTORY_GRAPH = YES\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_IMAGE_FORMAT = png\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_PATH = \n" >> $(doxyfile) $(ec)$(gprintf) "DOTFILE_DIRS = \n" >> $(doxyfile) $(ec)$(gprintf) "MAX_DOT_GRAPH_WIDTH = 1024\n" >> $(doxyfile) $(ec)$(gprintf) "MAX_DOT_GRAPH_HEIGHT = 1024\n" >> $(doxyfile) $(ec)$(gprintf) "MAX_DOT_GRAPH_DEPTH = 1000\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_TRANSPARENT = NO\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_MULTI_TARGETS = NO\n" >> $(doxyfile) $(ec)$(gprintf) "GENERATE_LEGEND = YES\n" >> $(doxyfile) $(ec)$(gprintf) "DOT_CLEANUP = YES\n" >> $(doxyfile) $(ec)$(gprintf) "SEARCHENGINE = NO\n" >> $(doxyfile) libxflaim-5.1.969/COPYING0000644000175000017500000004313310511001742016274 0ustar ahodgkinsonahodgkinson GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. libxflaim-5.1.969/COPYRIGHT0000644000175000017500000000440710511001742016535 0ustar ahodgkinsonahodgkinsonXFLAIM Database Engine Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, contact Novell, Inc. To contact Novell about this file by physical or electronic mail, you may find current contact information at www.novell.com On a Debian system, a copy of the GNU GPL is installed in /usr/share/common-licenses/GPL-2 ------------------------------------------------------------------------------ svn2cl.xsl Copyright (C) 2004, 2005, 2006 Arthur de Jong. 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. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. libxflaim-5.1.969/docs/0000755000175000017500000000000010511001742016165 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/docs/INSTALL.W320000644000175000017500000000754710511001742017605 0ustar ahodgkinsonahodgkinsonBuilding on Windows =================== Visual C++ 8.0 Express is good, but more to the point, it's free. To build XFLAIM, you will need to download and install VC8 Express and the latest Windows Platform SDK, as VC8 Express only comes with .NET libraries. You can get VC8 Express here: http://msdn.microsoft.com/vstudio/express/visualC/default.aspx Click the Download link on the right, and CAREFULLY follow steps 1 through 4 on that page. The 4th step links to a page that indicates how to install the Windows Platform SDK. It looks painful, but it's really rather simple. Once VC8 Express and the Platform SDK have been installed and configured to work correctly with each other (and after the obligatory set of reboots), you can simply double click on the xflaim solution (xflaim.sln) file to bring up the xflaim project in the VC8 IDE. Use the main or context menu options to build the desired targets. At some near future point, we hope to be able to build with autotools under Cygwin. Of course, VC8 Express (or Professional) will still be required, but at least the build process will be the same for all platforms. XFLAIM Runtime Library Use ========================= XFLAIM libraries - both static and dynamic - and the xflaim utilities consume the dynamic (DLL) form of the VC8 runtime libraries. This is in alignment with the use of runtime libraries on Unix platforms. It's more efficient and flexible to use the DLL versions of these libraries, and it allows Microsoft to update these libraries as necessary to fix security holes and defects which may be found in the future. With each new version of Windows and Microsoft tools, Microsoft platforms become more security minded - and more secure. This is generally done by copying features from Unix platforms into the Windows operating system and into the tools themselves. Visual Studio 2005 is no exception. The most significant security feature in VC8 (IMHO) is secure package deployment and executable module manifests. This is nothing less than the direct equivalent of RPATH's in Unix and Linux, and the usual security features - and annoying issues - come along with it. The VC8 runtime libraries include MSVCR80.DLL, MSVCP80.DLL and MSVCM80.DLL, which represent the C standard library, the C++ standard library, and the C math library, respectively. These libraries may no longer be simply dropped into the same directory as your executable and consumed. Executables and consumer DLL's need to be configured to build with a manifest file (a default setting for new projects), and the runtime libraries need to be "deployed". Deployment consists of running a significant algorithm to determine platform requirements and features, and making the right decisions to install these runtime libraries. Developers (like you) will not have a problem executing your own projects built against the XFLAIM libraries because you've installed VC8, which consumes the VC8 runtime libraries, and so deploys it during its install process. For more information, see this excellent article on the CodeProjects website: http://www.codeproject.com/cpp/vcredists_x86.asp Legacy Makefile =============== There is also a legacy makefile that has been hand written to target xflaim for all of the platforms that xflaim currently supports. If you don't want to use autotools, and you don't feel comfortable in the VC8 IDE, then you may build for windows by simply running make from the root of the XFLAIM project. This makefile accepts multiple auxilliary targets, which modify the build in various ways. These auxilliary targets include: debug release 32bit 64bit verbose usegcc flm_dbg_log True build targets include: libs (default) - xflaim libraries (static and dynamic) checkdb - checkdb.exe rebuild - rebuild.exe view - view.exe ut_basictest - basic unit tests sample - sample.exe Enjoy! libxflaim-5.1.969/docs/INSTALL0000644000175000017500000002220610511001742017220 0ustar ahodgkinsonahodgkinsonCopyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Windows ======= For Microsoft Windows building and installation information, please refer to INSTALL.W32. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. libxflaim-5.1.969/docs/NEWS0000644000175000017500000000000010511001742016652 0ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/docs/README0000644000175000017500000000000010511001742017033 0ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/docs/XFLAIMArchitecture.png0000644000175000017500000013536410511001742022232 0ustar ahodgkinsonahodgkinson‰PNG  IHDRaÒmΡQ pHYsÇÇ8’/vº¦IDATxœì½ohGšï_‚¼!d@‚¢0‘Ã(Äpe6/¬0Q˜@d²p%v!‘3ð[ûìÚØM^dœ]ðÚ;0?{f¤î`ÝÁ d° ‘_äbýÀC´Á^È`LÀ‚; _SÒ£çÔÿîêó|0íçÔ©®®ê§¾õTu÷i=Äú€ýýý‡ø;/OºrfÏC® %~g]œ>aÀÇ¿°atLéÛxÄj'¯÷§ßì=õÈàAó›±··Ç´î…XíäðFª‰£½”ÎB<"¶³QÈíäRᣓ6kžŸ~þÖê-W®ªì¾rJJéWr–þ ¦cæÍ[QËœQRúØŸa9{r`ñ®Fð"¾5e EÄvòª;¿âm[¾|~öµkQÉ¢¶ÓRuüo¤)[@4í´ø!Î(M;EüL0*Æ`þéΖâgq,)³ŠS ¿Ìº/Ÿ!§ÏX]¾ø¸–)`8«"¾uV2àœÎ½˜½R´Õ•ê§ÒÒÕ9öcI{I;2kƒmíÄÓV;…󫆊zv,ÇR÷Rw´àè·Îý:j@RS¤o}ÊgÖœž…”×'3øÁ”¢âS?ÚG:¿Ì£µ•Ú S¥Mýœyœ²&¶ÓYéÛ)&Íg```ú¹iç5Šbþô,4ÏO?¿úÁj¹Ë:ÅÚÉ vÊ 0t.\Bk'÷$w©+W,ªô£bíH×ì`pÇãÌiXï|gV¿µ”YñBaávj{ž*H‘Sýè3¡S dÕšZ¸*¸%RT/-v§‡Ò·Î}ÅøÇ*P¸|ÄÓRIC×U½'õj\S̺Kÿ*Îd%ÚÉ ãZ9KŠÓ*üüZ¾uR¬ü¤í?öÚû#\ʬ£.ïÞ¦èZ¬¸ÓÖ2a*íÕbí,7 …s2dù¶@;SN€´p ð:”ëGÚéÃÊof~zIû•s‰•Àí45R°þþÒä óL Büãô«¯Û÷­Hàv ^í̹Ë7×?ZžüÑ,CžÜÝÙ™Eúòåó| ‰‘ÜNáÞH¾dJ´ÁXÜ_á‰,&ÚYnÀTY@: ©0ñ³:%鹸·—\¾ÎÙI ,3;åÛY‚Ä'ºXråðàéymrig²ðíaž/´¯–íìi!F[‰¯W49M™•<å÷5P úc'§Ü°@;ÙöNg;2,%=ïÿØ ®Š$-gíÍHÒo»·:3i¨‡áFCgBwxRð¸åÃÁdP9§EËÁlgßIi¯ÿ}çO@‘îã¤L;  ÔÕ·‘‚pMÜND±æ¦vºÆ¾ïí¹øí´øÄó&¢qðÛ½Küv²£ÑK|ò•wP’´³nJðâ¼Ú‰åIíK$-o–œ»}õËÝHgQ¡%b#¥Â¿3$Z(~ð¨Ù34)!~Õ¨þÚ1r#-lå‹ÜH±Š=ÆCû6ÉtÝU:QG ’´Ô©PuMOmZ°¼ÅDÉF p¥c«SÚDô¯ñbïO·}ðm¤TKõHRŠú$%þˆ_2c:ÒñI´œ\-¾4$UëIuG‹í§Éyrµø6’é Rã n!;lž½þÃú­§2 4RÅ~Ö퀫Q'4í(,Ü}ÔN¤R©‘U0u9ç%å7åÁÔÖH>½ (úFšž€®ør­ŠhîS¥Æy2…)üK<¹å=«Êc·…YËÍ Æ*=_¸‘ÒDdM Xo U3À·jôÃùYe 7RœNñ+0¨„4¦cC0`D5NJm+íCAáFb´§Ù¾¤6XNºo 7RŒ×ÒÀ£vZ5Qä´Ô÷s ñ¥Òþ,ÜH-ÏH‰Li†ÔçµTí 7²Ü³ã<ûdi7²-tþâ38\&¥ÛY¸‘9âÛH1Ò¤ùKY&DÜ*o#=±¼’y¯qƒ¸‘¢…ÚÆ¬´¬æßþêþÂÌñØ-Üȵå«S³.­Üƒ·Zn¾1òäë¾)qéÎ-_>?òèÏ#ò>Ô¹ñÄÃÁÞ·ëDÔ~ä±qþO¤ˆ Ô– ŽUožÀØ|©Æ ŸÕ*&ö½ôtž´Pz^æy6x2kJ6Rô½¢S<‡X(z8 d#Å•ÍõˆêÏãz>%Z„§SÌ$m/PG?.)ð„¶•rnd%éy$øMü"@z$½ðê)»«ÿ‘¤ŸØ?úày\•Â,t$õXRJ!O–¦p# ¡¶Òm»ðïD…YôH¸=jÛʵ‘Ì9[(ÜH¤61¥¯J[iêÉõœEEi¤É{x°ÑŽCvLÈùƘÂ,ÔWê/Öìù‹â¬RáF–ÿ„ο©XuÎÆ˜(ÜÈ¢Ó…DϲCÕ9;¤Â,Ú TN66Òy}5Ù5ØÅ»îÛö+úÑ=yÿîÚÕ¿{ÖójUËv*‰ïÕ1å’o¡06>YxfZ{{O[àòåókï^Òæ”îÕ™¾å-„¦]𷳯] õüv€F6Ÿ‡L¿[²KÙ9ñÇTù[;…HJäÉr- E¢FÖ 52%¦õ)ÞÈî+;/Oì¶7¹‰— ¢7-xíÞ×y Eyx`ŒxÁžTx‘¤6³ý½mUö5Q¤‘fL¯¢ë4>ì[Kµ³H#µ/acš÷°uÖÄÝwÛ‰ÌrS­…}MJ´³H#¡àÓáûRŒ«…$Z¬‘Å.IÙ+ê$ÜË6‹5ÒÿÚÌÛˆbÄø6ø°{kî ѽfóà­Ì¯„Òt÷U…}t3¯Ä¾x,(º{ÉF¥DÃR²‘þ70ê¥×¢T¹#V¦‘RÛ|šZèÆŽ–ý 73 7Éß?Ò.å(½oáF–>€oæy÷ùÄÇBû²Ô#]8ï|2¸ã—Ï.–âÜ7å}i:5öA'ÂÙ—¤`Úwauk)Au9ÓU[Ú2HýÃä3í¾¬·õÌØkUˆò¾¤c«ç×m«$7HTO©JÚZ|ãÌ©‚ó:®Oá&ø²"öS¦uƒš!R,*‚6aÆq±‹½½S %úQ¢~_Ö‚:Ð-þ†N.dPSìàüªçÌû{¶}êKí 5Àvž\S™LW‚6sG²¢¾,úñzø\/žm‡?(Râå Å|YŸ—е’*ò¥4}éó¢½¶"™¸Gô¥x9®ôV¬_ÇÈùbàDô%FÛ0çÌЙÁµ(KáêJ—yÏGÒ»HäK¦k¤8;Ú¥dPgóêrrÚ½¥æ4­á[aãÌö%`½a%º/ÅôGØÜPÿ$Še¦î“b™Ç›J^ì]0ƒFµ²7“ÕAt_â?§Åué/A’†X¯R-Z”$¨~ÄR“öµÔÁü&1ü¤÷ht_…¢ZUçáÈ£ºõ"•ƒ¿2u V*U«:‰|YÈ‘øœÚ:5›d8ó›R,u°S¨¥é8ÿØ÷Æïÿ±ó— 6?ël¹#—/ŸÇoçŽß¤ ¢?¦»sGÂA7ïÊ{ ;¬^íÔìK€kHÈHr˜ý™G5›0¸ó&»B;1)R„'¤Âá[iwµXíîðÔuCxÈ>Þ[“X%DG½hP ÉZ4ôö{†tº—µÄë Âþ-(™ÎïnA<*že©j!/yS#Ý¡¥‰ÕzØ’íàËo)k"/‰Ø¤ó%¾É6Æà16åp7€^z¶EåHçËýÃÇ&‚·YŒÞac°5×D,_v|öÕÑî€Nâ×+j:gà±3Γb+–c(™=zôSÎòè Oq\oâøòëí·sàfKû]ÅòÞ 9›½X'&WQ­ÕQz•ã$Ž/·w:w𠺳£Ë»‹ìQsW±¶#VqçöNg;2ÌD%ÑQLGt gËeÄÊÌ‘ø´šlŒä á?ž¢IüËA![ß>}øXF.ääÈ‹ë÷®L[†¼"Ù)å‹ÿ>É ž‰ãï® Os×fDNŽœ†-óSäÜí{7Nö§ßì<õÈ06´€³ÏrÏ”§ääÈ3ÂÖV'÷"‘ÃìÜøØ³¿ß‰`¨;Þ{i’«â#ßwøm5[3ÉÉ‘ÿ` ÓøJÊcJצ4–\IH#[‚ÑColÏÐXðïXK¿¼¬Qì¾ò¬åÛÖ*^êMr §µŽì7È‘-ÙÜŽ,ýjÌÚ/+y3ã&Îw—¹Id9²%dïHñÒPüîVÁÞŸwÅûAYïFíoUËÉ…,¹|ù<ë}—§xRÖß_š|aþʇ[8Σó0Y:rìû`«>ÓòéÿY厄×9³^Šóÿ|còG³úýO–Žœ8}†MÕ…\¯"¿‰Y¼‚g¯éÞÝÙ6•“Y:¼ˆ‘^ŸŒ éUÊ¡N±mÁ›¥# rdKp;Rú£D-Ì»~ìàv$ü^¢º²Ãyep;’ÈrdK GÆBú£OR:6‚пŽÄ—„´—‡ÂžqéÁ§©iº¢f:YÒ…4é«*çWÈ…éÊ©X²À³„ Çbé)uI8›¬×Ç`ãü0RáîŒ=íyÝ0å”®ù¨¢°pLjG °'ððeÑŸ$ Õ¥ u*륤#‹ö} »P|+¥3å*¨V‘Õ})Qb¸³tA%Žb¡¤#%hGEËŽªaJ—2K)¦ƒªÃ¸ä{ˎ𕽸“iÏ Î¬º°´0€’Ž´ô&mÕ‹Rhwgf©s¨~ÅøˆÛ´¯‰¢çÄ?'PÒ‘¸ë1]W*Z¦è[J—¶¬·…Zn©¶”h²ÕâsN¤œjfÿ¶%ÉJ P‡l¨ýûO… )£Å”wd°ª¤ÖëWÜy¥<¥{’¶+¨¥™ÒM¨½ÓßêÔãH¦XœƒØ’ƒ«€O7·:`=îvê£f[T‡6¿‰ÚY…¢”0VíIÄ'×dÓvG©‹¨£þÊçp*Y:²"gcíb÷Ê‘V€çiU½¥Aºªx­ã}§ÒŽ”Î¤ U(ê.ZìÅÚ u¯¢ô£#‹Rúä²jû‚Ù 8Òÿù ÆdÎÿ†Ãïã=ó›(àÈÀëúô èHÑ×úQ!¢#‰ÛÓÄ›a‹2º#·§ ÔÒäXŽÄï+‰ɦã‰ÑÎSŽF±)ÀÃd;Þ:UÞdÞðêÑBÄr$‘˜Ú©½Ì(e`†+#>û–Ã¥ã•ÏÒ¦!‘#¥1Ÿh|¦ðIT3à<øJ7þ–!ðEpœ—,2@ ÁV/©«®­1”$r¤éìXΦzêµùµ9±“¤ õv¸~W$\À×ðÙT{ºN±VL¸Ö«Z 5§ä)ë=4Ó¡­°h`]—A¢;Ò„ÔÓñVÊ Ù–]Ôü>9%—¨Ù´51ù¸.jsdshšKÊÝ‘"ø‹Y_ [rj@<ÑrÂ¨Ž–8'ë xx/È&í.}e)ÃŽÓÏMC{]ÙÝ‘…Î;Øê(}«%À;b/jó¨é’aÞÙ\ ‰é9)WO™$#H”2àSë{-X²8Ý¢Z-5¾v;‘#}ÀgÊ$é£zêKÂËQ%è〫V8^zØøGì뢜YcÉÁwñ…'DŒ\ýÝÕéW>ýà†$>ÎÌO/ñ”sÿv‹«!÷"÷奕{¸ð ¿þxý£enLÍ^€œkËWE «•¶ ­¬ëKp˜ø â,‹Du‹Á)ª"ÇOvþ(ýäfÅŸf¾6a\-áÎæ:2¼ÀyiÂE½ÈúÜ‘ Dh±~E=|$ào‰Iµ#.jõe`Gú *Z¨– 'Ô{å ©Á‘¥ïÕõ å:z Ž$,õÚ‘ð,OÑÐ'4e²ãÆÕ¨¬xØ¢ô­ÝŠ”p¦Üî©™†'" âèåü!(±otG–+:»Ü]tåJÂÓG×Ó=)Ñ^A¹½XG2ô´Ç@ï«>J·6 ʵÎUÑ}£;WHØbÕ•ãRÝ“Š)u¡ º#YÙ¾©eàäY¾­2äòÔݵ‰¥éʨ¡ô‰JáÈÒ•³ƒªzB|+2àœ-¬ó08”Dj»JtG–žìhÁnÃn0¹Drž…~ ÒdO¢;RxÑ•«>gܙǧ”#`Çõ'º#ûr·/*Ý‘0´º2–$Áä¥)½È8R´'^« j24¯a‡¾”&Dx¯ê`ñk!º#cšÄªyb¸PPzQ_…莌Ý*ð™ 1þ9K©±¢;²ÊêØI^Úª­~ Ë€ßÍax$¥úïÔ£;2ž ›O¡‰kÅ›nÑéß`"cNÕ‹>Rfí–N‚*е…ëH,ÇP S¯½1/ñL{ï ã0v|E/²²³ eV‰ëHhOV]c¨%ìß p†êrd­T$ Ú*u cÒiŽ£µÁâEVdZPåýÑYËõªrñ¢À³ûî‡{ÿGtGö'é»otG† &Ô ª”Ù¤<ñ(Ἂk–À‘Q½ÈÌÓÉ‹jÈ©xQŸR‚Ñé-ª`š©,v¤ËXþ­öÏi"º#Ë-ª2B;óŒPT¢;²Ý^dº™ç~‘;w¡V ÑÙ?TŸ°T!…#=û¦D¹½j¤\…C-%S8²õTƒH™€Pªª92 UTUnL–(ìÈÊ^wæÍÎ}ð¥?¸òe?'Õ[¤ýû¾žv$ÑLÈ‘-¡GŠ·†Ý¿»6~rÊùæU;–Ýí%WXV+u:£þaÓ›;ñ{‘a¯Åÿl%|Œû„x[ ï"›È€_%‰»…éí°;ßîîl<9±q{Ž^»YC¹|ùü¢î ^øõÇ’j%$sEòŸ¬òô•ß,LœžoyQ[Ÿo°CsE·|ævÇoå[ñjg×Þ)¨Ç‘ØxÔæ³&m% Q¨mæ§—ð·¼C@Ÿàšæy@”Bâöx‰wÇu€W;×N=Ž$‚CŽl  ½ý±+O¯'Å»7Yñ·åˆ×»§’*’Ÿëê¿VÑ"®YÇ(ÙB¼æ” #ß–´úÁjØŽ<€Þöd âIŽæ¼ 1#ñ½ž°ÍŽW²üTN²ƒZHçH"*äÈ–@Žl äȖΑñæ–ñJ¶€Ÿ?NvP éon¯d }=kå ç–t‰æûÉ`!¾#¿^ÁŸö¿º)¥°Gn6yÒé _ýé²#»rÉv¤ã28täãúÍ‘RÛ,@NŸöw3KgÓˆ(Ù§X'u×›8Žô÷"†ïeo|¤b”=îÀcg‚Çᩎ?þX"¨O±üÄ1­nªûÒ@ÇUÊá Î $¼#“¡žDí‰ëd»»ÈU¿) >®öˆ=‰ÑúDGnï°‘â¾xÛõìÌa±âTzö÷£ùHéÚÛÏã‘dŒãHvà•Îô]Í>PŒ'Û;¦2µ£\O†*+ïæh)·W!¢9²KÇC‡:“ÞÈ`ú½¿ÓIqž,±òI #1üЕº‘q‰)ê0XyNªw–fMMªa œ1»Q:G†â@åÝ UÔ‹G”õ¥vl÷¯FX"Kp²qdçöá–,ðÔ_JOs*Yªp¨%¼#ÅO[J4¦s®í7{Ÿž·û’EêþÇh =,*áÉØÑ¤ñ<¡Â…ûû󮌶sjšéày–g}4<=ïß hþ*Dq¤ä%~{¦ŽxÇË…uSz¤ICDGÚ3 çI8ãt¤pd`¬£¾ÎÀB¯S;”c¡#AâŠ8ð•¹Îëf,m´“¥#P&>à¿Ø¾dæÎT9;R‡vŽÏ¥éNÔCÎÚæÈÔ” ™Oχõ"ë[GD±EóÓaª’¹##ˆz€À²wdAA¸¯fKöŽ`÷¨/1‚oƒ ±9t¹óò$cÇ{@[ÚÒ¶ÞíÐÛwZ2¸Dk MD³ MFäÓovžýýÆ­ç&N Bâð;ë;/üÕ´;ۻϰÞ];x¥£BÖÄ·—6¶¯þÇæ½—&F>¶õíÞÅ»›§FŽ?øcˆŸ~³÷ìï×o=7ÎM%~<: ·?þÉÄS îü…ÿäþèàà•É1Økþû#W&«{¡ MFäêg[¼ßÏÝÞdlXw¦OýqSŸt‰ë÷7çŸZ˜˜ê®FØèÃ7N¶àêg›\3s·?dT[•C­vÖ6|—§Ç·¾í(aâ@–¯Ÿ›»}ïÆéúÿj[ñsQCºÛm¾}âËmS<馩șޓgÏö-WlŸø²'*V¡+Ôã]éR„Œi2 8ÚÄœw‚©à±ëÒÆæýÝ­k“cÃlóÛ½…ùëá¬òèšž§Šxžæ®oÝÝ<¶01¢d›j'‚CšŒ‚å ŤÌBéPZW-Bul Í]Õ½¤©†ø[šêCÄ€4™.B.È*{ó³õ±‰S#ONðíý»kã'5?òà‚„CœŸæפšÍ§(ž®& M¦C¨¥´,Ww…«š“?šåÁV+$Î¥åO×?ZæÆ•·´˜_Q<±\=‰Š&S°ýÕý;Ü]œoW~³pê¹¹‘ÇÆ-»pé‚­*Ùryô[˜}jê¥s\oê·L™ˆVŒÞDpH“)àò›ùé%øˆm­ÔDKŠÅ°ï¨¦˜”IŠM i’ ši’ šEMÎ?­I¤‡KˆÖ£öüùÊñ° š$" ¤I‚h¤É~n´ø\Pmà\%Ÿê©y|öª Òdßaé ðTƒv¬d5óSx<¤;ºR þˆ«¦×i²ïÀ=OôKÐØâáS ø£*Tõ[¦ pM¤XjÏ Ùš Ë–k²è$Ç?€;¨úÑDÑ£„E Xu¦8ÉPëêU TH·Œ,eÄx¤-ªFZ®If¿±oÔtÕP[|i_f™¡Ñöc¬OSf5i¯¨G4Õ|ñP¥¦Ìé+o¡ýšÄ„$å„Þ¯ÕÎsV™©y{5 yŠj¬Pföƒª_­¤DÅÝËQTÕNjMª¡Æd°ÞN¬¦ŽÐƒv,áYÒ}ò¦ªÁ”œµòGŠ´ÌzñWÚ}q Ò.R±Zï.UÀ§ij?Áõg‡Õж—õÖ‡¡*™šÏ\UU‹ŠGRMúû ÈO–qÏÈÔxh'gC7³ö~UZê·Ú½L†ö’z¹ê5Ó!œ˜vñ/PÊV¢ª>G©NRMJý@:)º=4x:À„t  ÔÌ œè_`Š›f)Ö~¸r^WwÑžX)3ÎcOÔîèÌàÜÝÎãy85ƒ„åljHlRk(Ú΢ùµ,*‹õ¤›F Øùa)‡ ,ԦɺP`ç èÜÅžGÅrÜx¶Äaí8eÇ3[ ´søŠž¢ê¨^B$úN“5R{Ðvw©kJLÓo¼o I[Xì½ %•†2íÑqé# VØ’bË"Ašì ,&ˆ¨ ãº2ºÁb°«ªˆ”¬f“Ð*\Ê Œr秤ɾ@¡ó[HÑ~´¤WÇR¦©öJÚ³9[¤-$*¤I¢*1”éO- i’ šEÆš4­à ÂÞ‹à¯ÐsV?X­½GÅÒ¤öÅYaYéžÊ"šÃóÓÏóí­Õ[®Œ¾œURjïQ±4›ƒáÕÿâ‘}âô\5 ó <ñ ÚMŸ8=KMŠ þp2C4’ð±­¡2?MJ¾DÕC<‘¹aRšäô‡Êü4ɺÇØ¡W„m¢ökhD¤™$¶r~”Ÿ&a?µuC =át>·ØéùiÒB‹×}Kú´Ušä³Y» ¥G±Y©Ém‚§M8íÌPŸ‡ÔcZ{í õ´J“ÌC–êÙ¦_åh‚ v„ߨ$Ù«%K¥™ŽnǾ—¶ ¬·VÎV«GTµhìyL ’µC“p}U¸Ð.Km7µ#åÁ%Û¤¥Eô"¦ í"lµ7W¡z ®X‰ºIù»÷­ Y 4©õœ¥š®j@5Ôt mÓ^ÕI%´éžuV÷uVCB;^Xò3C–³ÁÌãŽ$HíEד½&‰ 8UÓQú6B ²×$¾? ¾×Ê«<3 -ŸÒôQ\ KJL&«ï ·Voõ[lÄd¯I¦Ü± uó :ÜÙÞ?jÀü.“øäQÓíÕЖ€±×„õ [M·WÆôm qbYâúA«mÐ$&” %¤>gÿ¨½Ö™Ó“2ù©ûúejB‰šxҷѲUš (HÜÏÀöT&î»&)âÌ>A’Y ÔºèGf­Œ© ‘)èOY¶J“¡™˜¨Ýº(ª úóår¨W%bw³ü4)Ý„”ØgŠèOD¿‚;ÞU.z’Ÿ&YïµrŸ_‡¹#ݱä6ŸÓ\ªØ‚L FA~šgGz¯‘Ö7iF$€»R]X¦ñ¯b2A²5É¥ñ Q/|ä•bKæ€ÐÜÕðP2ßõ‚çGÉf­µ«&!T¶Ø7„D-N‡À˜ B š®I~"LWçgÞì\%_úƒöK¢ˆi8=Í»XS¬ùš$LúÛ§‰ÉH“Ñ4H“…YùÍÂêoߺòþC£c»[›7~yqü‡§¦f/¸ö; Ä¢|—ùŸ/N¾0/>Š -„(=ÇÓtÖ–¯Nýd~æ§—ÄG.Ës—o·\¢ =œµ¾ðföµkø§Rkï^‡¯Ôƒ2å¬+]|®IÐ0;|úœu+Ìÿ‰Dœ¡Ÿ)…ÊØ%MƒwM>uÜúÓý¹º>4<ÂÑÍÿwAÌ]—/Ÿ¿ðë¹öx ZþåE‘âôÌúûK\ÛŸoðà£-“Oƒ¹®DPÚøÄx»õõ·ïpÙ\ùp 'jªeég¹ÀDä)üˆ¢ÀÁ‡Ÿ ód®áÍ?Ý7Öho~Ä$#M–G ˆ`îÊC™0¸>ay ß‚ ¥ßF±î'žÑ¼öB|;vbRú™3T=¼Â‡°/-ºöÞÎÏk51:¦Ø?HOð°T·(I“D>d€¼ ž»&’Œ4IvÒÄFLÓ5pdjÇŸ|Ï|¶ûÏ¦ç® N{Ó5 ѕӜÓààŸqÃ{¥ÅEzŽ’HÐ\4UêMBWvel"Фߦé"ɨå§XNàͰuíQ•¾Ð$þ tšÓJ´ øYsšžÓšTßB%Hòæ§ökr@ùk0ɼ€¨­`Ý©l_]ìÁkÎd­Nv  ýšT»rŽ¡÷ŒÇ”°¤l>­'£ /¬r·NÏJ¢ö HT9™þì>X“f)Q±ÏøÓMZF2SH¬*÷ï.º²!Ϧ†–4²lƒ&-“ ñ€t ¡Oô!’ÓȳŸê¯mÐ$¸'Í)«ÂÀɳÎë“ÇNéJï˜áîd‚ÄÁ9ÍAóÖ$ˆ°™jÄ]\íîR ÿÈz§ÄÚ] °ñ.j ê.¦ÌPšô±M#ŒÉ[“àžŒB%;ÔÖ‰ªOÈ`Úõ*ÍâLå«û )b}63r¦Ÿ¸¦$oM šì!)þ„ ÀVsªaŠp™>ßZ>Ú‹ª‹&{9,mÐ$vU£Ü¦FH‘¾ÒÆ"{µ({!ø[çÑỸ*ÒMâ´FS¢­´A“É.RGºèâŒN>y ¼Àx¤¼òY/yk2S) $=HÚ°_¹Q¯ÍøgËH‡ýIÞš¬ý>r$y`Á¨—XìB2éٔߙ¡àKë–§DZ@Þšd½ÊÎUjœÄ[Õ0}”(THßbšd Ôñ0-&{M}yY ͘~3Tï[²×¤ä¡ ® ‚å~cÑ3ÿ€ò|O¡òs¡9ÎJöšl&’Š´¢’„$-&µ¢rfÐæg½å;ÓMëEZOº²»Ù?ü ³Ú¯´éih‰&ƒ;, ¦> ê‚ ž+=ØQ›hú–™ÐM6ðŽÖ€ª4É£êÇRþè™M5,$‹²<áëFuIYïb’µC“ØIF°&ØþyZ« šÄ·CrôL5÷{{žÍŸ;]¬]R†*¼4mÐ$\zmà$Ö‰éò´>”ä*­ÕËE8gV?²Lhìõ‚à´A“ Åe‰Uáy±G’1°„vw¬võÛôxÞ»Ç7'*SZRÖ¾˜díÐdîHsWõÊŠ”"…Af•%C"˜²ÕH¡{÷É”ékÀ’KÓMæ~GWR´´¤h õ£Én…îæ{ÕÀŸì5 N[ìªãsO®°Ð^†£7aâÊZ IAëÇÎvÓÌëŸuÑMBLöD‚Cô·VoaY¦|/Œ qköšlÈy$ªÓ´9d]d¯I¢5Ô¯5 &‰Ñ(mÔE M6í>~ÓêCHÔå |Üô±H¡IÎâÝÚZHy‘H“AxÒ_š<{òhr2ÿóÅÉæ!Ý'’¯-_]þåEŸœ…ŽîY™ ¬üfaæ§—\¹ô¤¬g_Ñ_šd½³hµWm|²zý:—þ®¼ÿÅÐèh˜gÃzÞþêþÂÌñéW_‡-ŠZ¾|žÛkï^çöúûKK¿8{iåÞÈcã¼ë¯þö-Q&ëEì¨Ö„ÅË»‹1"ÌÿóÉͲÞñ=ûÚ5© "ÃîÎö9ǹ¿¿‚ÛÅ&hummgé‘kþçKƒßb.Ô¨=áøä°Þ³Ç›`(»_è;MâîxîßäÓÏL‹N#z V Ø\`S?™ÇÙľ¼O‹þĵķ<ó"ïåüŸVxW>ÜâßrwGnó»[›"oǾ?!íu€C3¥ ¬ O=7wîòMí¾â£©Q\‡bGϨh9’!&)¨qúœ¾Ó¤¥c]íŒ#¦ ÀÞ_öÔDhžl~¶>6qjhx„Û|ËíNʉI)ÛÈ£ú@Ýþ|ãæo/IƒCk›0ôð0ó@Û¨BhÎO8O~dTŒ>“‹ž½Ówš´À;:G;ßlÍýSgÒÈçŠS³ø\Q ¼ñA'òižOè0ñÖ+§ðî|FŠ$¦»¼¼ü#˜k¿_â)|,)BÐÔKçp™Ú&ð|îÊ3OœžšQ÷-×(iþ¬=:ÿŠˆ×_Œ>"§˜‡Ïþý•§ž›ƒtBÐ_š4õ6H?š> ŒÌvFn.±~ÃÙø ®FúÛêGH[1Ý…¯¸¤È)MùÔ&°nÖïëß(S SŽ.DÛY^Nˆ‘>úý@i’H ‰­¤I‚hUy†hÿmWŽ.UQ…øA]u(üd)åï•j!S9zûcW팓¢[ÃO×¹!RšÜË¡ƒ¢Î-믂”Œvj’»VêâÍïÙ¢zÒÏíÛÚGstP2Ú©I‚ÈÒ$A4 Ò$A4 Ò$A4 Ò$A4‹vjòùéç¥û âj{“/cª÷BX·!­¼ ™£ƒ’ÑNM ׿uKjØÏ äè d´G“/\þêèGLÈžg_¯å|ìÌA†šúAÏ»˜¾êùÕçÖo×>¬sí®ˆ©½uP½äªÉÎ_"èíÍjç6q”õÖí 1:A•ª LfÑê\…~koprÒ$vvQ7ûÐ)…¦Š¾‡Úƨ*u®^áŠô[{㑇&…¿£:[Bø¾¼ã¿^IY[Ö­pŨßÚ“<4™êäÍ,Ÿ˜_9Znydna.\µ[)Ël4Ö—^lï¸r˜ÙÞéüíd¥ÂBNÁ–[Jzm}ÔÐ^Ÿ<žn¹Ñ²Ñdç¯ ŠÄÓý%èø5П1† ãÚ–«¶)ªé5.·´íMIÕåFcÈF“¬OÖí¬Tw?ˆZù»â¦¹€%0jÓY3ÄÉF¼Þ—–:' ÈR“B*>Õ¬W¬ÃÊg‡Gg¬yCÃ=RJ–Ú~!’ýuà´A“`^nUéy…I?µó ˜Ò¡â99Ú½÷9„¤ ¯iòˆŽl¢EÎÚ—[‚zÝ»ÐPë V­6sÕ¤I?kËîW–>r[ÍOŠ* g]ô\ pÅÏàÌäÏá ’«&µ€PÁÐ~TÓ£FH½3e¿bxÔ•-5ÜßÊa›«&!ÜÁGaàDU{öü)ÈêOXĨ¯áâ<4óT`rÕ$ÓM8¥)*J-ùk¡gRWíO£ÚåOæY©&[À·»~€Œ5Ùn:Pž7xÊairÏœÈôÔ„®á9žViÒrá'Gßh'çjž›V”ž6"Ѷ²í­Ò$Sú1î²&»É¨¶_—Ê¥]„…¶iÒÒ#%‰¶‰¶¶«?i•&Õ~)-îU£ÉàJšš`j ‘/­Ò$A´€,5Y}ª&J¨^NTÔk¸í¤OH“Éñ^ní».±æ%N¦.4\g ?!M6í%çõ"kH“D}<=Ÿx6[éžs*H“µ‘ÝÌ3in;»çœÒdmtÇ”kª§›Û#µá níúÈõè^®RTRH“us¨“x"‹ ›–BÕÎKxH“MA}>‰ùÅ U­é¬}i’ ÄC^žÚcl1ÚÒ–¶õn½³öO>ÚÒ–¶ ¶é“¶´¥m‚mG>ùhK[Ú&ØR„¤-m´¥w{ãË­½ÿËæž…¾j‡ëiÓ\;96üð1{ºzÕmøµ—§ eåë¹6øŽ741õè0ιúåÖÎÿeóOŽªGxùÒQîï>Xº¿¹ôŸÛ×þjöÇÿþȵÉãP] »e!£nÏÞ¾ÏO1îÓÐÒÞ_ØèïÖn=7qjdØ’®–ÌÍUqãô¸Ø—+SÒê\·0"€~$-á£\\¿·µÇ–ž9~eòø§ßìàœ®Rž®îEÛ [F2ÞvçÛ~8Æíûß>ø˜HÇZbE'ŸtØ‚xž/þûäðà1‘>óè0Vãæ·^ÿá··¾}0úð1m R­x¹ož>ˆO=2¼õ7Gñ“Ã7wûÞÓÇ¥¶Ð6È–Q„Œ·}âÝuÑqÔ’”pç›> ôO[PÔ¨n¿».Ž«Î-%-ÁQ®¾ÍgË=9:ú–3:xlõËíAvÜT7ÚVÙ2Šñ¶¸"{¸w!'4cI÷,YÝ2%¿Z‚”óêl.LŒÙËá1óÌí{<ŠÚNÛ[F2ÒvõË-†4¶ôù–XI2´VÄ[KºZ>³~+¶7º€«87>ßÂ×–$-A9|ŽÍg­Ã†ù­ØKÄÌ­¿ØŽNÛr[F2Òvîö}+@HáEäa†½ ¥ƒĵq=v°…wºÀ{ñ¼VcºkKø(|E:1tìüú½K'súÍγ¿ßÀWYq‹–þsÛçlÐÖË(BÆØÂÕH¹6yÜrÕtPQNÇkK¸Ú yx סÈsóGâ¸p= ʹ2yÜrm ýâDgÖºp÷ž¸iº;Â[Ä3øœ ÚúoEÈ[íjPôlÓôBéRùsžyy´Ð^R R~ɹÞà~£ç^´­¾e!iKÛæl)BÒ–¶ ÚR„¤-m´¥I[Ú6hK’¶´mжj„zûcqÅœ¶é·3/ά¼·"^3çÌOÛ4[î—ÒןYÅI¤Gè°=3s† L¢v|´£Ý²Š’4™¡:!BàæÊMCv¢œª±¨‰"$AÆG;Ú-£IÁqªÆ¢&Šíh·Œ"$AÇ©‹š(BD`|´£Ý2Šy8QÜóPÓéÎGpªÆ¢&Š9!ôÆå·òÞ h’ÛŒ¤Ø$|´£Ý2Š9Òó'ÁI‡Í鋚ªFHÞ'?£>QüÌ/ýÑ•‰HÈü:[íh·Œ"$AÇ©‹šªFH‚ $|´£Ý2Чj,j¢IñÑŽvË(BDpœª±¨‰"d ¶7ïßùý™Ÿ]âöʯNýdndlœÛgO H9Å%kž®½v}ýgg6n¯à¯pNn_ZùtäÉ ñqó³õ·fO™®k‹’òØ+C˜ðÑŽvË(B¦Ëoõ·oM¿r‘ÛÜÊêë\Bö 3OA\–œÚ¢pe¸ž¹hÏýŠ~iY§j,j¢™Þ×E*¤@ÌúË<vŒ÷–&_œwe·áSÔØ‰I§þ ->ÚÑnEÈ”Ìþã•ÝÝW.#K¯Í 1ó0hRѹ_ßâÓc·?ßàöõ¿{^›ÍYÔÞ·»ç'‡/½OýŠpâTEM!Ó±ü¯)+ž¯2eåfŠŸ»[›8³Pšmâ™i1ÏÄsW KQ8õÒ¹+~14:¦-°ã£í–Q„LÆò›çyÆì× Ýs{ñÇO˜®åH8ç™–¢<+CØqªÆ¢&Š)à3Ào¶DÀáÿ8øðk§’ð‰1_"òùª+# íh·Œ"døz ‚ŸOúÜHÀSÙùË7^_¾ƒ¿å¹ê&Ÿ›Uöc§fæñá$ø^þEåpªÆ¢&Š)äáœ":å:vb’ÿ“r ›Ç^Kùªð´Eaœ•!$|´£Ý2Чj,j¢IñÑŽvË(BDpœª±¨‰"$AÆG;Ú-£IÁqªÆ¢¦Rý•Aô'óÝwŽùhG»eÕ#¤ö­gu½ùÊçþ^ h_3sl¯³é•NÕX"\€IÆG;Ú-«! ‚pªÆ¢&Šíh·Œ"$AÇ©‹š(BD`|´£Ý2Š}ܦò¹°ÙÌëŸÒ«½ì5ÔfpîU§j,j¢Ù_à÷úDí”ýŒv´[F²ŸQe©}ŸˆÈ€¿’>JùkAªSÚ¢VXÛ.§j,j¢Ù_àÎ*lÑ)ñ–õvMéוҌô\û[ê$Vm,C:4µ«:>ÚÑnEÈþÄ2eÕvbAÀ.›K僷˩‹šZ!‹NHŠ.«ÊM{Š%68bcQ7)mTøLª¾0}¼]>ÚÑnY?DHu&ƒ4G ‚4ç”PÇU¢µ7*ƒk"%ªò“¯]NÕXÔÔò HC©0 &à8³dŠ„j·–¾Un²YPœJà£Ôû-ß&@{8SÍ™ROíW¡ðÑŽvËú!Bš¦^"] högÚW=Š”™!Ç«‚‡lÚôà=ƒKÁ©‹šÚ!µ2°ç/¡UHÌ$=K¶W2 žõi8j…v´[ÖRB’¥üüµ!•&«¶j9þG7ao ö[ç9±PeßÄDªªS55¥ŽRw´ä …4cÄýþlï² %í¬5—öʼn`3Ô$ Kå0ë±$ŠæhókÝnÉãD€*–f"HUMøhG»e‰#$ö¨¤ É_i?JåØñìmb2ÔtS¢3³å+KžràέŽø´kÇ uØÑ™Šm|¸"Eª­d³ÞÃI…°Þé'ħªž5œª±¨)u„À©–oñG†z‰çyé+à´h ‘Ç”¾å)j"ë=ó8Ôï!®.P{L5ÇJÅZNCu“>šªª=c&|´£Ý²ÄÒ‡³h õl2U•³h+” «EÒŒÔÑM{ÙÑî ‰ØP3[ú’OUµçÁ„S55Õ!-'ȳ١($ûB™ý©X¬´{ÅÒ,<µ@H,-°¢8ßbiU¯žý<¨øhG»e‰#¤}„3}”¾ò9#L…—|K[b*öEuw)Å’Ÿ–€­³´È^[µ>þU-zª±¨)u„Äc˜”èù± &=ŸíHµ,•”ÆfÜd)ö…oË :Úʘj(Rfm¢´£ö£3ƒZ 8iGmÕoµ)…ªêSaŒv´[–8B‹½]Ó‡¢ù ±¨»6 »`@f¦rà@(í©D2œª±¨)u„Šv»¢ù‹cë‰E!ø+¼‹6Òá@Ò·Ò¾D¾øhG»euEȆcWÅ¢nViW¦ ›¦ŒuáãÈ48'i8ØÎNÌ­‡-ñ\‹Ioø£%ݧ5¿§j,j¢Ùà.îÊ['R=ý5€1µ±tÛ‹VÉG;Ú-£I0 p$t혳‡7i%ýàúØ¥¥­³ç¾ZLU2áTEM!ûèLjgÕÿ…Ô(õTÿUÑõgµ¦VàD\í¾¬wâm/YÝ×íh·Œ"dÿ`‰I¨’ 'ª–U@{\KŠšh*A›Mjšdk¿uâTEM!‰‘bkðÑŽvË(BÕ©W‡õ]‹S55Q„$ˆÀøhG»e! "8NÕXÔ”q„¬r5 âá£í–e!g^œqe!ÚFðQxõ3øãô›7M9ýqªÆ¢¦(r?ä)3±ÊØüzRæ“·vÚ×Lÿ­¾8#9½"«½ý ·ÔÙG;Ú-Ë7B ðÉ·iÄO4\œîTEMQ"$Aô3>ÚÑnY¾R, iÙWäât§j,jÊ2BŠ© Ø ŸÀý†v´[–o„$úŒFa§j,jÊ2BâIKó'0D2rºv´[–c„Ä#%¤4y¼$ú §j,jÊ2BýF^£°v´[–c„T§+¦ ÷™6h2•™?NÕXÔÔæö+"¦æûÂMÀG;Ú-Ë.Bâ '\²òÞ kö†¨ˆ:_…ôf:Ý©‹š2‹àPf3]B;](óæJ€GÀãá£í–e! ¢ù8UcQSfÒBc'0Drt«v´[F’ ‚ãTEMí‰Ìo4Õ¾´³åJ¨þËZŸœyœ´à‹ŸÒîÒuÑ…[ÈôÆ•v´[ֲ鼎»c•® ¯!õ/Á?gTJWÃtÞJœ ¸CÅUô¼pªÆ¢¦VEHf ’ÚžÄz#žÉf:Ô˜aGIÆø[íá£öÐj¼’”z( ¶–,Òòžgc {aºÛÑ||´£Ý²–EHæ$%¤˜ÉtC>¶qÇÒ 飤©C«GÇÅZz->œTˆ©æþ%hó›ÒµhÏ!C-òvQW6§j,jj[„d~+I,]ÐÔáJ †&UíE‘*³h¶þHZò¬¤Tÿ³”oxd.ÕX¶¬R]÷›)õKa/ær8³O7ÒvMU RŠt\%µ4 ’H¤šãú”àƒSŠL©I¡S𝙇j,jÊ>Bz¨J’NT{îâ8ÝT‚”hB*Vúˆ{ª]B€(AÕ$Ò|JPíß@Œ³±ÚÒpx䯙™3`«™ˆv´[Ö‚©Åâ9mÀÒòÏlJÔÚ¦ –¯LÙÊ–oÕÀåsLìûZr¹Ï„S55e!MðQÖÙÁb‘›¬Wíh·¬­’uŸH^ú£+qHí:lNÕXÔÔΙãÝdO, KÏ5gÃá#),3ÅG;Ú-kA„ÄÚƒ©NõÛÒõZ:º¶Úšœ=¼<«~‰¨âç>ÍzÖêTEMÙGHõ’¡Â#t8†ºÐ’.Ãâ«#Úë%8Ñçz£¶Rp]”™k"•/ʱWÆrœ^$¹7ÕßC6íh·¬R¥zx´°ˆ®¨½\íúRNU'Ì©d­Þ¤Š™4©>Z2íå”q9²’NÕXÔ”}„” J8à­ªF†”£Æ\¸”™•Å"~µ°‹v pVÆ>|Ti…JÖ+Iíh·¬}2`xÄZR#‰pØ;º³7ûäñÇ4Ë·*¡*àI¾AÒ©‹šZ!†GŒôp"ØÒGµû·GúÖ"{mgJÅLªSÕ<¦ÊÀUam¾AÒG;Ú-kY„ŒñGm¢ÏGÿœÎô¢5[5|>:³T J¤66NÕXÔÔª™j˜ŠÚ¹í4ª2as|i€v´[Ö¦0<¦¡ =hTešƒv8pö4§j,j¢I4gï ¾*lŸˆí£í–e!ÕÓAoL&š†S55e!Õ+oY¼Êš¨< wADÀñDÅ'*b|´£Ý²ì"$ч˜î$S£¸¶$désP§j,jÊ,B2ÝýbÓÅqŸ»mDø;=,°tôÑ!à£í–å!ÕñÒt² D‚ТÎWýÊ©‹šò‹Dâ? ‡…®²z'0i¦.áS55Q„$ò £QØG;Ú-Ë4Bâ Lš© Q;9Ý©‹šrb¼løHI´|Æíh·,ÓIô'éGaz–UÆyQË™h)Ÿ—,qíh·¬ùÒò+„€?¢'raõ3àôÆ>øáTEMMH}Ÿ¯N¿éÊT7>ÚÑnYó#$A`Â%æ«ÌC55e! ¢™øhG»e! "8NÕXÔD² Ÿ¬^ÿ»ç¹qî×·&ž™ve?¢Ä…¨³ºwÀ²ä“7Âíh·Œ"d ¸æ/ßzàÊ,¡1"#è׆ËïÊÚÖÐwGÄGA»ÿµýé7–ÿõâü›‹“/ÎCâ_œãÆÜ?]‰ë,/½6ÇU=ùÜìAÊ{KKoœ½ôþ½‘±q¦ðúòíÏ7Fžœà67xXñ™é*õÕ¯|øÅQMÄqW>]˜yJ4dwkó⟘~õõ™Ÿ]yDËožçöì×X?aúSÜv|´£Ý2Š%5Jl}yoêo/ðkÿë*ïè\{ßî^œ½¶¾Ã¿½¿qg¢+Þ¿¹ðx¿þ³3lïÏÆ3ÿca±ûRcžyðá!©ä±“‡A9¦ƒjÑ)ä{ã<…Wéüä°(ûÔé΀rÿîš({ã_.ò”ÍÏÖqäç…O¿zdLØqªÆ¢&ŠÁ?9% .Þ›¹ V߾…!KMèåç~uSdÛ¸½ÂmñÕʯ 4ù ”éÁm!'¬v8.Â+,RøÀÇŸ™îO5Š—w ’>ÚÑnEÈ€ˆéNYýí[žêÂhwá“ÏíÍû¬{ §«5áó\ ¯0ÿ§¦k'Ò}¾ò²f§>ª±¨‰"daø¼NUr¸0¤§ºfÿñ W‘Oov^âKÁÎü³ôpºzP-|_X:Z²ñ¥ãÔKóý -8(á£í–Q„,ŠXé]xûc1Í×N°œ¸^8.l1ËÂOV-7HÄòowksóO÷MÙø S›Îzªeø‘Ñ­¯7¹Òøj'NψãÂ’«ñâŸð©pë)÷;S55Q„,Œ¸†)â Ÿ@‚¹JáŽD¡+k[ç'‡yןû§+–¹Nø.<¢>õÜœ)ÛÔKçÆNž’µU™}ãÚò›ç×~w}îç×ù‚Ž<ØrAò½^_¾#²qÅ^Zù”W˜)sã~£ÜÛt>ÚÑnEÈrð $ÜNxÌq‚1ôÝ?¥©)|WJ™ȃo9@¢zPÓ!ŽvÿîÌ¢yx„;4‚‘''L%ô!%®è0ÕXÔD²Á•¯-9û™šôÑŽvË(Bö3ýú<Ù/øÎrS55Q„$þ:|´£Ý2Ša_Ëñ§S55Q„$#xIWY;ÌÿÀ•âˀf˜-Ù_bk2óM=NÕXÔD2„áÕÝÜ83s†d™=‡uíh·¬ù2Ò3Ë Ñ¤?É&^OZâ¢|3)ú¼hÊU鋚(Bf€éI‘rÝ¥™4sÄÄ/€õû|´£Ý²>‰°ôò?§!PíáÄ©‹š(B„‘jd.ÕX¶¬O"$¾bÏIPc!Y:UcQEH‚0Rnã£í–õC„„$|,w–‰þ¤Äµ4§j,jj„”¦©4kmž÷«#=©ãyDíh·¬"d;â<ëŽ,þ]¤à›öœ¡Àò?¨S55µÚÑnEÈrø–}BcµŠBwªÆ¢¦6DHiF/‹™‚aÅeäþg‹®,}ÄÀ ¯75W'ý”U«è}´£Ý²–EÈB}"#ÒKQw¡ 8UcQS"$3D<ËZ‘–‘yQïê±èEíh·¬eR’åÍ•›L™¸öÛ ƒ6#,¹,ýqªÆ¢¦6DHpÌ~‘­!E§d..×£|´£Ý²vDH<Ñ/zî3pâ¬ó‚O Uv¯²oT°[ý§ŽAÀý='àTEM-‰ÍœÃà˵ttQqèÆŠ­}øhG»e¹GHi°l ,A’°T´)ª~@Þ<]|«-SBÍ™ñ±˜µ2¢QþÕâTEMyGÈæû«'JQK•–*ȉóh‹b.ð¾ =©df¨F-¨kÈ&/O|´£Ý²Ü#¤ ÉkHKÄ<û½¤4Ÿ]j¤)XLѤRL¨kȦ9ãTEMyGH‰†œþh§£þP¥è^þjOCó×&íh·¬²É˜”€Óqh²(Á)*gz8uGŸú$ß„l¬NÕXÔÔ’Y×3vLë@Sº%é£jس•øØ`Iv“ñÑŽvËÚ!›ï!¢: FŠV#ë]`4ÜUD9Š>MZ/>ÚÑnY;"$ËjA´§j,jjC„Ì|ãÁ™Ó'›?Á Œ„Ç,\íh·¬5²™uì`)ú˲oÉȳNÕXÔÔ†™‘«$Ô« ¦>œ.Ý_Áß‘ðÑŽvËrÚU~¾úd½šQ§”¦o¥œvõjSˆ€8UcQSÞ2kíi)$Ÿ§ÚA^#¯v´[–{„Äär×3 <'-)-×]ð·XÚÄ =®Üä[ NÕXÔ”w„d½—sºä¢IíåP‘ÐÕ~ïóßj ê·xݨդ´°$‚ã£í–å!¥ñ2£{Ç€v•¨MQ m~ŸDSÔí ŸH¯; r¹Þ©‹š²DŸ ¾®=XÞj¿òÞŠú•?>ÚÑnYî’肯Gx9gfÎX¾5}åƒS55e!aŽ‘ËdÕr†0¡õrEÙÄÃG;Ú-Ë=B6Ö%êM?VVŠÎ½´—…J¨É4ÖÑZœª±¨)ûÙX$ÂÝ|œ™±ŠLŠ‚rüņï¦àj¨—gñÍéÛBGŒJ¨Õcl|´£Ý²Ü#d3Ñö`, IZªa€I“¸pþJÚ]ʦ­.¤ à‹:A4¹òÞŠöºNÅ+:ÌC55å!óz€ÐF?mN ¼£âTPL–2KåÀ(€³i•_¡D˜íh·,÷©>½Ñ·•èÊ…2 |vQ\”¢ ‰Ä~/ œª±¨)ï)Ñ(·|T¡Í\uðœvâlž»Ô<õÛ¿ÕË÷ÑŽvËr͹ó¡Õ•”¨@údóÏ`¯†6›½2õR]*û­È*8UcQS"$~b.¬Ïú–ÆÉ,î@ |´£Ý²ÖDHðPs¦¬YÓ¨ÀÈzÝÚœy §j,jjC„l¾‡ˆf¢Þù¨~σ¹TcÙ²6EȬÞ¨—s ]Ø,”9_u¹Î‰S55µ!Bæ ¾ï×'Ò*ÇÀá+çðÇÆêÓG;Ú-kG„ŽÉkUYÂVý çìÏÙXîjd¤ÿ¼®8UcQSK"dv3U˜°AZ&ùá™5®ª·úÝΨP“ê %ûhG»e툰z ë­dÄž²ªQõ#©ý—ûØ­Í÷¯S55µ$B¶“VÕ੦HÁVØûy"Çtw^èóæÊMȦæi,>ÚÑnY;"$cxÔN#¥˜&„$¥À.XcZ¥©ùY†à«è½Œï|¹çÁb?ÀÙˆq?ÐÓ}—Ò8UcQSÞ2…ö¢ >ÚÑnY "$Ñb\í¬ §j,jÊ;BDuÄXpðÑŽvË(BÍA¬å/ càTEM! "0>ÚÑnEH¢9Ÿ:Ö…S55Q„$ˆÀ7Z|´£Ý²4²öÇ‚¥Ó]{} uyG:n•@íTEM)"äâgåÛFé9{¢Ò à£í–¥‰ÑW8UcQSŠI}…v´[F’ ‚ãTEM! "0>ÚÑnY_EH¼RŸ8=3÷OW†FÇDºÏe§ÝÿÚ¾85:õÒ¹Ù7®¹ò`ýƒe¶÷`òÅ£LxÖ'+¿Z˜ùÙ%W.=)ë™NÕXÔÔ_:ÐÞ·»ç'‡/¼ýñøÉ)K~Ü縯¬m }wÄ’¿K¯Íñ-¤'Uôû–V#aÁG;Ú-뫉|xˆ÷H©Oóøé7–ÿõâü›‹ W¹&E~XVûÖ¥÷ïŒC6.W‘¡cøÅÅ?1ýêë¼Ço~¶þÖì©Ù¼2õ·X/»[›<7¶7ïCQœûw×®¾ò¬Ø]¤ð /çñyúÕ <°‹Z‰úãCk›ÀYoié³"QÚ΀Ô(ñà|Ș¿|cò¹YæB=:.lÞj89ð4ÁTxF8UcQSEH × ïß8eëË{\3üßÚÿºÊ{°èRjâÆ¹_ß½ùÚú׶Ȱö¿¯Aæûÿß·yçyxo6” ð~ eö ßló\–ÎÕÈ ØïoÜ™“´‡Ö6áúÏ·œ}ãÿ§Î61E› R¥QÚ£Oœîy] /|ê¥yq}»­8UcQSEHÄåaóÎ$f\R¾ãaŠgãK)À‹Ý­MiË?òDÑS7>Yå±…oá[‘Â`rA:pX·"ün¾1òä„i¯êHGŸûùu.Î×—ïˆ\|å,–©¢Q†b2ÆG;Ú-£‰á³A¸#x±v’î=Š+4W_yöÒû÷àŠNQàr€£ßèÈ(·gÿñŠB¹´®¬m‰¼žj´T›Àº­à‚éBê¾å…ƒ0/A{tq£5>â\Zùôüä0·ù S)² 8UcQSEHÓLÒy|Ûb°îi•%uîB¶”ˆ·$RÊäs³âÚN×6u/2áù°v_©QÒáÔ «)Ú£«s>(8 Ïíh·Œ"$¸ÆÓ2½9qªÆ¢¦>ŠDb¤°Ü?øhG»e! "8NÕXÔD’ ã£í–Q„$ˆà8UcQÓCƒo‚T®ÍÄ¥šËÓtM vM&no‡jj,™2±e]ž2<éÔÒ^ÖÒ¹k&‚ÌŠ ½³çê¿_yuТ „ü>™ó…žÎU–"FÛÕL%íÇâà2i©ËÎEógJ‚,ÚŃPúêåþg‹®,=-*Ô453–hcí-ÔR'Ww*xªä!HŸ.–Ž«tw‡ «É Ã€]ŠªS™»;'ÎjCŠmºîš‡ ;˜]œªjè4µþ¢_Ö.øS'2I§6õµ= ùRÄÉȲR  FA·¶Â¬RgÓõ ízr¿¾[ ’ƒ’ª±KâÃÅ 'A „×¹P™ƒIñƒuÓWj?65ÓÒüº4É’Œ›jlxò$PQ™WüâèãY½€Ë˃Z1…+Jõ³W/ e²nï·gfIDˆñé"‘fwõ®*Cás± _Ùʺ±m$&™Ìüñ›3Cê’¥OÃUÔs¡Bĺc¤ml(Ú&ÈÒ1"ýÅíuW ¡nÀŒe0I)ÐöËB5‰Ç jÒ3[!Äì cer—’™í…ÇýÏIâ É\]ýåZ•:&í% AÐé1‘ç–8\¤ëˆÍdɤ 0 OBÊc•&WAJWS}´”@rNðá>äàüW¸YU•Æ?a—« !'¨Nø#l¥ôÚUJ¤$ý¤½Y RRV¦*6±ƒoM¢MLâ ó»¡GÔK–‚Ôºcæy¬!bð’2âñÀZZJø“¥ ¥°VWˆ«ÎÁ’²È“®å€‡uEÊñHÐü9B–‚¨×u Å p¢OþDÄ|{ ìïW‚’l<:`w§fG{« µ= '‚­öüµ°¯»+P‘&K±‡˜ã‘ ™-¹ ²•ìÞ(­Ìd?a ŒG%šl'›QédeŠÎY–e3X¸;UVœ™ŽJdd׫*"GLZü£}ä];;Z%Héš «ûN#Š÷ µ•>m• ™â¤|}¦^Cֿɷ„–¶ RBº] éYôcmÍñ­†h‚È…¶ R꯸ïfžuKMPeIšÌš¶ ÒÞ³“¢Š¶ û¿ßMxÒ6AÚÉ4z¨P’Ÿ)ÈŽ>ä~ï³röÌM@«.’b»i• U™áu—6Cc±¬Õµ‹°Ó*ADî ¢Ad)Èê÷é ž,ÐÞ:–[æÄ,Ér­t‘z^£*™¬Ã~X3ç'HÓ=íÊz'å”RšæfÓ%¢Åä'H`¿÷Ý9j‘‚ dV'BªÈˆ¶9ÌТÖ`¿÷ӲƲ¬Yˆ„Yܵ³ÈLw´yÚv leK­d“c'öúkã©&P)6ÆÓd3;œ¼"5ÙŽŸÉ¦±ÉG¬#’.ïÎTKÆFLÑ>ªŽñ~Â^úrvRË)½úm¨ƒÖNV‚D½œ…öÁA/ïP)6JšlƒÅGST?jOEØóã vQ‚Ì)€V]™‘tXµ!™^¨¡Ò•‹Ð¥ I™Ì¯ï‚»ÛúuhÁ§9D›È[@¡U_ÃEØ ÍÕ  %‚l2¡®‹ÚË u”$¾0Þ HÑéëX‘äÂx› A&¡T¿TïdÀS;â#.p¿©?ƒ{/¤õ Qô¶¤`ÝÄ[×~Í¢\ÛûdRŠÞ¦kÂíþP¨—Ä d øß¦Ó>cÄ•ú dsQ—‹øa:Ö+ÎÜUJHFºl£Ú$–A‚l Ù)³–«¯¡~9$Q‰5Ù|52$Q/ IðàÄæ³xd’Y}ýøŽJ©'<É"0$ÈÚH]´É]3ø/]õÃ:HµA+(\C|£Òy–ðYü°Î ²N’­ ²P£D¡ g*?dˆ¿‚jMm=$ÈF|ÅHŠyB‚lÒã×%”™ÑÛI-$È&"-ŸœÏaC~aît¹óòäàà±½½ƒìØ{@6Ùd“M6Ù}nû÷5>×9œ´îu·ƒ]ƒl²É&›l²É>º°ã“›l²É&›l²ûÄîBëH²É&›l²ÉÖÙ´Ž$›l²É&›lÝ…Ö‘d“M6Ùd“­³iIv¾öÜíû«_îò„­¿™|HŸgøufåÆéñéLJ*æßyyÒYçá?ÌÙ›¾ú§Ý¹Œ]<=>÷ýc¦2ç>8<¯N þ¥pÛQS[vþ–îo¾õ‡Mí¾—þÛØ…ñ1œ_Ø¢½œ;/MŽ?¬¯›Ïy#›ìÚì.´Ž$;KûüÝ{<<ÜyqòÔ{ë ÷®œÓçïbúuÓovžýýy7O?U%¿æ¸Z[p˜.¢#/í‹—'‡; š§Ï—>ÛÜüæÁèÚ2Ïßîž—&O½»¾ðɽ+“cRžN+zË<öÎcštn›ÚrýóÍ‹ÿgóõŽ™ÎÉÂÆ&/V)»”Wï΋#½‘^w,²ÉnœMëH²s´oþçîÒ·ÅÈË·§ÞÛ˜:6bLŸ_ +ç©GøgÖYŠÅÉßcÝôÕ?íÌ}tÿÆÆ§:ŠRʾsOŽˆh$¥ßürwé?»gàáÃ3ðȱù'5ë9KœçŠŸg y%g¾7ªÖAØ #§¾;t棱ï ò¶HÇ:7>ÂÓyõn=7qjd°Ìy#›ìºì.´Ž$;3ûÎ×;gow¢Ë8.{Œo¹ÍãÍØw†äQxÑ›¾ó-»ÿåÎólðOŸ¾4<¿ÞtmTøvæÑQSt´Øwv»gàt÷ vÏÀéñ¹Û÷dž‡N=2è_ç¹âGáŸz"Ÿ®Ìéï ³w|%¹ oãâiÆOÝ­çÆ§øjÒp,²Én¢MëH²ó²·¾Ý{ö÷èÂGdÖ …yôg‡£0ìË4wææ¿?2õØð½—&Æ–£TñüœõÑŽ®öªöÖ7{Ͼ×=·•3ðûîxÄxÿòSùÿü~ûžy|hô¹ î ?BWÊï,Ÿl²Û]hIvNöñw6¦¹qzL½76w{ó©w7ü?S=ûv±¼mQ>V©üj=õ¶ k‹µïêŸv¦UÖ¾Vûø»‡g@©ÛÁxyʳζTòëÝ™G•™²×¾ÙáÛ›?šóºöÔãÃ|ùü÷žBRŽE6ÙM´iIvFöùÛ÷¸yít÷®›’‡§?ñÎ6Ï#?½"ð(?n~ ›>óè赿Ú;óÑÆ…Ž]<1fz.÷ÆçÛSŒ¤Ÿ¿Û=“úûŽ<ý‰/·yûLÚ}µmák¾ÎUÜî¿þôÞÅ#Ú2/­o_ýÍk56ý½áž{–ÀaþS#C¿8ÁÁ»ÐÁœçl²ë²»Ð:’ì´tÛÔÆKÿmŒ¯ÝõOôPú©¡añ„ÑÑ·žç™l²k±iIv.ö™Ç‡æ^ž²?áòÔ#ƒzóÀUSgùó›þ†ä‡_\H圹0>fùû¿sLÑèÐîœsSœm?716?>âl£Ï±Æ‡|Tè<“Mv vZG’M6Ùd“M¶Î¦u$Ùd“M6Ùdkì.´Ž$›l²É&›lMëH²É&›l²ÉÖØ]hI6Ùd“M6Ù:»Æuä±7>ïNA€ãÍ‘ì.õ­#ÛßßgÑ cæÅ™½½½ÁÁA¼]yoE|K¢ ‰Žv|bJ »Æu$A´ˆ‹ÀôsÓÚœ<}õƒUÖ» ÅK‚8À'¦·»Ô¹Ž$ˆv#95dJPP$=>1%†MëH‚ ¢éøÄ”àvZGAÍÆ'¦Ä°iIA4Ÿ˜ÜîBëH‚ ¢ÙøÄ”6­# ‚ ˆ¦ãS‚Û]hIq³N?7-~iÊÆ¿}~úyñ zÀ• zð‰)1lZGDl´¿áQ|¤ Hn|bJp»KýëHçÆ¢}À‹u¤‚P9š;úÄ”vCÖ‘‹Ÿíó”cƒìÁÞÁ·d“M6Ùd÷³}ö$š8îyÄ”àv—úבœcŒ=èþ#›l²É&›lŸ˜ÃnÈ:Òÿ¬‘M6Ùd“Ýv>1%¸Ý…Ö‘d“M6Ùd7ÎîÁ'¦Ä°iI6Ùd“Mví|bJp» ­#Én½|ùüڻׯ¼ÿÃé»[›_xbê¥s³o\Ãé=è辇j5466~bÊ”ŸuÏŒtNöþkwëËOùæß^øõÇO=3UúüM¶ÝîÁ'¦·»Ð:’ìVÙÏÿí…ѱ‰…ÙSb´öþ£ùž&¿À§|æ‘ì­­M^‡éW_ŸûÙ¥¿ZàAúÒʽ±'ǵùê±x¤ç…ŒM<5òØοýùÆêoß:÷ë[×ÿîyu_‹Í×Óouk5ó³K+¿Z¸Ú­ÕÈ“ãj~@NÿÎÐØÉ);ùò÷êß= ëlS~²É.m÷àSbØ´Ž$»}öøÉ©k~qþÇOðèÈÓ¯¼oht\›p– xÖ¯´&NÏÌüô·ùvëO÷fŽ_[ß|hȧ‚‘“S/[˜yJºšÊSxú©g¦¯ëöµØgku Õjq}‡=4T¨½|1Ê ^Ïüd“]ÂîÁ'¦·»Ð:’ìÚì;Ã<lÜ^áÛÁïŽZò3ë]I|?RàS‡å7ÏóíÜϯC:·yeVþmaöµkÚ:0©þ‡öôß\X{÷úúGËS?™ù×ß_â_M¿º`Ú×d/uk5ß­•HçöùÛ+ËÝZIù–3sá×·ÆONKù™«d“ío÷àSbØ´Ž$»}6<¡Ã#ÜʯÎOÛ¯(†½¹öÑ2jܾ85Êzáécߟî/Úc Ž»|ãúksc?˜ßݼÏÇç~}khxÄ´¯Ö^?¬ÕyC­Ô§Š˜î~dõóC6Ùžv>1%¸Ý…Ö‘d·ÊOèÌ¿¹8ùÜü±îÅÑ'/ÌýíµOÍ|ÊgyÄ1<ªMœžU#ÊÆíeíFÆ=9i¯>ÖÄs³ìµ¹›¿\8wùæ_.ˆ«¬poUÝWµÅÓCó—oLžžU£Ýúíå%ƒÇÃSNÇPÉ>ç¼D~²ÉvÚ=øÄ”6­#Én“}kùêò//JOèL=7;òÈØ[¯œšýû+Ï¿rç|ÊXòˆ'bx ëDµ=Mž>u÷σXŽõúÛwxýÅ•Ï+nª¿xzˆ×ŠŸíSK<}ónçá ^+ü‹ýœ:?d“]ÈîÁ'¦·»Ð:’ì–ØKov~yíÃ/‡Ç¤å ΞÐߢ»ñÙþÙî/"Ô{{ØæßòJ^|á ü;Kz,\äøŽ|-(®²ú×ÿâa­,y V¥ŸS-šŸl²v>1%†MëH²[cÏ¿qõ¢w©yGÇà¾#¤Ýoó(¿Õöz¯zÅòº¯éS޽RnÌv[W¢þ>yÀßÞ9L÷lKéüd“í´{ð‰)Áí.´Ž$›l²É&»qv>1%†MëH²É&›l²h÷àS‚Û]hI6Ùd“Mvãì|bJ ›Ö‘d“M6Ùd7ÐîÁ'¦·»Ð:’l²É&›ìÆÙ=øÄ”vÖ‘ûûûÌB™(wûìÉþjo»í¹“øÍvÎüd§·ÉGil~ž}†ýyœÇ'¦·»4béóì·ÉfyÈ&›l²³³ ãSbØMXGÂçì“Mvm摇ìzm摇ìêv|bJp» ­#›k3‡¦È*¾ÀåhÏ?œs5RšüåÓ®v|bJp» ­#›k3è]/âþsÖ\±Cö‚õ¨šÖ‘¸ž ÙðQÊ“ÌfyJÛ¼ÃUS u¦¢žsÈc)°¯#ñ•[»¿|ÚUÂ.ƒOL nw¡udsmæÊ#Pç€Ò½%‘_RÏS¸7ã<ø¾ˆzç?Í¡}:C“ÿ°Khç³bDÐî‹K†¶¨Q_}ÒFJ|G*çÿoïlBôHÒ;‚9TÔƒtØZ¶ÁÐA$èC—é….Ñ]Í løÐU³°£ñ‚GíOkû0­±¡­ÃXê…uU¼”l¤†Vƒ‡ÑZÉ #Ú¸ c(ÁZ0‡ªƒ¡62#3Þ'ãóÉ|#?ßÿFü;ëÉȈȌxò‰ŒˆWŸk|«›%>ì]°õw&]Ÿt¶Ža¯0¾Wíèo®8ÃL?hoxeûZ‡–çæ”q,šy/„¯é¨úôKÛ—Î[øþrÊÕ@׆ãSÚЈ#G­î9‡ÕVGgIØ^gæíŽŠ»c—¢6ô¸#oúºvŒbÛÛe¤ž[Ì2°Ë¢Êëœ]bÛÛ£ˆ³Ù{cÄÒw¿„çxï:ð¶=wbZ0lÚÖ¦ýu8>%¹ÎA9\-b6 GY{Eå«>åÐW32œ6‚hšGÞ¬<Ø×UøF¢l¯&<³ 쑨|Fm™:Ïv¹vÊñƽr>=_õ|v0°=íˆÝÙçB'×úÍi?R]ŽOiC#޵žã;Õ9ŠÚ=àÒ¬‹_3OóN9ËCX^Í—ªµÆ¾®Â)Ú¶f¢+³ Œ‘(úÆ pæÇHßYF…s¤+z¿„ç8ôp´`Ø@ϯ›Àñ)ÉuÎBÄ‘4„Ñû¾ˆÙ(|_ï± Á!‰œŒ?Ù±±‰'6N çMyAÎÈí¡ÓÞ]!fßmŒzPžž_öÃ`]‰ž+}æ÷Ng¬ÏÑöweŸ½=>1šÌkõ8¿:<;fž2v¯ûÒø™™_׆ãSÚÐÓŽ#+½Lù¾ïœÁOsPÚKº´wÆ=Ô†“©ë²fs”™ÔöFšv:³—jíh¡ßá<øb§½ÆÐÑ|úòÓLûÒqwÆ‚ÌúÑžÆ9—"Ðùê‡s]ªë>áçÁ®ÏÚp|JzÂqä™Ùá… # ýND[ï¡ý^\¾gÉÄ-d‡Ìï ×bæ?­6FBŒüÌ?Ã"|ÝÀû;­gËW´‘·h~8ö|-6ßè°ßÙ©×ôÍœZ.…¨^‹Úø´}.ÕzxÖ›[1-;=÷Њuè­æÙŽ­qšÀ¹Æñº÷È—Ž}܈õõ âÔó!é‹tŸ£GÝ+_‘ªñ÷¡§~Ëûe\W³LÎò}2Î5ò~ìz® ǧ$×9“#+wËx;*{òTé§P”ßIS=‘>O£°ó`§ͳñ$EmÂZw=¶wŸ/a¦YK‹˜FkÛCÐû¼nc=)âÙ'Ôáñ†=òmr|SÏÿX¼ =WAÓ mŸK5}¶5ÚKÙo¨ôÜåê¤ ù×½êYçºGRÐéÓö¨ŸNy9÷§}éØÇiþEY^Q¾ÙØö Z?võªû¾S×­SÖ^Ê®ç¹ö}Q64Ï´-(|y?v}Ö†ãSÚÐŽ#©6ÞÅtjÆ;miF«3Ò1¼¬ß7®Ûx”5‰®¼oVmœO Ö Ì> ×20Þ[}1=­jOïË6ù–Í/Õ-ÖnÌ¢,o ®ÓC‹˜Mô91žIãmOßS;û 6>mŸk¤c¿ÍÐsé3æ|óðåßYÞh{ä—·{äWB[¯§Î¡¡¡9º6ŸÒ†^8²Kí{§ë^+8ö"x®Â{ —½Î™Á¤m4†½ñެ\¦kÖÍ š.Õœ:èTé@·§Ãz~ÝŽOI®s+Ž— ›€¶¿=h›ÊwÊz®°Òïµ,бÐíêÌu㻋aã…ÖвÐo$êÜh~öìA´>—†­F¨fÖ9tXo'zl€s.t::«®%]ŽOiC#Žœ¶žy«o*¼Kilœ«0Ò1±¿c™ßz˻쳱óì;nxÊh~Džg;?ËÄ[ëãÆlE8? tªtÂÚ9RÍé}±µHçÉŒ·“!Œµpî‘ï[{4ÍèWƒh~ÚÐÆ`ß\ŠVu8>%¹ÎA9\-6Lí›m¤l¢_ãÕñhïfíÌDð¥ï)‹‘O_~4áÙú8½®O3ë¹w­ÐßPul>×é½”6fKzVO’±=£ÊŽãEUÞÂø–?7ú™Ñ™ Ÿ[ùïù.î«7á©[úWç¹´®œ3Ît:úŽsÆ™ìç$ü PèØ­ŸÚp|Jq$ô$µ‚oß ›ùõN¹Š)?Ìš5˜ÿ|X=nÌ–Ò#ÞÊ^QÌœª®Ø¡q¿1@ó ˆw7´¤×2Æ?8oNïn›Wèz;$«zvªs­m/kŸ«ë“¾ñl’UC›å] ³êt>ÍkU=¨¢øŠ˧¾v=4ãS’ëÄ‘ÃÕ‚amkN/6UMço:ç[çŠXšJÓÞP”õ¼íÙî°ü« éh mŸ«´vKѼµªé›Á¶ó›ºë\…3M §ìúíÁˆ é÷~ç8sˆØÎÕöÛ’Ó£kmÜ£êW eé{K« ǧ´¡GBCw£ÃfNM{óCvL¹çß9ýPÌâ µQØz™äØSí³ç×Ccm_Ëx3°cJg:;®YlÎ{¤ñ•]¡cwQ¹užkÇsÔ“iì{D5­}œj#Ÿ´~Œë"Žlª}ʧ¤ÃZkííD•°$ñŠpÑ×ÜËå`©}^0|/„åYvÙU<NG #Ï çÝœ]NûQr<ðüø<ºfÙ³³‡Â®ŸÚp|Jq$4t7Z0læ×;®èqß¹ŽY]Ö /ŸJÇ9â§Ï5ò`çg§úK;&v2r.\Ç}1œ/.ß ZŸÊ^˜UHÇ™‡ðuzöÝ#ûþVεì£×5ê¹6Ÿ’\ç Ž® hè hgœ$:ôŽ=jÁ°™ž® ǧ´¡GBCw£Ãfaµ/V㜛P †MrG§§›Àñ)ÉuâÈájÁ°†††® ǧ´¡GBCw£Ãº_-6Ðóë&p|Jrƒ8r¸Z0l ¡¡¡G§kÃñ)mhÄ‘}éÀ¬qèIjÁ°îW †M]½YÝy.jß±öÍ–ÒøÖ€Î£›Àñ)ÉuÎÈâÈè k¯¯qʱcÁ·þ ‡ä÷H¶ôäi¦EæpiiéèèÈþ÷Þç÷«f;…ãSÚÐãŠ#g[Ž—'N¬¿¹.Ÿ?ùïÝOïN£PÙØ•l3×9ô1”œs|Jr3²8F}–z(Îf¤p|Jz\qäMeíõ5ý¿R£ñ@ŽOI®sG@bð6œŽOiC#Žì=öBb€‰–žŽOI®sGv„ñ^IÁ;&„àø”64âHHÞ†[ãS’ëÄ‘]à{¡`€ €–ÞŸÒ†F `èp|Jrƒ8²uc/”Zã0Ò2ô‹¦ÐÐÐ)ôæYn“í´tPߑ쿎5âȶ‰Ž½PêŽÃD÷E„††žSƒAÀñ)ÉuâÈv±žòšúçÙŽŽÓ¡¡¡k>jS·{Ÿß£–nØ7nø‹ǧ´¡GŽšh ‡††žS3q¾ GmŽOI®sGæ;&§…CCC7Ö`(p|Jqä`á¸Éh ‡††žSGá4U0/Ÿ’\ç Ž(œù;œ¾€z3ÿÅ»lÞ/Ï~°Zýîàœ¿Ð»]ÖÓšê(ö€*hŽOiC#Ž2Ñ9âѾéÿ}Ô9{ÞZ:ðKÑmx2½*†i߆Nâ™Ã&ªeT‹cÚ7Ö_×CøW|ç¬Ãùu5ÿΘŒZãS’ëÄ‘ƒ&M†[¸òL{OŽíµ_²;óÙ‹j¯¤c2#¦1¼oÀãjoaäAkgŠdKªËB¯kôž†á}ižÕ Ég ,¶w×熽 Æ®g=SýW%¢ù ”WÚÐ|6È?–UØÏ›LPþgÔ9ÕôÙP3Öþ:Ë"b÷Î(‹°î;G`®†iàø”64âÈáˆ&-<܃û¼‹ö ô\‘§–õ>G^{ù_àZª—,óÆŽ†7Ug©^O±yv¶s‚ÑóåU(=ëåË^»H'V§w×çîÄâ3šÝÛy.ó/ªÞ‹æS»±h>UhïR)/ÉÉüypÞ_£¼î·Š:1½‚W¨òÒ:?‡Æ}—Æô¾×ÍA­uÏ Ÿ’\ç Ž¾h2ÐÂ96JÛÞT{µ½Ò?ÑžE÷¤FoˆŒØBŸHíuœg¼û=#IG§©{½€WpÆX:pYœÞÙãkŒÞV׳ʿ¶ÑÕöêê‚]ç4·FysæAÄž%Q¦iÄ‘3OV-‹°ò¨7Aî¯‘Ž³Nt|¹mý•Ûù¡ ‚ìŽOiC#Ž Îh2ÐÂuéì6ÉqM Wä\ÅŽk'¼h£óf¿×+œ#Ãúi±Ó¤0óo÷€²8½;31®«áçSÛ+Âu>¯ÞwÍœy¨[^}œ¾Í,ÓøÛóÌØéˆS'ºV¡eñ=cd_p|Jrƒ8²gd{»¼vyiiI¶:ý¯ó-ÕŽ&Ã-<û~“>ÙIQ{;ZR¨o/<ß{œ–3{+.´Íti”шœŒÞÓ¶¡ì”£”²f<érÑëÚ=¸q\ŸkÛrÍçauÔQßw7Ž¡kåAÐÑK×ó&ˆÍœñ·°ìÃuø«Ê?ó;«/‚ÄìÖÖáø”64âÈqaD“ÑÇým½¿Óou†~OgÚ7ν–mïëa+qž•ŽÃÆŠ'8ewž»üi×›mŸ*ŸÂò@ôZ>Í̃r3:ÎÙ^‘üûŸ N½ÍÊnÙsê$PŸ²P­ñE²Uï¸ú_RÁñ)ÉuâÈ‘¡¢I¥9-š©}qóNõ[u4±hNY4øÙ3ŸÒ†F9:ÔèÊMF[844_‡ãf~:Óß ‡Ç§$×9ˆ#ÇĽÏïñ¿GBCCÏ©ê­qdŸp|Jq䈰E+Ú“kߌ Á¹š’vŽLÚ³–Œ•4»4ìðæÐŽH®\ å&éo]Náø”ä:qdÏ8?ÒnF©à´ð´ZáœÙžç9I-¬Iz^®®æ|N >2 ­àÛ÷¨)¾hÒøÍH |j28>¥ 8rø~‡9Ú“k…}ÜX~èÚoEÏáŒGulá;דѸ$ÛVè²ð=½Æ¨‡Ãê@ô½Ëšð﨨O~¹œ6ƽàÔ³¯Nø×¢õÌÉšâ‹&Í BRÂñ)Éuâȡ㌠œžV+¢kàt¨î85+½¿kÖ~äÜêÎt] Õú\j¯ × í^ƨEø8Õö®xE>ó\©‚Ó¸SŸ®e(ײë\Q½§´ÞÂõ®gu~ÔŠ Z–@Dë¿mƒo“ýÀñ)mhÄ‘ÇA*¢-<¹VØ= ¨Æ^ŠÀjtª}½’Óëì}dŒ˜Oጷ„çº:5;n‹Ö‰Æ¶ í«[gúT;í©vÚ×½z×"°sÕÑ:‚¶Á·Éàø”ä:qäp D N O«…븊 ¨§ÔQ‘íM—«žI§£ãÚ;«ô ïE÷¢1Ÿ£1“†æìïÉ“ìÞ“·¿GR;>;,÷uÖ­>7\'_ÞŒXÍ·Ï ×s¸Nô¨;kEyêÖIøZhˆ&»†ãSÚЈ#K8‚TD[xr­pÚžRõªö·.eOGG‹?U½=W÷¶ôZ™Ã°b>šsôRükNGç£*Œø¬2ÒK*^‹z)Q­ê­µ§NFÞè¹ÛdWB]ÞM²_¼ïžÒQYu|æ5=ω]FAî¯ö÷¢,»¯Njůíiˆ&;…ãS’ëÄ‘%ê ¯…§ÕÑxbÃQ{Ïw&Û£8®å™ÛˆùŒ8Ò—7}Ü9Ê*b½³'Õʧy®uÜÐú\gØår¾UÐ|Òt÷Ô饄UW¬ú±êÄ—g}Üw¿ºÔaôn u8>¥ 8r˜p¤`´phŸ¦ñ_ÈÅÒ™ž6~ÅÚçá¢éLF‡Yp7É,;³C Áñ)ÉuâÈÃiáÐNí‹59çN[â¿Ô8€Ñb”]¹LzéDãp|Jq䨉¶phhè95Ÿ’\ç Žl½²ï·!ßäLÕÑpZ844tc͇06ü–X>¥ 8²mjÍ¯å £…CCCÏ©™è–îûýHú®]Ö5:¨û׿qËyoàÂñ)ÉuâÈÖaÎoÐl8-º±Qœß µw¬Û§yáø”64âÈ‘’ìÉøÙ¨ÓÐÚ{,:d¤´R:ŽOI®sGvAtĵî(+ ΀j‚ÎãSÚЈ#;#ðŽ9¥÷J™E{îníǧ$×9ˆ# xnŽOiC#Žìç;æÄÞ+ =Ÿ’\ç Ž€”,ìÛp‹ów8>¥ 8²cŒqŒ½0I¤¥3?7&(;ǧ$×9ˆ#»F¿c.Â{%`òtÑq|JqäœHogìŽÍÒ%»ÿÀ³‡††›V¿”©Þ†¥¾ýO›Í³Ž•…ÀËQþo^{ÝéÄ‘ `îÙ¡µú5Ý…ýõ%hèÑÛÔ€ãSÚЈ#“Ài-†æüJ-44ô¨uàmÔƒãS’ëÄ‘ à´hhèÔ·á ÐÝp1ǧ´¡G&ÙZ ¡¡¡•õàø”ä:qd˜­ZkPŽOiC#ŽL¿U,¬~ü«{·~pYxØúÓ½‹olpÒ©«·/dë·v3í›éÍ Å*±ëwúÆ9Ûæù“×6.)›½'Çœ4¡§­'Fx¡d‚!YŽOI®sG&€Ù*S+ïxîÕõ÷N~ý”s–üƒ¿Ý}±ÿìäé3Ì4ùZôo¬%tãÚúyûûÓW¿}!äê·¯Üÿ»["–ôâèi`ïcÞ ŸÒ†F™~«X4ý,w[î]|uCyD§ýÅom^³<~ûÃï+bpãïNŸ>¥Ï•žuï/®=þå]j#½ÑÖû7µäù—¯mœ7’’n{óOn‰Ó§Dž‡}—/.4´âÒ[·ÿâ?Û½ø-jóø—wäÿ®}÷š*‘q¤:o*N½~û‘¯´NnÿÈàÍûK_?¥¯ë,ï•÷n½»)ÈL“Ìlݪ–ÛN='HYT¼~óþW·ÞÝR·£½±ièiБƒ<Ÿ’\ç ŽL³U, þè{Ùøêêë>ïèÓïÞÜxÿ¦ ±æÑo^|õ¥½?»rå§w”͹–ÑÛ;?½cĦ:ÅÁóƒl„“ØìùìÚú7eo®F>³¤¾wùúí/N½|QÛ<}ø@º‡+?ùìÒkkÑúÍóý~z°ÿXü‡xö_¨èDþ«mÔÎÕ«Ñ<œ{eÍ8~êgÔŸÔñ[¹/ן )Ù@1ñ@Îki.½²öÅ«ë2´ÕÞHzY×Êã‡å3o§sð›ç®’éËRçêRǾzþìߟW^-;®jøÇo;ªE¾ý£Ê" Cï„ï´ÖÓ@í¬ݵ5A Éñ)ÉuâÈ0[Åê­÷vßÝ|ö«{4戞»ÿðþßþýs¯®_zcëÒkëGGbõí¬7WzÚ^Ãé‰lai#Ö¬)¤~ë»×d˜õàç·/¾±!ÿù(«a£Ï}ôðþGeIÏ¿¶.¯uùí«‡eIé‡g ~/KpõÛ[ç_]—ù—‘¢ÚùLs©§)òqWG¸âõ@šÐ¶ž üŽOiC#ŽL¿U,š^}}CüöPÆkß}oýíwÄ×N:íïÿlwåÂ¥S§Ï¨ãÒAÊãzÔn)÷ROŸ<Õs¯~ü éîßþ(󠱞(|\}‡»ÿéîÅ×·8å2´FêS/ŸÛøã»?Ü\úšÿªQÖerQzîGyI³ÑcâuöË’:Ó7ò`—"òxŽz2]u:Í«&oÊgóQæAIšÏ‰årþMQáÁÏvW¿³­š>³ÞYƒzp|Jrƒ82ÌV±˜úâ·¶dûÙ'm_|Ix¸ò“ÏVVÎè¸ä½O~!ݤ}×o?RSCöO¾´"Ó?saõúÏž^ûÖ7oÿÅ;ÔRšeß2«=‘7züÜëW¾vòÖ.﾿-ª3€|åU(½º¾uûÏß‘NW²Ú6êÜ«ŸüBºÉmOIö—^Zñë<®Þ¶/T¼[$øâ`ÿôJ–à™WÖÔ ™Oj)ã~šæÅ×6Nž<)½éîŽjÙx÷¦¾®&ú<@k=R¢#«š”!&ǧ´¡G&ß*V¯n\U£ˆêîFí¤X¹°š}ë²lèle¿²rÆ=X^W§cçM¬êãç^Y3fÄ8óæÔ³<¨ƒ¿sÒy]{ßÎóyIíüoå%ÕiéÒ”ï Îc=3HŸ+ã]gywóy­:}éM÷‚_ÏÐN=^¨çóÍkåûQ.Ÿ’\ç ŽL³U@CDñ«{G/^¬üÞ™¥ß9­W˜<}òàÞÿ½!ò8^ðÒžGƒp|Jqdø­zúÌïßÿç§OÜþoûz1åÆÝxë¯Í¾e2Òn¬§A`^kÊVÁó)ÉuâÈ0[4ô@ôÒïž:óõSç_YU#Ø¢þ<^èùõ4Hì }p|Jqdø­zYL ;ŽLï89>%¹ÎA™f«èLoŸ=Ñö.ÞÐÐÐsê  ½£1‘'ýuŸÒ†F9?Æ<û!°þæús˜$†/T)}Æ áø”ä:qä\¤|QJ„óÍtŒº k¯¯---Ñï}~Oàî€I çìp„Ìǧ´¡GN ùtÊ~Yä½³ñà‚¶¡~Q{GÛLFùòø[ëoÁ_‚±£ßÈíÀÑ82ïCÎñ)ÉuâHÒ`¼JK_è³”®qÞ.´ƒzË´£=€G¡ÃªíÖ Ç§´¡GN Õ/ëØEj„’€ö Ãª>ŽOI®sG6ŸÒ†F9ô—H ¾JÚ†~zL?£UÁñ)ÉuâH éèœãSÚЈ#§ñ%’‚¯’€–ðu,ø™B3á‘Õ”S^9>¥ 8r8¿DRðU²ê~†I¼z çƒ1±Ûmqd À„°÷‰’²ÃÀ¾õӾ˺t´àÇ9ž3šÂñ)mhÄ‘c'ð%’‚¯’]bûKYÿØRgY„Û­CFCˆ´Åçø”ä:q$-’²›ƒÇ¸Ý“QwŽ©·±§9ǧ´¡GŽšè—H ¾JÐ1>—9íf˜ÞA žOI®sGãlzÍ)óÐwÄÒ²8:,žhèRŸ8»=™G]3ù8òØúݶ^ÄÕÓrÔ¹F9¨ƒ4´ï™c~‰¤ ã«$ÿ7h¡JOÉ;Å´ËÉËÎñ)ÉuâHÐ1ÌzõÔÀ+i»8G¿ìà2ŸÒ†F9(øã«Êòî§wõ‘ð/1ÑÿMüìÖ†ßcB/”žvË2"KŸÙh%|Ôp|Jrƒ8rø,æqg;¤´õà6ÙcB/ ž CjzmÁ¹oǧ´¡G„ÀGGÑê“×üz¡ô¿­N«!g0_âÀñ)ÉuâÈApLVàŠnÞËzƒÙcFô‰³ÛvÒ’ã'{†Íñ“fšákÍŸNc}âBž‡;ÎãÂʛϾ–>qvSTë“nS=z| 6:Ò"p|Jqäp°'µN~ÖY^]EPíÙ#ŽN•Nc­qÏNT<¢Ï¾–VðíçÔSfÒ-º8>%¹ÎAÙWËIÏ gö˜Q­¨·â§ŠÍ‰ ›ÂÂŽ;Mƒ‡;¥œÙ„cJ:ñ<ølJõ S–—ÿUÓ1íÕÛƒ®+_\nÔ­AêØzôpÆZà=¸8>¥ 8²Gµ‘ð{̰Έõì3=‹/IÜ)Êÿ¨÷rïr3óŽG"œ·Â†¬Ž/sR$•_Å‘‡@>iYȵ ¤}5õö@Ê¥=«³ì¥SÌ®U);)—¿2ïSON£æØŽOI®sGß«å_9™=fTg½¶Ò…›©ÆUmÇ.qy-¦Wznªkç!pœD“;³Ã3{ ÿºÂб7’Pðôà|zœV‹îŽOiC#ŽÃï1ÃZ8»F³ã¶‡3âBçˆei_¤ãÕ¬äÁˆÉªC”Ôès5oä5Ê[9®ËhÛ[”ä³L‡–‹d;»–ŠSE0֌ݯ¨ž‹ô²Û-Ÿ’\ç ŽÇ®)&Ú®˜=fTgx¿±UÞÊsÕqíBìQS5ÂIÓ,mféP§§ÔÞ¨L¡ð.¥ÒØñI¿’ÂŽ5Zë>W{qû¸(G€gÅ×Þ.ϳõÕ-Ó™•«:ê››1FžëèÑÓÝZˆEƒãSÚЈ#‡ƒ±êCL¶]ñ{Ìv޲Î4µ©žK<¨;ýÊ·@O:… 'o¥w±u ?F|yæœÎÍ¿#:ËhØðïCOßXëD›sWp|Jrƒ8t ³Ç„^@=zìUÎZûö ,8>¥ 8r8Ø#«kå÷˜Ð ¥ðÃñ)ÉuâÈAàt‡Î”ã‡ÙcŽ[WgÄÄí»×IöâI­'Ȥ÷ÌêŽOiC#Ž‹4g‡ßc¶«ý+Ryß¾c­aÚ·­§m­“k¹½Âñ)ÉuâÈ¡°0sv˜=f»:°c€1ŸÓØ_ÆžËj΃-ŽT摺òà:7”gÛ^—%p-O>5ê­e €ŽOiC#Žõ‹~Ù–®ìecÙÐãÅ:ÅÒƒF÷Ä©î0 Šõˆù:B2¶é87º;Aq]ï7ñkGÊ|ŠÜzkYà‡ãS’ëÄ‘½ÁÙC1-ÇÉì1[ÕŠ¸}um"Åý=¬‚x)c£ŽMå†Ý ô:K‹Èµ´]Á[sÙ—Àǧ´¡GöÈ´<~Ù–ï*§Ï¼ zbÍ}S5á_§öΑ^gží‘a§ô\ë°ª}6ñzkYOæ‹ï¢¶ý9àø”ä:qdÿssŒf6¹æÄì1ÛÕÇÕÝp\½¶ÇÆÜOGSÆszÝȹÂõ½SŸëÉIäZ¢ŒA=a¨ˆÖU‡z"fÞ1(0áø”64âÈ~±W «Mní¿ÇlW¿­aÇsî8¯xz³t¼{ý0öÓ œkç™Æ² ®Å) ¿ÞZÓøáø”ä:qäPPË?bV€ÙcB/ ž,ÃyÓ•9YsýèèhiiIþ«´ÏXþõòÚee)ÿ½ûéÝÞJÁñ)mhÄ‘ sø=&ôBé) :Ó#JLt¹sWp|Jrƒ8²ŒðqêÑ$³Ç„^@=|^p ÞQyñµ××b†&}‘‚çSÚЈ#û¥Ïg®7ø=¦WÓÕô¾¹©B8VÙ3Ó¯«çOŸþ–/}šÑ3=ø/¸ Ùöç€ãS’ëÄ‘ c˜=¦C;~ûÐø=È™½}-÷ZõõœéØ+RªóQ£éŒZx¾¶àø”64âHÐ9ü³¢Í5‚åq5‡“®w4Vèë_6ï*gìôFmÌvÄ‹šâ¸g]Gx·9¡×„è¼é5šÆy³߬ʛ³,NtY¼¿VÍ+KŠÒ'‚/”ŽûTíëo®Ç gÜûü^Ïùçø”ä:q$èféö"9ávY{”ênsæná]åª6ùéþ_'&W¯äLjwƒ»ÍDŽ;÷›-ýk™ak÷gü-\»%¨}kKo­Ss—e6²5mÎ} ßß㛘3ÅE\ÝÂñ)mhÄ‘=²¨Ÿ.ø=¦× {X¯ÇŠÚûWÙ+êæMá°ñ|OÍÿ—ìTÝWV”éÌÒo¼«€eµ§z¶C¶;ÁÜßbK €ŽOI®sGöF`3ãø´`ö˜¦>&{Óй2ÛåHÛÛ×R8=ÄlÿR;–rȩ̈a£qØñŸao•Ô‘¾+—yÎÝj“üTcPc’ã\ºwowøúzôØk?èŸìƒ€ ǧ´¡G‚Îá÷˜¦Ö£•ŸyÒ^-ЃÏðzµãò»&ùS †3´¦®×ôÙSÜ^_åÍó­Ô87žŸâ7CìYQîs³ãdz¯¤îûUSO„QøÂZ+@z^õ¡àø”ä:qä X¤×OfÒ®8ÌoÃÛ­Í%¹ÎA9\&úúÉì1;ÒΕø =§ÔØÙ.šf-bõýdôèñ5Õi5á>àø”64âÈ@Ýa`þÎ$à÷˜­kçJ|c6^˨=ålï›rÞ¬={ÅçùŒïyÚCÛiê|úöžuŸ%N<º½îsû¾N~³…׬ǧ$×9ˆ#‡m3“n?̳ }LV"*l¯v<[‰¨×_f{ÐxWGh,¯Yzâl{]¦™ŽNœæ_k‹ü˜ûÙ;óßéC̺êD{ªŽ}|8DW€ bÕ‡‚ãSÚЈ#‡ƒñj|›tŸ3Jø=fº²Ú¡ÎéUoª±{üL;wk+½”qn(moǬÂÚ'Hyqç{@t5Kz"膌©­)áø”ä:qä °çìLfÙVñ–µ·ªÂîÍ#žŒØØö¾s©ökØd¸GŒs×^%¹ÎA 3ƱbÑàø”64âH³£¬úÍ` oŸ’\ç Ž€ ̵’ ;8>¥ 8l†W-Ÿ’\ç ŽP¼ ô…€ãSÚЈ#ÀÉ }Æ¢Áñ)ÉuÎDâHù(ï<<^^‡GÅUJ‡Ë¾öC±ûO¬t ¡¡©–-k¡ÚN¸¼Ûgû{cÈs’ý×±žXÉßó º–îŽOI®s&G*˜wºî ŽOiC#Ž„††††æè>áø”ä:q$44444K÷ǧ´¡GBCCCCstŸp|Jrƒ8ršzó‚ù{[W>Ü;÷ê†aŸÍ奯ôvy–qüîÿ¹vï¯~¬.´õ§{«olðÓì^ßx÷­Ç¿¼+õ΃¯Ä×NÚ6Ûgób>9æ§Лyj{OŽ™ö©´}Ý{?½&Ûâúÿ¼=7­N[ŸÐ]êÞàø”64âÈ k‘{/UÛÆLî§üøíKç^½óÎOïP{KÓÐã¸rzEJ4õî¾/äõ»®­Ÿ¿ý“kïÞ´m#¾VðíSi=¾&½ãëÜ´Z0l ¨û„ãS’ëÄ‘ÓÔ§ÍÊÙ‹ò¥oØßþðû÷ÿî–°¸ñ÷§OŸ:,ãQF“"QhüºMbÓý/_Û8o$%=Ó©oœ£ySiÊxîÖ·Tl§â]uüêÇŸ}ô½Ëf"·=úå»eت¹ñ³=½²®«?¿-Ë(S8õò9ù¯ÌáÊï»ø-Ã^qÿóÛ»ïnW¹ò“Ïν¶¦í÷÷Ÿ]ûÖ7 ›Õo_Ùxÿ¦²Ñõ³]Öž¬¥o>øj·,õÖ‡{_Ýö»þ» NŸ$ŸÏýìÎ_^S§k²Kç^?p]#ž{î¿SÚFÝYUc†å¹W×7ÿä–‘7áªÏÀ}Q:ZöŸîmäM¨"¬ŸßúÑÎÅ×·Šû’1ÒÉîûÙs"öìEó¹Pº78>¥ 8rÂZa?ú틃'~üöã{°—}kÖ³“ôè7/¾¿úÒÞŸ]¹òÓ;Byij¥,ãE¥ã_üêÞ­ï]V}–ºûòøý¿Ý•Ý–ô1—^[3ò ;©+Þ¡±¯â¥S§én /ž¿ó_OËnZö’kÿýúìøþþ;ßúÏò¿ðhÞó'v¸)½‘tò¸üWjéO­¬¬œ]¥öŠ•ÿrƈËüòö­\¦=©rÙuؽ¼næ™.œ0mrvóRÓôó»°õþM:ðUyèÀ³_Ý“o|è7z_|×¥u¢Ò)¼K™ÎƒòNé·ÅÁóƒlä–\kÿËg×Ö¿)Lx$Ù¾®SGË~ñÍ­ý'™ó»yÿ+±tRûÕo_È#ÒU¯¾¹¥ÊþX>~?¸üÞ'_¬\¸¨Ë¾ÿåùäÈ·®3ÖÂÏ^4Ÿ‹£û„ãS’ëÄ‘ÓÔŠmÕ)d`±ráRc™ö¢šÎÑožïÿóÓƒýÇâ?ijüB½\Ë©ÂÎ=~+þd&Hsñ[[»lËÎëñdЬ¬ŽÐ*V^>WÉóK§ÜÇO¯r®³~ö¯m\’Z:Å]Q‰å „|{XyùŒQéDEµÇ\}}Cž~ëÝ͇êøû{\QñÙÚwß;wñò7/\4ò ˆ¦égo®ùà7Ï\wÚ¨ðúò\ Œlû®k§£½‹:®ïõ|’s¯¬×:õ3v𶿨0˾þ?®ËXóîÿ¾–yÓü”{ÿï†üÓ[x]ÛÈœ‹ì¶^²¼ú¼Â~ö í{ן҆F9a-üß#½€Béý‡÷¥«/ã—ÞØºôÚúÑ‘X}ûª•áïí?‡«9;üëJïrÚ§tÞ4vž}Çm£\5¢Ë/»Lóâkû¿Pcªkÿíãºò¯'Ož”oi»T %ªõ£ðåšÞ÷àø”ä:qä4õld©‘ýÊ…Uço‰l¼3›'Y¾×ËÞ'ó"Ú¦LÓ^õ!_ö3Y¬QßÂÆÊgªã4Ï:Y9{QÍFQÇõÌ”W7Âù§õCmhýœ{}ÃH‡^‹æç|~ìt¶ò«u~êgŒ4Í®+ïT8Ãàª_YlGú$ÍZe—œúOÙGhéË—¾~Ò¾®|K ý6Nì™Öº78>¥ 8zÔúù—ïüåõl‘«Œé«³·¢çB×Ò}Âñ)ÉuâHhhèñé?¿ýèçw¤k\ýöùæ£v£{ƒãSÚЈ#¡¡¡Ç¨/¾¶‘}ÿvxC·¤û„ãS’ëÄ‘ÐÐÐÐÐ,ÝŸÒ†F ÍÑ}Âñ)ÉuNæ#_úëbÕѨ±÷”0–ÿú~̤-¾v||³Ó(E-Nœ8±þæúÑÑÑÒÒÒÝOï.` t†¬êµ××d=ËÚ¾÷ù=Tõ4@  ýÞš¯ 0*dÖÿʶ-{m}Pê·Öß’í\ôýp ¸ÜÀGnà#7ð‘€øHÀ |ä ¿_Þ—YíÅŒ_:l ýMÁðÎæj7süúà(@ ª¡†÷b¦ ËnŒ®êÀnæt££-4>r4èFëëÁÑ_§‚Vµ³WE:FЂ@à#LJë m·„À;N´ À>rÐÈfø«ì¯™–hÿNœ•vüë;…::KKÅ‘££/oúÎBõ_£P·øøË=yO|ã­ì?|Kü{Öšäÿ:ìqøÈA sÑAW{g¥éñÚZ¶rœvSmÿFÿh{>Qö’öñB«ÿ-uè^üËmû¸ÑÛNµžû"ÕýÍl$å½v¦ƒû»ÈÀGö€íÍ–Yí“ëŠ'Î_Ÿ‰“ÆØÚ+Uj—QÁ®Ÿ$Ú¼§“¨çQ·÷t |dëôî™z'¤µf+ÌzL!e‚6êY…#ìáÞ‘·XÇ‚E½‰‘Ý_ÕŽpÇ |d[dÍ;o-Ãôˆm÷æiêªn#i¼r JÇ¿ÜFOªÑo?ãm;Tg¥Èï¯ÀËÐLϬCç­¥³¦þïw{ô”Åk‡õ ‰™ÿkÕ“ 5‹¤§î—ÊýU•#âõ6]¼qöÚ‚@à#S2UïHu_žRÇåÌ|ŽWËç'+l·Õ;õ3v½Ð÷w„ÀG&£øv"&ë©–îªËv¾Pu+:¯Þ! ƒÈŒXý´¡«>Ày“[Àû;^à#“ÂkÑÍOjí^*çï}l1rȾn¡~ýÔÑzö™¢2'¶´1=tc-Ϫ¹PD_ øÈ¤ðZìtt‡k½ùy jG§iÙTzR…§ Ùº´÷Ú©bšqQÜbQã>:õl>vùv2»¿ ^: µýldyË¿Yÿ‹˜r¨ÀG&åÅበ› ã†ñè¬/{¸#ºE^±X&QßSÎ:Íò\sVˆè:NõõÚ‹é5ß„è³Ñ»GäëÙs˜ïò¹¯>21ÇOöŠ}Šž²è‰žì‰£CÑ9Åu_|uâìv´'¥½íˆæIVFüÊ8cA:M}Å‘8qa›Sf7zJ«DŽ0÷uXÀG&ç0‹±––eL)–eo.<ß?Æ¢ —ÿdGö\¥wìÁAæ¨ë.g5œ{Jᥔ‡PçÆ€*…3³Chï¾(fq³F$Ÿ·ç_IO)EÇØ3/š¾S»¿¥­:Mu`r]gèy+ŸO2µGÄësë)û>29>RÏkj’|×4§žëëÖðˆƒ‹#µ.>OŠx=pt£ xØ™‡˜Ú¾<Ìg/‡WWÓÑ 'à#“má<¯)ÛEî5ÕwÍB5Ôú|vœ—Ÿ î_~x½ŒGñâèbúíJe|´.“ûì¥Z%ÒL{Î2ÎêÐ`;èøÈäÄ[øTt÷x½µžÁÁ쉔V]í`ãźZ{÷ÑÍ1Ÿ½lÙl±‡àÖO]m¯³TÇÍ·(ªciFµ÷6ù Ëñ¿ øÈäD[ødt÷øòS¬žä÷˜¦wŒÙKgå*×Ûªå?{³[üU"Qm¬£uf¤Ó¶ž•Wýü¼f›ÀG&‡ÕÂ'¡»'ä­ÕjýM7ð·:­ãÓÓc›É|ö=]%ÂÑê‘Ñ:ZCS¯9¡ïÓƒ>29Ñ>Ý=¾üz\¯OuîyÔlTvÄzLðŸ=SÓU"üuÉúmiJ+I°¶2-ð‘Éaµj½ØK‘µpö¹Q]tMÓTyËw› ÛwOØ[›Ç³™Jd'¡;M1‘Þ¥ÇëùÌÿØØkgée²ÕjgŒI¿-—Ë„à)ç>29¡Ö;묫óW+ǹ=E@ †MX+86]Ò°¼E©â õ~bEÍ8ô˜à?Ÿõ´1¯{6– &þ¶¤žóQ·øÈäx[ì,>³V_#Eä¸öšåA—ââO²ȯ¥>̈,µ‡Üž3f ¤Op‘ÿ6Kö6`íJ1`¢÷.•ΉÕát4˜øÈäÄZfÄFk=®ÏÕøì©æØsÒ÷y¦.á×U\'™9@]ým–^nSc˜÷N0l"zÚoHTm,apÀG&ÇÛ2U„WtÊÕïŽÆ÷HmiŒ æ¥½í5¹Õûúé;{œîñåGxŽ;u%=2ºbdຘ„’ÇŽÕ‘ü½w©´˜ê’ÖôyÕXÂà€LN¨•êïŽÆSkT=ÝlÓs2Rê¹–¢8W¾é¸ìë§ïóL]öÖÑž1¤õŠþÊÞu2f9ï»;˜÷K0lXš¾!MÆSfßÚŸìXÏh|drâ-sÖ#ë¹vûÊ|<ûÜÒ¾êáL›@ïÉLßS–îñåGxŽ;u j}Z+zôštÄL¿QÙ÷=ÿ»]Þ±½w©´y­¬&É/p©}v²¿ÇîË@´ù¶4âg`pÀG&‡ÙJ' »Ç—'ÏMôìM¢ôštÍ¥"I¯j:R½ž¹+=#Ï®ú>œ²,çÿFmꢞ‡ôëuç¤ÎÛh|dr¸­qüº{|ùžãNH§†žõJÕ_7±ß` h3͆Ñ.ïXˆ–%•æ^k6¦R½¿î¥S)Fk¿û¡Û•íÇû øÈä0ŸÚšî§3çÞ:t÷øò£àäy1õXà”e9ÿ7jÓ®v¿Í”;áo?zÉ2…|m©Ø›_aX_=84>29ü§6¤SÑô÷†b¤¥8W8ÓiSw/?Âsܩ鰴»Ë«ÿvbÍ%®qn="¢eI¥»¼Vu‡Äž©Asà#“Ã|j£:#ܺª±£b–Ž9uÖñ%NÝ=¡ºâå9‰Î0Þ÷í&¨aÚ7ÖcSUoQhCƒ†ÀG&‡ÿÔ†µ¨iS±ŸÅ(ª¯ì¡ÃÏÃÐZ§¢ù¤Ã×ÂHG½ÍØõì÷E§ëeû æÿÇÉ›]Þ±-K*Ý嵆 Asà#“Ã|j£:Ã×ÃR[ë˜Æ”Ç“ŽìuO¨®xyN¢5¾:™7wÓÖó$Ë•ÝîU:å›MnÍOTNYTýGm  ™þSÖFkÍu†}s9yZ« Õ/ÏËÁtøZxÒ1û¿+;ìq$/?áòŽ…hYRé.¯5 š™æSÕ ¾ÍLÓ}v G¦ÐšÙqç÷H{Ä»zn%Ù¾HùÛ Y]ÍOTNYT½Em  ™þSÒÕ9«qÃÞ=ƒqd¦éðµ9Žmï&(Ⱦ¸ÚFy>õÍò¸º¯ñfs\þ~ËÜ#ä#"Z–TºËk AƒæÀG&‡ùÔN@w/? NžèZ+ýýqŒÕ߆g5ÞlæÛ[Ç®ŸáÃ)ËrþoÔÚР!ð‘Éá?µc×ÝãËðwê@:“Ô#"Z–TºËk AƒæÀG&‡ùÔN@w/? NžSNY–ó£6І LŽûIÍ~|§ÆêòV4ùµ¬ŠvÚŸ¸°[OÙ=¾2 Ïq§¤“LÓ=tºÚOǧGD´,©t‚k9·lÕD×Èfd]l›ýh|drO*™ß}š[Õ [{{`ÏÞ=œrqêa.íY¡‘¡{ÆêwG +ýôXà”EÕgÔ¦á\a%Nµí(ËðŽt Îu›iÐøÈä˜OçlÇ82óˆ-´3æÐ1_­}Xüï¹³nÛÐ^¾Ü5Ækv¯žãNH‡¯É¯R»fîX÷QÐë÷1s`Ÿt’k g:Õ6Å|> {~˜4>29ö“ê8N=i±=ímé¼Ç­zâ¼YšÞ}[2Œw[çû¯¿vO½Þª5­aÚTìíuºÎõ”꾤¥ œ²¨úŒÚt 3Ü?’EöNÒc땜ë6Ó !ð‘Éñ=æqº®è1«Ç3ŽA#«ìë Ö6š€M­Î× Ïq§¤Ãמ<ªÄ|ŠŠÖg=¦z+:®®§¢X79ßoDŒˆhYRé$×ʰGPO8öFV–}¯4œë6Р9ð‘ɱŸT÷ñÙjéµÍquu9=×é5]égØñhά…ÓëV{홿vO¨·bô©t†£[¿v¤qèÀ¹Îý]GZõµé@ çñbwûàoò¸4ç­‹Ÿ7Gú ð‘É1ŸNÛ ±£ê1ÞPïÉBÓÔß±èsžªNÓÞ·…¶ðãrgmm£ßmsíºÇ× Ïq§¤Ãׂ‘µ)´)j|ß›Køy³Ë;¢eI¥\+ßöÀ²íÑuÏ[×<4>29Ž'•ø'oë2¾a8ãÅxüáK“îÛRµ7Ód÷¢|½€Â—ÏÄš1Ê]±1ìóÝ÷7v-† œ²¨?jmhÐøÈ丟Ô軤¨Žvrì[Õd6ÁpZ/?ÂsÜ©éLRˆhYRé.¯5 š™æSëЕoQ û¾u÷øò£àäy1õXà”e9ÿ7jmhÐøÈäðŸÚ±ëîñåGxŽ;u Iê-K*=×µ¢ûæˆêü€èžVíkÐøÈä0ŸÚ èîñåGÁÉs ÍØ/½6ÖÛùF¼3ªìú>œ²,çÿFmZÕá}sªßòüôÛР!ð‘Éá?µc×ÝãËðwê@:3]Æ Ì4Óhcç{Ÿ#׊õhú#"Z–TzÎk‰¨M5v¬Ø{f5goc¾½±æŽAAsà#“Ã|j' »ãøøøÄ‰þžBÁÉ3KÛ;q›±; o k 7´¯ÞMBÙèØ… ÷qÊ58eYÎÿÚ´ª3êì›3Ó•ùêŽý­H:‰W€€†ÀG&£ìÇ»ëEgñÍñ±è¢zÝ£‹‚èhþϦ’æñlÀÊžºô¸²7v¬³€bþã¾² šàýM®Ã& 3‚ûæèäÖ¡x±ôš³ƒ ê4>2%ÊmLÛS*ïx|¼%:gV½f¯¡`埭Í4Õ®öºU}<ó—V,(ì½s½×®ãæñ@Y†Ž¾¿"2º(båm[+ø63­wí0Öª/=f ß®\;Ûñó9¾g`€ÀG¦§ÒÔ¼B »'ïHÑñ+}U0Ë"x6fš³Tsç÷ÙNô¢2ÚFê±çjt?&ë\¿,『O”ïCÑ2ÖÕ‚aТ¦MÅ^yGc¨V½uû^JЇ€æÀG¶…ÑÔ…Õ{_^!/Hï®Ñ†Ôðl#N¹¢ÚA5bGõŽoüîJµw+ÒÑß#ÍÝ!rû[æ±µSÿ™ÑpÊ5Vª‘¥Ž¥D¬¼íjæO­=°¼Çñ=²oà#»Àøt§Ú¼˜×ÔŸ@tnèƒÄ˘kÁ°ÑûåÚÞÕaö­1?îŒùè÷ÈPoX½.g:ÆÈ--︱¾|o²ïõ\ÏÀ„4h|dس]:öš•He¬цUv¾ŽÞ sgy–ÇêEO~Ù¡• 9¢Ú}΃‘þøÝ¡NO±œÿµaéJÌǰïIO 26Zö@Dm¦¤Asà#‡NÇK,ÆÈ”Vݳ¢ö<=5bëe¡m  ¦@ØB0læÒóìÏ©¾ƒ6;×£§ YgÕ ®ÃfJ4>Lå&EWßt…goUc.«o'¹@Ï~œ¯³L1ã_ëÉRYÔÕ}§ ÓÁ³ÞÆè5D¬7aißÞªÇåïiÓY<öd1/(­Wå>ÔY ÷}Oÿ ŒGƒæÀG‚ibvšMFä¼Z¯w4vd-=Ÿ¢°wîé›+<Ç›éÂ~O»Gî"iÐøH0q\&¿g õ8þ_GNûXšóØôââŸï¶Í¨·)iÐøH°@”ß,™=‹WÓ½[ÍK”q¤þiïžC,¿1¢Dl´°–6œz›’  ¿gñj÷:¹¶×M~#ÂØ¯G¨]ãSîHœDëmJ4>, Ìž¥õ‘Pg¥Ðèuök¬ç>, ñž¥/íÜv> ,Â.Ê;NtS­î€ ·—™Š¬™Ï™ûZÌ£†wL|$X,È8«Ç¿*ŒÙSJï˜»Æ Ò Ž)íïÖøU #²ŽùÆÃÒzîqñ#¯ðŽé‹Èägmà[Ô<Ø/ÆÏït<aîz8ß³ð‘`A9vÿÆ}žk˜ºX—‰ð1)œúlãgìʃð…½ sâF·ñAB™½Ãñ£`tÀGA;¸Ž?¤±.öèÁ·(Z>#  =;Càs @çš9‰#ÅjtÇž®p‡ô |$Mhãã!CãÿgyÇïó¯Q+IEND®B`‚libxflaim-5.1.969/docs/XFLAIMArchitecture.odg0000644000175000017500000003431310511001742022207 0ustar ahodgkinsonahodgkinsonPKð®|4Ÿ.Ä++mimetypeapplication/vnd.oasis.opendocument.graphicsPKð®|4Configurations2/PKð®|4 content.xmlí]ërÛºþß§à¨iç$ !‚wº±=¹Lf2c'n’3=Ó4 Jlx;$eKy‰þìûõIŠ I¢(R%'>Ì™cIÀ.€],¾Å. ñÕå2 „;”f~O &ŠœØõ£ÙùäׯïEsryñ§W±çù:scg¢(8Êñ«€¹£ìŒÕžOitÛ™ŸEvˆ²³Ü9‹•\g<õí‹•dù*èÍN‰yî-ó¾Ì„¶Ækßöï™óÜnjß÷e&´X©<»÷e^fèÅXëabçþÆ(–};ŸÌó<9›NïïïÁ½ât6…–eMim5`§¢Ki@©\gŠD:˦ÀiI¢Üî;>BË)Z„·(í­;·³š¤(Ã$X\b˜ýâyjöu7ëm]w³5;s;ímg”¸n*ŠÛßT—ç í|Þ2¿æôWÒ?×Wk»Jþ}ÚšªœÔOz‹É¨yþ8Ž«¡¶ØépeIR§ì3G}¿“ü>õs”räÎNrÇœJãq¸Mi˜N1…ˆîˆÉW‹ˆ("ka§¬º"ÎÜÖ¦»¾úâÌQh¯‰ýnbѲ܎֚ÉB?è= ˜¶ÅhíÈïm „–[:L×|ÃEéØÄgÓªÀ^ä1ávDº’³‹WlEÓ¿{O:?Ÿ¸ œžG¾ÂE ÅÄž¡É´u–6Xg©Ì}§,N씸'úAdLD¯®º“²Ý‚ELR¬„4÷Q&P߀Ùmq§þwìàì@´†ewp£Ä ëtw„ÛYS…¾ëT€)'Á.qä#Š“åiü0Äïnýt"*GÑóbøAŒ]ÍŸ=Ïq,ëñUŸ–ij?Ú¼ý{‘å¾·ê))£"¨$ÎÒø^œ#6Ç~ͳƒ ×zñYèGU©ä„UÙ½ïwKŠöP—>ªku§RºÕ¡òˆðgžá£8*gð‡’Ý:™ìx×^”TÂKô§–v=yô_·žäå­+%OÍeT,Ž–UTÓh®$&Üo5AéTËIuÈ?;úÀcn#ŸˆeêÛ,S×Ô=5}ŠîO®é­ Ëæžš>ÙF{; ³´¡Z[–7P-kuÜ48–Þ¦ÒU²V%¼´¸_"цgìßoÃÜû÷Úé ‹L ŒÃ8V˜Ö‘oË«b?rqÉžxÓ0žÇ7ë“ÖÔ»óbbÄþw²*‘$”Ï'ÿûï:‡WÊòãò—Ux¥”‹ aD\ Ô²tæµêÒ©Ú_¨p»†»‡0r—0\#œH”'Kl7м8EÄÐ ­!"°o1Oå'ô>¶ò#jHLCÈORCê€2Ÿ¤†´Á4$õIjHLCÊ“Ô1œ~ž(N›ƒiH}¢8m ¨¡§‰Ó$]7Š´Ÿ ¨¹êbÃyŸxéAa7Ÿ²ÉGÕ+îÝŒj³CEø[w‡Ú¶öàÓ·«SÞÍelr©kýc£tƒéØ{¼D2ŸÝ ¾ô áY9…EmjGxk·ñ^Ø!^-õö’Õ³•Tí¢ë¶¬à‘·'ë8ư M hòërãQ#D5BÔ) J‡ QPLu„¨¢Fˆ!ê!íˆÒ >d·€TOZY³ºCö® ñÛÇ h[’ G´¾a”;`Œ“G„~DøGÛ„Bh–:Ê#F5bÔ‹Q–©nDÊÊ)5bÔˆQ)o`”4Õªc”A¿(vXøùýÕI#å1GÄó€˜Ç¸„AÐ0;/Â)@µ )5`I›9Œi¿½¿zýáZx}óáw›ŒC„’ÞIº¢r2É@ÑùãfÈJÇÝî~â ÁÆË#ˆ`¶Š w‹@Œ¤)ĆeÉ@Ûs°a½¾¹¹úðöõן>î°¬Á/½ÖÆ ×ƒ]_z5*×.½î7#=.½¶¯uµsFT +x«dÖ¯üY&~ kˆë ¶óm‘L?# jé1®ƒÎ‘ó 7ŸØ~z´K•ÝÆ`Ú¼u¸©a(IÓWñ—U–£pxÕ¾v‰o#7™‰¾ý¿Úaòwámyþ <êÜqÓÔ:wx†ucð¹û5qíüËâ+Þ~dìúŽÙØÌ}J3ãÝfîž¼­ßæä)ÀÔŸº,PÛñ†ä^ŠÙ¶‰nµŠ®÷°[³)ºLæ{Y³Ä ØÏh¦qŽ-MøE„@3$ÃÒY7,Ͱž ”* µxg!©ÈpÜ CqÂçeXCGx/w:´>!²Àl~§Ñ¬;›®õ×3}¥¶_Д¦Juƒÿâ„…5Û”©(f8YPˇì|äMB¤K —^e@4ø^$¢ßG˜4ﷇႪ´Çâ¤eˆh–(GBÊXoå«RÕ(-ûÌJØËWø àxÏÀøYWÄØ‰YhÛÙSKHÜÙ_#"¦jêækDÄ$²%Éi?î—ñ»ÖÑk#2.FÛ¥sË<¼ ÑrŸÑZm£5…~a9Þ³ÒjQ÷²#*_nFå?l.tp0á€ÄçBu§À:˜FŸoë f…•M g»lWV‘4̺¾ªpˆýÝm¬tÄêZ×MÄêZ[mˆõà$ÞÞE0üQ^ŸÕ¼%•I*OÝ7•Gøz÷Ú.Â-’„mO-‰½C³ýŸBð¾šõЈýž‡!x{yR ‘ŠE¬Õðc×^êriƒnD ác„>*øh„¨$\ §d•OŠáÔ4†¼Ô6†Sc85†S=Àl §öÞ m`Þ ©’:‚Ùf#˜`ö“ƒ™$eܘX6bÙˆe‡bYë(l¿”ÓçT7¶×uœ€Ö@U½vþ©• Pº~„ãOd»ý…´¶k²ýgñziRÝ_“gÉ*°Èµ€wÅP2Wæ?É3ÔæÉ}¬ôµS]ÒËr†Á{Y @µã*ú˜;ÝË%޹Ó1wºÃ«;=Æ…&Ò–¹®è;Nm- [Ý¿OÚýó…)¾ ôÎwPv&|ˆ\´ô£ÙKáø5EèåðÉ>#'¾Céê¥ðΧ—ÕlòžüØBÏ‹UøÝþ3ª»‹M~EÕ¨g|,Ó¿ÔúwSî©MµG2Õñ[.Ñ›~†½—Š÷%0Ô¾ñ³/‹ÈüQ:^:Õ£Xi˜Åb–ä°Z…œ—Ð5 ð–¦B BC‘eSQuMÐhÅ4,ÉR`õÈ-D.¾Ä\ƒTÅjëˆKå^KyÊWX8ÆÅÊNfþÿuï X¿ë6÷wLòñÓC]RgO ÔÓ6|QÁ'ïáä¯péµECCúÒKßð,“Î1wù2lgGʈšµ yw¤aÌ3ê8ÿoÃÀâYk#Ž 8‚à‚›,ÇeªV»Æ`©ü/Dö}êA ŽÉò1Y>&ËÿpÉrV6+ß¼¬Ÿ-CŠèSc¦µgÊTÕ±³}¼M”ã׋ÿPKèúcÍ ‹PKð®|4 styles.xmlí\]ŽÛ6~ï) 雬ŸõþØ7HQô) ìæ´DÙL(Q ¨õ:—ècïדtHê‡ZK^)ñÆÎB ÄœqæãÌ3¦üöÝcL'˜g„%KË›ºÖ' I²^ZŸîÿ²o¬w·¿¼eQD¼YÇ8v&vgN²…&.­œ' †2’-ãl!‚KqR -Lî…šJ¨‡õW̦´À¢¯°ämÈ¢Uÿ™³)r´í+,ySSQýóñCíW<î;—äm@p’ö6Ss›òŒ±JU) ƒ]©ë»îÌÑŸ îíAö-'sƒ=8È Tˆ³¸ 4àóà°ñƒtùÊî˜ÐÞVo‡“ „ô†^ò®Z¤ÅF*¾-ó®N··oeà_0Ÿ¨ÿË –Ö{ÎÙÖš€ó.ÞþÁ—–;q'¾;¹põ8x_ìÁ˜íɱï~µœÛ·:ìB¡œI}¢Ç"Vî–Öš£tC«äMGjÌN9Åm@“drµQ.X–"¹3ÍŠhºAVÁ–æI re¹½òÒÊHœR\Ò!aa{Å1‚¤– NQR¤7@µcÂã)·Åª$E 6%’„Xâ,7(õ©ˆÚÈ"D3\Ù‰lei8ÝfUìÒ®=kó ÛÑC¶µÕä£ Ržà9X1­Q‰âo(eÙï÷Æ“Æï?Þ롆þšÛ^ãƒÙK‹³% Ž”ˆrÁâDnFõDù ø³T¨1Š’uŽÖ0„5°<4ùt×6%8+JJ5?ä Ñä%ÙäSBà0€[”Uª”‚µB‡Ô©dК \(WÒ–T‚…žm$S_}ƒ—ߣ €Ô¢XÅÑ­ZÅÒª\EmS¯I¬Ý¨I¥ÿ˜a¥ã5ü€‡Ö3ÁV|6OE=„û"ŸÃ( u|ë!ðÈPnn7† çüÕU¬‰‘G@WØ«*Áé…ݧTrKבÔ`€ˆ; .©m¡´² ©4σ Š”ï¦(”ÇQ"VÎàù—rƒ°bBÈlßF£8’²Gàd½1(× ÕŽDÂPŽ1hC&ΰ°›F6‰»VbiÒ+ÿÂâªS(%Yå'õ…m‰êa{•SŠÅDå8l –þ¨I¶çÀÖà9wÇþèØû£gƒÐØû£ctìž¡±?:öGÏ«?Úl`– ô¬@ˆ0šRgZ;ÎfƒjGi’w ›:¯È¸º52° ãjÃì÷Û0ý WÝØê»þðåð_f9Ú:‚n}I¨±Qóù]ƒs©«ý3K“c]=ÖÕc]}6uõXWuõXWuõy#4ÖÕG¯«u,íWÖOÇË:nU„–¢jØ£&:ƒ¾4Ý`Ê‹8§/矿@sæwÁé{ý1V–¤?p:šW¼Ê;Y²ápè^¨–?[èf-·ãˆ€ ¾Ñ£,ç?"^ÍÎÔÐËPu—£¾ìÔ*׸UK}Ã]¿-¤'oØåk¬òíÜÀ. %lk ûÚŽå¢úß]«…ç'¾s: S«æ+ ò¯§ó™9^^[õ½éå\ÝjÓ:rÀ0Nêwì)¬~ Ô¼&d˜ò<ÞP –nº‚i6¿lEb:»¼îØ]gWWmˆ”†ï!RBÕ‰HÊ¸àˆˆÃ€ìG`˜îmÅÏ?ØR¾‚ÉÜ ² ¾¬9ËÁýµÿ®åÎÿÍ—ºÂt/ÉR«ñó !ÉRÀÁ–ù»¾½ØÊ1&žaQS•¿QÜlne ‘À¶ ª,×Ïû\b” •Žvõõðd9$…õgó{½âr¶vz½X}xôŬ›5€¬Å=ÀQ$y¹s*.ç©”ÂX oÃþÔï)×›A¯u4—ɾÞ!¤c;5ÆO tÚ$æöPKòñç/îdFPKð®|4Éj;³nnmeta.xml OpenOffice.org/2.0$Win32 OpenOffice.org_project/680m1$Build-89902006-03-28T08:39:042006-03-28T14:55:33en-US20PT6H8M16SPKð®|4Thumbnails/thumbnail.png­Xg4 ÔnK)ŠWA±ŠRbÓR#5kÔ¦µjÅÞBÊ«!¼¶ZEÚ­Q{¤µ‚ vŒÆ^b¤!ø|ÿ¿Ÿß=ç®÷9ç>÷ÜsϹH#m37ó¥K—gÆv¡¯1\Ø¡Åk›—.ý“yöÔ4$ï­~²NX$ûS&Ë»OåtŒè¬#Äë®Zÿ{bD1u…¿ñ5›Ý‚²Ö†ÆC“ƒà ÃŽÁá2ØXLbíZoΈ<ëEÑÛÞâ4þ Ÿ´2Ò,ÎxÏÿb¨Q®Î¬‰8è:a÷†­¯‰Q ŒÚ'•ßßÎ9ÜmsûéCg8"j·àõÿC:¢õ¥ acŽx—ˆDbŸ#ÎâÃî´š½qÚ@ïgvÁ.m™¢¦ì ÈàS‹J~myQ ã;Æïgÿ¨œöÖ<¬Mv‘ÉY$ÅD‹ŒLîÄy9¢×„OÉ=€+æß¹O–?wâ)ÞÖïLNqáå˜èГ…oá=uG'0ofÎùÌL8®Á„ÉÄ—Ù5B™ˆË <”ðsÐ4È[ÏO­7Qgn˜3S”— ï¡IRf×Ñ>c× ë\³u½äu')£mam,Ÿ«×KCPMòJ–AЩ‡ù8‰Ü•V/&ž  í¦Þ‘¶Å*¸é­ýƒy¦…&'Šx‡H…¾|{÷:J¢qUÿèÙŽà‚ªÍlFž-–|Zb€V|`2ÃßœñùØ}0p—ýÌKicL‹§Òh–ƒÀ÷Î#üÔÊ Ëo½ìÍ —­Ëlûi­ûã[ÈÄÈ\øŽuö[|ßé¢M%ca^˜ó™£Õé2Ã[-›“ÈZ™ƒ§!±‹îR¶a_µ ‹CKš°‡X£bcNP‡Ikè~G^°ÁÔUê>å*¥:î¦ ï¼uZÒ\¼²ávy]iÒðQ„É¥Fe¿{Ñt…ïÄù/@Rýi ‹v ´Ìñ+ñ·Ëýý^¡RhlXìŸ|í© O/îÑé·qS¿ïhò›…ŽDSÜöï}rmA(ýQ¿Ã!@jʇ.gÒî.Äj0l¦Ãµ´©èó©ƒõ Ðì›Ì*‹±ÇÀrÛ¸µiKW³@[ÑÚQ8_¡F›/Xòاñ±o5æÅ+¸r¹)uTí ÛöùoÄ:Θ†Üº¹¼Y ˜dMfµºDH*õf©‚ŸZz©U5kôI_rfŸ"!±@“ªñ•þiŸ`Žä—°rÕ›¢ öh¢Ü´uZøQy7A—ªè hwRo$+”!Toy†\žf¶m nà:uËxÚ2óùv%×>Ã^= SÙA*²A°)Oz®Þð £3$êº ÷¦=ÖíNývdh.Ýfê‚ò;?^Å ³4Ï}oþô>ó}ÝÃèáù†šz÷Í-‚wWT·é_ r‡“¸ÖƒÕ`ˆC×}™eŸoêƒíŽ=VÌ®émÙ¹½Ë t‘9¢Ô›;krQ·[cOŠàMv§ñ«_s†9ø —ŒQÅu`X~d•‡Ÿá,çŸ9£¤”bh™ÏææÊ,·Çâ¸î1Zþ6ÓgŠ$Ïß~0DØm/úÈ´vv Ä =#ä£%\èI#àŽ+% z÷¹)Lò^îdÜ¡ý¨™·Ž5˜Ù-»„+{y|vÊG&„fs$a¢eçˆWÙ/Þ{¢»=·Wy¢‰RUû&0ŸáC;=´n¥;K˜›CC,[@LcØFŠå8Ǧ¿ù õ‚I[³„B£IJy!xöY¿j³¡í=:É«tìÎô* ëmCf‰]žyw}¸È`à«äˆ{†BÐfdßÜòkrT›ÆX²"IB±Ý\ÙÇé¬r^—Ë$“ ýac~çñŸ`Öµ¼>ïƒßæ}3ÜŸŸŠ¡‘ÆÆ.‡;/GÏü“ó c>㑦 Q²„(Ï6v¢8O-áwv hÜ» É~™o^gé‹9(’†&m -T²Ö$ï[Z·—f|ý°šwÍ2FÅlôpNZy#8y¥PvâTqϙĩÝ`ÙšÒ] ÖT|ÃþñâVà ®Ê2ø’<EG½ùÓ ˆuzæ;¶ ÒøS€jI`¯ PL¶šGµ‘9ßP¾²f±p¹`7 Ô5—$5Ö ò½7W‹~è ‚)«_R¶Tf{Üz‹Ö–«eák(v)d¸¼±ŠÌuüb¢6_RU¦%Z³Âì{&0 û)µÂ¼,8’âóÐášÚ¦é¯‰€“a¤ÓÑt7!ßϤàõ¾sîHóçv³ñGÀ¤5?T«<\VÑ ;‡ê… ƒóKƒá‘¼àiøŠóËõªÍÑÌmȺ l©ÿß=/çz35èBŒÖÒék9½Ño"`b¼\;TðC,Ù2¿®Œ\QÆ)v`Xzá›"ÚøÝÕ†à$™1U>¬—0ñ‡a½ýæ²=d» ­S+Uܪ5» ¹¥6*%Àeû°ò¿²§_Âá›ÝnTùùªðúÚ†uãÚ­ê5À;•XóÒÃ!‚O;=o`Y¢é<æQoåxå(¹‚J1é±&ë\”qâðbéÚÚc~Y±~Œ»¨ÿ±…RÄ V¹ÙÛ¤²z6C+çÓ¬csÍŸÞþêË!»£;©?Gzc[‡8Ýuëø{>Ì&4.žðùð„XœˆÊX+Â"4æ°Ö=ìnÛSÂÖÎ(yŽ ¦d?$3ÎÚ2Ñ´×YäL-åaýOkËà­MFÀöÌvnâÇ퇎rƒ¤›vÞ£l“D{ñšËJ1uNGÕÊtI­ÃŠÞØñrÎb«'nj0ìóº³ñ[¦ªØ$Âî€›Ç 7kÙø¨«ÛÕ﫽 cÚÇ9›ÒßÂçêyZ¼ÇÜÓnÔ|‹XaA‚úU‡ë Ê{NØfr–J [Ï÷旅Lk¥¼•‘ ÚÌë ·÷fM¦±Å›‘Ó}á%$üZV>¿«i³‡ôQÜô„9B©iróŠ[›(o?£ÌÏÀ4fû8ù¼[–wä§7Œ¹fZY)ôG@bj9×|Üç ¿½À3g“Óêýå‘pFè~'Ybd\Ð^ºùt_6ÿìÏ»”rU¬sOìíK¸óíÄEEÛ ?¨4îzým Ü6Ö Ëó„Ãdzûʧ°´yŽÄîUS¾#Ý…Œe7HU|1)¼©TYòW«ãœ#; ]–Öƒ ¯‰úíçú7F@Ò[ýc<€§Fý ¿ÑÿO_ºÿ#=gÜ<ôù/pH:õÒ…@4 žUª¿ŠþPKÂsÅ"[ÇPKð®|4 settings.xmlÝZÝSâH¿¿Â¢êž®0ÁU)e+áCqAA@Yk_†d s$3¹™‰‘ûë¯'!Q61–u<2™ùõçôtw8ýöä¹{˜ ÂèY©²¯—ö0µ˜M¨sVÚåjé[ýSöð@,\³™x˜Ê²ÀR±˩¨-Ÿ•Nk "jyXÔ¤Uc>¦«eµõÙµˆØräÉ%t~VšIé×4- ÃýðË>ãŽV999Ñ¢§«©>Ç ŒÞàúšu²£ÄÙe9{}=cì™iµ`)XÄø®jËûÕláwWZjnÙbžÕ¶q~ »‹d"xõø(?ú±å, þKµzr˜þg–ÈþAõëñÎøeùeBmü„íMZ8L6V´|‡/vá‡{ƒM!9xB©®ü¢’MçœØëc~ʘ‹-Õ+pü6gtSÇïÞCŠüSTÒà%r¢÷‘ƒ{ˆ;„Šâˆ¨k—PœJâÔt=ý[²Ía°p*}›¨aún .µ], 7D‹ÝW¹.ührä a«¹éæÉî-—x„"‰ûÌ]Djë€4¹¤¸T9Òõ¬ÒlÓ*ÂD±=ºhß&>øøÐ&ø¢T×vþTϲñÒ‡)Ñgá¦Ë¬9¶ßfE{êŠâÿ´^±sÙ6‹ùý&p1óÀÉŽ¯bõw8‡7OÊã°u2î›!v!¢a[¿+pGD®Ðcvªè“8qœ,$46Y6l¸Äšð“lÙ$õŒÏ#ED 1CÔÁ7lYS ÎÐ%6}ÌoX˜l㌹¦R‹2ðP‚)ßôÌ(¤gtŸô“[&~\å<,HLŽ3¦›ÿÿŠä¨àŠ$#¾Ê qS¹¯|Íè/¿ÀÓyχކÌ8•ñã/9”¢Ó¹Î­ÒátmëY³¸ô+ºÎ±=¹ <Ì‘d¼8ÕLš˜²(%M¦’£¬}¦ñãäøQŒ•ŠL‹ª,6µÌÍsôzÕÆ=cÞ5MÈ™vÈ ×»%iãLòåî­¶å@À£tãwzn†ï»‹±À¼‰$zÿtÈ$)WßtSúAÄÅ5¨R5­÷'ÅŸ:œæpƲh¶€¨C4Å2±"Iá¹â[»ö1½~n®hó«‰8‘ÙlšMHæ8‚„š7˜§ZŪ9: Þ5·Ž*˜Ï¢(+› M$fŸE;£ø®)†29ãûÌ ½ÍÚf|JlÓgçÊßtíBÍ$Ô|±ê1ÍꙢŸ‚:fÌ 'h«Ë£îÐO0±C¨ê@fFhQûÕõyƒúÛ2² €}–ãd3ä’Ö§Ùõ³¬yËwÌ©!À#ûµdPTÙÞQƒÏdlîâb^1¬0‘5/¢Ç±NãÕ—%¹‰€ñ‹i”Gèm"_Køò¸ˆb¼¢QÈ»˜ˆ†B¿*êIDaÛ·X=H!ü«øaþ³D§¬R0dß`d3ênžÂF"àxL·ú¡qFû5{oJÙ)æìx(±ôÏØ¤ìCÎäpäφç«#ÇÈ%rSíyº•,æp&õ‡ p¶.Zl·-WÎé²° 5s%f.’ AbEåçOâ«©øjæ>)» ²«}ŠåOŸ?–©-Ê3¿ªëºMË[1°‚Í´úå[­–ÓýK«#xû¤,m³í_ßç;éÆÓ]kÙº`æüAÜsXºÊ`“óuñ’=ñéd4¾›/gÅçjÁZ¦†@ª/Øjýüö:c<“ré—umJ®k¤œsöÞç[ÊÅg¼¿U<Á¯™éSNǜǕÓÁ…º… ¨T.¹*WÆÔϼ:´1±3¸6²ìö @etF{9×Þ[SjJC[¬ÄÑ%.¢é´ß˜2ð3¡ŽÖf^Ó&ç’Ë CD‘šшÒamšØoÛf—‘ðø[9ÁŽd²Ý†'?>†äÚ[ƒc[ ýèmX±‰íjcƒ¤¾›¡áÈҺžF;–W 5ùPKY¯Îà-èPKð®|4Ÿ.Ä++mimetypePKð®|4QConfigurations2/PKð®|4èúcÍ ‹ content.xmlPKð®|4òñç/îdF … styles.xmlPKð®|4Éj;³nn«meta.xmlPKð®|4ÂsÅ"[Ç?Thumbnails/thumbnail.pngPKð®|4‹ê~çM«( à.settings.xmlPKð®|4Y¯Îà-èg5META-INF/manifest.xmlPKÞ×6libxflaim-5.1.969/docs/docs/0000755000175000017500000000000010511001742017115 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/docs/docs/Doxyfile0000644000175000017500000001225310511001742020626 0ustar ahodgkinsonahodgkinsonPROJECT_NAME = "XFLAIM" PROJECT_NUMBER = "5.1.969" OUTPUT_DIRECTORY = /home/ahodgkinson/work/opensource/xflaim/build/docs CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = YES BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = "" STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 3 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = NO HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = NO SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = INPUT = src/xflaim.h \ /home/ahodgkinson/work/opensource/ftk/src/ftk.h FILE_PATTERNS = *.h RECURSIVE = NO EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO USE_HTAGS = NO VERBATIM_HEADERS = NO ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = CLASS_DIAGRAMS = YES HIDE_UNDOC_RELATIONS = YES HAVE_DOT = NO CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 MAX_DOT_GRAPH_HEIGHT = 1024 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES SEARCHENGINE = NO libxflaim-5.1.969/docs/docs/html/0000755000175000017500000000000010511001742020061 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/docs/docs/html/doxygen.png0000644000175000017500000000240110511001742022241 0ustar ahodgkinsonahodgkinson‰PNG  IHDRd-ok>ÂgAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<]PLTEǾÏ"&©ÈÎï¶»ÖÓÚú“¢Þ ¬à¶Âõ‡§ÕÙêÉÊÎáâæ{ŽÔ¡ëˆ™× ²ø§¬¹ÀÀ±ÝÝÎùùéõõçëëåED9×ÖËhg]_X<@:#mhUÿÿÿÝÀ1tRNSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÍvÿIDATxÚbC£: d#„„………h` @¡X",***LKˆ.–], ºX@t± €èb @ÑÅ€BµD„6–š%""´° € ˜% ˆ™B:H¢ˆ²Áf@• ˆRPy"K`\PbC(!II!h©…ëƒ(ñ„Ä!ꈬC„Ä…àl!0[X\J\$TMˆ(’>a$S„ Ù@ Ш@R.$‚¬LJBR¢‰AÌG1 ¬ Â(FȃÔPhhÁTÀ¢„%!`€&q°%u P ¹¢ ¬ € ¹CT$B¢à|‚ºW„¤Àl £!B`R$( …Ĉ‘’ž@AÅ%ĤÄ%@,(—ʂڱ%$ÁââRPmB U`1IˆYB  99€\1 yCCCÿf"[N 'Ü=TGÈ’øl8˜^Kû5<êSæRɤ”%î@@ à›Ê b1 qÅAXHˆ¸&ØB’R y n˜P„Ìã–4A €€j¹€€>Ü ˜ t!˜+(.ÈÅWQ±A2ÜÜMUÜ‚’’‚‚â `1 %`19€F< 3cZÄ`óe!\ˆ DÈ+. 83‹³Àä¸!lYYA -6‚EJŠ¢V €@©žXXX 4„å Ê@86Ð`RdB´€4I "Ý "–@xrÊŒ‚H€AÊ`—f ÉȰCŒ"XV0ɲ³C b@2…¬H ¬È“ p)!(ì‚ 0Ž4ˆ)(%RÁÎ ¶$€TÊ€¥Àþb‡b,säÐ@7À üѰ‚Òî?f¥Ö—\PIx!I´¦"”Ȉ’3¨ QY˜ÿt^^ÛØgv- }>WJOAV`$&#”¦8ùøø8€\FF ›SFJ$ÂÆ€ÐƊС䈉ÀÀ 4ª…Èäå -Á§‡ €H²…—ŸŸŸf ?ðâ5„ €k1Âd‰,ŒÃ ³ƒ“€.€"­F™ËË€àñ‚½ÁIÈ€"±Ù4ÉH gx|‚f©m)))9´. aMDƒ& ºX@t± €èb @ÑÅ€¢‹%DKˆ.–], ºX@t± €èb @€d`‚ɽSµOIEND®B`‚libxflaim-5.1.969/docs/docs/html/tab_b.gif0000644000175000017500000000004310511001742021614 0ustar ahodgkinsonahodgkinsonGIF89a€„°Ç,D;libxflaim-5.1.969/docs/docs/html/tab_l.gif0000644000175000017500000000130210511001742021625 0ustar ahodgkinsonahodgkinsonGIF89a ,Õö÷ùñô÷öøúüýþúûüùúûøùúêïóïóöÆÕßÒÞæØâéÞçíÝæìåìñèîòô÷ùóöø³ÈÕÁÒÝËÙâÏÜäÖá薴ŹɯÂÍ»ÎÙÃÔÞÂÓÝÈ×àÌÚâÕáèÙäê×âèåìðëðó„°ÇÑÞåÜæëãëïëñôîóõ÷úûûüüÿÿÿþþþ, ,ÿ@–P±É`H$!%CqVe2X­ŠÌJ(“Ä +€˜3 2$ÀÆ ¼kvŠä-Ëçõu*…"}ã|}|~q(" $f„ 'Žl(Œ&&$r‘™ › & ! )¢¤›{¨£¥r­ª°©¯„±¯¬´¦·»º³®«§¾¶ÃÂÀ¿²¹ÇÄËÆ²ÌÉεҽͼ„ÔÈÓ×иÙÝÕÏÙÊâÜßãçæê¾äÛÅëÇíáîÖìéïøñ÷õüÑðåùü¤Pß?‚ƒœÇÛBm åAœÎáÀ†%V܈î!Çk÷Ø/áÄ;^¤¨²$Æ–#Mf)f͇(WÎL‰“æKçÒ„° ’I)L:eD ¡Cµ´x*4 U¨h  %A«£^ÁNKb¬Ùe§X±‚´k»x!ÁÖí—2tÝÖ !¯š5tÛæé—À]$¬´%ƒXíâ.i[¬]Y­•ÊfžEëõkg`µ††:zëçÒž;£}ºµj×aa‹–Mš¶é׸cçž½»vïÛºƒóî›8ðáÈ‹'?®¼9óç©G_>Ýyuè¬_ßž]zwêß­‡Ç¾º¼mîæµG~½ûôÞთ/ž>ùööÙ«Ïÿ¿ÿýÿÅà|ÖWà}v;libxflaim-5.1.969/docs/docs/html/tab_r.gif0000644000175000017500000000503110511001742021636 0ustar ahodgkinsonahodgkinsonGIF89a,Õö÷ùñô÷öøúüýþúûüùúûøùúêïóïóöÆÕßÒÞæØâéÞçíÝæìåìñèîòô÷ùóöø³ÈÕÁÒÝËÙâÏÜäÖá薴ŹɯÂÍ»ÎÙÃÔÞÂÓÝÈ×àÌÚâÕáèÙäê×âèåìðëðó„°ÇÑÞåÜæëãëïëñôîóõ÷úûûüüÿÿÿþþþ,,ÿ@’pH,ȤrÉl:ŸÐ¨tJ­Z¯Ø¬v •h<¬pkL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~ÏwVa+‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ “*)^,*ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂö)'ÆÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæÚ¥(" ðñòóôõö÷øùúûüýþÿ H° ÁƒòK"ƒRHœH±¢Å‹3jÜȱ£Ç CŠI²¤É“(Sª\éÅu&@€ Á²¦Í›8sêÜɳ§Oÿ–(±€DУH“*]Ê´©Ó§P£JJµªÕ«X³jÝʵ«×¯S84± ‰hÓª]˶­Û·pãÊK·®Ý»xóêÝË·¯ß¿€Ó} âDÌf(^̸±ãÇ#KžL¹²å˘3kÞ̹³çÏ C‹m¹ðCÄHœXͺµë×°cËžM»¶íÛ¸sëÞÍ»·ïßÀƒ N÷ÃJ” Á®¹óçУKŸN½ºõëØ³kßν»÷ïàËO¾úñ€ dÇ@€‚‚L¤"ÉÈF:ò‘Œ¤$9† (8…&ÉÉNzò“  ¥(G©FB^²!˨)WÉÊVºò•°l¤)1™ wÄò–¸Ì¥.wÊYºäƒà¥0‡IÌbó¾|ÉHpÌf:ó™Ðìe pJ±ˆ€}Ȧ6·ÉÍnzó›à §8û0Â%"¸æ8×ÉÎvºóðŒ§<ÉPÎQ`ò%×$€>÷ÉÏ~úóŸ ¨@JЂô M¨BÊІ:ô¡¨D'ZPKF Ö¼&16ÊÑŽzô£ ©HGJRb ÷Lç5ÏÁÒ–ºô¥ÿ0©LgJÓšš#(e>¯‰Óžúô§@ ªP‡JÔ¢õ¨HMªR—ÊÔ¦:õ©PªT§JÕª&5;%U·ÊÕ®zõ«` «XÇJV«ÂC§‹ÑjY×ÊÖ¶ºõ­p«\ŠU´À¦xÍ«^÷Ê×¾úõ¯ÐÀi)$‚”ô°ˆM¬bËØÆ:vˆ, ಘͬf7ËÙÎzö³  ­hGKÚÒšö´¨M­jWËÚÖºöµ°­*$ÛSPô¶¸Í­nwËÛÞúö·ÀÅm +„â¸ÈM®r—ËÜæ:÷¹ÐE®?±9ÏêZ÷ºØÍ®v¿9€î"‚ºÛ ¯xÇKÞòb—™ÑLÿ¯z×Ë^A¢·½ð¯|ç†÷Ò÷¾øÍ¯0í«ßþú÷¿¡ä/€Là»×ÀN°‚ï(à;øÁ n0„'LaýJ¸ÂÎ0{/¬á{ؘþ°ˆG|Ë“øÄ(¥‰SÌâCrÅ.ޱŒ ãÛøÆv¬1ŽwÌc6ê¸Ç@ÞñƒLd¹ÈHNñ‘“Ìd/¹ÉPÎð“£LeO¹ÊXŽp–·|â+sùËýõ2˜ÇL_1“ùÌí53š×M5³ùÍÇt3œç¼_:ÛÙÂwÎs™õÌgøÊ¹Ï€p ýÌ?úÐ/F´¢ë¼èFãÒÐŽŽt!-éJã‘Ò–Îô1­éN»‘ÓžuÿA-êP“ºÔ>5ª3­êUWºÕ®Ž4¬cÝèYÓZѶ¾õ¡s­ëAóº×€þ5°ù,ìaç¹ØÆ¶3²“=çe3ûÍÎ~öš£-í3S»Úc¾6¶¿¬ímo¹ÛÞÆ2¸ÃMåq“Êæ>7“Ó­n$³»ÝD~7¼,ïyó¸ÞöÆ1¾ómã}óÛÈÿvµ¿Þâ\É/µÁNâ…3ÜÉ÷´Ã#Þá‰S\ÊguÆ-mñO¸ã0ÈC¾à‘“\Ë'_´ÉS^à•³|À.ùc.ó0לÐ4¿9~s®ó=÷¼Ï<ÿy|ƒ.ô4]ÏD?ºz“®ô67]ÙO§3Ó£ÞÌ©SÄW‡vÖÙl>õ­3Úëdî:Øu)ö±?ÚìÙF;˜Ë®öW²½í­|;ÜW)÷¹²îvtÞ˽w¾÷Ý|à×=xÂÞÝA;libxflaim-5.1.969/docs/docs/html/tabs.css0000644000175000017500000000333610511001742021531 0ustar ahodgkinsonahodgkinson/* tabs styles, based on http://www.alistapart.com/articles/slidingdoors */ DIV.tabs { float : left; width : 100%; background : url("tab_b.gif") repeat-x bottom; margin-bottom : 4px; } DIV.tabs UL { margin : 0px; padding-left : 10px; list-style : none; } DIV.tabs LI, DIV.tabs FORM { display : inline; margin : 0px; padding : 0px; } DIV.tabs FORM { float : right; } DIV.tabs A { float : left; background : url("tab_r.gif") no-repeat right top; border-bottom : 1px solid #84B0C7; font-size : x-small; font-weight : bold; text-decoration : none; } DIV.tabs A:hover { background-position: 100% -150px; } DIV.tabs A:link, DIV.tabs A:visited, DIV.tabs A:active, DIV.tabs A:hover { color: #1A419D; } DIV.tabs SPAN { float : left; display : block; background : url("tab_l.gif") no-repeat left top; padding : 5px 9px; white-space : nowrap; } DIV.tabs INPUT { float : right; display : inline; font-size : 1em; } DIV.tabs TD { font-size : x-small; font-weight : bold; text-decoration : none; } /* Commented Backslash Hack hides rule from IE5-Mac \*/ DIV.tabs SPAN {float : none;} /* End IE5-Mac hack */ DIV.tabs A:hover SPAN { background-position: 0% -150px; } DIV.tabs LI#current A { background-position: 100% -150px; border-width : 0px; } DIV.tabs LI#current SPAN { background-position: 0% -150px; padding-bottom : 6px; } DIV.nav { background : none; border : none; border-bottom : 1px solid #84B0C7; } libxflaim-5.1.969/docs/docs/html/doxygen.css0000644000175000017500000001604610511001742022257 0ustar ahodgkinsonahodgkinsonBODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { font-family: Geneva, Arial, Helvetica, sans-serif; } BODY,TD { font-size: 90%; } H1 { text-align: center; font-size: 160%; } H2 { font-size: 120%; } H3 { font-size: 100%; } CAPTION { font-weight: bold } DIV.qindex { width: 100%; background-color: #e8eef2; border: 1px solid #84b0c7; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.nav { width: 100%; background-color: #e8eef2; border: 1px solid #84b0c7; text-align: center; margin: 2px; padding: 2px; line-height: 140%; } DIV.navtab { background-color: #e8eef2; border: 1px solid #84b0c7; text-align: center; margin: 2px; margin-right: 15px; padding: 2px; } TD.navtab { font-size: 70%; } A.qindex { text-decoration: none; font-weight: bold; color: #1A419D; } A.qindex:visited { text-decoration: none; font-weight: bold; color: #1A419D } A.qindex:hover { text-decoration: none; background-color: #ddddff; } A.qindexHL { text-decoration: none; font-weight: bold; background-color: #6666cc; color: #ffffff; border: 1px double #9295C2; } A.qindexHL:hover { text-decoration: none; background-color: #6666cc; color: #ffffff; } A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } A.el { text-decoration: none; font-weight: bold } A.elRef { font-weight: bold } A.code:link { text-decoration: none; font-weight: normal; color: #0000FF} A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} A.codeRef:link { font-weight: normal; color: #0000FF} A.codeRef:visited { font-weight: normal; color: #0000FF} A:hover { text-decoration: none; background-color: #f2f2ff } DL.el { margin-left: -1cm } .fragment { font-family: Fixed, monospace; font-size: 95%; } PRE.fragment { border: 1px solid #CCCCCC; background-color: #f5f5f5; margin-top: 4px; margin-bottom: 4px; margin-left: 2px; margin-right: 8px; padding-left: 6px; padding-right: 6px; padding-top: 4px; padding-bottom: 4px; } DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } TD.md { background-color: #F4F4FB; font-weight: bold; } TD.mdPrefix { background-color: #F4F4FB; color: #606060; font-size: 80%; } TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } DIV.groupHeader { margin-left: 16px; margin-top: 12px; margin-bottom: 6px; font-weight: bold; } DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } BODY { background: white; color: black; margin-right: 20px; margin-left: 20px; } TD.indexkey { background-color: #e8eef2; font-weight: bold; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TD.indexvalue { background-color: #e8eef2; font-style: italic; padding-right : 10px; padding-top : 2px; padding-left : 10px; padding-bottom : 2px; margin-left : 0px; margin-right : 0px; margin-top : 2px; margin-bottom : 2px; border: 1px solid #CCCCCC; } TR.memlist { background-color: #f0f0f0; } P.formulaDsp { text-align: center; } IMG.formulaDsp { } IMG.formulaInl { vertical-align: middle; } SPAN.keyword { color: #008000 } SPAN.keywordtype { color: #604020 } SPAN.keywordflow { color: #e08000 } SPAN.comment { color: #800000 } SPAN.preprocessor { color: #806020 } SPAN.stringliteral { color: #002080 } SPAN.charliteral { color: #008080 } .mdTable { border: 1px solid #868686; background-color: #F4F4FB; } .mdRow { padding: 8px 10px; } .mdescLeft { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .mdescRight { padding: 0px 8px 4px 8px; font-size: 80%; font-style: italic; background-color: #FAFAFA; border-top: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; margin: 0px; } .memItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemLeft { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplItemRight { padding: 1px 8px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: none; border-right-style: none; border-bottom-style: none; border-left-style: none; background-color: #FAFAFA; font-size: 80%; } .memTemplParams { padding: 1px 0px 0px 8px; margin: 4px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #E0E0E0; border-right-color: #E0E0E0; border-bottom-color: #E0E0E0; border-left-color: #E0E0E0; border-top-style: solid; border-right-style: none; border-bottom-style: none; border-left-style: none; color: #606060; background-color: #FAFAFA; font-size: 80%; } .search { color: #003399; font-weight: bold; } FORM.search { margin-bottom: 0px; margin-top: 0px; } INPUT.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #e8eef2; } TD.tiny { font-size: 75%; } a { color: #1A41A8; } a:visited { color: #2A3798; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #84b0c7; } TH.dirtab { background: #e8eef2; font-weight: bold; } HR { height: 1px; border: none; border-top: 1px solid black; } libxflaim-5.1.969/docs/docs/html/main.html0000644000175000017500000000171310511001742021675 0ustar ahodgkinsonahodgkinson XFLAIM: Main Page

XFLAIM Documentation

5.1.969


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/files.html0000644000175000017500000000243510511001742022055 0ustar ahodgkinsonahodgkinson XFLAIM: File Index

XFLAIM File List

Here is a list of all documented files with brief descriptions:
ftk.h

Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/ftk_8h.html0000644000175000017500000031112610511001742022136 0ustar ahodgkinsonahodgkinson XFLAIM: ftk.h File Reference

ftk.h File Reference

#include <stdarg.h>

Classes

struct  PoolMemoryBlock
 Header for blocks in a memory pool. More...
struct  POOL_STATS
 Pool memory manager. More...

Defines

#define NE_FLM_OK   0
 0 - Operation was successful.
#define NE_FLM_BOF_HIT   0xC001
 0xC001 - Beginning of results encountered.
#define NE_FLM_EOF_HIT   0xC002
 0xC002 - End of results encountered.
#define NE_FLM_EXISTS   0xC004
 0xC004 - Object already exists.
#define NE_FLM_FAILURE   0xC005
 0xC005 - Internal failure.
#define NE_FLM_NOT_FOUND   0xC006
 0xC006 - An object was not found.
#define NE_FLM_BTREE_ERROR   0xC012
 0xC012 - Corruption found in b-tree.
#define NE_FLM_BTREE_FULL   0xC013
 0xC013 - B-tree cannot grow beyond current size.
#define NE_FLM_CONV_DEST_OVERFLOW   0xC01C
 0xC01C - Destination buffer not large enough to hold data.
#define NE_FLM_CONV_ILLEGAL   0xC01D
 0xC01D - Attempt to convert between data types is an unsupported conversion.
#define NE_FLM_CONV_NUM_OVERFLOW   0xC020
 0xC020 - Numeric overflow (> upper bound) converting to numeric type.
#define NE_FLM_DATA_ERROR   0xC022
 0xC022 - Corruption found in b-tree.
#define NE_FLM_ILLEGAL_OP   0xC026
 0xC026 - Illegal operation
#define NE_FLM_MEM   0xC037
 0xC037 - Attempt to allocate memory failed.
#define NE_FLM_NOT_UNIQUE   0xC03E
 0xC03E - Non-unique key.
#define NE_FLM_SYNTAX   0xC045
 0xC045 - Syntax error while parsing.
#define NE_FLM_NOT_IMPLEMENTED   0xC05F
 0xC05F - Attempt was made to use a feature that is not implemented.
#define NE_FLM_INVALID_PARM   0xC08B
 0xC08B - Invalid parameter passed into a function.
#define NE_FLM_IO_ACCESS_DENIED   0xC201
 0xC201 - Access to file is denied. Caller is not allowed access to a file.
#define NE_FLM_IO_BAD_FILE_HANDLE   0xC202
 0xC202 - Bad file handle or file descriptor.
#define NE_FLM_IO_COPY_ERR   0xC203
 0xC203 - Error occurred while copying a file.
#define NE_FLM_IO_DISK_FULL   0xC204
 0xC204 - Disk full.
#define NE_FLM_IO_END_OF_FILE   0xC205
 0xC205 - End of file reached while reading from the file.
#define NE_FLM_IO_OPEN_ERR   0xC206
 0xC206 - Error while opening the file.
#define NE_FLM_IO_SEEK_ERR   0xC207
 0xC207 - Error occurred while positioning (seeking) within a file.
#define NE_FLM_IO_DIRECTORY_ERR   0xC208
 0xC208 - Error occurred while accessing or deleting a directory.
#define NE_FLM_IO_PATH_NOT_FOUND   0xC209
 0xC209 - File not found.
#define NE_FLM_IO_TOO_MANY_OPEN_FILES   0xC20A
 0xC20A - Too many files open.
#define NE_FLM_IO_PATH_TOO_LONG   0xC20B
 0xC20B - File name too long.
#define NE_FLM_IO_NO_MORE_FILES   0xC20C
 0xC20C - No more files in directory.
#define NE_FLM_IO_DELETING_FILE   0xC20D
 0xC20D - Error occurred while deleting a file.
#define NE_FLM_IO_FILE_LOCK_ERR   0xC20E
 0xC20E - Error attempting to acquire a byte-range lock on a file.
#define NE_FLM_IO_FILE_UNLOCK_ERR   0xC20F
 0xC20F - Error attempting to release a byte-range lock on a file.
#define NE_FLM_IO_PATH_CREATE_FAILURE   0xC210
 0xC210 - Error occurred while attempting to create a directory or sub-directory.
#define NE_FLM_IO_RENAME_FAILURE   0xC211
 0xC211 - Error occurred while renaming a file.
#define NE_FLM_IO_INVALID_PASSWORD   0xC212
 0xC212 - Invalid file password.
#define NE_FLM_SETTING_UP_FOR_READ   0xC213
 0xC213 - Error occurred while setting up to perform a file read operation.
#define NE_FLM_SETTING_UP_FOR_WRITE   0xC214
 0xC214 - Error occurred while setting up to perform a file write operation.
#define NE_FLM_IO_CANNOT_REDUCE_PATH   0xC215
 0xC215 - Cannot reduce file name into more components.
#define NE_FLM_INITIALIZING_IO_SYSTEM   0xC216
 0xC216 - Error occurred while setting up to access the file system.
#define NE_FLM_FLUSHING_FILE   0xC217
 0xC217 - Error occurred while flushing file data buffers to disk.
#define NE_FLM_IO_INVALID_FILENAME   0xC218
 0xC218 - Invalid file name.
#define NE_FLM_IO_CONNECT_ERROR   0xC219
 0xC219 - Error connecting to a remote network resource.
#define NE_FLM_OPENING_FILE   0xC21A
 0xC21A - Unexpected error occurred while opening a file.
#define NE_FLM_DIRECT_OPENING_FILE   0xC21B
 0xC21B - Unexpected error occurred while opening a file in direct access mode.
#define NE_FLM_CREATING_FILE   0xC21C
 0xC21C - Unexpected error occurred while creating a file.
#define NE_FLM_DIRECT_CREATING_FILE   0xC21D
 0xC21D - Unexpected error occurred while creating a file in direct access mode.
#define NE_FLM_READING_FILE   0xC21E
 0xC21E - Unexpected error occurred while reading a file.
#define NE_FLM_DIRECT_READING_FILE   0xC21F
 0xC21F - Unexpected error occurred while reading a file in direct access mode.
#define NE_FLM_WRITING_FILE   0xC220
 0xC220 - Unexpected error occurred while writing to a file.
#define NE_FLM_DIRECT_WRITING_FILE   0xC221
 0xC221 - Unexpected error occurred while writing a file in direct access mode.
#define NE_FLM_POSITIONING_IN_FILE   0xC222
 0xC222 - Unexpected error occurred while positioning within a file.
#define NE_FLM_GETTING_FILE_SIZE   0xC223
 0xC223 - Unexpected error occurred while getting a file's size.
#define NE_FLM_TRUNCATING_FILE   0xC224
 0xC224 - Unexpected error occurred while truncating a file.
#define NE_FLM_PARSING_FILE_NAME   0xC225
 0xC225 - Unexpected error occurred while parsing a file's name.
#define NE_FLM_CLOSING_FILE   0xC226
 0xC226 - Unexpected error occurred while closing a file.
#define NE_FLM_GETTING_FILE_INFO   0xC227
 0xC227 - Unexpected error occurred while getting information about a file.
#define NE_FLM_EXPANDING_FILE   0xC228
 0xC228 - Unexpected error occurred while expanding a file.
#define NE_FLM_GETTING_FREE_BLOCKS   0xC229
 0xC229 - Unexpected error getting free blocks from file system.
#define NE_FLM_CHECKING_FILE_EXISTENCE   0xC22A
 0xC22A - Unexpected error occurred while checking to see if a file exists.
#define NE_FLM_RENAMING_FILE   0xC22B
 0xC22B - Unexpected error occurred while renaming a file.
#define NE_FLM_SETTING_FILE_INFO   0xC22C
 0xC22C - Unexpected error occurred while setting a file's information.
#define NE_FLM_IO_PENDING   0xC22D
 0xC22D - I/O has not yet completed
#define NE_FLM_ASYNC_FAILED   0xC22E
 0xC22E - An async I/O operation failed
#define NE_FLM_MISALIGNED_IO   0xC22F
 0xC22F - Misaligned buffer or offset encountered during I/O request
#define NE_FLM_STREAM_DECOMPRESS_ERROR   0xC400
 0xC400 - Error decompressing data stream.
#define NE_FLM_STREAM_NOT_COMPRESSED   0xC401
 0xC401 - Attempting to decompress a data stream that is not compressed.
#define NE_FLM_STREAM_TOO_MANY_FILES   0xC402
 0xC402 - Too many files in input stream.
#define NE_FLM_COULD_NOT_CREATE_SEMAPHORE   0xC500
 0xC500 - Could not create a semaphore.
#define NE_FLM_BAD_UTF8   0xC501
 0xC501 - An invalid byte sequence was found in a UTF-8 string
#define NE_FLM_ERROR_WAITING_ON_SEMAPHORE   0xC502
 0xC502 - Error occurred while waiting on a sempahore.
#define NE_FLM_BAD_SEN   0xC503
 0xC503 - Invalid simple encoded number.
#define NE_FLM_COULD_NOT_START_THREAD   0xC504
 0xC504 - Problem starting a new thread.
#define NE_FLM_BAD_BASE64_ENCODING   0xC505
 0xC505 - Invalid base64 sequence encountered.
#define NE_FLM_STREAM_EXISTS   0xC506
 0xC506 - Stream file already exists.
#define NE_FLM_MULTIPLE_MATCHES   0xC507
 0xC507 - Multiple items matched but only one match was expected.
#define NE_FLM_BTREE_KEY_SIZE   0xC508
 0xC508 - Invalid b-tree key size.
#define NE_FLM_BTREE_BAD_STATE   0xC509
 0xC509 - B-tree operation cannot be completed.
#define NE_FLM_COULD_NOT_CREATE_MUTEX   0xC50A
 0xC50A - Error occurred while creating or initializing a mutex.
#define NE_FLM_BAD_PLATFORM_FORMAT   0xC50B
 0xC50B - In-memory alignment of disk structures is incorrect
#define NE_FLM_LOCK_REQ_TIMEOUT   0xC50C
 0xC50C - Timeout while waiting for a lock object
#define NE_FLM_WAIT_TIMEOUT   0xC50D
 0xC50D - Timeout while waiting on a semaphore, condition variable, or reader/writer lock
#define NE_FLM_NOIP_ADDR   0xC900
 0xC900 - IP address not found
#define NE_FLM_SOCKET_FAIL   0xC901
 0xC901 - IP socket failure
#define NE_FLM_CONNECT_FAIL   0xC902
 0xC902 - TCP/IP connection failure
#define NE_FLM_BIND_FAIL   0xC903
 0xC903 - The TCP/IP services on your system may not be configured or installed.
#define NE_FLM_LISTEN_FAIL   0xC904
 0xC904 - TCP/IP listen failed
#define NE_FLM_ACCEPT_FAIL   0xC905
 0xC905 - TCP/IP accept failed
#define NE_FLM_SELECT_ERR   0xC906
 0xC906 - TCP/IP select failed
#define NE_FLM_SOCKET_SET_OPT_FAIL   0xC907
 0xC907 - TCP/IP socket operation failed
#define NE_FLM_SOCKET_DISCONNECT   0xC908
 0xC908 - TCP/IP disconnected
#define NE_FLM_SOCKET_READ_FAIL   0xC909
 0xC909 - TCP/IP read failed
#define NE_FLM_SOCKET_WRITE_FAIL   0xC90A
 0xC90A - TCP/IP write failed
#define NE_FLM_SOCKET_READ_TIMEOUT   0xC90B
 0xC90B - TCP/IP read timeout
#define NE_FLM_SOCKET_WRITE_TIMEOUT   0xC90C
 0xC90C - TCP/IP write timeout
#define NE_FLM_SOCKET_ALREADY_CLOSED   0xC90D
 0xC90D - Connection already closed
#define FLM_US_LANG   0
 English, United States.
#define FLM_AF_LANG   1
 Afrikaans.
#define FLM_AR_LANG   2
 Arabic.
#define FLM_CA_LANG   3
 Catalan.
#define FLM_HR_LANG   4
 Croatian.
#define FLM_CZ_LANG   5
 Czech.
#define FLM_DK_LANG   6
 Danish.
#define FLM_NL_LANG   7
 Dutch.
#define FLM_OZ_LANG   8
 English, Australia.
#define FLM_CE_LANG   9
 English, Canada.
#define FLM_UK_LANG   10
 English, United Kingdom.
#define FLM_FA_LANG   11
 Farsi.
#define FLM_SU_LANG   12
 Finnish.
#define FLM_CF_LANG   13
 French, Canada.
#define FLM_FR_LANG   14
 French, France.
#define FLM_GA_LANG   15
 Galician.
#define FLM_DE_LANG   16
 German, Germany.
#define FLM_SD_LANG   17
 German, Switzerland.
#define FLM_GR_LANG   18
 Greek.
#define FLM_HE_LANG   19
 Hebrew.
#define FLM_HU_LANG   20
 Hungarian.
#define FLM_IS_LANG   21
 Icelandic.
#define FLM_IT_LANG   22
 Italian.
#define FLM_NO_LANG   23
 Norwegian.
#define FLM_PL_LANG   24
 Polish.
#define FLM_BR_LANG   25
 Portuguese, Brazil.
#define FLM_PO_LANG   26
 Portuguese, Portugal.
#define FLM_RU_LANG   27
 Russian.
#define FLM_SL_LANG   28
 Slovak.
#define FLM_ES_LANG   29
 Spanish.
#define FLM_SV_LANG   30
 Swedish.
#define FLM_YK_LANG   31
 Ukrainian.
#define FLM_UR_LANG   32
 Urdu.
#define FLM_TK_LANG   33
 Turkey.
#define FLM_JP_LANG   34
 Japanese.
#define FLM_KO_LANG   35
 Korean.
#define FLM_CT_LANG   36
 Chinese-Traditional.
#define FLM_CS_LANG   37
 Chinese-Simplified.
#define FLM_LA_LANG   38
 another Asian language
#define FLM_COMP_CASE_INSENSITIVE   0x0001
 0x0001 = Do case sensitive comparison.
#define FLM_COMP_COMPRESS_WHITESPACE   0x0002
 0x0002 = Compare multiple whitespace characters as a single space.
#define FLM_COMP_NO_WHITESPACE   0x0004
 0x0004 = Ignore all whitespace during comparison.
#define FLM_COMP_NO_UNDERSCORES   0x0008
 0x0008 = Ignore all underscore characters during comparison.
#define FLM_COMP_NO_DASHES   0x0010
 0x0010 = Ignore all dash characters during comparison.
#define FLM_COMP_WHITESPACE_AS_SPACE   0x0020
 0x0020 = Treat newlines and tabs as spaces during comparison.
#define FLM_COMP_IGNORE_LEADING_SPACE   0x0040
 0x0040 = Ignore leading space characters during comparison.
#define FLM_COMP_IGNORE_TRAILING_SPACE   0x0080
 0x0080 = Ignore trailing space characters during comparison.

Typedefs

typedef FLMINT32 RCODE
 Return code.

Enumerations

enum  eColorType {
  FLM_BLACK = 0, FLM_BLUE, FLM_GREEN, FLM_CYAN,
  FLM_RED, FLM_MAGENTA, FLM_BROWN, FLM_LIGHTGRAY,
  FLM_DARKGRAY, FLM_LIGHTBLUE, FLM_LIGHTGREEN, FLM_LIGHTCYAN,
  FLM_LIGHTRED, FLM_LIGHTMAGENTA, FLM_YELLOW, FLM_WHITE
}
 Colors used for logging messages. More...
enum  eLogMessageSeverity {
  F_FATAL_MESSAGE = 0, F_WARN_MESSAGE, F_ERR_MESSAGE, F_INFO_MESSAGE,
  F_DEBUG_MESSAGE
}
 Message severity. More...
enum  eLockType { , FLM_LOCK_EXCLUSIVE, FLM_LOCK_SHARED }
 Types of locks that may be requested. More...

Functions

virtual void FLMAPI appendString (const char *pszStr)=0
 Append a string to the message.
virtual void FLMAPI newline (void)=0
 Append a newline to the message.
virtual void FLMAPI endMessage (void)=0
 End the current message.
FLMUINT FLMAPI f_languageToNum (const char *pszLanguage)
 Get the language string from a language code.
void FLMAPI f_languageToStr (FLMINT iLangNum, char *pszLanguage)
 Convert a language string to the appropriate language code.
virtual FLMBOOL FLMAPI addLockInfo (FLMUINT uiLockNum, FLMUINT uiThreadID, FLMUINT uiTime)=0
 Return lock information for a lock holder or waiter.

Detailed Description


Enumeration Type Documentation

enum eColorType
 

Colors used for logging messages.

Enumerator:
FLM_BLACK  0 = Black
FLM_BLUE  1 = Blue
FLM_GREEN  2 = Green
FLM_CYAN  3 = Cyan
FLM_RED  4 = Red
FLM_MAGENTA  5 = Magenta
FLM_BROWN  6 = Brown
FLM_LIGHTGRAY  7 = Light Gray
FLM_DARKGRAY  8 = Dark Gray
FLM_LIGHTBLUE  9 = Light Blue
FLM_LIGHTGREEN  10 = Light Green
FLM_LIGHTCYAN  11 = Light Cyan
FLM_LIGHTRED  12 = Light Red
FLM_LIGHTMAGENTA  13 = Light Magenta
FLM_YELLOW  14 = Light Yellow
FLM_WHITE  15 = White

enum eLockType
 

Types of locks that may be requested.

Enumerator:
FLM_LOCK_EXCLUSIVE  1 = Exclusive lock.
FLM_LOCK_SHARED  2 = Shared lock.

enum eLogMessageSeverity
 

Message severity.

Enumerator:
F_FATAL_MESSAGE  Indicates that a fatal error occurred - the kind that would normally require a shutdown or other corrective action by an administrator.
F_WARN_MESSAGE  Warning message.
F_ERR_MESSAGE  Non-fatal error message.
F_INFO_MESSAGE  Information-only message.
F_DEBUG_MESSAGE  Debug message.


Function Documentation

virtual FLMBOOL FLMAPI addLockInfo FLMUINT  uiLockNum,
FLMUINT  uiThreadID,
FLMUINT  uiTime
[pure virtual]
 

Return lock information for a lock holder or waiter.

This method is called for each thread that is either holding the lock or waiting to obtain the lock. The application should return TRUE from this method in order to continue, FALSE if it wants to stop and return from the IF_LockObject::getLockInfo() function.

Parameters:
uiLockNum  Position in queue (0 = lock holder, 1..n = lock waiter).
uiThreadID  Thread ID of the lock holder/waiter.
uiTime  For the lock holder, this is the amount of time the lock has been held. For a lock waiter, this is the amount of time the thread has been waiting to obtain the lock. Both times are milliseconds.

virtual void FLMAPI appendString const char *  pszStr  )  [pure virtual]
 

Append a string to the message.

This method may be called multiple times by to format a complete message. The message is not complete until the endMessage() method is called.

virtual void FLMAPI endMessage void   )  [pure virtual]
 

End the current message.

The application should finish logging, displaying, storing, etc. (whatever) the message. The object should be reset in case a new message is subsequently started.

void FLMAPI f_languageToStr FLMINT  iLangNum,
char *  pszLanguage
 

Convert a language string to the appropriate language code.

Parameters:
pszLanguage  Language string that is to be converted to a code.

virtual void FLMAPI newline void   )  [pure virtual]
 

Append a newline to the message.

This method is called when a multi-line message is being created. Rather than embedding a newline character, this method is used. This allows an application to recognize the fact that there are multiple lines in the message and to log, display, store, etc. (whatever) them accordingly.


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/annotated.html0000644000175000017500000000324410511001742022727 0ustar ahodgkinsonahodgkinson XFLAIM: Class List

XFLAIM Class List

Here are the classes, structs, unions and interfaces with brief descriptions:
POOL_STATSPool memory manager
PoolMemoryBlockHeader for blocks in a memory pool

Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/classes.html0000644000175000017500000000352610511001742022412 0ustar ahodgkinsonahodgkinson XFLAIM: Alphabetical List

XFLAIM Class Index

  P  
POOL_STATS   PoolMemoryBlock   


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/hierarchy.html0000644000175000017500000000277110511001742022734 0ustar ahodgkinsonahodgkinson XFLAIM: Hierarchical Index

XFLAIM Class Hierarchy

This inheritance list is sorted roughly, but not completely, alphabetically:
Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/functions.html0000644000175000017500000000434310511001742022763 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members Here is a list of all documented class members with links to the class documentation for each member:


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/functions_vars.html0000644000175000017500000000422010511001742024010 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members - Variables  


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/struct_p_o_o_l___s_t_a_t_s.html0000644000175000017500000000552010511001742026273 0ustar ahodgkinsonahodgkinson XFLAIM: POOL_STATS Struct Reference

POOL_STATS Struct Reference

Pool memory manager. More...

#include <ftk.h>

List of all members.

Public Attributes

FLMUINT uiAllocBytes
 Total number of bytes requested from GedPoolAlloc and GedPoolCalloc calls.
FLMUINT uiCount
 Number of frees and resets performed on the pool.


Detailed Description

Pool memory manager.

This structure is used to keep track of a pool of memory blocks that are used for pool memory allocation.


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/struct_p_o_o_l___s_t_a_t_s-members.html0000644000175000017500000000352710511001742027730 0ustar ahodgkinsonahodgkinson XFLAIM: Member List

POOL_STATS Member List

This is the complete list of members for POOL_STATS, including all inherited members.

uiAllocBytesPOOL_STATS
uiCountPOOL_STATS


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/struct_pool_memory_block.html0000644000175000017500000000764110511001742026076 0ustar ahodgkinsonahodgkinson XFLAIM: PoolMemoryBlock Struct Reference

PoolMemoryBlock Struct Reference

Header for blocks in a memory pool. More...

#include <ftk.h>

List of all members.

Public Attributes

PoolMemoryBlockpPrevBlock
 Points to the previous memory block in the memory pool.
FLMUINT uiBlockSize
 Total size of the memory block.
FLMUINT uiFreeOffset
 Offset in block where next allocation should be made.
FLMUINT uiFreeSize
 Amount of free memory left in block - from uiFreeOfs.


Detailed Description

Header for blocks in a memory pool.

This structure is at the head of each block that belongs to a pool of memory.


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/struct_pool_memory_block-members.html0000644000175000017500000000444310511001742027523 0ustar ahodgkinsonahodgkinson XFLAIM: Member List

PoolMemoryBlock Member List

This is the complete list of members for PoolMemoryBlock, including all inherited members.

pPrevBlockPoolMemoryBlock
uiBlockSizePoolMemoryBlock
uiFreeOffsetPoolMemoryBlock
uiFreeSizePoolMemoryBlock


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/group__retcodes.html0000644000175000017500000015315210511001742024141 0ustar ahodgkinsonahodgkinson XFLAIM: Return Codes

Return Codes


Defines

#define NE_FLM_OK   0
 0 - Operation was successful.
#define NE_FLM_BOF_HIT   0xC001
 0xC001 - Beginning of results encountered.
#define NE_FLM_EOF_HIT   0xC002
 0xC002 - End of results encountered.
#define NE_FLM_EXISTS   0xC004
 0xC004 - Object already exists.
#define NE_FLM_FAILURE   0xC005
 0xC005 - Internal failure.
#define NE_FLM_NOT_FOUND   0xC006
 0xC006 - An object was not found.
#define NE_FLM_BTREE_ERROR   0xC012
 0xC012 - Corruption found in b-tree.
#define NE_FLM_BTREE_FULL   0xC013
 0xC013 - B-tree cannot grow beyond current size.
#define NE_FLM_CONV_DEST_OVERFLOW   0xC01C
 0xC01C - Destination buffer not large enough to hold data.
#define NE_FLM_CONV_ILLEGAL   0xC01D
 0xC01D - Attempt to convert between data types is an unsupported conversion.
#define NE_FLM_CONV_NUM_OVERFLOW   0xC020
 0xC020 - Numeric overflow (> upper bound) converting to numeric type.
#define NE_FLM_DATA_ERROR   0xC022
 0xC022 - Corruption found in b-tree.
#define NE_FLM_ILLEGAL_OP   0xC026
 0xC026 - Illegal operation
#define NE_FLM_MEM   0xC037
 0xC037 - Attempt to allocate memory failed.
#define NE_FLM_NOT_UNIQUE   0xC03E
 0xC03E - Non-unique key.
#define NE_FLM_SYNTAX   0xC045
 0xC045 - Syntax error while parsing.
#define NE_FLM_NOT_IMPLEMENTED   0xC05F
 0xC05F - Attempt was made to use a feature that is not implemented.
#define NE_FLM_INVALID_PARM   0xC08B
 0xC08B - Invalid parameter passed into a function.
#define NE_FLM_IO_ACCESS_DENIED   0xC201
 0xC201 - Access to file is denied. Caller is not allowed access to a file.
#define NE_FLM_IO_BAD_FILE_HANDLE   0xC202
 0xC202 - Bad file handle or file descriptor.
#define NE_FLM_IO_COPY_ERR   0xC203
 0xC203 - Error occurred while copying a file.
#define NE_FLM_IO_DISK_FULL   0xC204
 0xC204 - Disk full.
#define NE_FLM_IO_END_OF_FILE   0xC205
 0xC205 - End of file reached while reading from the file.
#define NE_FLM_IO_OPEN_ERR   0xC206
 0xC206 - Error while opening the file.
#define NE_FLM_IO_SEEK_ERR   0xC207
 0xC207 - Error occurred while positioning (seeking) within a file.
#define NE_FLM_IO_DIRECTORY_ERR   0xC208
 0xC208 - Error occurred while accessing or deleting a directory.
#define NE_FLM_IO_PATH_NOT_FOUND   0xC209
 0xC209 - File not found.
#define NE_FLM_IO_TOO_MANY_OPEN_FILES   0xC20A
 0xC20A - Too many files open.
#define NE_FLM_IO_PATH_TOO_LONG   0xC20B
 0xC20B - File name too long.
#define NE_FLM_IO_NO_MORE_FILES   0xC20C
 0xC20C - No more files in directory.
#define NE_FLM_IO_DELETING_FILE   0xC20D
 0xC20D - Error occurred while deleting a file.
#define NE_FLM_IO_FILE_LOCK_ERR   0xC20E
 0xC20E - Error attempting to acquire a byte-range lock on a file.
#define NE_FLM_IO_FILE_UNLOCK_ERR   0xC20F
 0xC20F - Error attempting to release a byte-range lock on a file.
#define NE_FLM_IO_PATH_CREATE_FAILURE   0xC210
 0xC210 - Error occurred while attempting to create a directory or sub-directory.
#define NE_FLM_IO_RENAME_FAILURE   0xC211
 0xC211 - Error occurred while renaming a file.
#define NE_FLM_IO_INVALID_PASSWORD   0xC212
 0xC212 - Invalid file password.
#define NE_FLM_SETTING_UP_FOR_READ   0xC213
 0xC213 - Error occurred while setting up to perform a file read operation.
#define NE_FLM_SETTING_UP_FOR_WRITE   0xC214
 0xC214 - Error occurred while setting up to perform a file write operation.
#define NE_FLM_IO_CANNOT_REDUCE_PATH   0xC215
 0xC215 - Cannot reduce file name into more components.
#define NE_FLM_INITIALIZING_IO_SYSTEM   0xC216
 0xC216 - Error occurred while setting up to access the file system.
#define NE_FLM_FLUSHING_FILE   0xC217
 0xC217 - Error occurred while flushing file data buffers to disk.
#define NE_FLM_IO_INVALID_FILENAME   0xC218
 0xC218 - Invalid file name.
#define NE_FLM_IO_CONNECT_ERROR   0xC219
 0xC219 - Error connecting to a remote network resource.
#define NE_FLM_OPENING_FILE   0xC21A
 0xC21A - Unexpected error occurred while opening a file.
#define NE_FLM_DIRECT_OPENING_FILE   0xC21B
 0xC21B - Unexpected error occurred while opening a file in direct access mode.
#define NE_FLM_CREATING_FILE   0xC21C
 0xC21C - Unexpected error occurred while creating a file.
#define NE_FLM_DIRECT_CREATING_FILE   0xC21D
 0xC21D - Unexpected error occurred while creating a file in direct access mode.
#define NE_FLM_READING_FILE   0xC21E
 0xC21E - Unexpected error occurred while reading a file.
#define NE_FLM_DIRECT_READING_FILE   0xC21F
 0xC21F - Unexpected error occurred while reading a file in direct access mode.
#define NE_FLM_WRITING_FILE   0xC220
 0xC220 - Unexpected error occurred while writing to a file.
#define NE_FLM_DIRECT_WRITING_FILE   0xC221
 0xC221 - Unexpected error occurred while writing a file in direct access mode.
#define NE_FLM_POSITIONING_IN_FILE   0xC222
 0xC222 - Unexpected error occurred while positioning within a file.
#define NE_FLM_GETTING_FILE_SIZE   0xC223
 0xC223 - Unexpected error occurred while getting a file's size.
#define NE_FLM_TRUNCATING_FILE   0xC224
 0xC224 - Unexpected error occurred while truncating a file.
#define NE_FLM_PARSING_FILE_NAME   0xC225
 0xC225 - Unexpected error occurred while parsing a file's name.
#define NE_FLM_CLOSING_FILE   0xC226
 0xC226 - Unexpected error occurred while closing a file.
#define NE_FLM_GETTING_FILE_INFO   0xC227
 0xC227 - Unexpected error occurred while getting information about a file.
#define NE_FLM_EXPANDING_FILE   0xC228
 0xC228 - Unexpected error occurred while expanding a file.
#define NE_FLM_GETTING_FREE_BLOCKS   0xC229
 0xC229 - Unexpected error getting free blocks from file system.
#define NE_FLM_CHECKING_FILE_EXISTENCE   0xC22A
 0xC22A - Unexpected error occurred while checking to see if a file exists.
#define NE_FLM_RENAMING_FILE   0xC22B
 0xC22B - Unexpected error occurred while renaming a file.
#define NE_FLM_SETTING_FILE_INFO   0xC22C
 0xC22C - Unexpected error occurred while setting a file's information.
#define NE_FLM_IO_PENDING   0xC22D
 0xC22D - I/O has not yet completed
#define NE_FLM_ASYNC_FAILED   0xC22E
 0xC22E - An async I/O operation failed
#define NE_FLM_MISALIGNED_IO   0xC22F
 0xC22F - Misaligned buffer or offset encountered during I/O request
#define NE_FLM_STREAM_DECOMPRESS_ERROR   0xC400
 0xC400 - Error decompressing data stream.
#define NE_FLM_STREAM_NOT_COMPRESSED   0xC401
 0xC401 - Attempting to decompress a data stream that is not compressed.
#define NE_FLM_STREAM_TOO_MANY_FILES   0xC402
 0xC402 - Too many files in input stream.
#define NE_FLM_COULD_NOT_CREATE_SEMAPHORE   0xC500
 0xC500 - Could not create a semaphore.
#define NE_FLM_BAD_UTF8   0xC501
 0xC501 - An invalid byte sequence was found in a UTF-8 string
#define NE_FLM_ERROR_WAITING_ON_SEMAPHORE   0xC502
 0xC502 - Error occurred while waiting on a sempahore.
#define NE_FLM_BAD_SEN   0xC503
 0xC503 - Invalid simple encoded number.
#define NE_FLM_COULD_NOT_START_THREAD   0xC504
 0xC504 - Problem starting a new thread.
#define NE_FLM_BAD_BASE64_ENCODING   0xC505
 0xC505 - Invalid base64 sequence encountered.
#define NE_FLM_STREAM_EXISTS   0xC506
 0xC506 - Stream file already exists.
#define NE_FLM_MULTIPLE_MATCHES   0xC507
 0xC507 - Multiple items matched but only one match was expected.
#define NE_FLM_BTREE_KEY_SIZE   0xC508
 0xC508 - Invalid b-tree key size.
#define NE_FLM_BTREE_BAD_STATE   0xC509
 0xC509 - B-tree operation cannot be completed.
#define NE_FLM_COULD_NOT_CREATE_MUTEX   0xC50A
 0xC50A - Error occurred while creating or initializing a mutex.
#define NE_FLM_BAD_PLATFORM_FORMAT   0xC50B
 0xC50B - In-memory alignment of disk structures is incorrect
#define NE_FLM_LOCK_REQ_TIMEOUT   0xC50C
 0xC50C - Timeout while waiting for a lock object
#define NE_FLM_WAIT_TIMEOUT   0xC50D
 0xC50D - Timeout while waiting on a semaphore, condition variable, or reader/writer lock
#define NE_FLM_NOIP_ADDR   0xC900
 0xC900 - IP address not found
#define NE_FLM_SOCKET_FAIL   0xC901
 0xC901 - IP socket failure
#define NE_FLM_CONNECT_FAIL   0xC902
 0xC902 - TCP/IP connection failure
#define NE_FLM_BIND_FAIL   0xC903
 0xC903 - The TCP/IP services on your system may not be configured or installed.
#define NE_FLM_LISTEN_FAIL   0xC904
 0xC904 - TCP/IP listen failed
#define NE_FLM_ACCEPT_FAIL   0xC905
 0xC905 - TCP/IP accept failed
#define NE_FLM_SELECT_ERR   0xC906
 0xC906 - TCP/IP select failed
#define NE_FLM_SOCKET_SET_OPT_FAIL   0xC907
 0xC907 - TCP/IP socket operation failed
#define NE_FLM_SOCKET_DISCONNECT   0xC908
 0xC908 - TCP/IP disconnected
#define NE_FLM_SOCKET_READ_FAIL   0xC909
 0xC909 - TCP/IP read failed
#define NE_FLM_SOCKET_WRITE_FAIL   0xC90A
 0xC90A - TCP/IP write failed
#define NE_FLM_SOCKET_READ_TIMEOUT   0xC90B
 0xC90B - TCP/IP read timeout
#define NE_FLM_SOCKET_WRITE_TIMEOUT   0xC90C
 0xC90C - TCP/IP write timeout
#define NE_FLM_SOCKET_ALREADY_CLOSED   0xC90D
 0xC90D - Connection already closed

Typedefs

typedef FLMINT32 RCODE
 Return code.

Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/group__flm__languages.html0000644000175000017500000005023710511001742025274 0ustar ahodgkinsonahodgkinson XFLAIM: Flm_languages

Flm_languages


Defines

#define FLM_US_LANG   0
 English, United States.
#define FLM_AF_LANG   1
 Afrikaans.
#define FLM_AR_LANG   2
 Arabic.
#define FLM_CA_LANG   3
 Catalan.
#define FLM_HR_LANG   4
 Croatian.
#define FLM_CZ_LANG   5
 Czech.
#define FLM_DK_LANG   6
 Danish.
#define FLM_NL_LANG   7
 Dutch.
#define FLM_OZ_LANG   8
 English, Australia.
#define FLM_CE_LANG   9
 English, Canada.
#define FLM_UK_LANG   10
 English, United Kingdom.
#define FLM_FA_LANG   11
 Farsi.
#define FLM_SU_LANG   12
 Finnish.
#define FLM_CF_LANG   13
 French, Canada.
#define FLM_FR_LANG   14
 French, France.
#define FLM_GA_LANG   15
 Galician.
#define FLM_DE_LANG   16
 German, Germany.
#define FLM_SD_LANG   17
 German, Switzerland.
#define FLM_GR_LANG   18
 Greek.
#define FLM_HE_LANG   19
 Hebrew.
#define FLM_HU_LANG   20
 Hungarian.
#define FLM_IS_LANG   21
 Icelandic.
#define FLM_IT_LANG   22
 Italian.
#define FLM_NO_LANG   23
 Norwegian.
#define FLM_PL_LANG   24
 Polish.
#define FLM_BR_LANG   25
 Portuguese, Brazil.
#define FLM_PO_LANG   26
 Portuguese, Portugal.
#define FLM_RU_LANG   27
 Russian.
#define FLM_SL_LANG   28
 Slovak.
#define FLM_ES_LANG   29
 Spanish.
#define FLM_SV_LANG   30
 Swedish.
#define FLM_YK_LANG   31
 Ukrainian.
#define FLM_UR_LANG   32
 Urdu.
#define FLM_TK_LANG   33
 Turkey.
#define FLM_JP_LANG   34
 Japanese.
#define FLM_KO_LANG   35
 Korean.
#define FLM_CT_LANG   36
 Chinese-Traditional.
#define FLM_CS_LANG   37
 Chinese-Simplified.
#define FLM_LA_LANG   38
 another Asian language

Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/group__compare__rules.html0000644000175000017500000001306510511001742025326 0ustar ahodgkinsonahodgkinson XFLAIM: Compare_rules

Compare_rules


Defines

#define FLM_COMP_CASE_INSENSITIVE   0x0001
 0x0001 = Do case sensitive comparison.
#define FLM_COMP_COMPRESS_WHITESPACE   0x0002
 0x0002 = Compare multiple whitespace characters as a single space.
#define FLM_COMP_NO_WHITESPACE   0x0004
 0x0004 = Ignore all whitespace during comparison.
#define FLM_COMP_NO_UNDERSCORES   0x0008
 0x0008 = Ignore all underscore characters during comparison.
#define FLM_COMP_NO_DASHES   0x0010
 0x0010 = Ignore all dash characters during comparison.
#define FLM_COMP_WHITESPACE_AS_SPACE   0x0020
 0x0020 = Treat newlines and tabs as spaces during comparison.
#define FLM_COMP_IGNORE_LEADING_SPACE   0x0040
 0x0040 = Ignore leading space characters during comparison.
#define FLM_COMP_IGNORE_TRAILING_SPACE   0x0080
 0x0080 = Ignore trailing space characters during comparison.

Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/modules.html0000644000175000017500000000222710511001742022422 0ustar ahodgkinsonahodgkinson XFLAIM: Module Index

XFLAIM Modules

Here is a list of all modules:
Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/globals.html0000644000175000017500000005340510511001742022401 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members

Here is a list of all documented file members with links to the documentation:

- a -

- e -

- f -

- n -

  • NE_FLM_ACCEPT_FAIL : ftk.h
  • NE_FLM_ASYNC_FAILED : ftk.h
  • NE_FLM_BAD_BASE64_ENCODING : ftk.h
  • NE_FLM_BAD_PLATFORM_FORMAT : ftk.h
  • NE_FLM_BAD_SEN : ftk.h
  • NE_FLM_BAD_UTF8 : ftk.h
  • NE_FLM_BIND_FAIL : ftk.h
  • NE_FLM_BOF_HIT : ftk.h
  • NE_FLM_BTREE_BAD_STATE : ftk.h
  • NE_FLM_BTREE_ERROR : ftk.h
  • NE_FLM_BTREE_FULL : ftk.h
  • NE_FLM_BTREE_KEY_SIZE : ftk.h
  • NE_FLM_CHECKING_FILE_EXISTENCE : ftk.h
  • NE_FLM_CLOSING_FILE : ftk.h
  • NE_FLM_CONNECT_FAIL : ftk.h
  • NE_FLM_CONV_DEST_OVERFLOW : ftk.h
  • NE_FLM_CONV_ILLEGAL : ftk.h
  • NE_FLM_CONV_NUM_OVERFLOW : ftk.h
  • NE_FLM_COULD_NOT_CREATE_MUTEX : ftk.h
  • NE_FLM_COULD_NOT_CREATE_SEMAPHORE : ftk.h
  • NE_FLM_COULD_NOT_START_THREAD : ftk.h
  • NE_FLM_CREATING_FILE : ftk.h
  • NE_FLM_DATA_ERROR : ftk.h
  • NE_FLM_DIRECT_CREATING_FILE : ftk.h
  • NE_FLM_DIRECT_OPENING_FILE : ftk.h
  • NE_FLM_DIRECT_READING_FILE : ftk.h
  • NE_FLM_DIRECT_WRITING_FILE : ftk.h
  • NE_FLM_EOF_HIT : ftk.h
  • NE_FLM_ERROR_WAITING_ON_SEMAPHORE : ftk.h
  • NE_FLM_EXISTS : ftk.h
  • NE_FLM_EXPANDING_FILE : ftk.h
  • NE_FLM_FAILURE : ftk.h
  • NE_FLM_FLUSHING_FILE : ftk.h
  • NE_FLM_GETTING_FILE_INFO : ftk.h
  • NE_FLM_GETTING_FILE_SIZE : ftk.h
  • NE_FLM_GETTING_FREE_BLOCKS : ftk.h
  • NE_FLM_ILLEGAL_OP : ftk.h
  • NE_FLM_INITIALIZING_IO_SYSTEM : ftk.h
  • NE_FLM_INVALID_PARM : ftk.h
  • NE_FLM_IO_ACCESS_DENIED : ftk.h
  • NE_FLM_IO_BAD_FILE_HANDLE : ftk.h
  • NE_FLM_IO_CANNOT_REDUCE_PATH : ftk.h
  • NE_FLM_IO_CONNECT_ERROR : ftk.h
  • NE_FLM_IO_COPY_ERR : ftk.h
  • NE_FLM_IO_DELETING_FILE : ftk.h
  • NE_FLM_IO_DIRECTORY_ERR : ftk.h
  • NE_FLM_IO_DISK_FULL : ftk.h
  • NE_FLM_IO_END_OF_FILE : ftk.h
  • NE_FLM_IO_FILE_LOCK_ERR : ftk.h
  • NE_FLM_IO_FILE_UNLOCK_ERR : ftk.h
  • NE_FLM_IO_INVALID_FILENAME : ftk.h
  • NE_FLM_IO_INVALID_PASSWORD : ftk.h
  • NE_FLM_IO_NO_MORE_FILES : ftk.h
  • NE_FLM_IO_OPEN_ERR : ftk.h
  • NE_FLM_IO_PATH_CREATE_FAILURE : ftk.h
  • NE_FLM_IO_PATH_NOT_FOUND : ftk.h
  • NE_FLM_IO_PATH_TOO_LONG : ftk.h
  • NE_FLM_IO_PENDING : ftk.h
  • NE_FLM_IO_RENAME_FAILURE : ftk.h
  • NE_FLM_IO_SEEK_ERR : ftk.h
  • NE_FLM_IO_TOO_MANY_OPEN_FILES : ftk.h
  • NE_FLM_LISTEN_FAIL : ftk.h
  • NE_FLM_LOCK_REQ_TIMEOUT : ftk.h
  • NE_FLM_MEM : ftk.h
  • NE_FLM_MISALIGNED_IO : ftk.h
  • NE_FLM_MULTIPLE_MATCHES : ftk.h
  • NE_FLM_NOIP_ADDR : ftk.h
  • NE_FLM_NOT_FOUND : ftk.h
  • NE_FLM_NOT_IMPLEMENTED : ftk.h
  • NE_FLM_NOT_UNIQUE : ftk.h
  • NE_FLM_OK : ftk.h
  • NE_FLM_OPENING_FILE : ftk.h
  • NE_FLM_PARSING_FILE_NAME : ftk.h
  • NE_FLM_POSITIONING_IN_FILE : ftk.h
  • NE_FLM_READING_FILE : ftk.h
  • NE_FLM_RENAMING_FILE : ftk.h
  • NE_FLM_SELECT_ERR : ftk.h
  • NE_FLM_SETTING_FILE_INFO : ftk.h
  • NE_FLM_SETTING_UP_FOR_READ : ftk.h
  • NE_FLM_SETTING_UP_FOR_WRITE : ftk.h
  • NE_FLM_SOCKET_ALREADY_CLOSED : ftk.h
  • NE_FLM_SOCKET_DISCONNECT : ftk.h
  • NE_FLM_SOCKET_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_TIMEOUT : ftk.h
  • NE_FLM_SOCKET_SET_OPT_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_TIMEOUT : ftk.h
  • NE_FLM_STREAM_DECOMPRESS_ERROR : ftk.h
  • NE_FLM_STREAM_EXISTS : ftk.h
  • NE_FLM_STREAM_NOT_COMPRESSED : ftk.h
  • NE_FLM_STREAM_TOO_MANY_FILES : ftk.h
  • NE_FLM_SYNTAX : ftk.h
  • NE_FLM_TRUNCATING_FILE : ftk.h
  • NE_FLM_WAIT_TIMEOUT : ftk.h
  • NE_FLM_WRITING_FILE : ftk.h
  • newline() : ftk.h

- r -


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/globals_func.html0000644000175000017500000000414310511001742023407 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members  


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/globals_type.html0000644000175000017500000000320610511001742023434 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members  


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/globals_enum.html0000644000175000017500000000350110511001742023415 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members  


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/globals_eval.html0000644000175000017500000001057010511001742023404 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members  


Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/globals_defs.html0000644000175000017500000004313410511001742023400 0ustar ahodgkinsonahodgkinson XFLAIM: Class Members

 

- f -

- n -

  • NE_FLM_ACCEPT_FAIL : ftk.h
  • NE_FLM_ASYNC_FAILED : ftk.h
  • NE_FLM_BAD_BASE64_ENCODING : ftk.h
  • NE_FLM_BAD_PLATFORM_FORMAT : ftk.h
  • NE_FLM_BAD_SEN : ftk.h
  • NE_FLM_BAD_UTF8 : ftk.h
  • NE_FLM_BIND_FAIL : ftk.h
  • NE_FLM_BOF_HIT : ftk.h
  • NE_FLM_BTREE_BAD_STATE : ftk.h
  • NE_FLM_BTREE_ERROR : ftk.h
  • NE_FLM_BTREE_FULL : ftk.h
  • NE_FLM_BTREE_KEY_SIZE : ftk.h
  • NE_FLM_CHECKING_FILE_EXISTENCE : ftk.h
  • NE_FLM_CLOSING_FILE : ftk.h
  • NE_FLM_CONNECT_FAIL : ftk.h
  • NE_FLM_CONV_DEST_OVERFLOW : ftk.h
  • NE_FLM_CONV_ILLEGAL : ftk.h
  • NE_FLM_CONV_NUM_OVERFLOW : ftk.h
  • NE_FLM_COULD_NOT_CREATE_MUTEX : ftk.h
  • NE_FLM_COULD_NOT_CREATE_SEMAPHORE : ftk.h
  • NE_FLM_COULD_NOT_START_THREAD : ftk.h
  • NE_FLM_CREATING_FILE : ftk.h
  • NE_FLM_DATA_ERROR : ftk.h
  • NE_FLM_DIRECT_CREATING_FILE : ftk.h
  • NE_FLM_DIRECT_OPENING_FILE : ftk.h
  • NE_FLM_DIRECT_READING_FILE : ftk.h
  • NE_FLM_DIRECT_WRITING_FILE : ftk.h
  • NE_FLM_EOF_HIT : ftk.h
  • NE_FLM_ERROR_WAITING_ON_SEMAPHORE : ftk.h
  • NE_FLM_EXISTS : ftk.h
  • NE_FLM_EXPANDING_FILE : ftk.h
  • NE_FLM_FAILURE : ftk.h
  • NE_FLM_FLUSHING_FILE : ftk.h
  • NE_FLM_GETTING_FILE_INFO : ftk.h
  • NE_FLM_GETTING_FILE_SIZE : ftk.h
  • NE_FLM_GETTING_FREE_BLOCKS : ftk.h
  • NE_FLM_ILLEGAL_OP : ftk.h
  • NE_FLM_INITIALIZING_IO_SYSTEM : ftk.h
  • NE_FLM_INVALID_PARM : ftk.h
  • NE_FLM_IO_ACCESS_DENIED : ftk.h
  • NE_FLM_IO_BAD_FILE_HANDLE : ftk.h
  • NE_FLM_IO_CANNOT_REDUCE_PATH : ftk.h
  • NE_FLM_IO_CONNECT_ERROR : ftk.h
  • NE_FLM_IO_COPY_ERR : ftk.h
  • NE_FLM_IO_DELETING_FILE : ftk.h
  • NE_FLM_IO_DIRECTORY_ERR : ftk.h
  • NE_FLM_IO_DISK_FULL : ftk.h
  • NE_FLM_IO_END_OF_FILE : ftk.h
  • NE_FLM_IO_FILE_LOCK_ERR : ftk.h
  • NE_FLM_IO_FILE_UNLOCK_ERR : ftk.h
  • NE_FLM_IO_INVALID_FILENAME : ftk.h
  • NE_FLM_IO_INVALID_PASSWORD : ftk.h
  • NE_FLM_IO_NO_MORE_FILES : ftk.h
  • NE_FLM_IO_OPEN_ERR : ftk.h
  • NE_FLM_IO_PATH_CREATE_FAILURE : ftk.h
  • NE_FLM_IO_PATH_NOT_FOUND : ftk.h
  • NE_FLM_IO_PATH_TOO_LONG : ftk.h
  • NE_FLM_IO_PENDING : ftk.h
  • NE_FLM_IO_RENAME_FAILURE : ftk.h
  • NE_FLM_IO_SEEK_ERR : ftk.h
  • NE_FLM_IO_TOO_MANY_OPEN_FILES : ftk.h
  • NE_FLM_LISTEN_FAIL : ftk.h
  • NE_FLM_LOCK_REQ_TIMEOUT : ftk.h
  • NE_FLM_MEM : ftk.h
  • NE_FLM_MISALIGNED_IO : ftk.h
  • NE_FLM_MULTIPLE_MATCHES : ftk.h
  • NE_FLM_NOIP_ADDR : ftk.h
  • NE_FLM_NOT_FOUND : ftk.h
  • NE_FLM_NOT_IMPLEMENTED : ftk.h
  • NE_FLM_NOT_UNIQUE : ftk.h
  • NE_FLM_OK : ftk.h
  • NE_FLM_OPENING_FILE : ftk.h
  • NE_FLM_PARSING_FILE_NAME : ftk.h
  • NE_FLM_POSITIONING_IN_FILE : ftk.h
  • NE_FLM_READING_FILE : ftk.h
  • NE_FLM_RENAMING_FILE : ftk.h
  • NE_FLM_SELECT_ERR : ftk.h
  • NE_FLM_SETTING_FILE_INFO : ftk.h
  • NE_FLM_SETTING_UP_FOR_READ : ftk.h
  • NE_FLM_SETTING_UP_FOR_WRITE : ftk.h
  • NE_FLM_SOCKET_ALREADY_CLOSED : ftk.h
  • NE_FLM_SOCKET_DISCONNECT : ftk.h
  • NE_FLM_SOCKET_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_FAIL : ftk.h
  • NE_FLM_SOCKET_READ_TIMEOUT : ftk.h
  • NE_FLM_SOCKET_SET_OPT_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_FAIL : ftk.h
  • NE_FLM_SOCKET_WRITE_TIMEOUT : ftk.h
  • NE_FLM_STREAM_DECOMPRESS_ERROR : ftk.h
  • NE_FLM_STREAM_EXISTS : ftk.h
  • NE_FLM_STREAM_NOT_COMPRESSED : ftk.h
  • NE_FLM_STREAM_TOO_MANY_FILES : ftk.h
  • NE_FLM_SYNTAX : ftk.h
  • NE_FLM_TRUNCATING_FILE : ftk.h
  • NE_FLM_WAIT_TIMEOUT : ftk.h
  • NE_FLM_WRITING_FILE : ftk.h

Generated on Wed Oct 4 12:56:33 2006 for XFLAIM by  doxygen 1.4.6
libxflaim-5.1.969/docs/docs/html/index.html0000644000175000017500000000045610511001742022063 0ustar ahodgkinsonahodgkinson XFLAIM libxflaim-5.1.969/docs/docs/html/tree.html0000644000175000017500000001371610511001742021716 0ustar ahodgkinsonahodgkinson TreeView libxflaim-5.1.969/docs/docs/html/ftv2blank.png0000644000175000017500000000025610511001742022463 0ustar ahodgkinsonahodgkinson‰PNG  IHDR–ÖGtRNS”ý®tEXtSoftwaregif2png 2.4.2£^G%tEXtCommentUlead GIF SmartSaver Ver 2.0!ø×^SIDATxÚc8À€€0àBx<2Ër|IEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2doc.png0000644000175000017500000000037710511001742022145 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-PLTEÿÿÿÿÿÿÿÿÀÀÀ€€€ÿ³½ûÈtRNS@æØftEXtSoftwaregif2png 2.4.2£^GvIDATxÚc````c``d'0bqâ$8`qÊ'3001£2 pT2Si`q'Ê€\\À”¢ˆRKRSiÈ”„RRÅi 6त¤h‚ÄQ‚kqMaNUò`'E$pgc 0Åo#õÐàUƒG½IEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2folderclosed.png0000644000175000017500000000040310511001742024033 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-PLTEÿÿÿÿÿÿÀÀÀ€€€€€Bî@átRNS@æØftEXtSoftwaregif2png 2.4.2£^G}IDATxÚŽÁ „0C#ŒwûB½+øemï¢ÍÿÿŠ3­îZö°Úð†¦ˆ@S‚š×QB%®zv íêyyIsáò2_I¹í#` Í6fä@K´ÛÛåîx ù–m‹¢nùPd¦p¶P Óx†˜¦Þ]%Íþ߯ ˜`½ IEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2folderopen.png0000644000175000017500000000040510511001742023525 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-PLTEÿÿÿÿÿÿÿÿÀÀÀ€€€€€’2ŒåtRNS@æØftEXtSoftwaregif2png 2.4.2£^G|IDATxÚ…A‚0E_õ-SÖÆ××Ò ±Þÿ: !¢oóþÿ‹€ j<*aÅÓCiÁ´«ùp¬ƒÊ»û¤‹®&F¶LèÃú³}¨ë¥FTFN*âΕ=º°d“…› sqƒu×ò§ò ûÒëCùýò ¤)ËÚG¬DIEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2lastnode.png0000644000175000017500000000035110511001742023201 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù0PLTEÿÿÿ€€€Ó tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0io?ÍIDATxÚc`0ÀO È3$§ˆÊ3=IEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2link.png0000644000175000017500000000054610511001742022333 0ustar ahodgkinsonahodgkinson‰PNG  IHDR_Tq-0PLTEÿÿÿ€ÿÿ€ÿ€€€€€ÀÀÀ~›tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0Ù åJIDATxÚ}Œ± Â0Eo3´c~¡SW‹~€n®Z²¾:d5¸dì/Ÿy!PÐÉÍᾨ‚àf}ÀžQp@Ï­tÛbâ ÁÌa;j»;éhº¬Q$ìÜKhíÒ^~ý’¨m„²((8ÊÒLF-YÌ8\‹Ìãþ+qúy¼gy…TiÞÞ/øÏq•+¡xÐÿIEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2mlastnode.png0000644000175000017500000000024010511001742023353 0ustar ahodgkinsonahodgkinson‰PNG  IHDRÃÃÄy PLTEÿÿÿ€€€<^»,tRNS@æØftEXtSoftwaregif2png 2.4.2£^G#IDATxÚc`   `„Œ¡¡ ɨµPʉamºÀÜiÈIEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2mnode.png0000644000175000017500000000030210511001742022466 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù$PLTEÀÀÀ€€€S¾™tRNS@æØftEXtSoftwaregif2png 2.4.2£^G*IDATxÚc` .àBt§RT÷n €ñÁbÜLJJÜÜÜÈ"05˜ÚÑ·y'ª÷–IEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2node.png0000644000175000017500000000035310511001742022317 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù0PLTEÿÿÿ€€€Ó tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0io?ÍIDATxÚc`0ÀO ˆVŒÂ.RâúE:áIEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2plastnode.png0000644000175000017500000000024510511001742023363 0ustar ahodgkinsonahodgkinson‰PNG  IHDRÃÃÄy PLTEÿÿÿ€€€<^»,tRNS@æØftEXtSoftwaregif2png 2.4.2£^G(IDATxÚc` 0ach(`2µ BY 1,nÁÂåíu§IEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2pnode.png0000644000175000017500000000031010511001742022470 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù$PLTEÀÀÀ€€€S¾™tRNS@æØftEXtSoftwaregif2png 2.4.2£^G0IDATxÚc` .àBn&8ââTŠÊàÞ >DŒ›II‰››Y¦S;:ºk/ªËoIEND®B`‚libxflaim-5.1.969/docs/docs/html/ftv2vertline.png0000644000175000017500000000034510511001742023223 0ustar ahodgkinsonahodgkinson‰PNG  IHDRLƒ1Ù0PLTEÿÿÿ€€€Ó tRNS@æØftEXtSoftwaregif2png 2.4.2£^G&tEXtCommentUlead GIF SmartSaver Ver 2.0io?ÍIDATxÚc`0ÀO[!¦å<è:IEND®B`‚libxflaim-5.1.969/src/0000755000175000017500000000000010511001742016024 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/src/kyunlock.cpp0000644000175000017500000000667710511001742020407 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the routines to initialize and set up // structures for indexing. // // Tabs: 3 // // Copyright (c) 1992-2000, 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kyunlock.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Setup routine for the KREF_CNTRL structure for record updates. ****************************************************************************/ RCODE F_Db::krefCntrlCheck( void) { RCODE rc = NE_XFLM_OK; // Check if we need to flush keys between updates, but not during the // processing of an update. if( m_bKrefSetup) { if( isKrefOverThreshold() || (m_pOldNodeList && m_pOldNodeList->getNodeCount())) { if (RC_BAD( rc = keysCommit( FALSE))) { goto Exit; } } } else { m_uiKrefCount = 0; m_uiTotalKrefBytes = 0; m_pKrefPool = NULL; m_bReuseKrefPool = FALSE; m_bKrefCompoundKey = FALSE; m_pKrefReset = NULL; m_bKrefSetup = TRUE; if (m_eTransType == XFLM_UPDATE_TRANS) { m_pKrefPool = &m_pDatabase->m_krefPool; m_bReuseKrefPool = TRUE; m_pKrefPool->poolReset( NULL, TRUE); } else { m_tmpKrefPool.poolFree(); m_tmpKrefPool.poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE); m_pKrefPool = &m_tmpKrefPool; m_bReuseKrefPool = FALSE; } if( !m_pKrefTbl) { if( RC_BAD( rc = f_alloc( DEFAULT_KREF_TBL_SIZE * sizeof( KREF_ENTRY *), &m_pKrefTbl))) { goto Exit; } m_uiKrefTblSize = DEFAULT_KREF_TBL_SIZE; } if( !m_pucKrefKeyBuf) { if (RC_BAD( rc = f_alloc( XFLM_MAX_KEY_SIZE, &m_pucKrefKeyBuf))) { goto Exit; } } } m_pKrefReset = m_pKrefPool->poolMark(); flmAssert( m_pucKrefKeyBuf); Exit: if (RC_BAD( rc)) { krefCntrlFree(); } return( rc); } /**************************************************************************** Desc: Frees the memory associated with the KREF ****************************************************************************/ void F_Db::krefCntrlFree( void) { if( m_bKrefSetup) { if( m_bReuseKrefPool) { m_pKrefPool->poolReset( NULL, TRUE); } else { m_pKrefPool->poolFree(); m_pKrefPool->poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE); } m_pKrefPool = NULL; if( m_pKrefTbl && m_uiKrefTblSize != DEFAULT_KREF_TBL_SIZE) { f_free( &m_pKrefTbl); m_uiKrefTblSize = 0; } m_uiKrefCount = 0; m_uiTotalKrefBytes = 0; m_bReuseKrefPool = FALSE; m_bKrefCompoundKey = FALSE; m_pKrefReset = NULL; m_bKrefSetup = FALSE; if (m_pOldNodeList) { m_pOldNodeList->resetList(); } } } libxflaim-5.1.969/src/fdict.h0000644000175000017500000007064110511001742017276 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: F_Dict class definitions - internal object for database's // dictionary. // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdict.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FDICT_H #define FDICT_H #define FLM_HIGH_FIXED_ELEMENT_NUM 0xFFFF #define FLM_LOW_EXT_ELEMENT_NUM (FLM_HIGH_FIXED_ELEMENT_NUM + 1) #define FLM_HIGH_FIXED_ATTRIBUTE_NUM 0xFFFF #define FLM_LOW_EXT_ATTRIBUTE_NUM (FLM_HIGH_FIXED_ATTRIBUTE_NUM + 1) #define MAX_EXT_ATTR_ELM_ARRAY_SIZE 0xFFFF struct IXD; struct ICD; class F_Database; class F_AttrItem; class IF_CCS; /**************************************************************************** Desc: Attribute/Element definition structure. ****************************************************************************/ typedef struct AttrElmDef { // IMPORTANT NOTE: If adding pointers to this structure, be sure // to add code to F_Dict->clone() method to set them up properly. FLMUINT uiFlags; // Lower four bits has data type // Upper four bits has state // High bit indicates if it is an // attribute or not. // Value of zero means slot is not // used - allows us to memset an array // to zeroes and have all slots unused. #define ATTR_ELM_DATA_TYPE_MASK 0x000F #define ATTR_ELM_STATE_MASK 0x00F0 #define ATTR_ELM_STATE_ACTIVE 0x0010 // Normal active attribute or element #define ATTR_ELM_STATE_CHECKING 0x0020 // Attribute or element has been marked // to be checked #define ATTR_ELM_STATE_PURGE 0x0040 // Purge this attribute or element // from the database. // And delete the dictionary definition #define ATTR_ELM_FLAGS_MASK 0x0F00 #define ATTR_ELM_NS_DECL 0x0100 // Attribute is a namespace declaration #define ATTR_ELM_UNIQUE_SUBELMS 0x0200 // Element's sub-elements must all have unique name ids ICD * pFirstIcd; // Points to first ICD attribute or element is // indexed in. NULL if not indexed. } ATTR_ELM_DEF; /**************************************************************************** Desc: Extended attribute/element definition structure. ****************************************************************************/ typedef struct ExtAttrElmDef { FLMUINT uiDictNum; ATTR_ELM_DEF attrElmDef; } EXT_ATTR_ELM_DEF; /***************************************************************************** Desc: Dictionary definition info object *****************************************************************************/ class F_AttrElmInfo : public F_Object { public: F_AttrElmInfo() { m_pDocNode = NULL; m_pTargetNamespaceAttr = NULL; m_pNameAttr = NULL; resetInfo(); } ~F_AttrElmInfo() { resetInfo(); } void resetInfo( void); FINLINE FLMUINT getDataType( void) { return( m_uiDataType); } FINLINE FLMUINT getState( void) { return( m_uiState); } FINLINE FLMUINT getFlags( void) { return( m_uiFlags); } private: FLMUINT m_uiDictNum; FLMUINT m_uiDataType; FLMUINT m_uiFlags; FLMUINT m_uiState; ICD * m_pFirstIcd; IF_DOMNode * m_pDocNode; IF_DOMNode * m_pTargetNamespaceAttr; IF_DOMNode * m_pNameAttr; friend class F_Db; friend class F_DOMNode; friend class F_Dict; friend class F_NameTable; friend class F_NodeVerifier; friend class F_Query; }; /************************************************************************** Desc: Get an attribute or element's type. **************************************************************************/ FINLINE FLMUINT attrElmGetType( ATTR_ELM_DEF * pAttrElmDef) { return( pAttrElmDef->uiFlags & ATTR_ELM_DATA_TYPE_MASK); } /************************************************************************** Desc: See if an attribute or element is indexed. **************************************************************************/ FINLINE FLMBOOL attrElmIsIndexed( ATTR_ELM_DEF * pAttrElmDef) { return( pAttrElmDef->pFirstIcd ? TRUE : FALSE); } /************************************************************************** Desc: Get an attribute or element's state. **************************************************************************/ FINLINE FLMUINT attrElmGetState( ATTR_ELM_DEF * pAttrElmDef) { return( pAttrElmDef->uiFlags & ATTR_ELM_STATE_MASK); } /************************************************************************** Desc: Set an attribute or element's state. **************************************************************************/ FINLINE void attrElmSetState( ATTR_ELM_DEF * pAttrElmDef, FLMUINT uiState ) { pAttrElmDef->uiFlags = (pAttrElmDef->uiFlags & (~(ATTR_ELM_STATE_MASK))) | (uiState & ATTR_ELM_STATE_MASK); } /************************************************************************** Desc: Get an attribute or element's flags. **************************************************************************/ FINLINE FLMUINT attrElmGetFlags( ATTR_ELM_DEF * pAttrElmDef) { return( pAttrElmDef->uiFlags & ATTR_ELM_FLAGS_MASK); } /************************************************************************** Desc: Set an attribute or element's flags.. **************************************************************************/ FINLINE void attrElmSetFlags( ATTR_ELM_DEF * pAttrElmDef, FLMUINT uiFlags) { pAttrElmDef->uiFlags = (pAttrElmDef->uiFlags & (~(ATTR_ELM_FLAGS_MASK))) | (uiFlags & ATTR_ELM_FLAGS_MASK); } /**************************************************************************** Struct: LFILE (Logical File) Desc: This keeps track of the logical file information for an index or a Collection. ****************************************************************************/ typedef struct LFILE { // IMPORTANT NOTE: If adding pointers to this structure, be sure // to add code to F_Dict->clone() method to set them up properly. FLMUINT uiRootBlk; // Address of root block. FLMUINT uiBlkAddress; // Block address of LFile entry. FLMUINT uiOffsetInBlk; // Offset within block of entry. FLMUINT uiLfNum; // Index number or collection number. eLFileType eLfType; // Type of logical file FLMUINT uiEncId; // Encryption Id (0 if not encrypted) } LFILE; /**************************************************************************** Struct: F_COLLECTION (Collection) Desc: This keeps track of collections. ****************************************************************************/ typedef struct F_COLLECTION { LFILE lfInfo; // B-Tree information FLMBOOL bNeedToUpdateNodes; // Do we need to write out node info. // at commit time? FLMUINT64 ui64NextNodeId; // Next Node ID FLMUINT64 ui64FirstDocId; // First document ID FLMUINT64 ui64LastDocId; // Last document ID } F_COLLECTION; /**************************************************************************** Struct: F_PREFIX (Prefix) Desc: This keeps track of Prefixes. ****************************************************************************/ typedef struct F_PREFIX { FLMUINT64 ui64PrefixId; FLMUNICODE * puzPrefixName; } F_PREFIX; /**************************************************************************** Struct: F_ENCDEF (Encryption Definition) Desc: This keeps track of encryption definitions ****************************************************************************/ typedef struct { FLMUINT64 ui64EncDefId; FLMUINT64 ui64DocumentId; FLMUNICODE * puzEncDefName; FLMUINT uiEncKeySize; IF_CCS * pCcs; } F_ENCDEF; /**************************************************************************** Struct: IXD (Index Definition) Desc: This structure holds the information for an index definition. There may be multiple IXDs for the same index number. ****************************************************************************/ typedef struct IXD { // IMPORTANT NOTE: If adding pointers to this structure, be sure // to add code to F_Dict->clone() method to set them up properly. // ALSO, fixup indexDefsSame function in fdict.cpp FLMUINT uiIndexNum; // Index number. FLMUINT uiCollectionNum; // Collection number being indexed. ICD * pIcdTree; // Points to ICD tree ICD * pFirstKey; // Points to first key component ICD * pLastKey; // Points to last key component ICD * pFirstContext; // Points to first context-only component ICD * pLastContext; // Points to last context-only component ICD * pFirstData; // Points to first data component ICD * pLastData; // Points to last data component FLMUINT uiNumIcds; // Total ICDs for this index. FLMUINT uiNumKeyComponents; // Number of key components in the index. FLMUINT uiNumDataComponents; // Number of data components in the index. FLMUINT uiNumContextComponents; // Number of context-only components in // the index. FLMUINT uiFlags; #define IXD_ABS_POS 0x00001 // Maintain absolute positioning info. #define IXD_HAS_SUBSTRING 0x00002 #define IXD_OFFLINE 0x00004 // Index is offline - may or may // not be suspended. #define IXD_SUSPENDED 0x00008 // IXD_OFFLINE should also be set #define IXD_SINGLE_PATH 0x00010 // ICD list is a single path FLMUINT uiLanguage; // WP.LRS language number (not code!) FLMUINT64 ui64LastDocIndexed; // If value is not ~0 then // update index with keys from a document // update if doc id is <= this value. // NOTE: This is only guaranteed to be // correct for update transactions. // This field should only be used by // update transactions anyway. LFILE lfInfo; // B-Tree information. FLMUINT64 ui64IxDefNodeId; } IXD; /**************************************************************************** Struct: ICD (Index Component Definition) Desc: This structure contains an index component definition. ****************************************************************************/ typedef struct ICD { // IMPORTANT NOTE: If adding pointers to this structure, be sure // to add code to F_Dict->clone() method to set them up properly. // ALSO, fixup indexDefsSame function in fdict.cpp FLMUINT uiIndexNum; // Index number. IXD * pIxd; // IXD corresponding to uiIndexNum FLMUINT uiDictNum; // Attribute or element number. FLMUINT uiFlags; // The first 4 bits contain data type // Use FLM_XXXXX_TYPE definitions. FLMUINT uiCompareRules; // Comparison rules used for strings. ICD * pNextInChain; // Next ICD in the chain that has this // attribute or element number and is // used in another place. ICD * pParent; // Parent ICD ICD * pFirstChild; // First Child ICD ICD * pPrevSibling; // Previous Sibling ICD ICD * pNextSibling; // Next Sibling ICD FLMUINT uiCdl; // Place in CDL list where a node matching // this ICD should be put. FLMUINT uiKeyComponent; // Which key component is this? 0 means // it is not a key component. ICD * pNextKeyComponent; // Next key component ICD. Also used to // link context components ICD * pPrevKeyComponent; // Previous key component ICD. Also used // to link context components FLMUINT uiDataComponent; // Which data component is this? 0 means // it is not a data component. ICD * pNextDataComponent; // Next data component ICD. ICD * pPrevDataComponent; // Previous data component ICD. FLMUINT uiLimit; // Zero or # of characters/bytes to limit. #define ICD_DEFAULT_LIMIT 128 #define ICD_DEFAULT_SUBSTRING_LIMIT 48 } ICD; #define ICD_VALUE 0x00000010 // Value agrees with parsing syntax #define ICD_EACHWORD 0x00000020 // Index each and every word in the field #define ICD_PRESENCE 0x00000040 // Index the tag and NOT the value #define ICD_METAPHONE 0x00000080 // Index words of text strings using // metaphone values #define ICD_IS_ATTRIBUTE 0x00000100 // ICD is an attribute #define ICD_REQUIRED_PIECE 0x00000200 // Required piece (not optional) #define ICD_REQUIRED_IN_SET 0x00000400 // Required within a set of fields. #define ICD_SUBSTRING 0x00000800 // Index all substrings pieces #define ICD_ESC_CHAR 0x00001000 // Placehold so that a query can parse the input // string and find a literal '*' or '\\'. // Not specified in dictionary or held in ICD // Only a temporary flag. #define ICD_DESCENDING 0x00002000 // Sort in descending order. #define ICD_MISSING_HIGH 0x00004000 // Sort missing components high instead of low. FINLINE FLMUINT icdGetDataType( ICD * pIcd) { return( pIcd->uiFlags & 0x0F); } FINLINE void icdSetDataType( ICD * pIcd, FLMUINT uiDataType) { pIcd->uiFlags = (pIcd->uiFlags & 0xFFFFFFF0) | (uiDataType & 0xF); } /**************************************************************************** Struct: IX_ITEM (Indexed Item - Attribute or Element) Desc: This structure is used to track all indexed attributes and elements whose numbers are greater than or equal to FLM_LOW_EXT_ELEMENT_NUM (for elements) or FLM_LOW_EXT_ATTRIBUTE_NUM (for attributes). ****************************************************************************/ typedef struct IndexedItem { FLMUINT uiDictNum; ICD * pFirstIcd; } IX_ITEM; /**************************************************************************** Struct: RESERVED_TAG_NAME Desc: This structure is used strictly to set up a static table (see fntable.cpp) which lists all reserved tag numbers and their types. ****************************************************************************/ typedef struct ReservedTag { const char * pszTagName; FLMUINT uiTagNum; FLMUINT uiDataType; FLMUNICODE * puzNamespace; } RESERVED_TAG_NAME; /************************************************************************** Desc: This class is the FLAIM dictionary class. **************************************************************************/ class F_Dict : public F_Object { public: // Constructor and destructor F_Dict(); ~F_Dict(); void resetDict( void); RCODE getElement( F_Db * pDb, FLMUINT uiElementNum, F_AttrElmInfo * pElmInfo); RCODE getAttribute( F_Db * pDb, FLMUINT uiAttributeNum, F_AttrElmInfo * pAttrInfo); RCODE getNextElement( F_Db * pDb, FLMUINT * puiElementNum, F_AttrElmInfo * pElmInfo); RCODE getNextAttribute( F_Db * pDb, FLMUINT * puiAttributeNum, F_AttrElmInfo * pAttrInfo); FINLINE FLMUINT getCollectionCount( FLMBOOL bCountPredefined) { FLMUINT uiCount = m_uiHighestCollectionNum ? m_uiHighestCollectionNum - m_uiLowestCollectionNum + 1 : 0; if (bCountPredefined) { // Add 2 for the FLM_DATA_COLLECTION and // FLM_DICT_COLLECTION uiCount += 2; } return( uiCount); } RCODE getCollection( FLMUINT uiCollectionNum, F_COLLECTION ** ppCollection, FLMBOOL bOfflineOk = FALSE); RCODE getPrefixId( F_Db * pDb, const FLMUNICODE * puzPrefix, FLMUINT * puiPrefixId); RCODE getPrefixId( F_Db * pDb, const char * pszPrefix, FLMUINT * puiPrefixId); FINLINE RCODE getPrefix( FLMUINT uiPrefixId, FLMUNICODE * puzPrefixBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { return( getPrefix( TRUE, uiPrefixId, (void *)puzPrefixBuf, uiBufSize, puiCharsReturned)); } FINLINE RCODE getPrefix( FLMUINT uiPrefixId, char * pszPrefixBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { return( getPrefix( FALSE, uiPrefixId, (void *)pszPrefixBuf, uiBufSize, puiCharsReturned)); } RCODE getPrefix( FLMUINT uiPrefixId, F_PREFIX ** ppPrefix); RCODE getEncDefId( F_Db * pDb, const FLMUNICODE * puzEncDef, FLMUINT * puiEncDefId); RCODE getEncDefId( F_Db * pDb, const char * pszEncDef, FLMUINT * puiEncDefId); FINLINE RCODE getEncDef( FLMUINT uiEncDefId, FLMUNICODE * puzEncDefBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { return getEncDef( TRUE, uiEncDefId, (void *)puzEncDefBuf, uiBufSize, puiCharsReturned); } FINLINE RCODE getEncDef( FLMUINT uiEncDefId, char * pszEncDefBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { return getEncDef( FALSE, uiEncDefId, (void *)pszEncDefBuf, uiBufSize, puiCharsReturned); } RCODE getEncDef( FLMUINT uiEncDefId, F_ENCDEF ** ppEncDef); RCODE getDefinitionDoc( F_Db * pDb, FLMUINT uiTag, FLMUINT uiDictId, F_DOMNode ** ppDoc); FINLINE FLMUINT getPrefixCount(void) { FLMUINT uiCount = m_uiHighestPrefixNum ? m_uiHighestPrefixNum - m_uiLowestPrefixNum + 1 : 0; return( uiCount); } FINLINE FLMUINT getEncDefCount(void) { FLMUINT uiCount = m_uiHighestEncDefNum ? m_uiHighestEncDefNum - m_uiLowestEncDefNum + 1 : 0; return( uiCount); } FINLINE FLMUINT getIndexCount( FLMBOOL bCountPredefined) { FLMUINT uiCount = m_uiHighestIxNum ? m_uiHighestIxNum - m_uiLowestIxNum + 1 : 0; if (bCountPredefined) { // Add 2 for the pre-defined indexes. uiCount += 2; } return( uiCount); } FINLINE FLMUINT getIxdOffset( FLMUINT uiIndexNum ) { if (uiIndexNum <= XFLM_MAX_INDEX_NUM) { return( uiIndexNum - m_uiLowestIxNum + 2); } else { switch (uiIndexNum) { case XFLM_DICT_NUMBER_INDEX: return( 0); case XFLM_DICT_NAME_INDEX: return( 1); default: flmAssert( 0); return( 0xFFFF); } } } RCODE getIndex( FLMUINT uiIndexNum, LFILE ** ppLFile, IXD ** ppIxd, FLMBOOL bOfflineOk = FALSE); IXD * getNextIndex( FLMUINT uiIndexNum, FLMBOOL bOkToGetPredefined); F_COLLECTION * getNextCollection( FLMUINT uiCollectionNum, FLMBOOL bOkToGetPredefined); FINLINE ATTR_ELM_DEF * getElementDef( FLMUINT uiElementNum) { ATTR_ELM_DEF * pElementDef = NULL; if (uiElementNum >= m_uiLowestElementNum && uiElementNum <= m_uiHighestElementNum) { pElementDef = &m_pElementDefTbl [uiElementNum - m_uiLowestElementNum]; if (pElementDef && !attrElmGetState( pElementDef)) { pElementDef = NULL; } } return( pElementDef); } FINLINE ATTR_ELM_DEF * getReservedElementDef( FLMUINT uiElementNum) { ATTR_ELM_DEF * pElementDef = &m_pReservedElementDefTbl [uiElementNum - XFLM_FIRST_RESERVED_ELEMENT_TAG]; if (!attrElmGetState( pElementDef)) { pElementDef = NULL; } return( pElementDef); } FINLINE ATTR_ELM_DEF * getAttributeDef( FLMUINT uiAttributeNum) { ATTR_ELM_DEF * pAttributeDef = NULL; if (uiAttributeNum >= m_uiLowestAttributeNum && uiAttributeNum <= m_uiHighestAttributeNum) { pAttributeDef = &m_pAttributeDefTbl [uiAttributeNum - m_uiLowestAttributeNum]; if (pAttributeDef && !attrElmGetState( pAttributeDef)) { pAttributeDef = NULL; } } return( pAttributeDef); } FINLINE ATTR_ELM_DEF * getReservedAttributeDef( FLMUINT uiAttributeNum) { ATTR_ELM_DEF * pAttributeDef = &m_pReservedAttributeDefTbl [uiAttributeNum - XFLM_FIRST_RESERVED_ATTRIBUTE_TAG]; if (!attrElmGetState( pAttributeDef)) { pAttributeDef = NULL; } return( pAttributeDef); } void linkToDatabase( F_Database * pDatabase); void unlinkFromDatabase( void); RCODE linkIcdInChain( ICD * pIcd); RCODE linkIcds( ICD * pIcdTree); void unlinkIcdFromChain( ICD * pIcd); void unlinkIcds( ICD * pIcdTree); FINLINE FLMUINT getUseCount( void) { return( m_uiUseCount); } FINLINE FLMUINT decrUseCount( void) { return( --m_uiUseCount); } FINLINE void incrUseCount( void) { m_uiUseCount++; } FINLINE F_Dict * getPrev( void) { return( m_pPrev); } FINLINE F_Dict * getNext( void) { return( m_pNext); } FINLINE F_Database * getDatabase( void) { return( m_pDatabase); } RCODE copyIXD( IXD ** ppDestIxd, IXD * pSrcIxd); RCODE cloneDict( F_Dict * pSrcDict); RCODE checkElementReferences( // was checkReferences FLMUINT uiElementNum); RCODE checkAttributeReferences( // was checkReferences FLMUINT uiAttributeNum); RCODE checkCollectionReferences( FLMUINT uiCollectionNum); RCODE updateDict( F_Db * pDb, FLMUINT uiDictType, FLMUINT64 ui64DocumentID, FLMUINT uiDictNumber, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE setupPredefined( FLMUINT uiDefaultLanguage); FINLINE FLMUINT getDictSeq( void) { return( m_uiDictSeq); } RCODE allocNameTable( void); FINLINE F_NameTable * getNameTable( void) { return( m_pNameTable); } RCODE allocElementTable( FLMUINT uiLowestElementNum, FLMUINT uiHighestElementNum); RCODE allocAttributeTable( FLMUINT uiLowestAttributeNum, FLMUINT uiHighestAttributeNum); RCODE allocIndexTable( FLMUINT uiLowestIndexNum, FLMUINT uiHighestIndexNum); RCODE allocPrefixTable( FLMUINT uiLowestPrefixNum, FLMUINT uiHighestPrefixNum); RCODE allocEncDefTable( FLMUINT uiLowestEncDefNum, FLMUINT uiHighestEncDefNum); RCODE allocCollectionTable( FLMUINT uiLowestCollectionNum, FLMUINT uiHighestCollectionNum); private: RCODE reallocTbl( FLMUINT uiNewId, FLMUINT uiElementSize, void ** ppvTbl, FLMUINT * puiLowest, FLMUINT * puiHighest, FLMUINT uiAdjustFactor, FLMUINT uiMaxId); RCODE updateElementDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiElementNumber, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE updateAttributeDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiAttributeNumber, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE updateIndexDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiIndexNumber, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE updateCollectionDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiCollectionNumber, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE updatePrefixDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiPrefixNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE updateEncDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiEncDefNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting); IX_ITEM * findIxItem( IX_ITEM * pIxTbl, FLMUINT uiNumItems, FLMUINT uiTagNum, FLMUINT * puiInsertPos = NULL); FINLINE IX_ITEM * findIxElement( FLMUINT uiElementNum, FLMUINT * puiInsertPos = NULL) { return( findIxItem( m_pIxElementTbl, m_uiNumIxElements, uiElementNum, puiInsertPos)); } FINLINE IX_ITEM * findIxAttribute( FLMUINT uiAttributeNum, FLMUINT * puiInsertPos = NULL) { return( findIxItem( m_pIxAttributeTbl, m_uiNumIxAttributes, uiAttributeNum, puiInsertPos)); } RCODE getExtElement( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiElementNum, F_AttrElmInfo * pElmInfo); RCODE getExtAttribute( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiAttributeNum, F_AttrElmInfo * pAttrInfo); void setExtElementFirstIcd( FLMUINT uiElementNum, ICD * pFirstIcd); void setExtAttributeFirstIcd( FLMUINT uiAttributeNum, ICD * pFirstIcd); FINLINE EXT_ATTR_ELM_DEF * getExtElementDef( FLMUINT uiElementNum) { return( &m_pExtElementDefTbl [uiElementNum % m_uiExtElementDefTblSize]); } FINLINE EXT_ATTR_ELM_DEF * getExtAttributeDef( FLMUINT uiAttributeNum) { return( &m_pExtAttributeDefTbl [uiAttributeNum % m_uiExtAttributeDefTblSize]); } RCODE getNextDictNumNodeIds( // fdict.cpp F_Db * pDb); RCODE createNextDictNums( // fdict.cpp F_Db * pDb); RCODE allocNextDictNum( // fdict.cpp F_Db * pDb, FLMUINT uiDictType, FLMUINT * puiDictNumber); RCODE setNextDictNum( // fdict.cpp F_Db * pDb, FLMUINT uiDictType, FLMUINT uiDictNumber); RCODE getPrefix( // fdict.cpp FLMBOOL bUnicode, FLMUINT uiPrefixId, void * pvPrefixBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned); RCODE getEncDef( // fdict.cpp FLMBOOL bUnicode, FLMUINT uiEncDefId, void * pvEncDefBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned); F_Dict * m_pNext; // Pointer to next F_Dict object in the list, // if any. All versions of a dictionary that // are currently in use are linked together. // Usually, there will be only one local // dictionary in the list. F_Dict * m_pPrev; // Previous F_Dict object in the list. F_Database * m_pDatabase; // database this dictionary is associated with. // A null value means it is not yet linked // to a database. FLMUINT m_uiDictSeq; // This is the sequence number of the // dictionary F_Pool m_dictPool; // Pool for all allocations except tables. // Fixed element definition table - used for elements whose tag numbers // are less than or equal to FLM_HIGH_FIXED_ELEMENT_NUM ATTR_ELM_DEF * m_pElementDefTbl; FLMUINT m_uiLowestElementNum; FLMUINT m_uiHighestElementNum; // Reserved element definition table - used for elements whose tag numbers // are in the "reserved tag" range. ATTR_ELM_DEF * m_pReservedElementDefTbl; // Extended element definition table - used for elements whose tag // numbers are greater than or equal to FLM_LOW_EXT_ELEMENT_NUM EXT_ATTR_ELM_DEF * m_pExtElementDefTbl; FLMUINT m_uiExtElementDefTblSize; F_MUTEX m_hExtElementDefMutex; // Table for tracking ALL indexed elements whose tag number is // greater than or equal to FLM_LOW_EXT_ELEMENT_NUM. IX_ITEM * m_pIxElementTbl; FLMUINT m_uiIxElementTblSize; FLMUINT m_uiNumIxElements; // Fixed attribute definition table - used for attributes whose tag numbers // are less than or equal to FLM_HIGH_FIXED_ATTRIBUTE_NUM ATTR_ELM_DEF * m_pAttributeDefTbl; FLMUINT m_uiLowestAttributeNum; FLMUINT m_uiHighestAttributeNum; // Reserved attribute definition table - used for attributes whose tag // numbers are in the "reserved tag" range. ATTR_ELM_DEF * m_pReservedAttributeDefTbl; // Extended attribute definition table - used for attributes whose tag // numbers are greater than or equal to FLM_LOW_EXT_ATTRIBUTE_NUM EXT_ATTR_ELM_DEF * m_pExtAttributeDefTbl; FLMUINT m_uiExtAttributeDefTblSize; F_MUTEX m_hExtAttributeDefMutex; // Table for tracking ALL indexed attributes whose tag number is // greater than or equal to FLM_LOW_EXT_ATTRIBUTE_NUM. IX_ITEM * m_pIxAttributeTbl; FLMUINT m_uiIxAttributeTblSize; FLMUINT m_uiNumIxAttributes; // Pre-defined collections F_COLLECTION * m_pDictCollection; F_COLLECTION * m_pDataCollection; F_COLLECTION * m_pMaintCollection; // User defined Collections F_COLLECTION ** m_ppCollectionTbl; FLMUINT m_uiLowestCollectionNum; FLMUINT m_uiHighestCollectionNum; // User defined prefixes F_PREFIX ** m_ppPrefixTbl; FLMUINT m_uiLowestPrefixNum; FLMUINT m_uiHighestPrefixNum; // User defined encryption defs F_ENCDEF ** m_ppEncDefTbl; FLMUINT m_uiLowestEncDefNum; FLMUINT m_uiHighestEncDefNum; // Pre-defined indexes IXD * m_pNameIndex; IXD * m_pNumberIndex; // User defined indexes IXD ** m_ppIxdTbl; FLMUINT m_uiLowestIxNum; FLMUINT m_uiHighestIxNum; ICD * m_pRootIcdList; FLMUINT m_uiUseCount; // Number of F_Db structures currently // pointing to this dictionary. // Name table F_NameTable * m_pNameTable; // Keep track of whether or not the database is operating in limited mode. This // field is copied from the database when the dictionary is created or cloned FLMBOOL m_bInLimitedMode; friend class F_Database; friend class F_Db; friend class F_Query; friend class F_DbCheck; friend class F_BTreeInfo; friend class F_AttrItem; }; RCODE fdictGetDataType( // fdict.cpp char * pszDataType, FLMUINT * puiDataType); const char * fdictGetDataTypeStr( // fdict.cpp FLMUINT uiDataType); #endif // #ifndef FDICT_H libxflaim-5.1.969/src/btreeinfo.cpp0000644000175000017500000005003410511001742020507 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Class for gathering b-tree information. // // Tabs: 3 // // Copyright (c) 2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: btreeinfo.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Collect information about a block. ****************************************************************************/ RCODE F_BTreeInfo::collectBlockInfo( F_Db * pDb, LFILE * pLFile, BTREE_INFO * pBTreeInfo, F_BTREE_BLK_HDR * pBlkHdr, IXD * pIxd) { RCODE rc = NE_XFLM_OK; XFLM_BTREE_LEVEL_INFO * pLevelInfo = &pBTreeInfo->levelInfo [m_uiCurrLevel]; FLMUINT uiLoop; FLMBYTE * pucOffset; FLMBYTE * pucEntry; FLMBYTE * pucTmp; FLMUINT uiKeyLen; FLMUINT uiDataLen; F_CachedBlock * pCachedBlock = NULL; FLMUINT uiBlkAddr; FLMUINT uiOADataLen; FLMBYTE * pucKey; // Block level better be the same as our current level we are // supposedly processing. flmAssert( (FLMUINT)pBlkHdr->ui8BlkLevel == m_uiCurrLevel); pLevelInfo->ui64BlockCount++; pLevelInfo->ui64BlockLength += (FLMUINT64)m_uiBlockSize; pLevelInfo->ui64ElmOffsetOverhead += ((FLMUINT64)pBlkHdr->ui16NumKeys * 2); pLevelInfo->ui64ElmCount += (FLMUINT64)pBlkHdr->ui16NumKeys; pLevelInfo->ui64BlockFreeSpace += pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; // Traverse through each key. pucOffset = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr); for (uiLoop = 0; uiLoop < (FLMUINT)pBlkHdr->ui16NumKeys; uiLoop++, pucOffset += 2) { pucEntry = ((FLMBYTE *)pBlkHdr) + FB2UW( pucOffset); uiDataLen = 0; uiOADataLen = 0; uiKeyLen = 0; switch (pBlkHdr->stdBlkHdr.ui8BlkType) { case BT_LEAF: // Elements are: // Key Length - 2 bytes // Key - Key Length bytes uiKeyLen = (FLMUINT)FB2UW( pucEntry); pLevelInfo->ui64ElmKeyLengthOvhd += 2; pucKey = pucEntry + 2; break; case BT_NON_LEAF: // Elements are: // Child Blk Address - 4 bytes // Key Length - 2 bytes // Key - Key Length bytes pLevelInfo->ui64ElmChildAddrsOvhd += 4; uiKeyLen = (FLMUINT)FB2UW( pucEntry + 4); pLevelInfo->ui64ElmKeyLengthOvhd += 2; pucKey = pucEntry + 6; break; case BT_NON_LEAF_COUNTS: // Elements are: // Child Block Address - 4 bytes // Counts - 4 bytes // Key Length - 2 bytes // Key - Key Length bytes uiKeyLen = (FLMUINT)FB2UW( pucEntry + 8); pLevelInfo->ui64ElmKeyLengthOvhd += 2; pLevelInfo->ui64ElmCountsOvhd += 4; pLevelInfo->ui64ElmChildAddrsOvhd += 4; pucKey = pucEntry + 10; break; case BT_LEAF_DATA: // Elements are: // Flags - 1 byte // Key Length - 1 or 2 bytes // Data Length - 1 or 2 bytes // Overall data Length - 0 or 4 bytes // Key - Key Length bytes // Data - Data Length bytes. NOTE: May be a four byte blk // address if data is stored in data-only blocks. pLevelInfo->ui64ElmFlagOvhd++; if (!(*pucEntry & BTE_FLAG_FIRST_ELEMENT)) { pLevelInfo->ui64ContElmCount++; } pucTmp = pucEntry + 1; if (bteKeyLenFlag( pucEntry)) { // Two byte key length uiKeyLen = (FLMUINT)FB2UW( pucTmp); pLevelInfo->ui64ElmKeyLengthOvhd += 2; pucTmp += 2; } else { // One byte key length uiKeyLen = (FLMUINT)(*pucTmp); pLevelInfo->ui64ElmKeyLengthOvhd++; pucTmp++; } if (bteDataLenFlag( pucEntry)) { // Two byte data length uiDataLen = (FLMUINT)FB2UW( pucTmp); pLevelInfo->ui64ElmDataLenOvhd += 2; pucTmp += 2; } else { // One byte data length. uiDataLen = (FLMUINT)(*pucTmp); pLevelInfo->ui64ElmDataLenOvhd++; pucTmp++; } // Check for the presence of the OverallDataLength field (4 bytes). if (bteOADataLenFlag( pucEntry)) { uiOADataLen = (FLMUINT)FB2UD( pucTmp); pLevelInfo->ui64ElmOADataLenOvhd += 4; pucTmp += 4; } pucKey = pucTmp; if (bteDataBlockFlag( pucEntry)) { flmAssert( uiDataLen == 4); flmAssert( uiOADataLen); // Skip over the key to get to the data only block address. pucTmp += uiKeyLen; uiBlkAddr = (FLMUINT)FB2UD( pucTmp); while (uiBlkAddr) { if (RC_BAD( pDb->m_pDatabase->getBlock( pDb, pLFile, uiBlkAddr, NULL, &pCachedBlock))) { goto Exit; } // Block better be a data-only block. flmAssert( pCachedBlock->m_pBlkHdr->ui8BlkType == BT_DATA_ONLY); pLevelInfo->ui64DataOnlyBlockCount++; pLevelInfo->ui64DataOnlyBlockLength += (FLMUINT64)m_uiBlockSize; pLevelInfo->ui64DataOnlyBlockFreeSpace += (FLMUINT64)pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; // Subtract from uiIODataLen - should go to exactly // zero by the time we leave this loop. uiOADataLen -= (m_uiBlockSize - sizeofDOBlkHdr( pCachedBlock->m_pBlkHdr) - pCachedBlock->m_pBlkHdr->ui16BlkBytesAvail); uiBlkAddr = (FLMUINT)pCachedBlock->m_pBlkHdr->ui32NextBlkInChain; ScaReleaseCache( pCachedBlock, FALSE); pCachedBlock = NULL; } // Better have accounted for the exact amount of data that // was given in the overall data length field. flmAssert( !uiOADataLen); } break; default: pucKey = NULL; flmAssert( 0); break; } if (uiKeyLen) { pLevelInfo->ui64ElmKeyLength += (FLMUINT64)uiKeyLen; } if (uiDataLen) { pLevelInfo->ui64ElmDataLength += (FLMUINT64)uiDataLen; } // If this is an index, parse the key. if (pIxd && uiKeyLen) { FLMUINT uiComponentLen; FLMBYTE * pucKeyEnd = pucKey + uiKeyLen; ICD * pIcd; pIcd = pIxd->pFirstKey; while (pIcd) { flmAssert( pucKey + 2 <= pucKeyEnd); uiComponentLen = getKeyComponentLength( pucKey); if (uiComponentLen == KEY_HIGH_VALUE || uiComponentLen == KEY_LOW_VALUE) { uiComponentLen = 0; } pLevelInfo->ui64KeyComponentLengthsSize += 2; if (uiComponentLen) { pLevelInfo->ui64KeyDataSize += (FLMUINT64)uiComponentLen; } pucKey += (2 + uiComponentLen); pIcd = pIcd->pNextKeyComponent; } pLevelInfo->ui64KeyIdSize += (FLMUINT64)(pucKeyEnd - pucKey); } } Exit: if (pCachedBlock) { ScaReleaseCache( pCachedBlock, FALSE); } return( rc); } /**************************************************************************** Desc: Collect information on a b-tree. ****************************************************************************/ RCODE F_BTreeInfo::collectBTreeInfo( F_Db * pDb, LFILE * pLFile, BTREE_INFO * pBTreeInfo, IXD * pIxd) { RCODE rc = NE_XFLM_OK; FLMUINT uiNameBufSize; FLMUINT uiLeftBlocks [MAX_LEVELS]; F_Database * pDatabase = pDb->m_pDatabase; F_CachedBlock * pCachedBlock = NULL; F_BTREE_BLK_HDR * pBlkHdr; const char * pszName = NULL; m_uiBlockSize = pDatabase->m_uiBlockSize; // Get the size of buffer needed to hold the name of the b-tree. uiNameBufSize = 0; if (pIxd) { switch (pLFile->uiLfNum) { case XFLM_DICT_NUMBER_INDEX: pszName = "DictNumberIx"; break; case XFLM_DICT_NAME_INDEX: pszName = "DictNameIx"; break; default: if (RC_BAD( rc = pDb->m_pDict->m_pNameTable->getFromTagTypeAndNum( pDb, pIxd ? ELM_INDEX_TAG : ELM_COLLECTION_TAG, pLFile->uiLfNum, NULL, NULL, &uiNameBufSize, NULL, NULL, NULL, NULL, TRUE))) { goto Exit; } break; } } else { switch (pLFile->uiLfNum) { case XFLM_MAINT_COLLECTION: pszName = "MaintCollection"; break; case XFLM_DATA_COLLECTION: pszName = "DataCollection"; break; case XFLM_DICT_COLLECTION: pszName = "DictCollection"; break; default: if (RC_BAD( rc = pDb->m_pDict->m_pNameTable->getFromTagTypeAndNum( pDb, pIxd ? ELM_INDEX_TAG : ELM_COLLECTION_TAG, pLFile->uiLfNum, NULL, NULL, &uiNameBufSize, NULL, NULL, NULL, NULL, TRUE))) { goto Exit; } break; } } if (pszName) { // Allocate a name buffer. uiNameBufSize = f_strlen( pszName) + 1; if (RC_BAD( rc = m_pool.poolAlloc( uiNameBufSize, (void **)(&pBTreeInfo->pszLfName)))) { goto Exit; } f_strcpy( pBTreeInfo->pszLfName, pszName); } else { // Allocate a name buffer. uiNameBufSize++; if (RC_BAD( rc = m_pool.poolAlloc( uiNameBufSize, (void **)(&pBTreeInfo->pszLfName)))) { goto Exit; } // Get the name of the b-tree. if (RC_BAD( rc = pDb->m_pDict->m_pNameTable->getFromTagTypeAndNum( pDb, pIxd ? ELM_INDEX_TAG : ELM_COLLECTION_TAG, pLFile->uiLfNum, NULL, pBTreeInfo->pszLfName, &uiNameBufSize, NULL, NULL, NULL, NULL, TRUE))) { goto Exit; } } m_uiCurrLfNum = pLFile->uiLfNum; m_bIsCollection = pIxd ? FALSE : TRUE; m_pszCurrLfName = pBTreeInfo->pszLfName; // Reset the information for the b-tree. uiLfNum should have already // been set by the caller. flmAssert( pBTreeInfo->uiLfNum == pLFile->uiLfNum); pBTreeInfo->uiNumLevels = 0; f_memset( &pBTreeInfo->levelInfo [0], 0, sizeof( pBTreeInfo->levelInfo)); // Read the root block to see how many levels are in the b-tree. if (RC_BAD( pDatabase->getBlock( pDb, pLFile, pLFile->uiRootBlk, NULL, &pCachedBlock))) { goto Exit; } pBlkHdr = (F_BTREE_BLK_HDR *)pCachedBlock->m_pBlkHdr; pBTreeInfo->uiNumLevels = pBlkHdr->ui8BlkLevel + 1; flmAssert( pBTreeInfo->uiNumLevels <= MAX_LEVELS); // Better be a root block, and better not have a prev and next // block address. flmAssert( isRootBlk( pBlkHdr)); flmAssert( pBlkHdr->stdBlkHdr.ui32BlkAddr == (FLMUINT32)pLFile->uiRootBlk); flmAssert( !pBlkHdr->stdBlkHdr.ui32PrevBlkInChain); flmAssert( !pBlkHdr->stdBlkHdr.ui32NextBlkInChain); m_uiCurrLevel = pBlkHdr->ui8BlkLevel; // Gather information for the root block. if (RC_BAD( rc = collectBlockInfo( pDb, pLFile, pBTreeInfo, pBlkHdr, pIxd))) { goto Exit; } m_ui64CurrLfBlockCount = 1; m_ui64CurrLevelBlockCount = 1; m_ui64TotalBlockCount = 1; if (RC_BAD( rc = doCallback())) { goto Exit; } // Get all of the leftmost blocks in the b-tree. uiLeftBlocks [pBlkHdr->ui8BlkLevel] = pLFile->uiRootBlk; if (!pBlkHdr->ui8BlkLevel) { flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF || pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA); } else { FLMUINT uiLevel; FLMBYTE * pucEntry; FLMUINT uiBlkAddr; // Gather up the leftmost blocks uiLevel = pBlkHdr->ui8BlkLevel; while (uiLevel) { uiLevel--; // Get the left-most down-block pointer - which is the first one // in the array. pucEntry = ((FLMBYTE *)pBlkHdr) + sizeofBTreeBlkHdr( pBlkHdr); pucEntry = ((FLMBYTE *)pBlkHdr) + FB2UW( pucEntry); uiLeftBlocks [uiLevel] = (FLMUINT)FB2UD( pucEntry); // Get the leftmost block at the next level down in the b-tree ScaReleaseCache( pCachedBlock, FALSE); pCachedBlock = NULL; if (RC_BAD( pDatabase->getBlock( pDb, pLFile, uiLeftBlocks [uiLevel], NULL, &pCachedBlock))) { goto Exit; } pBlkHdr = (F_BTREE_BLK_HDR *)pCachedBlock->m_pBlkHdr; // If we are at level zero, we better be on a leaf block. if (!uiLevel) { flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF || pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA); } } // Now do each level of the b-tree. Have already done the root // block, so we start one level down from it. m_uiCurrLevel = pBTreeInfo->uiNumLevels - 2; for (;;) { uiBlkAddr = uiLeftBlocks [m_uiCurrLevel]; m_ui64CurrLevelBlockCount = 0; while (uiBlkAddr) { if (pCachedBlock) { ScaReleaseCache( pCachedBlock, FALSE); pCachedBlock = NULL; } if (RC_BAD( pDatabase->getBlock( pDb, pLFile, uiBlkAddr, NULL, &pCachedBlock))) { goto Exit; } pBlkHdr = (F_BTREE_BLK_HDR *)pCachedBlock->m_pBlkHdr; // Gather information for the block. if (RC_BAD( rc = collectBlockInfo( pDb, pLFile, pBTreeInfo, pBlkHdr, pIxd))) { goto Exit; } m_ui64CurrLfBlockCount++; m_ui64CurrLevelBlockCount++; m_ui64TotalBlockCount++; if (RC_BAD( rc = doCallback())) { goto Exit; } // Go to the next block in the chain. uiBlkAddr = pBlkHdr->stdBlkHdr.ui32NextBlkInChain; } if (!m_uiCurrLevel) { break; } // Go down to the next level in the b-tree. m_uiCurrLevel--; } } Exit: if (pCachedBlock) { ScaReleaseCache( pCachedBlock, FALSE); } return( rc); } /**************************************************************************** Desc: Collect b-tree information for an index. If uiIndexNum is zero, collect b-tree information for ALL indexes. If we already have information on the index, we will clear the information and get it again. ****************************************************************************/ RCODE FLMAPI F_BTreeInfo::collectIndexInfo( IF_Db * ifpDb, FLMUINT uiIndexNum, IF_BTreeInfoStatus * pInfoStatus) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; BTREE_INFO * pIndexInfo; IXD * pIxd; FLMUINT uiLoop; // Start a read transaction, if no other transaction is going. if (pDb->getTransType() == XFLM_NO_TRANS) { if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } m_pInfoStatus = pInfoStatus; m_uiCurrLfNum = 0; m_bIsCollection = FALSE; m_pszCurrLfName = NULL; m_uiCurrLevel = 0; m_ui64CurrLfBlockCount = 0; m_ui64CurrLevelBlockCount = 0; m_ui64TotalBlockCount = 0; if (!uiIndexNum) { m_uiNumIndexes = 0; for (;;) { if ((pIxd = pDb->m_pDict->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; if (RC_BAD( rc = collectIndexInfo( ifpDb, uiIndexNum, pInfoStatus))) { goto Exit; } } } else { // See if we can find the b-tree already in our list. uiLoop = 0; pIndexInfo = m_pIndexArray; while (uiLoop < m_uiNumIndexes && pIndexInfo->uiLfNum != uiIndexNum) { uiLoop++; pIndexInfo++; } if (uiLoop == m_uiNumIndexes) { pIndexInfo = NULL; } // See if the index is defined in the database if (RC_BAD( rc = pDb->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { if (rc == NE_XFLM_BAD_IX) { rc = NE_XFLM_OK; // If we previously had the index, remove it from the array. if (pIndexInfo) { if (uiLoop < m_uiNumIndexes - 1) { f_memmove( pIndexInfo, &pIndexInfo[ 1], sizeof( BTREE_INFO) * (m_uiNumIndexes - uiLoop - 1)); } m_uiNumIndexes--; } } goto Exit; } // If we previously had the index, reset its information. // Otherwise, create a new index info structure and // add it to the array. if (!pIndexInfo) { // Allocate space for a new index info structure in the array. if (m_uiNumIndexes == m_uiIndexArraySize) { if (RC_BAD( rc = f_realloc( sizeof( BTREE_INFO) * (m_uiIndexArraySize + 5), &m_pIndexArray))) { goto Exit; } m_uiIndexArraySize += 5; } pIndexInfo = &m_pIndexArray [m_uiNumIndexes]; pIndexInfo->uiLfNum = uiIndexNum; m_uiNumIndexes++; } // Get the index information if (RC_BAD( rc = collectBTreeInfo( pDb, &pIxd->lfInfo, pIndexInfo, pIxd))) { goto Exit; } } Exit: if (bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: Collect b-tree information for a collection. If uiCollectionNum is zero, collect b-tree information for ALL collections. If we already have information on the collection, we will clear the information and get it again. ****************************************************************************/ RCODE FLMAPI F_BTreeInfo::collectCollectionInfo( IF_Db * ifpDb, FLMUINT uiCollectionNum, IF_BTreeInfoStatus * pInfoStatus) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; BTREE_INFO * pCollectionInfo; F_COLLECTION * pCollection; FLMUINT uiLoop; // Start a read transaction, if no other transaction is going. if (pDb->getTransType() == XFLM_NO_TRANS) { if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } m_pInfoStatus = pInfoStatus; m_uiCurrLfNum = 0; m_bIsCollection = FALSE; m_pszCurrLfName = NULL; m_uiCurrLevel = 0; m_ui64CurrLfBlockCount = 0; m_ui64CurrLevelBlockCount = 0; m_ui64TotalBlockCount = 0; if (!uiCollectionNum) { m_uiNumCollections = 0; for (;;) { if ((pCollection = pDb->m_pDict->getNextCollection( uiCollectionNum, TRUE)) == NULL) { break; } uiCollectionNum = pCollection->lfInfo.uiLfNum; if (RC_BAD( rc = collectCollectionInfo( ifpDb, uiCollectionNum, pInfoStatus))) { goto Exit; } } } else { // See if we can find the b-tree already in our list. uiLoop = 0; pCollectionInfo = m_pCollectionArray; while (uiLoop < m_uiNumCollections && pCollectionInfo->uiLfNum != uiCollectionNum) { uiLoop++; pCollectionInfo++; } if (uiLoop == m_uiNumCollections) { pCollectionInfo = NULL; } // See if the index is defined in the database if (RC_BAD( rc = pDb->m_pDict->getCollection( uiCollectionNum, &pCollection, TRUE))) { if (rc == NE_XFLM_BAD_COLLECTION) { rc = NE_XFLM_OK; // If we previously had the index, remove it from the array. if (pCollectionInfo) { if (uiLoop < m_uiNumCollections - 1) { f_memmove( pCollectionInfo, &pCollectionInfo[ 1], sizeof( BTREE_INFO) * (m_uiNumCollections - uiLoop - 1)); } m_uiNumCollections--; } } goto Exit; } // If we previously had the index, reset its information. // Otherwise, create a new index info structure and // add it to the array. if (!pCollectionInfo) { // Allocate space for a new index info structure in the array. if (m_uiNumCollections == m_uiCollectionArraySize) { if (RC_BAD( rc = f_realloc( sizeof( BTREE_INFO) * (m_uiCollectionArraySize + 5), &m_pCollectionArray))) { goto Exit; } m_uiCollectionArraySize += 5; } pCollectionInfo = &m_pCollectionArray [m_uiNumCollections]; pCollectionInfo->uiLfNum = uiCollectionNum; m_uiNumCollections++; } // Get the index information if (RC_BAD( rc = collectBTreeInfo( pDb, &pCollection->lfInfo, pCollectionInfo, NULL))) { goto Exit; } } Exit: if (bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: Create an empty b-tree info. object and return it's interface... ****************************************************************************/ RCODE FLMAPI F_DbSystem::createIFBTreeInfo( IF_BTreeInfo ** ppBTreeInfo) { RCODE rc = NE_XFLM_OK; F_BTreeInfo * pBTreeInfo; if ((pBTreeInfo = f_new F_BTreeInfo) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } *ppBTreeInfo = pBTreeInfo; pBTreeInfo = NULL; Exit: if( pBTreeInfo) { pBTreeInfo->Release(); } return( rc); } libxflaim-5.1.969/src/fdbcnfig.cpp0000644000175000017500000007127110511001742020302 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Database config get/set functions // // Tabs: 3 // // Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbcnfig.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Set the RFL keep files flag. ****************************************************************************/ RCODE FLMAPI F_Db::setRflKeepFilesFlag( FLMBOOL bKeepFiles) { RCODE rc = NE_XFLM_OK; FLMBOOL bDbLocked = FALSE; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Make sure we don't have a transaction going if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // Make sure there is no active backup running m_pDatabase->lockMutex(); if (m_pDatabase->m_bBackupActive) { m_pDatabase->unlockMutex(); rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); goto Exit; } m_pDatabase->unlockMutex(); // Need to lock the database but not start a transaction yet. if (!(m_uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED))) { if (RC_BAD( rc = dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; } // If we aren't changing the keep flag, jump to exit without doing // anything. if ((bKeepFiles && m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles) || (!bKeepFiles && !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles)) { goto Exit; // Will return NE_XFLM_OK; } // Force a checkpoint and roll to the next RFL file numbers. // When changing from keep to no-keep or vice versa, we need to // go to a new RFL file so that the new RFL file gets new // serial numbers and a new keep or no-keep flag. if (RC_BAD( rc = doCheckpoint( FLM_NO_TIMEOUT))) { goto Exit; } f_memcpy( &m_pDatabase->m_uncommittedDbHdr, &m_pDatabase->m_lastCommittedDbHdr, sizeof( XFLM_DB_HDR)); m_pDatabase->m_uncommittedDbHdr.ui8RflKeepFiles = (FLMUINT8)(bKeepFiles ? (FLMUINT8)1 : (FLMUINT8)0); // Force a new RFL file - this will also write out the entire // log header - including the changes we made above. if (RC_BAD( rc = m_pDatabase->m_pRfl->finishCurrFile( this, TRUE))) { goto Exit; } Exit: if (bDbLocked) { dbUnlock(); } return( rc); } /**************************************************************************** Desc: Set the RFL directory for a database. ****************************************************************************/ RCODE FLMAPI F_Db::setRflDir( const char * pszNewRflDir) { RCODE rc = NE_XFLM_OK; FLMBOOL bDbLocked = FALSE; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Make sure we don't have a transaction going if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // Make sure there is no active backup running m_pDatabase->lockMutex(); if (m_pDatabase->m_bBackupActive) { m_pDatabase->unlockMutex(); rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); goto Exit; } m_pDatabase->unlockMutex(); // Make sure the path exists and that it is a directory // rather than a file. if (pszNewRflDir && *pszNewRflDir) { if (!gv_XFlmSysData.pFileSystem->isDir( pszNewRflDir)) { rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); goto Exit; } } // Need to lock the database because we can't change the RFL // directory until after the checkpoint has completed. The // checkpoint code will unlock the transaction, but not the // file if we have an explicit lock. We need to do this to // prevent another transaction from beginning before we have // changed the RFL directory. if (!(m_uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED))) { if( RC_BAD( rc = dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; } // Force a checkpoint and roll to the next RFL file numbers. Both // of these steps are necessary to ensure that we won't have to do // any recovery using the current RFL file - because we do not // move the current RFL file to the new directory. Forcing the // checkpoint ensures that we have no transactions that will need // to be recovered if we were to crash. Rolling the RFL file number // ensures that no more transactions will be logged to the current // RFL file. if (RC_BAD( rc = doCheckpoint( FLM_NO_TIMEOUT))) { goto Exit; } // Force a new RFL file. if (RC_BAD( rc = m_pDatabase->m_pRfl->finishCurrFile( this, FALSE))) { goto Exit; } // Set the RFL directory to the new value now that we have // finished the checkpoint and rolled to the next RFL file. m_pDatabase->lockMutex(); rc = m_pDatabase->m_pRfl->setRflDir( pszNewRflDir); m_pDatabase->unlockMutex(); Exit: if (bDbLocked) { dbUnlock(); } return( rc); } /**************************************************************************** Desc: Set the RFL file size limits for a database. ****************************************************************************/ RCODE FLMAPI F_Db::setRflFileSizeLimits( FLMUINT uiMinRflSize, FLMUINT uiMaxRflSize) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Make sure the limits are valid. // Maximum must be enough to hold at least one packet plus // the RFL header. Minimum must not be greater than the // maximum. NOTE: Minimum and maximum are allowed to be // equal, but in all cases, maximum takes precedence over // minimum. We will first NOT exceed the maximum. Then, // if possible, we will go above the minimum. if (uiMaxRflSize < RFL_MAX_PACKET_SIZE + 512) { uiMaxRflSize = RFL_MAX_PACKET_SIZE + 512; } if (uiMaxRflSize > gv_XFlmSysData.uiMaxFileSize) { uiMaxRflSize = gv_XFlmSysData.uiMaxFileSize; } if (uiMinRflSize > uiMaxRflSize) { uiMinRflSize = uiMaxRflSize; } // Start an update transaction. Must not already be one going. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Commit the transaction. m_pDatabase->m_uncommittedDbHdr.ui32RflMinFileSize = (FLMUINT32)uiMinRflSize; m_pDatabase->m_uncommittedDbHdr.ui32RflMaxFileSize = (FLMUINT32)uiMaxRflSize; bStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, FALSE))) { goto Exit; } Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Roll to the next RFL file for this database ****************************************************************************/ RCODE FLMAPI F_Db::rflRollToNextFile( void) { RCODE rc = NE_XFLM_OK; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // NOTE: finishCurrFile will not roll to the next file if the current // file has not been created. if (RC_BAD( rc = m_pDatabase->m_pRfl->finishCurrFile( this, FALSE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Set keep aborted transactions in RFL flag. ****************************************************************************/ RCODE FLMAPI F_Db::setKeepAbortedTransInRflFlag( FLMBOOL bKeep ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Start an update transaction. Must not already be one going. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Change the uncommitted log header m_pDatabase->m_uncommittedDbHdr.ui8RflKeepAbortedTrans = (FLMUINT8)(bKeep ? (FLMUINT8)1 : (FLMUINT8)0); // Commit the transaction. bStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, FALSE))) { goto Exit; } Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Set auto turn off keep RFL flag. ****************************************************************************/ RCODE FLMAPI F_Db::setAutoTurnOffKeepRflFlag( FLMBOOL bAutoTurnOff ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Start an update transaction. Must not already be one going. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Change the uncommitted log header m_pDatabase->m_uncommittedDbHdr.ui8RflAutoTurnOffKeep = (FLMUINT8)(bAutoTurnOff ? (FLMUINT8)1 : (FLMUINT8)0); // Commit the transaction. bStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, FALSE))) { goto Exit; } Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Retrieves the Checkpoint info for the database passed in. This assumes global mutex has already been locked. *****************************************************************************/ void F_Database::getCPInfo( XFLM_CHECKPOINT_INFO * pCheckpointInfo) { FLMUINT uiElapTime; FLMUINT uiCurrTime; flmAssert( pCheckpointInfo); f_memset( pCheckpointInfo, 0, sizeof( XFLM_CHECKPOINT_INFO)); if (m_pCPInfo) { pCheckpointInfo->bRunning = m_pCPInfo->bDoingCheckpoint; if (pCheckpointInfo->bRunning) { if (m_pCPInfo->uiStartTime) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, m_pCPInfo->uiStartTime); pCheckpointInfo->ui32RunningTime = (FLMUINT32)FLM_TIMER_UNITS_TO_MILLI( uiElapTime); } else { pCheckpointInfo->ui32RunningTime = 0; } pCheckpointInfo->bForcingCheckpoint = m_pCPInfo->bForcingCheckpoint; if (m_pCPInfo->uiForceCheckpointStartTime) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, m_pCPInfo->uiForceCheckpointStartTime); pCheckpointInfo->ui32ForceCheckpointRunningTime = (FLMUINT32)FLM_TIMER_UNITS_TO_MILLI( uiElapTime); } else { pCheckpointInfo->ui32ForceCheckpointRunningTime = 0; } pCheckpointInfo->ui32ForceCheckpointReason = (FLMUINT32)m_pCPInfo->iForceCheckpointReason; pCheckpointInfo->bWritingDataBlocks = m_pCPInfo->bWritingDataBlocks; pCheckpointInfo->ui32LogBlocksWritten = (FLMUINT32)m_pCPInfo->uiLogBlocksWritten; pCheckpointInfo->ui32DataBlocksWritten = (FLMUINT32)m_pCPInfo->uiDataBlocksWritten; } pCheckpointInfo->ui32BlockSize = (FLMUINT32)m_uiBlockSize; pCheckpointInfo->ui32DirtyCacheBytes = (FLMUINT32)(m_uiDirtyCacheCount * m_uiBlockSize); if (m_pCPInfo->uiStartWaitTruncateTime) { uiCurrTime = FLM_GET_TIMER(); uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, m_pCPInfo->uiStartWaitTruncateTime); pCheckpointInfo->ui32WaitTruncateTime = (FLMUINT32)FLM_TIMER_UNITS_TO_MILLI( uiElapTime); } else { pCheckpointInfo->ui32WaitTruncateTime = 0; } } } /**************************************************************************** Desc: Retrieves the Checkpoint info for the database. *****************************************************************************/ void FLMAPI F_Db::getCheckpointInfo( XFLM_CHECKPOINT_INFO * pCheckpointInfo) { m_pDatabase->lockMutex(); m_pDatabase->getCPInfo( pCheckpointInfo); m_pDatabase->unlockMutex(); } /**************************************************************************** Desc: Returns current RFL file number ****************************************************************************/ RCODE FLMAPI F_Db::getRflFileNum( FLMUINT * puiRflFileNum ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiLastCPFile; FLMUINT uiLastTransFile; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // Get the CP and last trans RFL file numbers. Need to // return the higher of the two. No need to lock the // mutex because we are in an update transaction. uiLastCPFile = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflLastCPFileNum; uiLastTransFile = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum; *puiRflFileNum = uiLastCPFile > uiLastTransFile ? uiLastCPFile : uiLastTransFile; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns highest not used RFL file number ****************************************************************************/ RCODE FLMAPI F_Db::getHighestNotUsedRflFileNum( FLMUINT * puiHighestNotUsedRflFileNum ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiLastCPFile; FLMUINT uiLastTransFile; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // Get the CP and last trans RFL file numbers. Need to // return the lower of the two minus 1. uiLastCPFile = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflLastCPFileNum; uiLastTransFile = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum; *puiHighestNotUsedRflFileNum = (FLMUINT)((uiLastCPFile < uiLastTransFile ? uiLastCPFile : uiLastTransFile) - 1); Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns RFL file size limits for the database ****************************************************************************/ RCODE FLMAPI F_Db::getRflFileSizeLimits( FLMUINT * puiRflMinFileSize, FLMUINT * puiRflMaxFileSize ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } if (puiRflMinFileSize) { *puiRflMinFileSize = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflMinFileSize; } if (puiRflMaxFileSize) { *puiRflMaxFileSize = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflMaxFileSize; } Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns RFL keep flag for the database ****************************************************************************/ RCODE FLMAPI F_Db::getRflKeepFlag( FLMBOOL * pbKeep ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } *pbKeep = m_pDatabase->m_uncommittedDbHdr.ui8RflKeepFiles ? TRUE : FALSE; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns last backup transaction ID for the database ****************************************************************************/ RCODE FLMAPI F_Db::getLastBackupTransID( FLMUINT64 * pui64LastBackupTransID ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } *pui64LastBackupTransID = m_pDatabase->m_uncommittedDbHdr.ui64LastBackupTransID; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns blocks changed since the last backup for the database ****************************************************************************/ RCODE FLMAPI F_Db::getBlocksChangedSinceBackup( FLMUINT * puiBlocksChangedSinceBackup ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } *puiBlocksChangedSinceBackup = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32BlksChangedSinceBackup; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns the auto-turn-off-keep-RFL flag for the database ****************************************************************************/ RCODE FLMAPI F_Db::getAutoTurnOffKeepRflFlag( FLMBOOL * pbAutoTurnOff ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } *pbAutoTurnOff = m_pDatabase->m_uncommittedDbHdr.ui8RflAutoTurnOffKeep ? TRUE : FALSE; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns the keep aborted transactions in RFL flag for the database ****************************************************************************/ RCODE FLMAPI F_Db::getKeepAbortedTransInRflFlag( FLMBOOL * pbKeep ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } *pbKeep = m_pDatabase->m_uncommittedDbHdr.ui8RflKeepAbortedTrans ? TRUE : FALSE; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns disk space usage for the database ****************************************************************************/ RCODE FLMAPI F_Db::getDiskSpaceUsage( FLMUINT64 * pui64DataSize, FLMUINT64 * pui64RollbackSize, FLMUINT64 * pui64RflSize) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiEndAddress; FLMUINT uiLastFileNumber; FLMUINT64 ui64LastFileSize; char szTmpName [F_PATH_MAX_SIZE]; char szRflDir [F_PATH_MAX_SIZE]; IF_FileHdl * pFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // See if they want the database files sizes. if (pui64DataSize) { uiEndAddress = m_uiLogicalEOF; uiLastFileNumber = FSGetFileNumber( uiEndAddress); // Last file number better be in the proper range. flmAssert( uiLastFileNumber >= 1 && uiLastFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER); // Get the actual size of the last file. if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiLastFileNumber, &ui64LastFileSize))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (uiLastFileNumber > 1) { rc = NE_XFLM_OK; ui64LastFileSize = 0; } else { // Should always be a data file #1 RC_UNEXPECTED_ASSERT( rc); goto Exit; } } else { goto Exit; } } // One of two situations exists with respect to the last // file: 1) it has not been fully written out yet (blocks // are still cached, or 2) it has been written out and // extended beyond what the logical EOF shows. We want // the larger of these two possibilities. if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) { ui64LastFileSize = FSGetFileOffset( uiEndAddress); } if (uiLastFileNumber == 1) { // Only one file - use last file's size. *pui64DataSize = ui64LastFileSize; } else { // Size is the sum of full size for all files except the last one, // plus the calculated (or actual) size of the last one. (*pui64DataSize) = (FLMUINT64)(uiLastFileNumber - 1) * (FLMUINT64)m_pDatabase->m_uiMaxFileSize + ui64LastFileSize; } } // See if they want the rollback files sizes. if (pui64RollbackSize) { uiEndAddress = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RblEOF; uiLastFileNumber = FSGetFileNumber( uiEndAddress); // Last file number better be in the proper range. flmAssert( !uiLastFileNumber || (uiLastFileNumber >= FIRST_LOG_BLOCK_FILE_NUMBER && uiLastFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER)); // Get the size of the last file number. if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiLastFileNumber, &ui64LastFileSize))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (uiLastFileNumber) { rc = NE_XFLM_OK; ui64LastFileSize = 0; } else { // Should always have rollback file #0 RC_UNEXPECTED_ASSERT( rc); goto Exit; } } else { goto Exit; } } // If the EOF offset for the last file is greater than the // actual file size, use it instead of the actual file size. if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) { ui64LastFileSize = FSGetFileOffset( uiEndAddress); } // Special case handling here because rollback file numbers start with // zero and then skip to a file number that is one beyond the // highest data file number - so the calculation for file size needs // to account for this. if (!uiLastFileNumber) { *pui64RollbackSize = ui64LastFileSize; } else { FLMUINT uiFirstLogFileNum = FIRST_LOG_BLOCK_FILE_NUMBER; // Add full size of file zero plus a full size for every file // except the last one. (*pui64RollbackSize) = (FLMUINT64)(uiLastFileNumber - uiFirstLogFileNum + 1) * (FLMUINT64)m_pDatabase->m_uiMaxFileSize + ui64LastFileSize; } } // See if they want the roll-forward log file sizes. if (pui64RflSize) { char * pszDbFileName = m_pDatabase->m_pszDbPath; *pui64RflSize = 0; // Scan the RFL directory for // RFL files. The call below to rflGetDirAndPrefix is done // to get the prefix. It will not return the correct // RFL directory name, because we are passing in a NULL // RFL directory path (which may or may not be correct). // That's OK, because we get the RFL directory directly // from the F_Rfl object anyway. if (RC_BAD( rc = rflGetDirAndPrefix( pszDbFileName, NULL, szRflDir))) { goto Exit; } // We need to get the RFL directory from the F_Rfl object. m_pDatabase->lockMutex(); f_strcpy( szRflDir, m_pDatabase->m_pRfl->getRflDirPtr()); m_pDatabase->unlockMutex(); // See if the directory exists. If not, we are done. if (gv_XFlmSysData.pFileSystem->isDir( szRflDir)) { // Open the directory and scan for RFL files. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openDir( szRflDir, "*", &pDirHdl))) { goto Exit; } for (;;) { if (RC_BAD( rc = pDirHdl->next())) { if (rc == NE_FLM_IO_NO_MORE_FILES) { rc = NE_XFLM_OK; break; } else { goto Exit; } } pDirHdl->currentItemPath( szTmpName); // If the item looks like an RFL file name, get // its size. if (!pDirHdl->currentItemIsDir() && rflGetFileNum( szTmpName, &uiLastFileNumber)) { // Open the file and get its size. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( szTmpName, gv_XFlmSysData.uiFileOpenFlags, &pFileHdl))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; ui64LastFileSize = 0; } else { goto Exit; } } else { if (RC_BAD( rc = pFileHdl->size( &ui64LastFileSize))) { goto Exit; } } if (pFileHdl) { pFileHdl->Release(); pFileHdl = NULL; } (*pui64RflSize) += ui64LastFileSize; } } } } Exit: if (pFileHdl) { pFileHdl->Release(); } if (pDirHdl) { pDirHdl->Release(); } if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns the next incremental backup sequence number for the database ****************************************************************************/ RCODE FLMAPI F_Db::getNextIncBackupSequenceNum( FLMUINT * puiNextIncBackupSequenceNum ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } else if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } *puiNextIncBackupSequenceNum = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32IncBackupSeqNum; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Returns list of lock waiters in an object that allows caller to iterate through the list. ****************************************************************************/ RCODE FLMAPI F_Db::getLockWaiters( IF_LockInfoClient * pLockInfo ) { RCODE rc = NE_XFLM_OK; if (m_pDatabase->m_pDatabaseLockObj) { rc = m_pDatabase->m_pDatabaseLockObj->getLockInfo( pLockInfo); } else { pLockInfo->setLockCount( 0); } return( rc); } /**************************************************************************** Desc: Returns RFL directory for the database ****************************************************************************/ void FLMAPI F_Db::getRflDir( char * pszRflDir ) { m_pDatabase->lockMutex(); f_strcpy( pszRflDir, m_pDatabase->m_pRfl->getRflDirPtr()); m_pDatabase->unlockMutex(); } /**************************************************************************** Desc: Returns database serial number ****************************************************************************/ void FLMAPI F_Db::getSerialNumber( char * pucSerialNumber) { m_pDatabase->lockMutex(); f_memcpy( pucSerialNumber, m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum, XFLM_SERIAL_NUM_SIZE); m_pDatabase->unlockMutex(); } libxflaim-5.1.969/src/fxml.cpp0000644000175000017500000037351610511001742017515 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: XML parser // // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fxml.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" // Global data extern FLMUNICODE gv_uzXFLAIMNamespace[]; static FLMUNICODE gv_puzNamespaceDeclPrefix[] = { FLM_UNICODE_x, FLM_UNICODE_m, FLM_UNICODE_l, FLM_UNICODE_n, FLM_UNICODE_s, 0 }; static FLMUNICODE gv_puzXMLPrefix[] = { FLM_UNICODE_x, FLM_UNICODE_m, FLM_UNICODE_l, 0 }; FLMUNICODE gv_puzXMLNSURI[] = { FLM_UNICODE_h, FLM_UNICODE_t, FLM_UNICODE_t, FLM_UNICODE_p, FLM_UNICODE_COLON, FLM_UNICODE_FSLASH, FLM_UNICODE_FSLASH, FLM_UNICODE_w, FLM_UNICODE_w, FLM_UNICODE_w, FLM_UNICODE_PERIOD, FLM_UNICODE_w, FLM_UNICODE_3, FLM_UNICODE_c, FLM_UNICODE_PERIOD, FLM_UNICODE_o, FLM_UNICODE_r, FLM_UNICODE_g, FLM_UNICODE_FSLASH, FLM_UNICODE_T, FLM_UNICODE_R, FLM_UNICODE_FSLASH, FLM_UNICODE_1, FLM_UNICODE_9, FLM_UNICODE_9, FLM_UNICODE_9, FLM_UNICODE_FSLASH, FLM_UNICODE_R, FLM_UNICODE_E, FLM_UNICODE_C, FLM_UNICODE_HYPHEN, FLM_UNICODE_x, FLM_UNICODE_m, FLM_UNICODE_l, FLM_UNICODE_HYPHEN, FLM_UNICODE_n, FLM_UNICODE_a, FLM_UNICODE_m, FLM_UNICODE_e, FLM_UNICODE_s, FLM_UNICODE_HYPHEN, FLM_UNICODE_1, FLM_UNICODE_9, FLM_UNICODE_9, FLM_UNICODE_9, FLM_UNICODE_0, FLM_UNICODE_1, FLM_UNICODE_1, FLM_UNICODE_4, 0 }; FSTATIC RCODE exportUniValue( IF_OStream * pOStream, FLMUNICODE * puzStr, FLMUINT uiStrChars, FLMBOOL bEncodeSpecialChars, FLMUINT uiIndentCount); /**************************************************************************** Desc: Constructor ****************************************************************************/ F_XMLImport::F_XMLImport() { m_uiValBufSize = 0; m_pucValBuf = NULL; m_bSetup = FALSE; m_fnStatus = NULL; m_pvCallbackData = NULL; m_tmpPool.poolInit( 4096); m_attrPool.poolInit( 4096); m_puzCurrLineBuf = NULL; m_uiCurrLineBufMaxChars = 0; reset(); } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_XMLImport::~F_XMLImport() { reset(); if( m_pucValBuf) { f_free( &m_pucValBuf); } if( m_puzCurrLineBuf) { f_free( &m_puzCurrLineBuf); } m_tmpPool.poolFree(); m_attrPool.poolFree(); } /**************************************************************************** Desc: Resets member variables so the object can be reused ****************************************************************************/ void F_XMLImport::reset( void) { m_uiCurrLineNum = 0; m_uiCurrLineNumChars = 0; m_uiCurrLineOffset = 0; m_ucUngetByte = 0; m_uiCurrLineFilePos = 0; m_uiCurrLineBytes = 0; m_pStream = NULL; m_uiFlags = 0; m_eXMLEncoding = XFLM_XML_USASCII_ENCODING; m_pDb = NULL; m_uiCollection = 0; f_memset( &m_importStats, 0, sizeof( XFLM_IMPORT_STATS)); popNamespaces( getNamespaceCount()); m_tmpPool.poolReset( NULL); resetAttrList(); } /**************************************************************************** Desc: Initializes the object (allocates buffers, etc.) ****************************************************************************/ RCODE F_XMLImport::setup( void) { RCODE rc = NE_XFLM_OK; flmAssert( !m_bSetup); if( RC_BAD( rc = resizeValBuffer( 2048))) { goto Exit; } m_bSetup = TRUE; Exit: if( RC_BAD( rc)) { if( m_pucValBuf) { f_free( &m_pucValBuf); m_pucValBuf = NULL; } } return( rc); } /**************************************************************************** Desc: Reads data from the input stream and builds a FLAIM record ****************************************************************************/ RCODE F_XMLImport::import( IF_IStream * pStream, F_Db * pDb, FLMUINT uiCollection, FLMUINT uiFlags, F_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, F_DOMNode ** ppNewNode, XFLM_IMPORT_STATS * pImportStats) { RCODE rc = NE_XFLM_OK; // Reset the state of the parser reset(); // If a root element was passed in, do some sanity checks // before importing the XML stream if (pNodeToLinkTo) { FLMUINT uiTmp; if( RC_BAD( rc = pNodeToLinkTo->getCollection( pDb, &uiTmp))) { goto Exit; } if( uiTmp != uiCollection) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } } m_pDb = pDb; m_uiCollection = uiCollection; // Set up namespace support. Un-prefixed names (NULL prefix) are // not bound to a namespace (NULL URI). The 'xml' namespace prefix // is, by definition, bound to 'http://www.w3.org/XML/1998/namespace' if( RC_BAD( rc = pushNamespace( NULL, NULL))) { goto Exit; } if( RC_BAD( rc = pushNamespace( gv_puzXMLPrefix, gv_puzXMLNSURI))) { goto Exit; } m_pStream = pStream; m_uiFlags = uiFlags; if( RC_BAD( rc = processProlog())) { goto Exit; } if( RC_BAD( rc = processElement( pNodeToLinkTo, eInsertLoc, ppNewNode))) { goto Exit; } // Call the status hook one last time m_importStats.uiDocuments++; if( m_fnStatus) { m_fnStatus( XML_STATS, (void *)&m_importStats, NULL, NULL, m_pvCallbackData); } // Tally and return the import stats if( pImportStats) { pImportStats->uiChars += m_importStats.uiChars; pImportStats->uiAttributes += m_importStats.uiAttributes; pImportStats->uiElements += m_importStats.uiElements; pImportStats->uiText += m_importStats.uiText; pImportStats->uiDocuments += m_importStats.uiDocuments; } Exit: if( RC_BAD( rc) && pImportStats) { pImportStats->uiErrLineNum = m_importStats.uiErrLineNum ? m_importStats.uiErrLineNum : m_uiCurrLineNum; pImportStats->uiErrLineOffset = m_importStats.uiErrLineOffset ? m_importStats.uiErrLineOffset : m_uiCurrLineOffset; pImportStats->eErrorType = ( XMLParseError)( m_importStats.eErrorType); pImportStats->uiErrLineFilePos = m_importStats.uiErrLineFilePos; pImportStats->uiErrLineBytes = m_importStats.uiErrLineBytes; pImportStats->eXMLEncoding = m_importStats.eXMLEncoding; } m_pDb = NULL; m_uiCollection = 0; return( rc); } /**************************************************************************** Desc: Process an XML prolog ****************************************************************************/ RCODE F_XMLImport::processProlog( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if (lineHasToken( "createNode( m_pDb, DATA_NODE, 0, XFLM_LAST_CHILD, (IF_DOMNode **)&pData))) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset, XML_ERR_CREATING_DATA_NODE, m_uiCurrLineFilePos, m_uiCurrLineBytes); goto Exit; } switch( pParent->m_pCachedNode->getDataType()) { case XFLM_TEXT_TYPE: { if( RC_BAD( rc = pData->setUnicode( m_pDb, puzTextStart))) { goto Exit; } m_importStats.uiText++; if( m_fnStatus && (m_importStats.uiText % 50) == 0) { m_fnStatus( XML_STATS, (void *)&m_importStats, NULL, NULL, m_pvCallbackData); } break; } case XFLM_NUMBER_TYPE: { FLMUINT64 ui64Val; FLMBOOL bNeg; if( RC_BAD( rc = unicodeToNumber64( puzTextStart, &ui64Val, &bNeg))) { goto Exit; } if( !bNeg) { if( RC_BAD( rc = pData->setUINT64( m_pDb, ui64Val))) { goto Exit; } } else { if( RC_BAD( rc = pData->setINT64( m_pDb, -((FLMINT64)ui64Val)))) { goto Exit; } } break; } case XFLM_BINARY_TYPE: { if( RC_BAD( rc = pData->setBinary( m_pDb, pucValue, uiValueLen))) { goto Exit; } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } Exit: if( pData) { pData->Release(); } return( rc); } /**************************************************************************** Desc: Processes an XML element ****************************************************************************/ RCODE F_XMLImport::processElement( F_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, F_DOMNode ** ppNewNode) { RCODE rc = NE_XFLM_OK; FLMBOOL bHasContent; FLMBOOL bFlushedValue = FALSE; FLMUINT uiChars; FLMUINT uiOffset = 0; FLMUNICODE uChar; F_DOMNode * pElement = NULL; F_XMLNamespace * pNamespace = NULL; FLMUNICODE * puzPrefix; FLMUNICODE * puzLocal; FLMUINT uiStartNSCount = getNamespaceCount(); FLMUINT uiTmp; FLMUINT uiWhitespaceStartOffset = 0; FLMBOOL bNamespaceDecl; FLMUINT uiSavedLineNum = 0; FLMUINT uiSavedOffset = 0; FLMUINT uiSavedFilePos = 0; FLMUINT uiSavedLineBytes = 0; if( RC_BAD( rc = processSTag( pNodeToLinkTo, eInsertLoc, &bHasContent, &pElement))) { goto Exit; } if (ppNewNode) { *ppNewNode = pElement; (*ppNewNode)->AddRef(); } if( !bHasContent) { goto Exit; } for( ;;) { if ((uChar = getChar()) == 0) { uChar = ASCII_NEWLINE; if (RC_BAD( rc = getLine())) { goto Exit; } } if( uChar == FLM_UNICODE_LT) { if( uiWhitespaceStartOffset) { // Set the offset to where the whitespace would // have started. flmAssert( uiWhitespaceStartOffset <= uiOffset); uiOffset = uiWhitespaceStartOffset; uiWhitespaceStartOffset = 0; } if( uiOffset) { // Flush the value if( pElement) { if( pElement->m_pCachedNode->getDataType() == XFLM_TEXT_TYPE || pElement->m_pCachedNode->getDataType() == XFLM_NUMBER_TYPE) { if( uiOffset + 1 >= m_uiValBufSize) { if( RC_BAD( rc = resizeValBuffer( uiOffset + 2))) { goto Exit; } } m_pucValBuf[ uiOffset] = 0; m_pucValBuf[ uiOffset + 1] = 0; } if( RC_BAD( rc = flushElementValue( pElement, m_pucValBuf, uiOffset))) { goto Exit; } } bFlushedValue = TRUE; uiOffset = 0; } // Preserve start location for error handling if necessary uiSavedLineNum = m_uiCurrLineNum; uiSavedOffset = m_uiCurrLineOffset; uiSavedFilePos = m_uiCurrLineFilePos; uiSavedLineBytes = m_uiCurrLineBytes; if (lineHasToken( "?")) { if( RC_BAD( rc = processPI( pElement, uiSavedLineNum, uiSavedOffset, uiSavedFilePos, uiSavedLineBytes))) { goto Exit; } } else if (lineHasToken( "!--")) { if( RC_BAD( rc = processComment( pElement, uiSavedLineNum, uiSavedOffset, uiSavedFilePos, uiSavedLineBytes))) { goto Exit; } } else if (lineHasToken( "![CDATA[")) { if( RC_BAD( rc = processCDATA( pElement, uiSavedLineNum, uiSavedOffset, uiSavedFilePos, uiSavedLineBytes))) { goto Exit; } } else if (lineHasToken( "/")) { break; } else if( gv_XFlmSysData.pXml->isNameChar( peekChar())) { // Unget the "<" - because processElement expect to see // "m_pCachedNode->getDataType() == XFLM_BINARY_TYPE) { ungetChar(); if( RC_BAD( rc = getBinaryVal( &uiOffset))) { goto Exit; } } else if (uChar == FLM_UNICODE_AMP) { if( RC_BAD( rc = processReference( &uChar))) { goto Exit; } flmAssert( uChar); if (pElement->m_pCachedNode->getDataType() != XFLM_NODATA_TYPE) { *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; uiOffset += sizeof( FLMUNICODE); uiWhitespaceStartOffset = 0; if( uiOffset >= m_uiValBufSize) { if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) { goto Exit; } } } } else { if( pElement->m_pCachedNode->getDataType() != XFLM_NODATA_TYPE) { if( m_uiFlags & FLM_XML_COMPRESS_WHITESPACE_FLAG) { if( gv_XFlmSysData.pXml->isWhitespace( uChar)) { // If uiOffset is zero, this is still leading // white space, and we should ignore it. // Otherwise, we need to keep track of where // whitespace began. if( !uiOffset) { uChar = 0; } else { uiWhitespaceStartOffset = uiOffset; } } else { // Last character is not whitespace. uiWhitespaceStartOffset = 0; } } if( uChar) { *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; uiOffset += sizeof( FLMUNICODE); if( uiOffset >= m_uiValBufSize) { if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) { goto Exit; } } } } } } flmAssert( !uiOffset); uiSavedOffset = m_uiCurrLineOffset; if( RC_BAD( rc = getQualifiedName( &uiChars, &puzPrefix, &puzLocal, &bNamespaceDecl, NULL))) { goto Exit; } // Validate that the end tag matches the start tag if( pElement) { // Element names cannot be "xmlns" or begin with "xmlns:" if (bNamespaceDecl) { setErrInfo( m_uiCurrLineNum, uiSavedOffset, XML_ERR_XMLNS_IN_ELEMENT_NAME, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } if( puzPrefix) { FLMUINT uiPrefixId1; FLMUINT uiPrefixId2; if( RC_BAD( rc = m_pDb->m_pDict->getPrefixId( m_pDb, puzPrefix, &uiPrefixId1))) { goto Exit; } if( RC_BAD( rc = pElement->getPrefixId( m_pDb, &uiPrefixId2))) { goto Exit; } if( uiPrefixId1 != uiPrefixId2) { setErrInfo( m_uiCurrLineNum, uiSavedOffset, XML_ERR_ELEMENT_NAME_MISMATCH, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } else { if( RC_BAD( rc = pElement->getPrefixId( m_pDb, &uiTmp))) { goto Exit; } if( uiTmp) { setErrInfo( m_uiCurrLineNum, uiSavedOffset, XML_ERR_ELEMENT_NAME_MISMATCH, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } if( RC_BAD( rc = findNamespace( puzPrefix, &pNamespace))) { if( rc == NE_XFLM_NOT_FOUND) { setErrInfo( m_uiCurrLineNum, uiSavedOffset, XML_ERR_PREFIX_NOT_DEFINED, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); } goto Exit; } if( RC_BAD( rc = m_pDb->getElementNameId( pNamespace->getURIPtr(), puzLocal, &uiTmp))) { if( rc == NE_XFLM_NOT_FOUND) { setErrInfo( m_uiCurrLineNum, uiSavedOffset, XML_ERR_ELEMENT_NAME_MISMATCH, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); } goto Exit; } if( pElement->getNameId() != uiTmp) { setErrInfo( m_uiCurrLineNum, uiSavedOffset, XML_ERR_ELEMENT_NAME_MISMATCH, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } // Skip any whitespace after the name if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } // Get the ending ">" if( getChar() != FLM_UNICODE_GT) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: if( pNamespace) { pNamespace->Release(); } popNamespaces( getNamespaceCount() - uiStartNSCount); if( pElement) { pElement->Release(); } return( rc); } /**************************************************************************** Desc: Processes an XML STag ****************************************************************************/ RCODE F_XMLImport::processSTag( F_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, FLMBOOL * pbHasContent, F_DOMNode ** ppElement) { FLMUNICODE uChar; FLMUINT uiChars; F_DOMNode * pElement = NULL; F_XMLNamespace * pNamespace = NULL; FLMUNICODE * puzTmpPrefix; FLMUNICODE * puzPrefix = NULL; FLMUNICODE * puzTmpLocal; FLMUNICODE * puzLocal = NULL; FLMUINT uiNameId; FLMUINT uiAllocSize; void * pvMark = m_tmpPool.poolMark(); RCODE rc = NE_XFLM_OK; FLMBOOL bNamespaceDecl; FLMUINT uiSavedLineNum; FLMUINT uiSavedOffset; FLMUINT uiSavedFilePos; FLMUINT uiSavedLineBytes; *pbHasContent = FALSE; if( getChar() != FLM_UNICODE_LT) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_ELEMENT_LT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } uiSavedLineNum = m_uiCurrLineNum; uiSavedOffset = m_uiCurrLineOffset; uiSavedFilePos = m_uiCurrLineFilePos; uiSavedLineBytes = m_uiCurrLineBytes; if( RC_BAD( rc = getQualifiedName( &uiChars, &puzTmpPrefix, &puzTmpLocal, &bNamespaceDecl, NULL))) { goto Exit; } // Element names cannot be "xmlns" or begin with "xmlns:" if (bNamespaceDecl) { setErrInfo( uiSavedLineNum, uiSavedOffset, XML_ERR_XMLNS_IN_ELEMENT_NAME, uiSavedFilePos, uiSavedLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } uiAllocSize = (f_unilen( puzTmpLocal) + 1) * sizeof( FLMUNICODE); if( RC_BAD( rc = m_tmpPool.poolAlloc( uiAllocSize, (void **)&puzLocal))) { goto Exit; } f_unicpy( puzLocal, puzTmpLocal); if( puzTmpPrefix) { // Need to save the prefix, because as parsing // continues, the scratch buffer will be overwritten uiAllocSize = (f_unilen( puzTmpPrefix) + 1) * sizeof( FLMUNICODE); if( RC_BAD( rc = m_tmpPool.poolAlloc( uiAllocSize, (void **)&puzPrefix))) { goto Exit; } f_unicpy( puzPrefix, puzTmpPrefix); } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } // Read the attributes resetAttrList(); uChar = peekChar(); if( uChar != FLM_UNICODE_GT && uChar != FLM_UNICODE_FSLASH) { if( RC_BAD( rc = processAttributeList())) { goto Exit; } } // Find or create the element's name ID if( RC_BAD( rc = findNamespace( puzPrefix, &pNamespace))) { if( rc == NE_XFLM_NOT_FOUND) { setErrInfo( uiSavedLineNum, uiSavedOffset, XML_ERR_PREFIX_NOT_DEFINED, uiSavedFilePos, uiSavedLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); } goto Exit; } if( RC_BAD( rc = m_pDb->getElementNameId( pNamespace->getURIPtr(), puzLocal, &uiNameId))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG) || (pNamespace->getURIPtr() && f_unicmp( pNamespace->getURIPtr(), gv_uzXFLAIMNamespace) == 0)) { rc = RC_SET( NE_XFLM_UNDEFINED_ELEMENT_NAME); goto Exit; } // Automatically extend the schema uiNameId = 0; if( RC_BAD( rc = m_pDb->createElementDef( pNamespace->getURIPtr(), puzLocal, XFLM_TEXT_TYPE, &uiNameId))) { goto Exit; } } // Create the element node if( pNodeToLinkTo) { if( RC_BAD( rc = pNodeToLinkTo->createNode( m_pDb, ELEMENT_NODE, uiNameId, eInsertLoc, (IF_DOMNode **)&pElement))) { setErrInfo( uiSavedLineNum, uiSavedOffset, XML_ERR_CREATING_ELEMENT_NODE, uiSavedFilePos, uiSavedLineBytes); goto Exit; } } else { if( RC_BAD( rc = m_pDb->createRootElement( m_uiCollection, uiNameId, (IF_DOMNode **)&pElement))) { setErrInfo( uiSavedLineNum, uiSavedOffset, XML_ERR_CREATING_ROOT_ELEMENT, uiSavedFilePos, uiSavedLineBytes); goto Exit; } } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } // Need to end with ">" or "/>" uChar = getChar(); if( uChar == FLM_UNICODE_GT) { *pbHasContent = TRUE; } else if( uChar == FLM_UNICODE_FSLASH) { if( getChar() != FLM_UNICODE_GT) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } else { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } // Set the element's prefix if( RC_BAD( rc = addAttributesToElement( pElement))) { goto Exit; } if( puzPrefix) { if( RC_BAD( rc = pElement->setPrefix( m_pDb, puzPrefix))) { goto Exit; } } if( ppElement) { *ppElement = pElement; pElement = NULL; } m_importStats.uiElements++; if( m_fnStatus && (m_importStats.uiElements % 50) == 0) { m_fnStatus( XML_STATS, (void *)&m_importStats, NULL, NULL, m_pvCallbackData); } Exit: if( pElement) { pElement->Release(); } if( pNamespace) { pNamespace->Release(); } m_tmpPool.poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Processes an element's attributes ****************************************************************************/ RCODE F_XMLImport::processAttributeList( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiChars; FLMUNICODE * puzLocal; FLMUNICODE * puzPrefix; XML_ATTR * pAttr = NULL; FLMBOOL bFoundDefaultNamespace = FALSE; FLMUINT uiNamespaceCount = 0; FLMBOOL bNamespaceDecl; FLMBOOL bDefaultNamespaceDecl; FLMUINT uiSavedLineNum; FLMUINT uiSavedOffset; FLMUINT uiSavedFilePos; FLMUINT uiSavedLineBytes; for( ;;) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if( !gv_XFlmSysData.pXml->isNameChar( peekChar())) { break; } uiSavedLineNum = m_uiCurrLineNum; uiSavedOffset = m_uiCurrLineOffset; uiSavedFilePos = m_uiCurrLineFilePos; uiSavedLineBytes = m_uiCurrLineBytes; if( RC_BAD( rc = getQualifiedName( &uiChars, &puzPrefix, &puzLocal, &bNamespaceDecl, &bDefaultNamespaceDecl))) { goto Exit; } if( RC_BAD( rc = allocAttribute( &pAttr))) { goto Exit; } pAttr->uiLineNum = uiSavedLineNum; pAttr->uiLineOffset = uiSavedOffset; pAttr->uiLineFilePos = uiSavedFilePos; pAttr->uiLineBytes = uiSavedLineBytes; if( RC_BAD( rc = setPrefix( pAttr, puzPrefix))) { goto Exit; } if( RC_BAD( rc = setLocalName( pAttr, puzLocal))) { goto Exit; } if (bNamespaceDecl) { if (bDefaultNamespaceDecl) { pAttr->uiFlags |= F_DEFAULT_NS_DECL; } else { pAttr->uiFlags |= F_PREFIXED_NS_DECL; } } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } // Attribute name must be followed by an "=" if( getChar() != FLM_UNICODE_EQ) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_EQ, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } pAttr->uiValueLineNum = m_uiCurrLineNum; pAttr->uiValueLineOffset = m_uiCurrLineOffset; if( RC_BAD( rc = processAttValue( pAttr))) { goto Exit; } m_importStats.uiAttributes++; if( m_fnStatus && (m_importStats.uiAttributes % 50) == 0) { m_fnStatus( XML_STATS, (void *)&m_importStats, NULL, NULL, m_pvCallbackData); } } // Push any namespace declarations onto the stack for( pAttr = m_pFirstAttr; pAttr; pAttr = pAttr->pNext) { // Duplicate namespace declarations are not allowed within a single element. // So, multiple default namespace declarations or multiple uses of the same // prefix in will result in a syntax error. if( pAttr->uiFlags & F_DEFAULT_NS_DECL) { // Default namespace declaration if( bFoundDefaultNamespace) { setErrInfo( pAttr->uiLineNum, pAttr->uiLineOffset, XML_ERR_MULTIPLE_XMLNS_DECLS, pAttr->uiLineFilePos, pAttr->uiLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } if( !pAttr->puzVal || *pAttr->puzVal == 0) { // No namespace if( RC_BAD( rc = pushNamespace( NULL, NULL))) { goto Exit; } } else { if( RC_BAD( rc = pushNamespace( NULL, pAttr->puzVal))) { goto Exit; } } uiNamespaceCount++; bFoundDefaultNamespace = TRUE; } else if( pAttr->uiFlags & F_PREFIXED_NS_DECL) { // Check for a unique prefix within current element if( RC_OK( rc = findNamespace( &pAttr->puzLocalName [6], NULL, uiNamespaceCount))) { setErrInfo( pAttr->uiLineNum, pAttr->uiLineOffset, XML_ERR_MULTIPLE_PREFIX_DECLS, pAttr->uiLineFilePos, pAttr->uiLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } else if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } else { rc = NE_XFLM_OK; } if( RC_BAD( rc = pushNamespace( &pAttr->puzLocalName [6], pAttr->puzVal))) { goto Exit; } uiNamespaceCount++; } } Exit: return( rc); } /**************************************************************************** Desc: Processes an XML declaration ****************************************************************************/ RCODE F_XMLImport::processXMLDecl( void) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; // Have already eaten the "isWhitespace( uChar)) { if (RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } else { goto Process_Question_Mark; } if (lineHasToken( "encoding")) { if( RC_BAD( rc = processEncodingDecl())) { goto Exit; } uChar = peekChar(); if (!uChar || gv_XFlmSysData.pXml->isWhitespace( uChar)) { if (RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } else { goto Process_Question_Mark; } } if (lineHasToken( "standalone")) { if( RC_BAD( rc = processSDDecl())) { goto Exit; } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } Process_Question_Mark: if (!lineHasToken( "?>")) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset, XML_ERR_EXPECTING_QUEST_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Processes an XML document type declaration ****************************************************************************/ RCODE F_XMLImport::processDocTypeDecl( void) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; // Have already eaten the "isWhitespace( uChar)) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if (lineHasToken( "SYSTEM")) { if( RC_BAD( rc = processID( FALSE))) { goto Exit; } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } else if (lineHasToken( "PUBLIC")) { if( RC_BAD( rc = processID( TRUE))) { goto Exit; } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } } if( peekChar() == FLM_UNICODE_LBRACKET) { // Eat up the '[' (void)getChar(); for( ;;) { uChar = getChar(); if( uChar == FLM_UNICODE_PERCENT) { if( RC_BAD( rc = processPERef())) { goto Exit; } } else if( uChar == FLM_UNICODE_RBRACKET) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } break; } else if (gv_XFlmSysData.pXml->isWhitespace( uChar)) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } else { ungetChar(); if( RC_BAD( rc = processMarkupDecl())) { goto Exit; } } } } if( getChar() != FLM_UNICODE_GT) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: See if the current line has the specified token in it starting from the current offset. ****************************************************************************/ FLMBOOL F_XMLImport::lineHasToken( const char * pszToken) { FLMUINT uiOffset; uiOffset = m_uiCurrLineOffset; while (uiOffset < m_uiCurrLineNumChars) { if (m_puzCurrLineBuf [uiOffset] != (FLMUNICODE)(*pszToken)) { // Do NOT change m_uiCurrLineOffset if we return FALSE. return( FALSE); } pszToken++; uiOffset++; if (*pszToken == 0) { m_uiCurrLineOffset = uiOffset; return( TRUE); } } return( FALSE); } /**************************************************************************** Desc: Processes an XML markup declaration ****************************************************************************/ RCODE F_XMLImport::processMarkupDecl( void) { RCODE rc = NE_XFLM_OK; if (lineHasToken( "isWhitespace( uChar)) { if (RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if( peekChar() == FLM_UNICODE_GT) { break; } if( RC_BAD( rc = processAttDef())) { goto Exit; } uiAttDefCount++; } else if (uChar == FLM_UNICODE_GT) { break; } else { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset, XML_ERR_EXPECTING_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } uChar = getChar(); flmAssert( uChar == FLM_UNICODE_GT); if( !uiAttDefCount) { setErrInfo( uiAttListLineNum, uiAttListLineOffset, XML_ERR_MUST_HAVE_ONE_ATT_DEF, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Processes and entity declaration ****************************************************************************/ RCODE F_XMLImport::processEntityDecl( void) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMBOOL bGEDecl = FALSE; // Have already eaten up the "isQuoteChar( uChar)) { if( RC_BAD( rc = processEntityValue())) { goto Exit; } } else { if (lineHasToken( "SYSTEM")) { if( RC_BAD( rc = processID( TRUE))) { goto Exit; } } else if (lineHasToken( "PUBLIC")) { if( RC_BAD( rc = processID( FALSE))) { goto Exit; } } else { if (RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } goto Process_GT; } if (!gv_XFlmSysData.pXml->isWhitespace( peekChar())) { goto Process_GT; } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if (!bGEDecl) { goto Process_GT; } if (!lineHasToken( "NDATA")) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset, XML_ERR_EXPECTING_NDATA, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } // Whitespace must be between the NDATA and the name if( RC_BAD( rc = skipWhitespace( TRUE))) { goto Exit; } if( RC_BAD( rc = getName( NULL))) { goto Exit; } if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } } Process_GT: if( getChar() != FLM_UNICODE_GT) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_GT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Processes an XML ID ****************************************************************************/ RCODE F_XMLImport::processID( FLMBOOL bPublicId) { RCODE rc = NE_XFLM_OK; // Have already eaten the "SYSTEM" or "PUBLIC" token if( RC_BAD( rc = skipWhitespace( TRUE))) { goto Exit; } if (bPublicId) { // Public ID if( RC_BAD( rc = getPubidLiteral())) { goto Exit; } // Must be whitespace if it wasn't a '>' if( RC_BAD( rc = skipWhitespace( TRUE))) { goto Exit; } } // Get the system ID if (RC_BAD( rc = getSystemLiteral())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Processes a notation declaration ****************************************************************************/ RCODE F_XMLImport::processNotationDecl( void) { RCODE rc = NE_XFLM_OK; // Have already eaten up the "isQuoteChar( uChar)) { ungetChar(); if( RC_BAD( rc = processAttValue( NULL))) { goto Exit; } } else { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_INVALID_DEFAULT_DECL, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Processes a content specification ****************************************************************************/ RCODE F_XMLImport::processContentSpec( void) { RCODE rc = NE_XFLM_OK; if (lineHasToken( "EMPTY") || lineHasToken( "ANY")) { goto Exit; } if (getChar() == FLM_UNICODE_LPAREN) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if (lineHasToken( "#PCDATA")) { if( RC_BAD( rc = processMixedContent())) { goto Exit; } } else if (lineHasToken( "#")) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset + 1, XML_ERR_EXPECTING_PCDATA, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } else { if( RC_BAD( rc = processChildContent())) { goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: Processes mixed content ****************************************************************************/ RCODE F_XMLImport::processMixedContent( void) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMBOOL bExpectingAsterisk = FALSE; // Have eaten up the "(#PCDATA" for( ;;) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } uChar = getChar(); if( uChar == FLM_UNICODE_RPAREN) { break; } else if( uChar == FLM_UNICODE_PIPE) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } if( RC_BAD( rc = getName( NULL))) { goto Exit; } bExpectingAsterisk = TRUE; } else { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_RPAREN_OR_PIPE, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } if( bExpectingAsterisk) { if( getChar() != FLM_UNICODE_ASTERISK) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_ASTERISK, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Processes child content ****************************************************************************/ RCODE F_XMLImport::processChildContent( void) { FLMUNICODE uChar; FLMUINT uiItemCount = 0; FLMUINT uiDelimCount = 0; FLMBOOL bChoice = FALSE; FLMBOOL bSeq = FALSE; RCODE rc = NE_XFLM_OK; // Have eaten up the "(" for( ;;) { if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } uChar = getChar(); if( uChar == FLM_UNICODE_LPAREN) { if( RC_BAD( rc = processChildContent())) { goto Exit; } uiItemCount++; } else if (uChar == FLM_UNICODE_RPAREN) { if( !uiItemCount || (uiItemCount - 1) != uiDelimCount) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EMPTY_CONTENT_INVALID, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } break; } else if (uChar == FLM_UNICODE_PIPE) { if( bSeq) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_CANNOT_MIX_CHOICE_AND_SEQ, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } bChoice = TRUE; uiDelimCount++; } else if (uChar == FLM_UNICODE_COMMA) { if (bChoice) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_CANNOT_MIX_CHOICE_AND_SEQ, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } bSeq = TRUE; uiDelimCount++; } else { ungetChar(); if( RC_BAD( rc = getName( NULL))) { goto Exit; } uiItemCount++; uChar = peekChar(); if (uChar == FLM_UNICODE_QUEST || uChar == FLM_UNICODE_ASTERISK || uChar == FLM_UNICODE_PLUS) { // Eat up a "?", "*", or "+" (void)getChar(); } } } uChar = peekChar(); if( uChar == FLM_UNICODE_QUEST || uChar == FLM_UNICODE_ASTERISK || uChar == FLM_UNICODE_PLUS) { // Eat up a "?", "*", or "+" (void)getChar(); } Exit: return( rc); } /**************************************************************************** Desc: Processes a misc. declaration ****************************************************************************/ RCODE F_XMLImport::processMisc( void) { RCODE rc = NE_XFLM_OK; for( ;;) { if( RC_BAD( rc = skipWhitespace( FALSE))) { if( rc == NE_FLM_IO_END_OF_FILE || rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } if (lineHasToken( "")) { break; } if ((uChar = getChar()) == 0) { if (RC_BAD( rc = getLine())) { goto Exit; } uChar = ASCII_NEWLINE; } *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; uiOffset += sizeof( FLMUNICODE); if( uiOffset >= uiMaxOffset) { if (RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) { goto Exit; } uiMaxOffset = m_uiValBufSize; } } if( pParent) { if( RC_BAD( rc = pParent->createNode( m_pDb, COMMENT_NODE, 0, XFLM_LAST_CHILD, (IF_DOMNode **)&pComment))) { setErrInfo( uiSavedLineNum, uiSavedOffset, XML_ERR_CREATING_COMMENT_NODE, uiSavedFilePos, uiSavedLineBytes); goto Exit; } *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = 0; if( RC_BAD( rc = pComment->setUnicode( m_pDb, (FLMUNICODE *)m_pucValBuf))) { goto Exit; } pComment->Release(); pComment = NULL; } Exit: if( pComment) { pComment->Release(); } return( rc); } /**************************************************************************** Desc: Processes a CDATA tag ****************************************************************************/ RCODE F_XMLImport::processCDATA( F_DOMNode * pParent, FLMUINT uiSavedLineNum, FLMUINT uiSavedOffset, FLMUINT uiSavedFilePos, FLMUINT uiSavedLineBytes) { FLMUNICODE uChar; FLMUINT uiOffset = 0; F_DOMNode * pCData = NULL; RCODE rc = NE_XFLM_OK; // Have already eaten up the "")) { break; } if ((uChar = getChar()) == 0) { if (RC_BAD( rc = getLine())) { goto Exit; } uChar = ASCII_NEWLINE; } *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; uiOffset += sizeof( FLMUNICODE); if( uiOffset >= m_uiValBufSize) { if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) { goto Exit; } } } if( pParent) { if( RC_BAD( rc = pParent->createNode( m_pDb, CDATA_SECTION_NODE, 0, XFLM_LAST_CHILD, (IF_DOMNode **)&pCData))) { setErrInfo( uiSavedLineNum, uiSavedOffset, XML_ERR_CREATING_CDATA_NODE, uiSavedFilePos, uiSavedLineBytes); goto Exit; } *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = 0; if( RC_BAD( rc = pCData->setUnicode( m_pDb, (FLMUNICODE *)m_pucValBuf))) { goto Exit; } pCData->Release(); pCData = NULL; } Exit: if( pCData) { pCData->Release(); } return( rc); } /**************************************************************************** Desc: Skips any whitespace characters in the input stream ****************************************************************************/ RCODE F_XMLImport::skipWhitespace( FLMBOOL bRequired) { FLMUNICODE uChar; FLMUINT uiCount = 0; RCODE rc = NE_XFLM_OK; for( ;;) { if ((uChar = getChar()) == 0) { uiCount++; if (RC_BAD( rc = getLine())) { goto Exit; } continue; } if( !gv_XFlmSysData.pXml->isWhitespace( uChar)) { ungetChar(); break; } uiCount++; } if( !uiCount && bRequired) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset, XML_ERR_EXPECTING_WHITESPACE, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_XMLImport::resizeValBuffer( FLMUINT uiSize) { RCODE rc = NE_XFLM_OK; if( uiSize == m_uiValBufSize) { goto Exit; } if( uiSize == ~((FLMUINT)0)) { uiSize = m_uiValBufSize + 2048; } if( m_pucValBuf) { if( uiSize) { if( RC_BAD( rc = f_realloc( uiSize, &m_pucValBuf))) { goto Exit; } } else { f_free( &m_pucValBuf); m_pucValBuf = NULL; } } else { flmAssert( !m_pucValBuf); if( RC_BAD( rc = f_alloc( uiSize, &m_pucValBuf))) { goto Exit; } } m_uiValBufSize = uiSize; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_XMLImport::getBinaryVal( FLMUINT * puiLength) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMUNICODE uChar2; FLMUINT uiOffset = 0; FLMBOOL bHavePreamble; flmAssert( *puiLength == 0); for( ;;) { bHavePreamble = FALSE; if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } Retry: uChar = getChar(); if( !f_isHexChar( uChar)) { if( uChar != FLM_UNICODE_LT) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_HEX_DIGIT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } ungetChar(); break; } uChar2 = getChar(); if( uChar == FLM_UNICODE_0 && (uChar2 == FLM_UNICODE_X || uChar2 == FLM_UNICODE_x)) { if( bHavePreamble) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_HEX_DIGIT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } bHavePreamble = TRUE; goto Retry; } if( !f_isHexChar( uChar2)) { setErrInfo( m_uiCurrLineNum, m_uiCurrLineOffset - 1, XML_ERR_EXPECTING_HEX_DIGIT, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } if( uiOffset >= m_uiValBufSize) { if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) { goto Exit; } } m_pucValBuf[ uiOffset++] = (f_getHexVal( uChar) << 4) | f_getHexVal( uChar2); if( RC_BAD( rc = skipWhitespace( FALSE))) { goto Exit; } uChar = getChar(); if( uChar != FLM_UNICODE_COMMA) { ungetChar(); } } *puiLength = uiOffset; Exit: return( rc); } /**************************************************************************** Desc: Constructor ****************************************************************************/ F_XMLNamespaceMgr::F_XMLNamespaceMgr() { m_pFirstNamespace = NULL; m_uiNamespaceCount = 0; } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_XMLNamespaceMgr::~F_XMLNamespaceMgr() { popNamespaces( m_uiNamespaceCount); } /**************************************************************************** Desc: ****************************************************************************/ void F_XMLNamespaceMgr::popNamespaces( FLMUINT uiCount) { F_XMLNamespace * pTmpNamespace; flmAssert( uiCount <= m_uiNamespaceCount); while( uiCount && m_pFirstNamespace) { pTmpNamespace = m_pFirstNamespace; m_pFirstNamespace = m_pFirstNamespace->m_pNext; pTmpNamespace->m_pNext = NULL; pTmpNamespace->Release(); m_uiNamespaceCount--; uiCount--; } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLNamespaceMgr::findNamespace( FLMUNICODE * puzPrefix, F_XMLNamespace ** ppNamespace, FLMUINT uiMaxSearchSize) { F_XMLNamespace * pTmpNamespace = m_pFirstNamespace; RCODE rc = NE_XFLM_OK; while( pTmpNamespace) { if( !uiMaxSearchSize) { pTmpNamespace = NULL; break; } if( !puzPrefix && !pTmpNamespace->m_puzPrefix) { break; } else if( puzPrefix && pTmpNamespace->m_puzPrefix) { if( f_unicmp( puzPrefix, pTmpNamespace->m_puzPrefix) == 0) { break; } } pTmpNamespace = pTmpNamespace->m_pNext; uiMaxSearchSize--; } if( !pTmpNamespace) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } if( ppNamespace) { if( *ppNamespace) { (*ppNamespace)->Release(); } pTmpNamespace->AddRef(); *ppNamespace = pTmpNamespace; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLNamespaceMgr::pushNamespace( FLMUNICODE * puzPrefix, FLMUNICODE * puzNamespaceURI) { F_XMLNamespace * pNewNamespace = NULL; RCODE rc = NE_XFLM_OK; if( (pNewNamespace = f_new F_XMLNamespace) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pNewNamespace->setPrefix( puzPrefix))) { goto Exit; } if( RC_BAD( rc = pNewNamespace->setURI( puzNamespaceURI))) { goto Exit; } pNewNamespace->m_pNext = m_pFirstNamespace; m_pFirstNamespace = pNewNamespace; pNewNamespace = NULL; m_uiNamespaceCount++; Exit: if( pNewNamespace) { pNewNamespace->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLNamespaceMgr::pushNamespace( F_XMLNamespace * pNamespace) { flmAssert( m_pFirstNamespace != pNamespace && !pNamespace->m_pNext); pNamespace->AddRef(); pNamespace->m_pNext = m_pFirstNamespace; m_pFirstNamespace = pNamespace; m_uiNamespaceCount++; return( NE_XFLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLNamespace::setPrefix( FLMUNICODE * puzPrefix) { FLMUINT uiLen; RCODE rc = NE_XFLM_OK; if( m_puzPrefix) { f_free( &m_puzPrefix); } if( puzPrefix) { uiLen = f_unilen( puzPrefix); if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), &m_puzPrefix))) { goto Exit; } f_unicpy( m_puzPrefix, puzPrefix); } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLNamespace::setURI( FLMUNICODE * puzURI) { FLMUINT uiLen; RCODE rc = NE_XFLM_OK; if( m_puzURI) { f_free( &m_puzURI); } if( puzURI) { uiLen = f_unilen( puzURI); if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), &m_puzURI))) { goto Exit; } f_unicpy( m_puzURI, puzURI); } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLNamespace::setup( FLMUNICODE * puzPrefix, FLMUNICODE * puzURI, F_XMLNamespace * pNext) { FLMUINT uiLen; RCODE rc = NE_XFLM_OK; flmAssert( !m_puzPrefix); flmAssert( !m_puzURI); flmAssert( !m_pNext); if( puzPrefix) { uiLen = f_unilen( puzPrefix); if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), &m_puzPrefix))) { goto Exit; } f_unicpy( m_puzPrefix, puzPrefix); } if( puzURI) { uiLen = f_unilen( puzURI); if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * (uiLen + 1), &m_puzURI))) { goto Exit; } f_unicpy( m_puzURI, puzURI); } m_pNext = pNext; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XMLImport::addAttributesToElement( F_DOMNode * pElement) { RCODE rc = NE_XFLM_OK; XML_ATTR * pAttr; F_XMLNamespace * pNamespace = NULL; F_DOMNode * pTmpNode = NULL; FLMUINT uiNameId; FLMUINT uiAttrDataType; // Make sure any prefixes (e.g., xmlns:xxxx) are added to the database // before they are used - in case they are used by the attributes // themselves. for( pAttr = m_pFirstAttr; pAttr; pAttr = pAttr->pNext) { if( pAttr->uiFlags & F_PREFIXED_NS_DECL) { FLMUINT uiPrefixId; // Create the prefix (stored in &puzLocalName [6]) if it doesn't // already exist if( RC_BAD( rc = m_pDb->m_pDict->getPrefixId( m_pDb, &pAttr->puzLocalName [6], &uiPrefixId))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } uiPrefixId = 0; if( RC_BAD( rc = m_pDb->createPrefixDef( TRUE, &pAttr->puzLocalName [6], &uiPrefixId))) { goto Exit; } } } } // Add the attributes to the element // // NOTE: The XML namespace specification states that the names // of all unqualified attributes are assigned to the // appropriate per-element-type partition. This means that // the combination of the attribute name with the parent // element's type and namespace name is used to uniquely // identify each unqualified attribute. // // For sake of clarity and useability, however, the parser // deviates from the namespace specification. Each unprefixed // attribute encountered by the parser will inherit the // namespace of the parent element, even if the namespace // is a default namespace. for( pAttr = m_pFirstAttr; pAttr; pAttr = pAttr->pNext) { if( pAttr->uiFlags & F_DEFAULT_NS_DECL) { // Add the namespace declaration to the element if( RC_BAD( rc = pElement->createAttribute( m_pDb, ATTR_XMLNS_TAG, (IF_DOMNode **)&pTmpNode))) { goto Exit; } if( RC_BAD( rc = pTmpNode->setUnicode( m_pDb, pAttr->puzVal))) { goto Exit; } if( RC_BAD( rc = pTmpNode->addModeFlags( m_pDb, FDOM_READ_ONLY))) { goto Exit; } } else if( pAttr->uiFlags & F_PREFIXED_NS_DECL) { // Find the attribute definition if( RC_BAD( rc = m_pDb->getAttributeNameId( NULL, pAttr->puzLocalName, &uiNameId))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG)) { rc = RC_SET( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME); goto Exit; } uiNameId = 0; if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, pAttr->puzLocalName, XFLM_TEXT_TYPE, &uiNameId, (IF_DOMNode **)&pTmpNode))) { goto Exit; } } // Add the namespace declaration to the element if( RC_BAD( rc = pElement->createAttribute( m_pDb, uiNameId, (IF_DOMNode **)&pTmpNode))) { goto Exit; } if( RC_BAD( rc = pTmpNode->setUnicode( m_pDb, pAttr->puzVal))) { goto Exit; } if( RC_BAD( rc = pTmpNode->addModeFlags( m_pDb, FDOM_READ_ONLY))) { goto Exit; } } else { if( pAttr->puzPrefix) { if( RC_BAD( rc = findNamespace( pAttr->puzPrefix, &pNamespace))) { if( rc == NE_XFLM_NOT_FOUND) { setErrInfo( pAttr->uiLineNum, pAttr->uiLineOffset, XML_ERR_PREFIX_NOT_DEFINED, pAttr->uiLineFilePos, pAttr->uiLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); } goto Exit; } } else { if( pNamespace) { pNamespace->Release(); } pNamespace = NULL; } if( RC_BAD( rc = m_pDb->getAttributeNameId( pNamespace ? pNamespace->getURIPtr() : NULL, pAttr->puzLocalName, &uiNameId))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG) || (pNamespace && f_unicmp( pNamespace->getURIPtr(), gv_uzXFLAIMNamespace) == 0)) { rc = RC_SET( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME); goto Exit; } uiNameId = 0; if( RC_BAD( rc = m_pDb->createAttributeDef( pNamespace ? pNamespace->getURIPtr() : NULL, pAttr->puzLocalName, XFLM_TEXT_TYPE, &uiNameId))) { goto Exit; } } if( RC_BAD( rc = pElement->createAttribute( m_pDb, uiNameId, (IF_DOMNode **)&pTmpNode))) { goto Exit; } if (pAttr->puzPrefix) { if( RC_BAD( rc = pTmpNode->setPrefix( m_pDb, pAttr->puzPrefix))) { if( rc == NE_XFLM_NOT_FOUND) { setErrInfo( pAttr->uiLineNum, pAttr->uiLineOffset, XML_ERR_PREFIX_NOT_DEFINED, pAttr->uiLineFilePos, pAttr->uiLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); } goto Exit; } } if( RC_BAD( rc = pTmpNode->getDataType( m_pDb, &uiAttrDataType))) { goto Exit; } switch( uiAttrDataType) { case XFLM_TEXT_TYPE: { if( RC_BAD( rc = pTmpNode->setUnicode( m_pDb, pAttr->puzVal))) { goto Exit; } break; } case XFLM_NUMBER_TYPE: { FLMUINT64 ui64Val; FLMBOOL bNeg; if( RC_BAD( rc = unicodeToNumber64( pAttr->puzVal, &ui64Val, &bNeg))) { goto Exit; } if( !bNeg) { if( RC_BAD( rc = pTmpNode->setUINT64( m_pDb, ui64Val))) { goto Exit; } } else { if( RC_BAD( rc = pTmpNode->setINT64( m_pDb, -((FLMINT64)ui64Val)))) { goto Exit; } } break; } case XFLM_BINARY_TYPE: { FLMBOOL bHavePreamble; FLMUNICODE * puzStr = pAttr->puzVal; FLMUINT uiOffset = 0; // Convert the Unicode value to binary while( puzStr && *puzStr) { bHavePreamble = FALSE; while( gv_XFlmSysData.pXml->isWhitespace( *puzStr)) { puzStr++; } Retry: if( !f_isHexChar( *puzStr)) { break; } if( *puzStr == FLM_UNICODE_0 && (puzStr[ 1] == FLM_UNICODE_X || puzStr[ 1] == FLM_UNICODE_x)) { if( bHavePreamble) { setErrInfo( pAttr->uiValueLineNum, pAttr->uiValueLineOffset, XML_ERR_INVALID_BINARY_ATTR_VALUE, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } bHavePreamble = TRUE; puzStr += 2; goto Retry; } if( !f_isHexChar( puzStr[ 1])) { setErrInfo( pAttr->uiValueLineNum, pAttr->uiValueLineOffset, XML_ERR_INVALID_BINARY_ATTR_VALUE, m_uiCurrLineFilePos, m_uiCurrLineBytes); rc = RC_SET( NE_XFLM_INVALID_XML); goto Exit; } if( uiOffset >= m_uiValBufSize) { if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) { goto Exit; } } m_pucValBuf[ uiOffset++] = (f_getHexVal( *puzStr) << 4) | f_getHexVal( puzStr[ 1]); puzStr += 2; while( gv_XFlmSysData.pXml->isWhitespace( *puzStr)) { puzStr++; } if( *puzStr == FLM_UNICODE_COMMA) { puzStr++; } } if( RC_BAD( rc = pTmpNode->setBinary( m_pDb, m_pucValBuf, uiOffset))) { goto Exit; } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } } } Exit: if( pTmpNode) { pTmpNode->Release(); } if( pNamespace) { pNamespace->Release(); } return( rc); } // Some forward declarations class F_Element; class F_Attribute; /***************************************************************************** Desc: Keeps track of an attribute that we are going to output *****************************************************************************/ class F_Attribute : public F_Object { public: F_Attribute( F_Element * pElement) { m_uiTmpSpaceSize = sizeof( m_uzTmpSpace); m_puzName = &m_uzTmpSpace [0]; reset( pElement); } ~F_Attribute(); FINLINE void reset( F_Element * pElement) { m_uiNameChars = 0; m_bIsNamespaceDecl = FALSE; m_bDefaultNamespaceDecl = FALSE; m_uiNamespaceChars = 0; m_uiValueChars = 0; m_uiPrefixChars = 0; m_pElement = pElement; } RCODE allocNameSpace( void); RCODE setupAttribute( IF_Db * pDb, IF_DOMNode * pNode); RCODE setPrefix( void); RCODE outputAttr( IF_OStream * pOStream); FINLINE F_Attribute * getNext( void) { return( m_pNext); } private: FLMUNICODE m_uzTmpSpace [150]; FLMUINT m_uiTmpSpaceSize; FLMBOOL m_bIsNamespaceDecl; FLMBOOL m_bDefaultNamespaceDecl; FLMUNICODE * m_puzName; FLMUINT m_uiNameChars; FLMUNICODE * m_puzNamespace; FLMUINT m_uiNamespaceChars; FLMUNICODE * m_puzValue; FLMUINT m_uiValueChars; FLMUNICODE * m_puzPrefix; FLMUINT m_uiPrefixChars; F_Element * m_pElement; F_Attribute * m_pNext; friend class F_Element; }; /***************************************************************************** Desc: Destructor for F_Attribute class. *****************************************************************************/ F_Attribute::~F_Attribute() { if (m_puzName != &m_uzTmpSpace [0]) { f_free( &m_puzName); } } /***************************************************************************** Desc: Keeps track of an element that we are going to output *****************************************************************************/ class F_Element : public F_Object { public: F_Element( F_Element * pParentElement, F_Attribute ** ppAvailAttrs, FLMUINT * puiNextPrefixNum) { m_uiTmpSpaceSize = sizeof( m_uzTmpSpace); m_puzName = &m_uzTmpSpace [0]; m_pFirstAttr = NULL; m_pLastAttr = NULL; m_pNext = NULL; m_uiIndentCount = 0; m_bIsDocumentRoot = FALSE; reset( pParentElement, ppAvailAttrs, puiNextPrefixNum); } ~F_Element() { F_Attribute * pAttr; F_Attribute * pTmpAttr; // Delete all of the attributes pAttr = m_pFirstAttr; while (pAttr) { pTmpAttr = pAttr; pAttr = pAttr->m_pNext; delete pTmpAttr; } if (m_puzName != &m_uzTmpSpace [0]) { f_free( &m_puzName); } } FINLINE void reset( F_Element * pParentElement, F_Attribute ** ppAvailAttrs, FLMUINT * puiNextPrefixNum) { m_uiNameChars = 0; m_uiNamespaceChars = 0; m_uiPrefixChars = 0; m_pParentElement = pParentElement; m_puiNextPrefixNum = puiNextPrefixNum; m_ppAvailAttrs = ppAvailAttrs; m_pNext = NULL; m_uiIndentCount = 0; m_bIsDocumentRoot = FALSE; } FINLINE void setIndentCount( FLMUINT uiIndentCount) { m_uiIndentCount = uiIndentCount; } FINLINE void setDocumentRoot( FLMBOOL bIsDocumentRoot) { m_bIsDocumentRoot = bIsDocumentRoot; } RCODE allocAttr( F_Attribute ** ppAttr); FINLINE void makeAttrAvail( F_Attribute * pAttr) { pAttr->m_pNext = *m_ppAvailAttrs; *m_ppAvailAttrs = pAttr; } FINLINE void makeAllAttrsAvail( void) { if (m_pFirstAttr) { m_pLastAttr->m_pNext = *m_ppAvailAttrs; *m_ppAvailAttrs = m_pFirstAttr; m_pFirstAttr = NULL; m_pLastAttr = NULL; } } RCODE saveAttribute( IF_Db * pDb, IF_DOMNode * pNode); RCODE allocNameSpace( void); RCODE setupElement( IF_Db * pDb, IF_DOMNode * pNode); RCODE addNamespaceDecl( FLMUNICODE * puzPrefix, FLMUINT uiPrefixChars, FLMUNICODE * puzNamespace, FLMUINT uiNamespaceChars, F_Attribute ** ppAttr); void genPrefix( FLMUNICODE * puzPrefix, FLMUINT * puiPrefixChars); RCODE findPrefix( FLMUNICODE * puzNamespace, FLMUINT uiNamespaceChars, FLMBOOL bForElement, FLMUNICODE ** ppuzPrefix, FLMUINT * puiPrefixChars); FINLINE RCODE setPrefix( void) { return( findPrefix( m_puzNamespace, m_uiNamespaceChars, TRUE, &m_puzPrefix, &m_uiPrefixChars)); } FINLINE F_Element * getParentElement( void) { return( m_pParentElement); } RCODE outputElem( IF_OStream * pOStream, FLMBOOL bStartOfElement, FLMBOOL bEndOfElement, FLMBOOL bAddNewLine); RCODE outputLocalData( IF_OStream * pOStream, IF_DOMNode * pDbNode, IF_Db * ifpDb, eExportFormatType eFormatType, FLMUINT uiIndentCount); FINLINE F_Element * getNext( void) { return( m_pNext); } FINLINE void makeAvail( F_Element ** ppAvailElements) { m_pNext = *ppAvailElements; *ppAvailElements = this; } private: FLMUNICODE m_uzTmpSpace [100]; FLMUINT m_uiTmpSpaceSize; FLMUNICODE * m_puzName; FLMUINT m_uiNameChars; FLMUNICODE * m_puzNamespace; FLMUINT m_uiNamespaceChars; FLMUNICODE * m_puzPrefix; FLMUINT m_uiPrefixChars; F_Attribute * m_pFirstAttr; F_Attribute * m_pLastAttr; F_Element * m_pParentElement; F_Element * m_pNext; FLMUINT * m_puiNextPrefixNum; F_Attribute ** m_ppAvailAttrs; FLMBOOL m_bIsDocumentRoot; FLMUINT m_uiIndentCount; friend class F_Attribute; }; /***************************************************************************** Desc: Allocate space to hold the name, namespace, and value for an attribute. *****************************************************************************/ RCODE F_Attribute::allocNameSpace( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiSpaceNeeded; FLMUNICODE * puzTmp; uiSpaceNeeded = (m_uiNameChars + m_uiNamespaceChars + m_uiValueChars + 3) * sizeof( FLMUNICODE); if (uiSpaceNeeded > m_uiTmpSpaceSize) { if (RC_BAD( rc = f_alloc( uiSpaceNeeded, &puzTmp))) { goto Exit; } if (m_puzName != &m_uzTmpSpace [0]) { f_free( &m_puzName); } m_puzName = puzTmp; m_uiTmpSpaceSize = uiSpaceNeeded; } m_puzNamespace = &m_puzName [m_uiNameChars + 1]; m_puzValue = &m_puzNamespace [m_uiNamespaceChars + 1]; Exit: return( rc); } /***************************************************************************** Desc: Setup an attribute with its namespace, etc. *****************************************************************************/ RCODE F_Attribute::setupAttribute( IF_Db * pDb, IF_DOMNode * pNode ) { RCODE rc = NE_XFLM_OK; // Determine if the attribute is a namespace declaration if (RC_BAD( rc = pNode->isNamespaceDecl( pDb, &m_bIsNamespaceDecl))) { goto Exit; } // Get the length of the name of the attribute if (RC_BAD( rc = pNode->getLocalName( pDb, (FLMUNICODE *)NULL, 0, &m_uiNameChars))) { goto Exit; } // If it is a namespace declaration, no need to get the namespace URI, // we already know what it is, and we will output it with an xmlns prefix // Otherwise, we need to get the namespace so we can determine a prefix, // if any. If the namespace is the same namespace as the enclosing // element, we do not need to output a prefix. if (!m_bIsNamespaceDecl) { // Get the number of characters in the namespace of the attribute if (RC_BAD( rc = pNode->getNamespaceURI( pDb, (FLMUNICODE *)NULL, 0, &m_uiNamespaceChars))) { goto Exit; } } // Get the number of characters in the attribute's value. if (RC_BAD( rc = pNode->getUnicodeChars( pDb, &m_uiValueChars))) { goto Exit; } // Allocate space for the name, namespace, and value if (RC_BAD( rc = allocNameSpace())) { goto Exit; } // Get the attribute name. if (RC_BAD( rc = pNode->getLocalName( pDb, m_puzName, (m_uiNameChars + 1) * sizeof( FLMUNICODE), &m_uiNameChars))) { goto Exit; } // Get the namespace, if necessary if (m_uiNamespaceChars) { if (RC_BAD( rc = pNode->getNamespaceURI( pDb, m_puzNamespace, (m_uiNamespaceChars + 1) * sizeof( FLMUNICODE), &m_uiNamespaceChars))) { goto Exit; } } // Get the value, if any if (m_uiValueChars) { if (RC_BAD( rc = pNode->getUnicode( pDb, m_puzValue, (m_uiValueChars + 1) * sizeof( FLMUNICODE), 0, m_uiValueChars, &m_uiValueChars))) { goto Exit; } } // If it is a namespace declaration, the local name must either be // "xmlns" or begin with "xmlns:" if (m_bIsNamespaceDecl) { // Make sure name is "xmlns" or begins with "xmlns:" if (m_uiNameChars != 5 && m_uiNameChars <= 6) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_NAMESPACE_DECL); goto Exit; } if (!isXMLNS( m_puzName)) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_NAMESPACE_DECL); goto Exit; } else if (m_uiNameChars == 5) { m_bDefaultNamespaceDecl = TRUE; } else if (m_puzName [5] != ':') { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_NAMESPACE_DECL); goto Exit; } } Exit: return( rc); } /***************************************************************************** Desc: Set the prefix for an attribute. *****************************************************************************/ RCODE F_Attribute::setPrefix( void) { RCODE rc = NE_XFLM_OK; // If this is a namespace declaration, there should be no prefix in the name. if (m_bIsNamespaceDecl) { flmAssert( !m_uiPrefixChars); } // Only need to set a prefix on an attribute if it has a namespace // Otherwise, leave it alone - no prefix. else if (m_uiNamespaceChars) { // See if we can find a namespace declaration in either // this element's attributes, or any of its parent element // attributes. if (RC_BAD( rc = m_pElement->findPrefix( m_puzNamespace, m_uiNamespaceChars, FALSE, &m_puzPrefix, &m_uiPrefixChars))) { goto Exit; } } Exit: return( rc); } /***************************************************************************** Desc: Export a unicode string to the string buffer - as UTF8. *****************************************************************************/ FSTATIC RCODE exportUniValue( IF_OStream * pOStream, FLMUNICODE * puzStr, FLMUINT uiStrChars, FLMBOOL bEncodeSpecialChars, FLMUINT uiIndentCount ) { RCODE rc = NE_XFLM_OK; FLMBYTE ucTmp [4]; FLMUINT uiLen; FLMUINT uiCharOffset = 0; FLMUNICODE uzChar; FLMBOOL bIndent = FALSE; FLMUINT uiICount = 0; while ( *puzStr && uiCharOffset < uiStrChars) { uzChar = *puzStr; // Handle encoding of special characters if (bEncodeSpecialChars) { if (uzChar == '<') { if (RC_BAD( rc = pOStream->write( (void *)"<", 4))) { goto Exit; } } else if (uzChar == '>') { if (RC_BAD( rc = pOStream->write( (void *)">", 4))) { goto Exit; } } else if (uzChar == '&') { if (RC_BAD( rc = pOStream->write( (void *)"&", 5))) { goto Exit; } } else if (uzChar == '\'') { if (RC_BAD( rc = pOStream->write( (void *)"'", 6))) { goto Exit; } } else if (uzChar == '"') { if (RC_BAD( rc = pOStream->write( (void *)""", 6))) { goto Exit; } } else { goto Normal_Encoding; } } else { Normal_Encoding: // Output the character as UTF8. if (uzChar <= 0x007F) { // New Line char found. Need to indent. if( uzChar == ASCII_NEWLINE) { bIndent = TRUE; } ucTmp [0] = (FLMBYTE)uzChar; uiLen = 1; } else if (*puzStr <= 0x07FF) { ucTmp [0] = (FLMBYTE)(0xC0 | (FLMBYTE)(uzChar >> 6)); ucTmp [1] = (FLMBYTE)(0x80 | (FLMBYTE)(uzChar & 0x003F)); uiLen = 2; } else { ucTmp [0] = (FLMBYTE)(0xE0 | (FLMBYTE)(uzChar >> 12)); ucTmp [1] = (FLMBYTE)(0x80 | (FLMBYTE)((uzChar & 0x0FC0) >> 6)); ucTmp [2] = (FLMBYTE)(0x80 | (FLMBYTE)(uzChar & 0x003F)); uiLen = 3; } if (RC_BAD( rc = pOStream->write( (void *)&ucTmp[0], uiLen))) { goto Exit; } if( bIndent && uiIndentCount) { for( uiICount = uiIndentCount; uiICount; uiICount--) { if (RC_BAD( rc = pOStream->write( (void *)"\t", 1))) { goto Exit; } } bIndent = FALSE; } } puzStr++; uiCharOffset++; } Exit: return( rc); } /***************************************************************************** Desc: Output an attribute to the string buffer. *****************************************************************************/ RCODE F_Attribute::outputAttr( IF_OStream * pOStream) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = pOStream->write( (void *)" ", 1))) { goto Exit; } if (m_uiPrefixChars) { if (RC_BAD( rc = exportUniValue( pOStream, m_puzPrefix, m_uiPrefixChars, FALSE, 0))) { goto Exit; } if (RC_BAD( rc = pOStream->write( (void *)":", 1))) { goto Exit; } } if (RC_BAD( rc = exportUniValue( pOStream, m_puzName, m_uiNameChars, FALSE, 0))) { goto Exit; } if (RC_BAD( rc = pOStream->write( (void *)"=\"", 2))) { goto Exit; } if (RC_BAD( rc = exportUniValue( pOStream, m_puzValue, m_uiValueChars, TRUE, 0))) { goto Exit; } if (RC_BAD( rc = pOStream->write( (void *)"\"", 1))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: Allocate a new attribute. *****************************************************************************/ RCODE F_Element::allocAttr( F_Attribute ** ppAttr ) { RCODE rc = NE_XFLM_OK; if ((*ppAttr = *m_ppAvailAttrs) != NULL) { *m_ppAvailAttrs = (*ppAttr)->m_pNext; (*ppAttr)->reset( this); } else { if ((*ppAttr = f_new F_Attribute( this)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } Exit: return( rc); } /***************************************************************************** Desc: Save an attribute in an element. Put at end of list. *****************************************************************************/ RCODE F_Element::saveAttribute( IF_Db * pDb, IF_DOMNode * pNode ) { RCODE rc = NE_XFLM_OK; F_Attribute * pAttr = NULL; if (RC_BAD( rc = allocAttr( &pAttr))) { goto Exit; } // Set up the attribute if (RC_BAD( rc = pAttr->setupAttribute( pDb, pNode))) { goto Exit; } // Put attribute at end of list of attributes. pAttr->m_pNext = NULL; if (m_pLastAttr) { m_pLastAttr->m_pNext = pAttr; } else { m_pFirstAttr = pAttr; } m_pLastAttr = pAttr; // Set pAttr to NULL so it won't be made available at exit. pAttr = NULL; Exit: if (pAttr) { makeAttrAvail( pAttr); } return( rc); } /***************************************************************************** Desc: Allocate space for the element's name and namespace. *****************************************************************************/ RCODE F_Element::allocNameSpace( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiSpaceNeeded; FLMUNICODE * puzTmp; uiSpaceNeeded = (m_uiNameChars + m_uiNamespaceChars + 2) * sizeof( FLMUNICODE); // Allocate space for the name and namespace if (uiSpaceNeeded > m_uiTmpSpaceSize) { if (RC_BAD( rc = f_alloc( uiSpaceNeeded, &puzTmp))) { goto Exit; } if (m_puzName != &m_uzTmpSpace [0]) { f_free( &m_puzName); } m_puzName = puzTmp; m_uiTmpSpaceSize = uiSpaceNeeded; } m_puzNamespace = &m_puzName [m_uiNameChars + 1]; Exit: return( rc); } /***************************************************************************** Desc: Setup an element with its namespace, etc. *****************************************************************************/ RCODE F_Element::setupElement( IF_Db * pDb, IF_DOMNode * pNode ) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pAttrNode = NULL; F_Attribute * pAttr; // Get the length of the name of the element if (RC_BAD( rc = pNode->getLocalName( pDb, (FLMUNICODE *)NULL, 0, &m_uiNameChars))) { goto Exit; } // Get the number of characters in the namespace of the element if (RC_BAD( rc = pNode->getNamespaceURI( pDb, (FLMUNICODE *)NULL, 0, &m_uiNamespaceChars))) { goto Exit; } if (RC_BAD( rc = allocNameSpace())) { goto Exit; } // Get the element name. if (RC_BAD( rc = pNode->getLocalName( pDb, m_puzName, (m_uiNameChars + 1) * sizeof( FLMUNICODE), &m_uiNameChars))) { goto Exit; } // Get the namespace, if necessary if (m_uiNamespaceChars) { if (RC_BAD( rc = pNode->getNamespaceURI( pDb, m_puzNamespace, (m_uiNamespaceChars + 1) * sizeof( FLMUNICODE), &m_uiNamespaceChars))) { goto Exit; } } // See if the node has any attributes. for (;;) { rc = (RCODE)(pAttrNode ? pAttrNode->getNextSibling( pDb, &pAttrNode) : pNode->getFirstAttribute( pDb, &pAttrNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { goto Exit; } } if (RC_BAD( rc = saveAttribute( pDb, pAttrNode))) { goto Exit; } } // Get the prefix for the element if (RC_BAD( rc = setPrefix())) { goto Exit; } // Set the prefix for every attribute pAttr = m_pFirstAttr; while (pAttr) { if (RC_BAD( rc = pAttr->setPrefix())) { goto Exit; } pAttr = pAttr->m_pNext; } Exit: if (pAttrNode) { pAttrNode->Release(); } return( rc); } /***************************************************************************** Desc: Add an attribute that is a namespace to an element. *****************************************************************************/ RCODE F_Element::addNamespaceDecl( FLMUNICODE * puzPrefix, FLMUINT uiPrefixChars, FLMUNICODE * puzNamespace, FLMUINT uiNamespaceChars, F_Attribute ** ppAttr ) { RCODE rc = NE_XFLM_OK; F_Attribute * pAttr = NULL; // If uiPrefixChars is zero, we are being asked to create a default // namespace. But that can only be output once in the element, // so make sure it is not already declared. If it is, do nothing. if (!uiPrefixChars) { pAttr = m_pFirstAttr; while (pAttr && !pAttr->m_bDefaultNamespaceDecl) { pAttr = pAttr->m_pNext; } if (pAttr) { goto Exit; } } if (RC_BAD( rc = allocAttr( &pAttr))) { goto Exit; } pAttr->m_bIsNamespaceDecl = TRUE; // name will be "xmlns:" or, in the case of no namespace, "xmlns" if (!uiPrefixChars) { // "xmlns" - but make sure not already declared. pAttr->m_uiNameChars = 5; pAttr->m_bDefaultNamespaceDecl = TRUE; } else { // "xmlns:" pAttr->m_uiNameChars = uiPrefixChars + 6; } pAttr->m_uiNamespaceChars = 0; pAttr->m_uiValueChars = uiNamespaceChars; if (RC_BAD( rc = pAttr->allocNameSpace())) { goto Exit; } // Always output "xmlns" as the first part of the name f_memcpy( pAttr->m_puzName, gv_puzNamespaceDeclPrefix, 5 * sizeof( FLMUNICODE)); if (uiPrefixChars) { pAttr->m_puzName [5] = ':'; f_memcpy( &pAttr->m_puzName [6], puzPrefix, uiPrefixChars * sizeof( FLMUNICODE)); pAttr->m_puzName [6 + uiPrefixChars] = 0; } else { pAttr->m_puzName [5] = 0; } if (uiNamespaceChars) { f_memcpy( pAttr->m_puzValue, puzNamespace, uiNamespaceChars * sizeof( FLMUNICODE)); } pAttr->m_puzValue [pAttr->m_uiValueChars] = 0; // Put new namespace decl at front of list of attributes. if ((pAttr->m_pNext = m_pFirstAttr) == NULL) { m_pLastAttr = pAttr; } m_pFirstAttr = pAttr; *ppAttr = pAttr; // Set pAttr to NULL so that it won't be made available at exit. pAttr = NULL; Exit: if (pAttr) { makeAttrAvail( pAttr); } return( rc); } /***************************************************************************** Desc: Generate a random prefix, ensure that it is not defined anywhere in the path. *****************************************************************************/ void F_Element::genPrefix( FLMUNICODE * puzPrefix, FLMUINT * puiPrefixChars ) { FLMUINT uiTmp; FLMUINT uiPrefixChars; FLMUNICODE * puzTmp; F_Attribute * pAttr; F_Element * pElement; puzPrefix [0] = 'p'; puzPrefix [1] = 'r'; puzPrefix [2] = 'f'; puzPrefix [3] = 'x'; for (;;) { // Append the number in reverse digit order - it really doesn't matter // because we're just trying to generate a unique prefix number. puzTmp = &puzPrefix [4]; uiPrefixChars = 4; uiTmp = *m_puiNextPrefixNum; do { *puzTmp++ = (FLMUNICODE)((uiTmp % 10) + '0'); uiPrefixChars++; uiTmp /= 10; } while (uiTmp); // See if the prefix is defined. pAttr = m_pFirstAttr; pElement = this; while (pAttr) { if (pAttr->m_bIsNamespaceDecl && pAttr->m_uiNameChars > 6 && pAttr->m_uiNameChars - 6 == uiPrefixChars && f_memcmp( puzPrefix, &pAttr->m_puzName [6], uiPrefixChars * sizeof( FLMUNICODE)) == 0) { break; } if ((pAttr = pAttr->m_pNext) == NULL) { pElement = pElement->m_pParentElement; while (pElement && !pElement->m_pFirstAttr) { pElement = pElement->m_pParentElement; } if (!pElement) { break; } pAttr = pElement->m_pFirstAttr; } } // If the prefix was not defined, we can use it. if (!pAttr) { break; } (*m_puiNextPrefixNum)++; } puzPrefix [uiPrefixChars] = 0; *puiPrefixChars = uiPrefixChars; } /***************************************************************************** Desc: Find a prefix for a namespace *****************************************************************************/ RCODE F_Element::findPrefix( FLMUNICODE * puzNamespace, FLMUINT uiNamespaceChars, FLMBOOL bForElement, FLMUNICODE ** ppuzPrefix, FLMUINT * puiPrefixChars) { RCODE rc = NE_XFLM_OK; F_Attribute * pAttr = m_pFirstAttr; F_Element * pElement = this; FLMUNICODE uzPrefix [50]; FLMUINT uiPrefixChars; for (;;) { if ( pAttr) { if (pAttr->m_bIsNamespaceDecl && uiNamespaceChars == pAttr->m_uiValueChars && (!uiNamespaceChars || f_memcmp( puzNamespace, pAttr->m_puzValue, uiNamespaceChars * sizeof( FLMUNICODE)) == 0)) { // Don't set the prefix if it is the default namespace. if (!pAttr->m_bDefaultNamespaceDecl) { // Prefix comes after the "xmlns:" *ppuzPrefix = &pAttr->m_puzName [6]; *puiPrefixChars = pAttr->m_uiNameChars - 6; goto Exit; } // Default namespace is only OK for elements, // but not attributes. We don't want to count // attributes as having been "found" if they matched // the default namespace. This routine is only called // for attributes if the attribute namepace is non-empty. else if (bForElement) { goto Exit; } } pAttr = pAttr->m_pNext; } if ( !pAttr) { pElement = pElement->m_pParentElement; while (pElement && !pElement->m_pFirstAttr) { pElement = pElement->m_pParentElement; } if (!pElement) { break; } pAttr = pElement->m_pFirstAttr; } } // If namespaces is empty, the only declaration that is legal is // a default namespace declaration. if (!uiNamespaceChars) { if (RC_BAD( rc = addNamespaceDecl( NULL, 0, NULL, 0, &pAttr))) { goto Exit; } } else { // Manufacture a prefix that is not used in the hierarchy yet. genPrefix( uzPrefix, &uiPrefixChars); if (RC_BAD( rc = addNamespaceDecl( uzPrefix, uiPrefixChars, puzNamespace, uiNamespaceChars, &pAttr))) { goto Exit; } *ppuzPrefix = &pAttr->m_puzName [6]; *puiPrefixChars = pAttr->m_uiNameChars - 6; } Exit: return( rc); } /***************************************************************************** Desc: Output the element name, with its attributes - this marks the beginning of the element. *****************************************************************************/ RCODE F_Element::outputElem( IF_OStream * pOStream, FLMBOOL bStartOfElement, FLMBOOL bEndOfElement, FLMBOOL bAddNewLine) { RCODE rc = NE_XFLM_OK; F_Attribute * pAttr; F_Attribute * pPrevAttr; FLMUINT uiIndentCount = 0; FLMBOOL bEndNode; bEndNode = ( m_bIsDocumentRoot && !bStartOfElement); if( bAddNewLine && ( !m_bIsDocumentRoot || bEndNode)) { if (RC_BAD( rc = pOStream->write( (void *)"\n", 1))) { goto Exit; } for( uiIndentCount = 0; uiIndentCount < m_uiIndentCount; uiIndentCount++) { if (RC_BAD( rc = pOStream->write( (void *)"\t", 1))) { goto Exit; } } } // Output the element name if ( bStartOfElement) { if (RC_BAD( rc = pOStream->write( (void *)"<", 1))) { goto Exit; } } else { if (RC_BAD( rc = pOStream->write( (void *)"write( (void *)":", 1))) { goto Exit; } } if (RC_BAD( rc = exportUniValue( pOStream, m_puzName, m_uiNameChars, FALSE, 0))) { goto Exit; } if (bStartOfElement) { // Output the attributes. As we go, remove any attributes that are // not namespace declarations. They are not needed after this. pPrevAttr = NULL; pAttr = m_pFirstAttr; while (pAttr) { if (RC_BAD( rc = pAttr->outputAttr( pOStream))) { goto Exit; } if (!pAttr->m_bIsNamespaceDecl) { if (pPrevAttr) { pPrevAttr->m_pNext = pAttr->m_pNext; makeAttrAvail( pAttr); pAttr = pPrevAttr->m_pNext; } else { m_pFirstAttr = pAttr->m_pNext; makeAttrAvail( pAttr); pAttr = m_pFirstAttr; } // See if we deleted the last attribute in the list. if (!pAttr) { m_pLastAttr = pPrevAttr; } } else { pPrevAttr = pAttr; pAttr = pAttr->m_pNext; } } } // Close out the element if (RC_BAD( rc = (RCODE)(bStartOfElement && bEndOfElement ? pOStream->write( (void *)"/>", 2) : pOStream->write( (void *)">", 1)))) { goto Exit; } if ( bAddNewLine && bEndNode) { if (RC_BAD( rc = pOStream->write( (void *)"\n", 1))) { goto Exit; } } Exit: return( rc); } /***************************************************************************** Desc: Output Data that is contained on an element node. *****************************************************************************/ RCODE F_Element::outputLocalData( IF_OStream * pOStream, IF_DOMNode * pDbNode, IF_Db * ifpDb, eExportFormatType eFormatType, FLMUINT uiIndentCount) { RCODE rc = NE_XFLM_OK; FLMUNICODE uzTmpData [150]; FLMUNICODE * puzData = &uzTmpData [0]; FLMUINT uiDataBufSize = sizeof( uzTmpData); FLMUINT uiChars; if (RC_BAD( rc = pDbNode->getUnicodeChars( ifpDb, &uiChars))) { goto Exit; } if (uiDataBufSize < (uiChars + 1) * sizeof( FLMUNICODE)) { FLMUNICODE * puzNew; if (RC_BAD( rc = f_alloc( (uiChars + 1) * sizeof( FLMUNICODE), &puzNew))) { goto Exit; } if (puzData != &uzTmpData [0]) { f_free( &puzData); } puzData = puzNew; uiDataBufSize = (uiChars + 1) * sizeof( FLMUNICODE); } if (RC_BAD( rc = pDbNode->getUnicode( ifpDb, puzData, uiDataBufSize, 0, uiChars, &uiChars))) { goto Exit; } // Output the value. if (RC_BAD( rc = exportUniValue( pOStream, puzData, uiChars, TRUE, eFormatType >= XFLM_EXPORT_INDENT_DATA ? uiIndentCount : 0))) { goto Exit; } Exit: return ( rc); } /***************************************************************************** Desc: Outputs a UTF8 stream of XML, starting at the specified node. Node and all of its descendant nodes are output. *****************************************************************************/ RCODE FLMAPI F_Db::exportXML( IF_DOMNode * pStartNode, IF_OStream * pOStream, eExportFormatType eFormatType) { RCODE rc = NE_XFLM_OK; F_Element * pAvailElements = NULL; F_Element * pTmpElement; F_Attribute * pAvailAttrs = NULL; F_Attribute * pTmpAttr; FLMUNICODE uzTmpData [150]; FLMUNICODE * puzData = &uzTmpData [0]; FLMUINT uiDataBufSize = sizeof( uzTmpData); IF_DOMNode * pDbNode = NULL; eDomNodeType ePrevNodeType; F_Element * pCurrElement = NULL; FLMUINT uiNextPrefixNum = 0; FLMBOOL bStartOfDocument = TRUE; FLMBOOL bShouldFormat = FALSE; FLMBOOL bIsDataLocal = FALSE; FLMUINT uiIndentCount = 0; FLMUINT uiICount = 0; // This routine should only be called if the node type is element node. flmAssert( pStartNode->getNodeType() == ELEMENT_NODE); ePrevNodeType = ELEMENT_NODE; pDbNode = pStartNode; pDbNode->AddRef(); for (;;) { // Output the current node, depending on its type. if( pDbNode->getNodeType() == ELEMENT_NODE) { if (pAvailElements) { pTmpElement = pAvailElements; pAvailElements = pAvailElements->getNext(); pTmpElement->reset( pCurrElement, &pAvailAttrs, &uiNextPrefixNum); } else { if ((pTmpElement = f_new F_Element( pCurrElement, &pAvailAttrs, &uiNextPrefixNum)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } pCurrElement = pTmpElement; if (RC_BAD( rc = pCurrElement->setupElement( (IF_Db *)this, pDbNode))) { goto Exit; } if( eFormatType >= XFLM_EXPORT_INDENT) { pCurrElement->setIndentCount(uiIndentCount); } if( pDbNode == pStartNode) { pCurrElement->setDocumentRoot( TRUE); } // Only want a New Line and tabs for Element if: // 1) New Line format is indicated // 2) Previous Element Was NOT Data bShouldFormat = ( (eFormatType >= XFLM_EXPORT_NEW_LINE) && (ePrevNodeType != DATA_NODE)) ? TRUE : FALSE; if( RC_BAD( rc = pDbNode->isDataLocalToNode( (IF_Db *)this, &bIsDataLocal))) { goto Exit; } if( bIsDataLocal) { if( RC_BAD( rc = pCurrElement->outputElem( pOStream, TRUE, FALSE, bShouldFormat))) { goto Exit; } pCurrElement->outputLocalData( pOStream, pDbNode, (IF_Db *)this, eFormatType, uiIndentCount); } if( RC_OK( rc = pDbNode->getFirstChild( (IF_Db *)this, &pDbNode))) { if( !bIsDataLocal && RC_BAD( rc = pCurrElement->outputElem( pOStream, TRUE, FALSE, bShouldFormat))) { goto Exit; } bStartOfDocument = FALSE; uiIndentCount++; ePrevNodeType = ELEMENT_NODE; continue; } if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } // Write out the "/>" for the element, because it had no // child nodes. if( bIsDataLocal) { if( RC_BAD( rc = pCurrElement->outputElem( pOStream, FALSE, TRUE, bShouldFormat))) { goto Exit; } } else { if( RC_BAD( rc = pCurrElement->outputElem( pOStream, TRUE, TRUE, bShouldFormat))) { goto Exit; } } // We are now done with this element ePrevNodeType = ELEMENT_NODE; pTmpElement = pCurrElement; pCurrElement = pCurrElement->getParentElement(); pTmpElement->makeAvail( &pAvailElements); if( !pCurrElement) { break; } Get_Element_Sibling: // See if we have a sibling. Go up tree until we find // a node that has a sibling. for( ;;) { if( RC_OK( rc = pDbNode->getNextSibling( (IF_Db *)this, &pDbNode))) { break; } if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } // Need to close previous element if( uiIndentCount) { uiIndentCount--; } if( RC_BAD( rc = pCurrElement->outputElem( pOStream, FALSE, TRUE, eFormatType >= XFLM_EXPORT_NEW_LINE ? TRUE : FALSE))) { goto Exit; } if( RC_BAD( rc = pDbNode->getParentNode( (IF_Db *)this, &pDbNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // There should be a parent node at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } pTmpElement = pCurrElement; pCurrElement = pCurrElement->getParentElement(); pTmpElement->makeAvail( &pAvailElements); if( !pCurrElement) { pDbNode->Release(); pDbNode = NULL; goto Exit; } } } else { // Only output data, comment, and cdata nodes. if( pDbNode->getNodeType() == DATA_NODE || pDbNode->getNodeType() == COMMENT_NODE || pDbNode->getNodeType() == CDATA_SECTION_NODE) { FLMUINT uiChars; if( RC_BAD( rc = pDbNode->getUnicodeChars( (IF_Db *)this, &uiChars))) { goto Exit; } if( uiDataBufSize < (uiChars + 1) * sizeof( FLMUNICODE)) { FLMUNICODE * puzNew; if( RC_BAD( rc = f_alloc( (uiChars + 1) * sizeof( FLMUNICODE), &puzNew))) { goto Exit; } if( puzData != &uzTmpData [0]) { f_free( &puzData); } puzData = puzNew; uiDataBufSize = (uiChars + 1) * sizeof( FLMUNICODE); } if( RC_BAD( rc = pDbNode->getUnicode( (IF_Db *)this, puzData, uiDataBufSize, 0, uiChars, &uiChars))) { goto Exit; } if( pDbNode->getNodeType() == DATA_NODE) { // Output the value if (RC_BAD( rc = exportUniValue( pOStream, puzData, uiChars, TRUE, eFormatType >= XFLM_EXPORT_INDENT_DATA ? uiIndentCount : 0))) { goto Exit; } ePrevNodeType = DATA_NODE; } else if( pDbNode->getNodeType() == COMMENT_NODE) { //If Comment Node follows Data Node do not add new line if( eFormatType >= XFLM_EXPORT_INDENT_DATA && ePrevNodeType != DATA_NODE) { if (RC_BAD( rc = pOStream->write( (void *)"\n", 1))) { goto Exit; } for( uiICount = 0; uiICount < uiIndentCount; uiICount++) { if (RC_BAD( rc = pOStream->write( (void *)"\t", 1))) { goto Exit; } } } // Output the beginning of a comment if (RC_BAD( rc = pOStream->write( (void *)"", 3))) { goto Exit; } ePrevNodeType = COMMENT_NODE; } else { // Output the beginning of a cdata section if( RC_BAD( rc = pOStream->write( (void *)"write( (void *)"]]>", 3))) { goto Exit; } ePrevNodeType = CDATA_SECTION_NODE; } } // Have a data node, or comment node probably // In any case, see if there are any sibling nodes. // If not, go back to enclosing element node. if( RC_OK( rc = pDbNode->getNextSibling( (IF_Db *)this, &pDbNode))) { continue; } if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } // Go back up to enclosing element if( RC_BAD( rc = pDbNode->getParentNode( (IF_Db *)this, &pDbNode))) { // There better be a parent node or we have a corruption! if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Parent node better be an element if( pDbNode->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // If we were traversing the attributes of an element, // we need to now go back and get its child nodes. // Write out the for the element if( RC_BAD( rc = pCurrElement->outputElem( pOStream, FALSE, TRUE, FALSE))) { goto Exit; } // We are now done with this element if( uiIndentCount) { uiIndentCount--; } ePrevNodeType = ELEMENT_NODE; pTmpElement = pCurrElement; pCurrElement = pCurrElement->getParentElement(); pTmpElement->makeAvail( &pAvailElements); if( !pCurrElement) { break; } goto Get_Element_Sibling; } } Exit: if( puzData != &uzTmpData [0]) { f_free( &puzData); } while( pCurrElement) { pTmpElement = pCurrElement; pCurrElement = pCurrElement->getParentElement(); delete pTmpElement; } while( pAvailElements) { pTmpElement = pAvailElements; pAvailElements = pAvailElements->getNext(); delete pTmpElement; } while( pAvailAttrs) { pTmpAttr = pAvailAttrs; pAvailAttrs = pAvailAttrs->getNext(); delete pTmpAttr; } if( pDbNode) { pDbNode->Release(); } return( rc); } libxflaim-5.1.969/src/fdoclist.cpp0000644000175000017500000001302210511001742020335 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Document list object implementation // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdoclist.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define MAX_PENDING_NODES 255 /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL F_NodeList::findNode( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId, FLMUINT * puiPos ) { FLMBOOL bFound = FALSE; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblCollection; FLMUINT64 ui64TblDocument; FLMUINT64 ui64TblNodeId; FLMINT iCmp; // Do binary search in the table if ((uiTblSize = m_uiNumNodes) == 0) { *puiPos = 0; goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblCollection = m_pNodeTbl[ uiMid].uiCollection; ui64TblDocument = m_pNodeTbl[ uiMid].ui64Document; ui64TblNodeId = m_pNodeTbl[ uiMid].ui64NodeId; if( uiCollection == uiTblCollection) { if( ui64Document == ui64TblDocument) { if( ui64NodeId == ui64TblNodeId) { iCmp = 0; } else if( ui64NodeId < ui64TblNodeId) { iCmp = -1; } else { iCmp = 1; } } else if( ui64Document < ui64TblDocument) { iCmp = -1; } else { iCmp = 1; } } else if( uiCollection < uiTblCollection) { iCmp = -1; } else { iCmp = 1; } if (!iCmp) { // Found Match bFound = TRUE; *puiPos = uiMid; goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found *puiPos = (iCmp < 0 ? uiMid : uiMid + 1); goto Exit; } if (iCmp < 0) { if (uiMid == 0) { *puiPos = 0; goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { *puiPos = uiMid + 1; goto Exit; } uiLow = uiMid + 1; } } Exit: return( bFound); } /***************************************************************************** Desc: Add a node to the node list. If it is already there, it is ok. ******************************************************************************/ RCODE F_NodeList::addNode( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; FLMUINT uiInsertPos; // Cannot allow collection or document to be zero flmAssert( uiCollection && ui64Document); if( m_uiLastCollection == uiCollection && m_ui64LastDocument == ui64Document && m_ui64LastNodeId == ui64NodeId) { goto Exit; } if( !findNode( uiCollection, ui64Document, ui64NodeId, &uiInsertPos)) { // Have we reached the limit of the number of documents we will // keep pending in a transaction? if( m_uiNumNodes == MAX_PENDING_NODES) { rc = RC_SET( NE_XFLM_TOO_MANY_PENDING_NODES); goto Exit; } // See if we need to allocate the table if( !m_pNodeTbl) { if (RC_BAD( rc = f_alloc( sizeof( NODE_LIST_ITEM) * MAX_PENDING_NODES, &m_pNodeTbl))) { goto Exit; } m_uiNodeTblSize = MAX_PENDING_NODES; } flmAssert( uiInsertPos <= m_uiNumNodes); // Make room for the new node ID if( uiInsertPos < m_uiNumNodes) { f_memmove( &m_pNodeTbl[ uiInsertPos+1], &m_pNodeTbl[ uiInsertPos], sizeof( NODE_LIST_ITEM) * (m_uiNumNodes - uiInsertPos)); } m_pNodeTbl[ uiInsertPos].uiCollection = uiCollection; m_pNodeTbl[ uiInsertPos].ui64Document = ui64Document; m_pNodeTbl[ uiInsertPos].ui64NodeId = ui64NodeId; m_uiNumNodes++; } // Save collection and document id - this is an optimization // that will keep us from calling findNode too much if // we are working inside the same document. m_uiLastPosition = uiInsertPos; m_uiLastCollection = uiCollection; m_ui64LastDocument = ui64Document; m_ui64LastNodeId = ui64NodeId; Exit: return( rc); } /***************************************************************************** Desc: Remove a node from the node list. If it is not there, it is ok. ******************************************************************************/ void F_NodeList::removeNode( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId) { FLMUINT uiPos; // Cannot allow collection or document to be zero flmAssert( uiCollection && ui64Document); if( m_uiLastCollection == uiCollection && m_ui64LastDocument == ui64Document && m_ui64LastNodeId == ui64NodeId) { flmAssert( m_uiLastPosition < m_uiNumNodes); removeNode( m_uiLastPosition); } else { if( findNode( uiCollection, ui64Document, ui64NodeId, &uiPos)) { flmAssert( uiPos < m_uiNumNodes); removeNode( uiPos); } } } libxflaim-5.1.969/src/flerror.cpp0000644000175000017500000001627310511001742020214 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains error routines that are used throughout FLAIM. // // Tabs: 3 // // Copyright (c) 1997-2000, 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flerror.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: ****************************************************************************/ const char * FlmCorruptStrings[ FLM_NUM_CORRUPT_ERRORS] = { "BAD_CHAR", /*1*/ "BAD_ASIAN_CHAR", /*2*/ "BAD_CHAR_SET", /*3*/ "BAD_TEXT_FIELD", /*4*/ "BAD_NUMBER_FIELD", /*5*/ "BAD_FIELD_TYPE", /*6*/ "BAD_IX_DEF", /*7*/ "MISSING_REQ_KEY_FIELD", /*8*/ "BAD_TEXT_KEY_COLL_CHAR", /*9*/ "BAD_TEXT_KEY_CASE_MARKER", /*10*/ "BAD_NUMBER_KEY", /*11*/ "BAD_BINARY_KEY", /*12*/ "BAD_CONTEXT_KEY", /*13*/ "BAD_KEY_FIELD_TYPE", /*14*/ "Not_Used_15", /*15*/ "Not_Used_16", /*16*/ "Not_Used_17", /*17*/ "BAD_KEY_LEN", /*18*/ "BAD_LFH_LIST_PTR", /*19*/ "BAD_LFH_LIST_END", /*20*/ "INCOMPLETE_NODE", /*21*/ "BAD_BLK_END", /*22*/ "KEY_COUNT_MISMATCH", /*23*/ "REF_COUNT_MISMATCH", /*24*/ "BAD_CONTAINER_IN_KEY", /*25*/ "BAD_BLK_HDR_ADDR", /*26*/ "BAD_BLK_HDR_LEVEL", /*27*/ "BAD_BLK_HDR_PREV", /*28*/ // WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE // REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h "BAD_BLK_HDR_NEXT", /*29*/ "BAD_BLK_HDR_TYPE", /*30*/ "BAD_BLK_HDR_ROOT_BIT", /*31*/ "BAD_BLK_HDR_BLK_END", /*32*/ "BAD_BLK_HDR_LF_NUM", /*33*/ "BAD_AVAIL_LIST_END", /*34*/ "BAD_PREV_BLK_NEXT", /*35*/ "BAD_FIRST_LAST_ELM_FLAG", /*36*/ "nu", /*37*/ "BAD_LEM", /*38*/ "BAD_ELM_LEN", /*39*/ "BAD_ELM_KEY_SIZE", /*40*/ "BAD_ELM_KEY", /*41*/ "BAD_ELM_KEY_ORDER", /*42*/ "nu", /*43*/ "BAD_CONT_ELM_KEY", /*44*/ "NON_UNIQUE_FIRST_ELM_KEY", /*45*/ "BAD_ELM_OFFSET", /*46*/ "BAD_ELM_INVALID_LEVEL", /*47*/ "BAD_ELM_FLD_NUM", /*48*/ "BAD_ELM_FLD_LEN", /*49*/ "BAD_ELM_FLD_TYPE", /*50*/ "BAD_ELM_END", /*51*/ "BAD_PARENT_KEY", /*52*/ "BAD_ELM_DOMAIN_SEN", /*53*/ "BAD_ELM_BASE_SEN", /*54*/ "BAD_ELM_IX_REF", /*55*/ "BAD_ELM_ONE_RUN_SEN", /*56*/ "BAD_ELM_DELTA_SEN", /*57*/ "BAD_ELM_DOMAIN", /*58*/ // WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE // REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h "BAD_LAST_BLK_NEXT", /*59*/ "BAD_FIELD_PTR", /*60*/ "REBUILD_REC_EXISTS", /*61*/ "REBUILD_KEY_NOT_UNIQUE", /*62*/ "NON_UNIQUE_ELM_KEY_REF", /*63*/ "OLD_VIEW", /*64*/ "COULD_NOT_SYNC_BLK", /*65*/ "IX_REF_REC_NOT_FOUND", /*66*/ "IX_KEY_NOT_FOUND_IN_REC", /*67*/ "KEY_NOT_IN_KEY_REFSET", /*68*/ "BAD_BLK_CHECKSUM", /*69*/ "BAD_LAST_DRN", /*70*/ "BAD_FILE_SIZE", /*71*/ "nu", /*72*/ "BAD_DATE_FIELD", /*73*/ "BAD_TIME_FIELD", /*74*/ "BAD_TMSTAMP_FIELD", /*75*/ "BAD_DATE_KEY", /*76*/ "BAD_TIME_KEY", /*77*/ "BAD_TMSTAMP_KEY", /*78*/ "BAD_BLOB_FIELD", /*79*/ // WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE // REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h "BAD_PCODE_IXD_TBL", /*80*/ "NODE_QUARANTINED", /*81*/ "BAD_BLK_TYPE", /*82*/ "BAD_ELEMENT_CHAIN", /*83*/ "BAD_ELM_EXTR_DATA", /*84*/ "BAD_BLOCK_STRUCTURE", /*85*/ "BAD_ROOT_PARENT", /*86*/ "BAD_ROOT_LINK", /*87*/ "BAD_PARENT_LINK", /*88*/ "BAD_INVALID_ROOT", /*89*/ "BAD_FIRST_CHILD_LINK", /*90*/ "BAD_LAST_CHILD_LINK", /*91*/ "BAD_PREV_SIBLING_LINK", /*92*/ "BAD_NEXT_SIBLING_LINK", /*93*/ "BAD_ANNOTATION_LINK", /*95*/ "UNSUPPORTED_NODE_TYPE", /*96*/ "BAD_INVALID_NAME_ID", /*97*/ "BAD_INVALID_PREFIX_ID", /*98*/ "BAD_DATA_BLOCK_COUNT", /*99*/ "FLM_BAD_AVAIL_SIZE", /*100*/ "BAD_NODE_TYPE", /*101*/ "BAD_CHILD_ELM_COUNT", /*102*/ }; /**************************************************************************** Desc: The primary purpose of this function is to provide a way to easily trap errors when they occur. Just put a breakpoint in this function to catch them. Note: Some of the most common errors will be coded so the use can set a break point. ****************************************************************************/ #ifdef FLM_DEBUG RCODE flmMakeErr( RCODE rc, const char * pszFile, int iLine, FLMBOOL bAssert) { if( rc == NE_XFLM_OK) { return NE_XFLM_OK; } // Switch on warning type return codes if( rc <= NE_XFLM_NOT_FOUND) { switch(rc) { case NE_XFLM_BOF_HIT: break; case NE_XFLM_EOF_HIT: break; case NE_XFLM_RFL_END: break; case NE_XFLM_EXISTS: break; case NE_XFLM_NOT_FOUND: break; } goto Exit; } switch(rc) { case NE_FLM_IO_BAD_FILE_HANDLE: break; case NE_XFLM_DATA_ERROR: flmLogError( rc, "", pszFile, iLine); break; case NE_XFLM_BTREE_ERROR: flmLogError( rc, "", pszFile, iLine); break; case NE_XFLM_MEM: break; case NE_XFLM_OLD_VIEW: break; case NE_XFLM_SYNTAX: break; case NE_XFLM_BLOCK_CRC: flmLogError( rc, "", pszFile, iLine); break; case NE_XFLM_CACHE_ERROR: flmLogError( rc, "", pszFile, iLine); break; case NE_XFLM_NOT_IMPLEMENTED: break; case NE_XFLM_CONV_DEST_OVERFLOW: break; case NE_XFLM_KEY_OVERFLOW: break; case NE_XFLM_FAILURE: break; case NE_XFLM_ILLEGAL_OP: break; case NE_XFLM_BAD_COLLECTION: break; default: rc = rc; break; } Exit: #if defined( FLM_DEBUG) if( bAssert) { flmAssert( 0); } #else F_UNREFERENCED_PARM( bAssert); #endif return( rc); } #endif #if defined( FLM_WATCOM_NLM) int gv_iFlerrorDummy(void) { return( 0); } #endif /**************************************************************************** Desc: Returns a pointer to the string representation of a corruption error code. ****************************************************************************/ const char * FLMAPI F_DbSystem::checkErrorToStr( FLMINT iCheckErrorCode) { if( (iCheckErrorCode >= 1) && (iCheckErrorCode <= FLM_NUM_CORRUPT_ERRORS)) { return( FlmCorruptStrings [iCheckErrorCode - 1]); } else if( iCheckErrorCode == 0) { return( "OK"); } else { return( "Unknown Error"); } } libxflaim-5.1.969/src/kyqsort.cpp0000644000175000017500000007241610511001742020256 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains specific q-sort code to sort FLAIM's KREF structures. // // Tabs: 3 // // Copyright (c) 1990-2000, 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kyqsort.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define KY_SWAP( pKrefTbl, leftP, rightP) \ pTempKref = pKrefTbl [leftP]; \ pKrefTbl [leftP] = pKrefTbl [rightP]; \ pKrefTbl [rightP] = pTempKref FSTATIC RCODE ixKeyGetNodeId( IXD * pIxd, const FLMBYTE * pucKey, const FLMBYTE * pucKeyEnd, FLMUINT uiKeyComponent, FLMUINT64 * pui64NodeId); FSTATIC RCODE ixKeyGetUnicode( F_Db * pDb, ICD * pIcd, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiKeyComponent, F_OldNodeList * pOldNodeList, F_DataVector * pSearchKey, F_DynaBuf * pDynaBuf); FSTATIC RCODE ixKeyGetBinary( F_Db * pDb, ICD * pIcd, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiKeyComponent, F_OldNodeList * pOldNodeList, F_DataVector * pSearchKey, F_DynaBuf * pDynaBuf); FSTATIC RCODE krefQuickSort( F_Db * pDb, IXD * pIxd, KREF_ENTRY ** pEntryTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds); FSTATIC RCODE krefKillDups( F_Db * pDb, IXD * pIxd, KREF_ENTRY ** pKrefTbl, FLMUINT * puiKrefTotal); /*************************************************************************** Desc: Compares result set entries during the finalization stage to allow the result set to be sorted and to remove duplicates. *****************************************************************************/ FSTATIC RCODE ixKeyGetNodeId( IXD * pIxd, const FLMBYTE * pucKey, const FLMBYTE * pucKeyEnd, FLMUINT uiKeyComponent, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; FLMUINT uiComponent; FLMUINT uiComponentLen; // Skip past all of the remaining key components so we can get to the // node ID list. We are currently positioned on the key component // specified in uiKeyComponent. NOTE: uiKeyComponent is zero-based, // 0=1st component, 1=2nd component, etc. uiComponent = uiKeyComponent; while (pucKey < pucKeyEnd && uiComponent < pIxd->uiNumKeyComponents) { uiComponentLen = getKeyComponentLength( pucKey); if (uiComponentLen != KEY_HIGH_VALUE && uiComponentLen != KEY_LOW_VALUE) { pucKey += (uiComponentLen + 2); } else { pucKey += 2; } uiComponent++; } // See if there are node IDs in the key. A 0xFF could be present if // we have set a "high" node ID. if (pucKey >= pucKeyEnd || *pucKey == 0xFF) { *pui64NodeId = 0; goto Exit; } // At this point, we better have all of the node ids, including // document ID. // Skip past the document ID if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, NULL))) { goto Exit; } if (pucKey >= pucKeyEnd) { *pui64NodeId = 0; goto Exit; } // Skip all of the node ids up to the one we want for (uiComponent = 0; uiComponent < uiKeyComponent; uiComponent++) { // Skip the component node ID - passing a NULL for the last // parameter is cheaper than getting it out. if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, NULL))) { goto Exit; } if (pucKey >= pucKeyEnd) { *pui64NodeId = 0; goto Exit; } } // Should now be positioned on the one we want, extract it. if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, pui64NodeId))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Do binary comparison. *****************************************************************************/ FINLINE FLMINT ixKeyCompareBinary( const void * pvData1, FLMUINT uiLen1, const void * pvData2, FLMUINT uiLen2, FLMBOOL bSortAscending) { FLMINT iCompare; if (uiLen1 > uiLen2) { if ((iCompare = f_memcmp( pvData1, pvData2, uiLen2)) >= 0) { return( bSortAscending ? 1 : -1); } else { return( bSortAscending ? -1 : 1); } } else if (uiLen1 < uiLen2) { if ((iCompare = f_memcmp( pvData1, pvData2, uiLen1)) <= 0) { return( bSortAscending ? -1 : 1); } else { return( bSortAscending ? 1 : -1); } } else { if ((iCompare = f_memcmp( pvData1, pvData2, uiLen1)) != 0) { if (iCompare < 0) { return( bSortAscending ? -1 : 1); } else { return( bSortAscending ? 1 : -1); } } } return( 0); } /*************************************************************************** Desc: Get the unicode value for a particular key component. *****************************************************************************/ FSTATIC RCODE ixKeyGetUnicode( F_Db * pDb, ICD * pIcd, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiKeyComponent, F_OldNodeList * pOldNodeList, F_DataVector * pSearchKey, F_DynaBuf * pDynaBuf) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; IF_PosIStream * pIStream = NULL; eDomNodeType eNodeType; if (ui64NodeId) { eNodeType = (pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? ATTRIBUTE_NODE : ELEMENT_NODE; // If there is an old-node list, first see if we can get the data // from there. If it is not in there, see if we can get it from // the database. if (pOldNodeList) { FLMBYTE * pucData; FLMUINT uiDataLen; FLMUINT uiDummy; if( pOldNodeList->findNodeInList( eNodeType, uiCollection, ui64NodeId, pIcd->uiDictNum, &pucData, &uiDataLen, &uiDummy)) { void * pvBuffer = NULL; // Allocate the space needed. if (RC_BAD( rc = pDynaBuf->allocSpace( uiDataLen, &pvBuffer))) { goto Exit; } f_memcpy( pvBuffer, pucData, uiDataLen); goto Exit; } } if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId, pIcd->uiDictNum, &pNode))) { flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } } else { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, &pNode))) { flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } } if (RC_BAD( rc = pNode->getUnicode( (IF_Db *)pDb, pDynaBuf))) { goto Exit; } } else { if (RC_BAD( rc = pSearchKey->getUnicode( uiKeyComponent, pDynaBuf))) { goto Exit; } } Exit: if (pIStream) { pIStream->Release(); } if (pNode) { pNode->Release(); } return( rc); } /*************************************************************************** Desc: Get the binary value for a particular key component. *****************************************************************************/ FSTATIC RCODE ixKeyGetBinary( F_Db * pDb, ICD * pIcd, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiKeyComponent, F_OldNodeList * pOldNodeList, F_DataVector * pSearchKey, F_DynaBuf * pDynaBuf) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; IF_PosIStream * pIStream = NULL; eDomNodeType eNodeType; if (ui64NodeId) { eNodeType = (pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? ATTRIBUTE_NODE : ELEMENT_NODE; // If there is an old-node list, first see if we can get the data // from there. If it is not in there, see if we can get it from // the database. if (pOldNodeList) { FLMBYTE * pucData; FLMUINT uiDataLen; FLMUINT uiDummy; if( pOldNodeList->findNodeInList( eNodeType, uiCollection, ui64NodeId, pIcd->uiDictNum, &pucData, &uiDataLen, &uiDummy)) { void * pvBuffer = NULL; // Allocate the space needed. if (RC_BAD( rc = pDynaBuf->allocSpace( uiDataLen, &pvBuffer))) { goto Exit; } f_memcpy( pvBuffer, pucData, uiDataLen); goto Exit; } } if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId, pIcd->uiDictNum, &pNode))) { flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } } else { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, &pNode))) { flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } } if( RC_BAD( rc = pNode->getBinary( (IF_Db *)pDb, pDynaBuf))) { goto Exit; } } else { if (RC_BAD( rc = pSearchKey->getBinary( uiKeyComponent, pDynaBuf))) { goto Exit; } } Exit: if (pIStream) { pIStream->Release(); } if (pNode) { pNode->Release(); } return( rc); } /*************************************************************************** Desc: Compares result set entries during the finalization stage to allow the result set to be sorted and to remove duplicates. *****************************************************************************/ RCODE ixKeyCompare( F_Db * pDb, IXD * pIxd, F_DataVector * pSearchKey, F_OldNodeList * pOldNodeList1, F_OldNodeList * pOldNodeList2, FLMBOOL bCompareDocId, FLMBOOL bCompareNodeIds, const void * pvKey1, FLMUINT uiKeyLen1, const void * pvKey2, FLMUINT uiKeyLen2, FLMINT * piCompare) { RCODE rc = NE_XFLM_OK; FLMUINT uiKeyComponent; ICD * pIcd; FLMUINT uiComponentLen1; FLMUINT uiComponentLen2; FLMBOOL bTruncated1; FLMBOOL bTruncated2; const FLMBYTE * pucKey1 = (const FLMBYTE *)pvKey1; const FLMBYTE * pucKey2 = (const FLMBYTE *)pvKey2; const FLMBYTE * pucKeyEnd1 = pucKey1 + uiKeyLen1; const FLMBYTE * pucKeyEnd2 = pucKey2 + uiKeyLen2; FLMBOOL bSortAscending; FLMBOOL bSortMissingHigh; FLMUINT64 ui64NodeId1; FLMUINT64 ui64NodeId2; flmAssert( uiKeyLen1 && uiKeyLen2); // Loop for each compound piece of key uiKeyComponent = 0; pIcd = pIxd->pFirstKey; for (;;) { bSortAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE : TRUE; bSortMissingHigh = (pIcd->uiFlags & ICD_MISSING_HIGH) ? TRUE : FALSE; uiComponentLen1 = getKeyComponentLength( pucKey1); uiComponentLen2 = getKeyComponentLength( pucKey2); // See if either component is a "high" key // NOTE: KEY_HIGH_VALUE always sorts highest, regardless of // ascending or descending. It is never actually stored. It is // only passed in for searching. if (uiComponentLen1 == KEY_HIGH_VALUE) { if (uiComponentLen2 == KEY_HIGH_VALUE) { uiComponentLen1 = uiComponentLen2 = 0; goto Test_Exclusive; } else { *piCompare = 1; goto Exit; } } else if (uiComponentLen2 == KEY_HIGH_VALUE) { *piCompare = -1; goto Exit; } // See if either component is a "low" key // NOTE: KEY_LOW_VALUE always sorts lowest, regardless of // ascending or descending. It is never actually stored. It is // only passed in for searching. if (uiComponentLen1 == KEY_LOW_VALUE) { if (uiComponentLen2 == KEY_LOW_VALUE) { uiComponentLen1 = uiComponentLen2 = 0; goto Test_Exclusive; } else { *piCompare = -1; goto Exit; } } else if (uiComponentLen2 == KEY_LOW_VALUE) { *piCompare = 1; goto Exit; } // See if either component is missing. Need to apply the rules for // sorting missing components in that case. if (!uiComponentLen1) { if (uiComponentLen2) { if (bSortMissingHigh) { *piCompare = bSortAscending ? 1 : -1; } else { *piCompare = bSortAscending ? -1 : 1; } goto Exit; } else { goto Test_Exclusive; } } else if (!uiComponentLen2) { if (bSortMissingHigh) { *piCompare = bSortAscending ? -1 : 1; } else { *piCompare = bSortAscending ? 1 : -1; } goto Exit; } else { // Component length must not exceed remaining length of key. flmAssert( pucKey1 + 2 + uiComponentLen1 <= pucKeyEnd1 && pucKey2 + 2 + uiComponentLen2 <= pucKeyEnd2); if ((*piCompare = ixKeyCompareBinary( pucKey1 + 2, uiComponentLen1, pucKey2 + 2, uiComponentLen2, bSortAscending)) != 0) { goto Exit; } // Data is equal, see if one or ther other is truncated. bTruncated1 = isKeyComponentTruncated( pucKey1); bTruncated2 = isKeyComponentTruncated( pucKey2); if (bTruncated1 || bTruncated2) { if (!bTruncated2) { *piCompare = bSortAscending ? 1 : -1; goto Exit; } else if (!bTruncated1) { *piCompare = bSortAscending ? -1 : 1; goto Exit; } if (isSearchKeyComponent( pucKey1)) { flmAssert( pSearchKey); ui64NodeId1 = pSearchKey->getID( uiKeyComponent); // The search key better have a node ID or the untruncated // value. flmAssert( ui64NodeId1 || !pSearchKey->isRightTruncated( uiKeyComponent)); } else { // Need to read the data from the nodes and do a comparison. // Get each node ID. if (RC_BAD( rc = ixKeyGetNodeId( pIxd, pucKey1, pucKeyEnd1, uiKeyComponent, &ui64NodeId1))) { goto Exit; } flmAssert( ui64NodeId1); } if (isSearchKeyComponent( pucKey2)) { flmAssert( pSearchKey); ui64NodeId2 = pSearchKey->getID( uiKeyComponent); // The search key better have a node ID or the untruncated // value. flmAssert( ui64NodeId2 || !pSearchKey->isRightTruncated( uiKeyComponent)); } else { // Need to read the data from the nodes and do a comparison. // Get each node ID. if (RC_BAD( rc = ixKeyGetNodeId( pIxd, pucKey2, pucKeyEnd2, uiKeyComponent, &ui64NodeId2))) { goto Exit; } flmAssert( ui64NodeId2); } // If the node IDs are equal, we can skip fetching the data, because // it will be the same. if (ui64NodeId1 != ui64NodeId2) { FLMBYTE ucDynaBuf1[ 64]; FLMBYTE ucDynaBuf2[ 64]; F_DynaBuf dynaBuf1( ucDynaBuf1, sizeof( ucDynaBuf1)); F_DynaBuf dynaBuf2( ucDynaBuf2, sizeof( ucDynaBuf2)); // Better be binary data or text data. switch (icdGetDataType( pIcd)) { case XFLM_TEXT_TYPE: { if (RC_BAD( rc = ixKeyGetUnicode( pDb, pIcd, pIxd->uiCollectionNum, ui64NodeId1, uiKeyComponent, pOldNodeList1, pSearchKey, &dynaBuf1))) { goto Exit; } if (RC_BAD( rc = ixKeyGetUnicode( pDb, pIcd, pIxd->uiCollectionNum, ui64NodeId2, uiKeyComponent, pOldNodeList2, pSearchKey, &dynaBuf2))) { goto Exit; } if (RC_BAD( rc = f_compareUnicodeStrings( dynaBuf1.getUnicodePtr(), dynaBuf1.getDataLength(), FALSE, dynaBuf2.getUnicodePtr(), dynaBuf2.getDataLength(), FALSE, pIcd->uiCompareRules, pIxd->uiLanguage, piCompare))) { goto Exit; } if (*piCompare < 0) { *piCompare = bSortAscending ? -1 : 1; goto Exit; } else if (*piCompare > 0) { *piCompare = bSortAscending ? 1 : -1; goto Exit; } break; } case XFLM_BINARY_TYPE: { if (RC_BAD( rc = ixKeyGetBinary( pDb, pIcd, pIxd->uiCollectionNum, ui64NodeId1, uiKeyComponent, pOldNodeList1, pSearchKey, &dynaBuf1))) { goto Exit; } if (RC_BAD( rc = ixKeyGetBinary( pDb, pIcd, pIxd->uiCollectionNum, ui64NodeId2, uiKeyComponent, pOldNodeList2, pSearchKey, &dynaBuf2))) { goto Exit; } if ((*piCompare = ixKeyCompareBinary( dynaBuf1.getBufferPtr(), dynaBuf1.getDataLength(), dynaBuf2.getBufferPtr(), dynaBuf2.getDataLength(), bSortAscending)) != 0) { goto Exit; } break; } default: rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } } } Test_Exclusive: // See if either component is exclusive - everything else is // equal up to this point. if (isKeyComponentLTExclusive( pucKey1)) { if (!isKeyComponentLTExclusive( pucKey2)) { *piCompare = bSortAscending ? -1 : 1; goto Exit; } } else if (isKeyComponentGTExclusive( pucKey1)) { if (!isKeyComponentGTExclusive( pucKey2)) { *piCompare = bSortAscending ? 1 : -1; goto Exit; } } else if (isKeyComponentLTExclusive( pucKey2)) { *piCompare = bSortAscending ? 1 : -1; goto Exit; } else if (isKeyComponentGTExclusive( pucKey2)) { *piCompare = bSortAscending ? -1 : 1; goto Exit; } // Position to the end of this component pucKey1 += (2 + uiComponentLen1); pucKey2 += (2 + uiComponentLen2); pIcd = pIcd->pNextKeyComponent; uiKeyComponent++; // If there are no more ICDs, we are done with the key // components. if (!pIcd) { break; } // See if we are out of key components - this may be a search that // passed in only a partial key. if (pucKey1 >= pucKeyEnd1) { *piCompare = (pucKey2 >= pucKeyEnd2) ? 0 : -1; goto Exit; } else if (pucKey2 >= pucKeyEnd2) { *piCompare = 1; goto Exit; } } // Compare the node ID list, if being requested to. Includes comparing of the // last byte, which is the total number of bytes in the node ID list. if (bCompareDocId || bCompareNodeIds) { // See if we have a node IDs - this may be a search that // passed in only a partial key and there are no NODE ids on it. if (pucKey1 >= pucKeyEnd1) { *piCompare = (pucKey2 >= pucKeyEnd2) ? 0 : -1; goto Exit; } else if (pucKey2 >= pucKeyEnd2) { *piCompare = 1; goto Exit; } // See if either one has an ID buffer of "high" if (*pucKey1 == 0xFF) { // Key1 has a "high" set of node IDs, see what key2 has. *piCompare = (*pucKey2 == 0xFF) ? 0 : 1; goto Exit; } else if (*pucKey2 == 0xFF) { // Key2 has a "high" set of node IDs, key1 does not. *piCompare = -1; goto Exit; } else if (bCompareNodeIds) { FLMUINT uiNodeIDLen1 = (FLMUINT)(pucKeyEnd1 - pucKey1); FLMUINT uiNodeIDLen2 = (FLMUINT)(pucKeyEnd2 - pucKey2); // We will always compare doc ID if we are also comparing // node ID. Hence, although it is probably not entirely // necessary, we require the caller to also set bCompareDocId // to TRUE, just so that he doesn't think it is possible to // compare the node ids without comparing the document id. flmAssert( bCompareDocId); *piCompare = ixKeyCompareBinary( pucKey1, uiNodeIDLen1, pucKey2, uiNodeIDLen2, TRUE); } else { FLMUINT64 ui64DocId1; FLMUINT64 ui64DocId2; // Get the document ID and compare it, and only it. // At this point, both keys should be positioned to // get the document ID. if (RC_BAD( rc = f_decodeSEN64( &pucKey1, pucKeyEnd1, &ui64DocId1))) { goto Exit; } if (RC_BAD( rc = f_decodeSEN64( &pucKey2, pucKeyEnd2, &ui64DocId2))) { goto Exit; } if (ui64DocId1 == ui64DocId2) { *piCompare = 0; } else if (ui64DocId1 < ui64DocId2) { *piCompare = -1; } else { *piCompare = 1; } } } else { *piCompare = 0; } Exit: return( rc); } /**************************************************************************** Desc: Compare function used to compare index number and key ****************************************************************************/ FINLINE RCODE krefCompareIxAndKey( F_Db * pDb, IXD * pIxd, KREF_ENTRY * pKrefA, KREF_ENTRY * pKrefB, FLMINT * piCompare) { RCODE rc = NE_XFLM_OK; // Compare index numbers if ((*piCompare = ((FLMINT) pKrefA->ui16IxNum) - ((FLMINT) pKrefB->ui16IxNum)) != 0) { goto Exit; } if (!pIxd || pIxd->uiIndexNum != (FLMUINT)pKrefA->ui16IxNum) { if (RC_BAD( rc = pDb->getDict()->getIndex( (FLMUINT)pKrefA->ui16IxNum, NULL, &pIxd, TRUE))) { goto Exit; } } if (RC_BAD( rc = ixKeyCompare( pDb, pIxd, NULL, pKrefA->bDelete ? pDb->getOldNodeList() : NULL, pKrefB->bDelete ? pDb->getOldNodeList() : NULL, TRUE, TRUE, &pKrefA [1], (FLMUINT)pKrefA->ui16KeyLen, &pKrefB [1], (FLMUINT)pKrefB->ui16KeyLen, piCompare))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Compare function used to compare key data ****************************************************************************/ FINLINE FLMBOOL krefIsKeyDataEqual( KREF_ENTRY * pKrefA, KREF_ENTRY * pKrefB) { if( pKrefA->uiDataLen != pKrefB->uiDataLen) { return( FALSE); } if( pKrefA->uiDataLen) { if( f_memcmp( (FLMBYTE *)(&pKrefA [1]) + pKrefA->ui16KeyLen + 1, (FLMBYTE *)(&pKrefB [1]) + pKrefB->ui16KeyLen + 1, pKrefA->uiDataLen) != 0) { return( FALSE); } } return( TRUE); } /**************************************************************************** Desc: Compare function used to compare two keys. ****************************************************************************/ FINLINE RCODE krefSortCompare( F_Db * pDb, IXD * pIxd, KREF_ENTRY * pKrefA, KREF_ENTRY * pKrefB, FLMINT * piCompare) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIxd, pKrefA, pKrefB, piCompare))) { goto Exit; } if (*piCompare == 0) { *piCompare = (pKrefA->uiSequence < pKrefB->uiSequence) ? -1 : 1; } Exit: return( rc); } /**************************************************************************** Desc: Checks if the current database has any UNIQUE indexes that need to checked. Also does duplicate processing for the record. ****************************************************************************/ RCODE F_Db::processDupKeys( IXD * pIxd) { RCODE rc = NE_XFLM_OK; // Sort and remove duplicates if (m_uiKrefCount > 1) { if (RC_BAD( rc = krefQuickSort( this, pIxd, m_pKrefTbl, 0, m_uiKrefCount - 1))) { goto Exit; } if (RC_BAD( rc = krefKillDups( this, pIxd, m_pKrefTbl, &m_uiKrefCount))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Commit (write out) all keys that have built up in the KREF table. ****************************************************************************/ RCODE F_Db::keysCommit( FLMBOOL bCommittingTrans, FLMBOOL bSortKeys) { RCODE rc = NE_XFLM_OK; FLMUINT uiRflToken = 0; // If the Kref has not been initialized, there is no // work to do. if (m_bKrefSetup) { LFILE * pLFile = NULL; IXD * pIxd = NULL; FLMUINT uiTotal = m_uiKrefCount; KREF_ENTRY * pKref; KREF_ENTRY ** pKrefTbl = m_pKrefTbl; FLMUINT uiKrefNum; FLMUINT uiLastIxNum; // We should not have reached this point if bAbortTrans is TRUE if( RC_BAD( m_AbortRc)) { rc = RC_SET_AND_ASSERT( m_AbortRc); goto Exit; } // Sort the KREF table, if it contains more than one key. // This will sort all keys from the same index the same. if (uiTotal > 1 && bSortKeys) { processDupKeys( NULL); uiTotal = m_uiKrefCount; } // Disable RFL logging if( uiTotal) { m_pDatabase->m_pRfl->disableLogging( &uiRflToken); } // Loop through the KREF table outputting all keys uiLastIxNum = 0; for (uiKrefNum = 0; uiKrefNum < uiTotal; uiKrefNum++) { pKref = pKrefTbl [uiKrefNum]; // See if the LFILE changed flmAssert( pKref->ui16IxNum); if (pKref->ui16IxNum != uiLastIxNum) { uiLastIxNum = pKref->ui16IxNum; if (RC_BAD( rc = m_pDict->getIndex( uiLastIxNum, &pLFile, &pIxd, TRUE))) { goto Exit; } } // Flush the key to the index if (m_pKeyColl) { m_pKeyColl->addKey( this, pIxd, pKref); } else { if (RC_BAD(rc = refUpdate( pLFile, pIxd, pKref, TRUE))) { if (rc != NE_XFLM_NOT_UNIQUE) { RC_UNEXPECTED_ASSERT( rc); } goto Exit; } } } if (bCommittingTrans) { krefCntrlFree(); } else { // Empty the table out so we can add more keys in this trans. m_pKrefPool->poolReset( NULL, TRUE); m_uiKrefCount = 0; m_uiTotalKrefBytes = 0; } } Exit: if( RC_BAD( rc)) { setMustAbortTrans( rc); } if( uiRflToken) { m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } return( rc); } /*************************************************************************** Desc: Quick sort an array of KREF_ENTRY * values. ****************************************************************************/ FSTATIC RCODE krefQuickSort( F_Db * pDb, IXD * pIxd, KREF_ENTRY ** pEntryTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds) { RCODE rc = NE_XFLM_OK; FLMUINT uiLBPos; FLMUINT uiUBPos; FLMUINT uiMIDPos; FLMUINT uiLeftItems; FLMUINT uiRightItems; KREF_ENTRY * pCurEntry; KREF_ENTRY * pTempKref; FLMINT iCompare; Iterate_Larger_Half: uiUBPos = uiUpperBounds; uiLBPos = uiLowerBounds; uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; pCurEntry = pEntryTbl[ uiMIDPos ]; for( ;;) { for (;;) { if (uiLBPos != uiMIDPos) { if (RC_BAD( rc = krefSortCompare( pDb, pIxd, pEntryTbl[ uiLBPos], pCurEntry, &iCompare))) { goto Exit; } if (iCompare >= 0) { break; } } if (uiLBPos >= uiUpperBounds) { break; } uiLBPos++; } for (;;) { if (uiUBPos != uiMIDPos) { if (RC_BAD( rc = krefSortCompare( pDb, pIxd, pCurEntry, pEntryTbl[ uiUBPos], &iCompare))) { goto Exit; } if (iCompare >= 0) { break; } } if (!uiUBPos) { break; } uiUBPos--; } if (uiLBPos < uiUBPos) // Interchange and continue loop. { // Interchange [uiLBPos] with [uiUBPos]. KY_SWAP( pEntryTbl, uiLBPos, uiUBPos ); uiLBPos++; // Scan from left to right. uiUBPos--; // Scan from right to left. } else // Past each other - done { break; } } // Check for swap( LB, MID ) - cases 3 and 4 if (uiLBPos < uiMIDPos) { // Interchange [uiLBPos] with [uiMIDPos] KY_SWAP( pEntryTbl, uiMIDPos, uiLBPos ); uiMIDPos = uiLBPos; } else if (uiMIDPos < uiUBPos) { // Interchange [uUBPos] with [uiMIDPos] KY_SWAP( pEntryTbl, uiMIDPos, uiUBPos ); uiMIDPos = uiUBPos; } // Check the left piece. uiLeftItems = (uiLowerBounds + 1 < uiMIDPos ) ? uiMIDPos - uiLowerBounds // 2 or more : 0; uiRightItems = (uiMIDPos + 1 < uiUpperBounds ) ? uiUpperBounds - uiMIDPos // 2 or more : 0; if (uiLeftItems < uiRightItems) { // Recurse on the LEFT side and goto the top on the RIGHT side. if (uiLeftItems) { if (RC_BAD( rc = krefQuickSort( pDb, pIxd, pEntryTbl, uiLowerBounds, uiMIDPos - 1))) { goto Exit; } } uiLowerBounds = uiMIDPos + 1; goto Iterate_Larger_Half; } else if (uiLeftItems) // Compute a truth table to figure out this check. { // Recurse on the RIGHT side and goto the top for the LEFT side. if (uiRightItems) { if (RC_BAD( rc = krefQuickSort( pDb, pIxd, pEntryTbl, uiMIDPos + 1, uiUpperBounds))) { goto Exit; } } uiUpperBounds = uiMIDPos - 1; goto Iterate_Larger_Half; } Exit: return( rc); } /**************************************************************************** Desc: Kill all duplicate keys in the KREF table. ****************************************************************************/ FSTATIC RCODE krefKillDups( F_Db * pDb, IXD * pIxd, KREF_ENTRY ** pKrefTbl, FLMUINT * puiKrefTotal) { RCODE rc = NE_XFLM_OK; FLMUINT uiCurKref = 0; FLMUINT uiLastKref = *puiKrefTotal; FLMUINT uiFirstForKey; FLMUINT uiLastForKey; FLMUINT uiNewPosOffset = 0; FLMINT iCompare; while( uiCurKref < uiLastKref) { uiFirstForKey = uiLastForKey = uiCurKref; uiCurKref = uiFirstForKey + 1; while( uiCurKref < uiLastKref) { if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIxd, pKrefTbl[ uiFirstForKey], pKrefTbl[ uiCurKref], &iCompare))) { goto Exit; } if (iCompare) { break; } uiLastForKey = uiCurKref++; } if( uiFirstForKey == uiLastForKey) { pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey]; continue; } if( pKrefTbl[ uiFirstForKey]->bDelete) { if( pKrefTbl[ uiLastForKey]->bDelete) { pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey]; } else { TestCancel: // See if the operations cancel each other. If they don't, we // need to keep both operations if( !krefIsKeyDataEqual( pKrefTbl[ uiFirstForKey], pKrefTbl[ uiLastForKey])) { pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey]; pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiLastForKey]; } } } else { if( pKrefTbl[ uiLastForKey]->bDelete) { goto TestCancel; } else { pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiLastForKey]; } } } *puiKrefTotal = uiNewPosOffset; Exit: return( rc); } libxflaim-5.1.969/src/fsrefupd.cpp0000644000175000017500000000524610511001742020355 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Insert and delete keys in an index B-Tree. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsrefupd.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /*************************************************************************** Desc: Update (add or delete) a single reference *****************************************************************************/ RCODE F_Db::refUpdate( LFILE * pLFile, IXD * pIxd, KREF_ENTRY * pKrefEntry, FLMBOOL bNormalUpdate) { RCODE rc = NE_XFLM_OK; F_Btree * pbtree = NULL; IXKeyCompare compareObject; // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } flmAssert( pLFile->uiRootBlk); compareObject.setIxInfo( this, pIxd); if (bNormalUpdate && pKrefEntry->bDelete) { compareObject.setOldNodeList( m_pOldNodeList); } if( RC_BAD( rc = pbtree->btOpen( this, pLFile, (pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE, (pIxd->pFirstData) ? TRUE : FALSE, &compareObject))) { goto Exit; } if (!pKrefEntry->bDelete) { pbtree->btResetBtree(); if( RC_BAD( rc = pbtree->btInsertEntry( (FLMBYTE *)&pKrefEntry [1], pKrefEntry->ui16KeyLen, pKrefEntry->uiDataLen ? ((FLMBYTE *)(&pKrefEntry [1])) + 1 + pKrefEntry->ui16KeyLen : NULL, pKrefEntry->uiDataLen, TRUE, TRUE))) { goto Exit; } } else { pbtree->btResetBtree(); if (RC_BAD( rc = pbtree->btRemoveEntry( (FLMBYTE *)&pKrefEntry [1], pKrefEntry->ui16KeyLen))) { if (rc == NE_XFLM_NOT_FOUND) { // Already been deleted, ignore the error condition and go on. RC_UNEXPECTED_ASSERT( rc); rc = NE_XFLM_OK; } goto Exit; } } Exit: if (pbtree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); } return( rc); } libxflaim-5.1.969/src/fcache.h0000644000175000017500000025015610511001742017417 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Various classes used to manage cache. // // Tabs: 3 // // Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fcache.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FCACHE_H #define FCACHE_H class F_Rfl; class F_DOMNode; class F_Db; class F_DbSystem; class F_Database; class F_LocalNodeCache; class F_CachedNode; class F_CachedBlock; class F_BlockCacheMgr; class F_NodeCacheMgr; class F_GlobalCacheMgr; class F_CacheList; class F_CachedItem; class F_Btree; class F_BTreeIStream; class F_NodeInfo; class F_BTreeInfo; class F_DynaBuf; class F_AttrItem; class F_NodeRelocator; class F_NodeDataRelocator; class F_NodeListRelocator; class F_AttrListRelocator; class F_AttrItemRelocator; class F_AttrBufferRelocator; class F_BlockRelocator; #define MIN_HASH_BUCKETS 0x10000 // 65536 buckets - multiple of 2. #define MAX_HASH_BUCKETS 0x20000000 // roughly 500,000,000 buckets. FLMUINT caGetBestHashTblSize( // scache.cpp FLMUINT uiCurrItemCount); FINLINE FLMUINT minItemCount( FLMUINT uiNumHashBuckets) { return( uiNumHashBuckets / 4); } FINLINE FLMUINT maxItemCount( FLMUINT uiNumHashBuckets) { return( uiNumHashBuckets * 4); } FINLINE FLMBOOL shouldRehash( FLMUINT uiItemCount, FLMUINT uiNumBuckets) { return( ((uiItemCount > maxItemCount( uiNumBuckets) && uiNumBuckets < MAX_HASH_BUCKETS) || (uiItemCount < minItemCount( uiNumBuckets) && uiNumBuckets > MIN_HASH_BUCKETS)) ? TRUE : FALSE); } /*************************************************************************** Desc: See if enough time has passed since we last tried to rehash. We don't want to be continually trying to rehash. ***************************************************************************/ FINLINE FLMBOOL checkHashFailTime( FLMUINT * puiHashFailTime) { if (*puiHashFailTime) { FLMUINT uiCurrTime = FLM_GET_TIMER(); if (FLM_ELAPSED_TIME( uiCurrTime, (*puiHashFailTime)) >= gv_XFlmSysData.uiRehashAfterFailureBackoffTime) { *puiHashFailTime = 0; return( TRUE); } else { return( FALSE); } } else { return( TRUE); } } /***************************************************************************** Desc: Cached item ******************************************************************************/ class F_CachedItem : public F_Object { public: F_CachedItem() { m_pNextInGlobal = NULL; m_pPrevInGlobal = NULL; } virtual ~F_CachedItem() { } private: F_CachedItem * m_pPrevInGlobal; F_CachedItem * m_pNextInGlobal; friend class F_CacheList; friend class F_CachedBlock; friend class F_CachedNode; friend class F_GlobalCacheMgr; friend class F_BlockCacheMgr; friend class F_NodeCacheMgr; friend class F_Database; friend class F_Db; friend class F_NodeRelocator; friend class F_NodeDataRelocator; friend class F_NodeListRelocator; friend class F_AttrListRelocator; friend class F_AttrItemRelocator; friend class F_AttrBufferRelocator; friend class F_BlockRelocator; }; /*************************************************************************** Desc: Object for keeping track of an MRU/LRU list of cached items (nodes or blocks) ***************************************************************************/ class F_CacheList { public: F_CacheList() { m_pMRUItem = NULL; m_pLRUItem = NULL; m_pLastMRUItem = NULL; } ~F_CacheList() { flmAssert( !m_pMRUItem); flmAssert( !m_pLRUItem); flmAssert( !m_pLastMRUItem); } // Link a cached item into the global list as the MRU item. This routine // assumes that the cache mutex for managing this list // has already been locked. FINLINE void linkGlobalAsMRU( F_CachedItem * pItem) { if ((pItem->m_pNextInGlobal = m_pMRUItem) != NULL) { pItem->m_pNextInGlobal->m_pPrevInGlobal = pItem; } else { m_pLRUItem = pItem; m_pLastMRUItem = pItem; } pItem->m_pPrevInGlobal = NULL; m_pMRUItem = pItem; flmAssert( pItem != pItem->m_pPrevInGlobal); flmAssert( pItem != pItem->m_pNextInGlobal); } // Link a cached item into the global list as the last MRU item. // This routine assumes that the cache mutex for managing this list // has already been locked. FINLINE void linkGlobalAsLastMRU( F_CachedItem * pItem) { if( !m_pLastMRUItem) { flmAssert( !m_pMRUItem); linkGlobalAsMRU( pItem); return; } flmAssert( m_pLastMRUItem); if( m_pLastMRUItem->m_pNextInGlobal) { m_pLastMRUItem->m_pNextInGlobal->m_pPrevInGlobal = pItem; pItem->m_pNextInGlobal = m_pLastMRUItem->m_pNextInGlobal; } else { flmAssert( m_pLRUItem == m_pLastMRUItem); m_pLRUItem = pItem; } m_pLastMRUItem->m_pNextInGlobal = pItem; pItem->m_pPrevInGlobal = m_pLastMRUItem; m_pLastMRUItem = pItem; flmAssert( pItem != pItem->m_pPrevInGlobal); flmAssert( pItem != pItem->m_pNextInGlobal); } // Link a cached item into the global list as the LRU item. This routine // assumes that the cache mutex for managing this list // has already been locked. FINLINE void linkGlobalAsLRU( F_CachedItem * pItem) { if ((pItem->m_pPrevInGlobal = m_pLRUItem) != NULL) { pItem->m_pPrevInGlobal->m_pNextInGlobal = pItem; } else { flmAssert( !m_pMRUItem); flmAssert( !m_pLastMRUItem); m_pMRUItem = pItem; m_pLastMRUItem = pItem; } pItem->m_pNextInGlobal = NULL; m_pLRUItem = pItem; flmAssert( pItem != pItem->m_pPrevInGlobal); flmAssert( pItem != pItem->m_pNextInGlobal); } // Unlink a cached item from the global list. This routine // assumes that the cache mutex for managing this list // has already been locked. FINLINE void unlinkGlobal( F_CachedItem * pItem) { if( pItem == m_pLastMRUItem) { if( m_pLastMRUItem->m_pPrevInGlobal) { m_pLastMRUItem = m_pLastMRUItem->m_pPrevInGlobal; } else { m_pLastMRUItem = m_pLastMRUItem->m_pNextInGlobal; } } if (pItem->m_pNextInGlobal) { flmAssert( pItem != m_pLRUItem); pItem->m_pNextInGlobal->m_pPrevInGlobal = pItem->m_pPrevInGlobal; } else { m_pLRUItem = pItem->m_pPrevInGlobal; } if (pItem->m_pPrevInGlobal) { flmAssert( pItem != m_pMRUItem); pItem->m_pPrevInGlobal->m_pNextInGlobal = pItem->m_pNextInGlobal; } else { m_pMRUItem = pItem->m_pNextInGlobal; } pItem->m_pNextInGlobal = NULL; pItem->m_pPrevInGlobal = NULL; } // Moves a cached item one step closer to the MRU slot in the global list. // This routine assumes that the cache mutex for managing this list // has already been locked. FINLINE void stepUpInGlobal( F_CachedItem * pItem) { F_CachedItem * pPrevItem; if ((pPrevItem = pItem->m_pPrevInGlobal) != NULL) { if( pItem == m_pLastMRUItem) { m_pLastMRUItem = m_pLastMRUItem->m_pPrevInGlobal; } if (pPrevItem->m_pPrevInGlobal) { pPrevItem->m_pPrevInGlobal->m_pNextInGlobal = pItem; } else { m_pMRUItem = pItem; } pItem->m_pPrevInGlobal = pPrevItem->m_pPrevInGlobal; pPrevItem->m_pPrevInGlobal = pItem; pPrevItem->m_pNextInGlobal = pItem->m_pNextInGlobal; if (pItem->m_pNextInGlobal) { pItem->m_pNextInGlobal->m_pPrevInGlobal = pPrevItem; } else { m_pLRUItem = pPrevItem; } pItem->m_pNextInGlobal = pPrevItem; } } private: F_CachedItem * m_pMRUItem; F_CachedItem * m_pLRUItem; F_CachedItem * m_pLastMRUItem; friend class F_CachedItem; friend class F_NodeCacheMgr; friend class F_BlockCacheMgr; friend class F_CachedBlock; friend class F_NodeRelocator; friend class F_NodeDataRelocator; friend class F_NodeListRelocator; friend class F_AttrListRelocator; friend class F_AttrItemRelocator; friend class F_AttrBufferRelocator; friend class F_BlockRelocator; }; /*************************************************************************** Desc: Global cache manager for XFLAIM. ***************************************************************************/ class F_GlobalCacheMgr : public F_Object { public: F_GlobalCacheMgr(); ~F_GlobalCacheMgr(); RCODE setup( void); FINLINE void incrTotalBytes( FLMUINT uiIncrAmount) { m_pSlabManager->incrementTotalBytesAllocated( uiIncrAmount); } FINLINE void decrTotalBytes( FLMUINT uiDecrAmount) { m_pSlabManager->decrementTotalBytesAllocated( uiDecrAmount); } FINLINE FLMUINT totalBytes( void) { return( m_pSlabManager->totalBytesAllocated()); } FINLINE FLMUINT availSlabs( void) { return( m_pSlabManager->availSlabs()); } FINLINE FLMUINT allocatedSlabs( void) { return( m_pSlabManager->getTotalSlabs()); } FINLINE FLMBOOL cacheOverLimit( void) { if( allocatedSlabs() > m_uiMaxSlabs) { return( TRUE); } return( FALSE); } RCODE setCacheLimit( FLMUINT uiMaxCache, FLMBOOL bPreallocateCache); RCODE setDynamicMemoryLimit( FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave); RCODE setHardMemoryLimit( FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate); void getCacheInfo( XFLM_CACHE_INFO * pMemInfo); RCODE adjustCache( FLMUINT * puiCurrTime, FLMUINT * puiLastCacheAdjustTime); RCODE clearCache( IF_Db * pDb); FINLINE void lockMutex( void) { f_mutexLock( m_hMutex); } FINLINE void unlockMutex( void) { f_mutexUnlock( m_hMutex); } private: IF_SlabManager * m_pSlabManager; FLMUINT m_uiMaxBytes; FLMUINT m_uiMaxSlabs; FLMBOOL m_bCachePreallocated; FLMBOOL m_bDynamicCacheAdjust; // Is cache to be dynamically adjusted? FLMUINT m_uiCacheAdjustPercent; // Percent of available memory to adjust to. FLMUINT m_uiCacheAdjustMin; // Minimum limit to adjust cache to. FLMUINT m_uiCacheAdjustMax; // Maximum limit to adjust cache to. FLMUINT m_uiCacheAdjustMinToLeave; // Minimum bytes to leave when adjusting cache. FLMUINT m_uiCacheAdjustInterval; // Interval for adjusting cache limit. FLMUINT m_uiCacheCleanupInterval; // Interval for cleaning up old things out of // cache. FLMUINT m_uiUnusedCleanupInterval; // Interval for cleaning up unused structures F_MUTEX m_hMutex; // Mutex to control access to global cache // manager object. friend class F_CachedItem; friend class F_CachedNode; friend class F_CachedBlock; friend class F_BlockCacheMgr; friend class F_NodeCacheMgr; friend class F_Database; friend class F_DbSystem; }; /**************************************************************************** Desc: Class for moving cache blocks in cache. ****************************************************************************/ class F_BlockRelocator : public IF_Relocator { public: F_BlockRelocator() { } virtual ~F_BlockRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: This class manages block cache. ****************************************************************************/ class F_BlockCacheMgr : public F_Object { public: F_BlockCacheMgr(); ~F_BlockCacheMgr(); RCODE initCache( void); void cleanupLRUCache( void); void cleanupReplaceList( void); void cleanupFreeCache( void); void reduceReuseList( void); RCODE reduceCache( F_Db * pDb); RCODE rehash( void); RCODE allocBlock( F_Db * pDb, F_CachedBlock ** ppSCache); // Returns a pointer to the correct entry in the block cache hash table for // the given block address FINLINE F_CachedBlock ** blockHash( FLMUINT uiSigBitsInBlkSize, FLMUINT uiBlkAddress) { return( &m_ppHashBuckets[ (uiBlkAddress >> uiSigBitsInBlkSize) & m_uiHashMask]); } FINLINE void defragmentMemory( FLMBOOL bMutexLocked = FALSE) { if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); } m_pBlockAllocator->defragmentMemory(); if( !bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } } private: RCODE initHashTbl( void); F_CacheList m_MRUList; // List of all block objects in MRU order. F_CachedBlock * m_pMRUReplace; // Pointer to the MRU end of the list // of cache items with no flags set. F_CachedBlock * m_pLRUReplace; // Pointer to the LRU end of the list // of cache items with no flags set. F_CachedBlock * m_pFirstFree; // Pointer to a linked list of cache // blocks that need to be freed. // Cache blocks in this list are no // longer associated with a file and can // be freed or re-used as needed. They // are linked using the pNextInFile and // pPrevInFile pointers. F_CachedBlock * m_pLastFree; // Pointer to a linked list of cache // blocks that need to be freed. // Cache blocks in this list are no // longer associated with a file and can // be freed or re-used as needed. They // are linked using the pNextInFile and // pPrevInFile pointers. XFLM_CACHE_USAGE m_Usage; // Contains usage information. FLMUINT m_uiFreeBytes; // Number of free bytes FLMUINT m_uiFreeCount; // Number of free blocks FLMUINT m_uiReplaceableCount; // Number of blocks whose flags are 0 FLMUINT m_uiReplaceableBytes; // Number of bytes belonging to blocks whose // flags are 0 FLMBOOL m_bAutoCalcMaxDirty; // Flag indicating we should automatically // calculate maximum dirty cache. FLMUINT m_uiMaxDirtyCache; // Maximum cache that can be dirty. FLMUINT m_uiLowDirtyCache; // When maximum dirty cache is exceeded, // threshhold it should be brought back // under FLMUINT m_uiTotalUses; // Total number of uses currently held // on blocks in cache. FLMUINT m_uiBlocksUsed;// Total number of blocks in cache that // are being used. FLMUINT m_uiPendingReads; // Total reads currently pending. FLMUINT m_uiIoWaits; // Number of times multiple threads // were reading the same block from // disk at the same time. F_CachedBlock ** m_ppHashBuckets;// This is a pointer to a hash table that // is used to find cache blocks. Each // element in the table points to a // linked list of F_CachedBlock objects that // all hash to the same hash bucket. FLMUINT m_uiNumBuckets;// This contains the number of buckets // in the hash table. FLMUINT m_uiHashFailTime; // Last time we tried to rehash and // failed. Want to wait before we // retry again. FLMUINT m_uiHashMask; // Bits that are significant // for the number of hash buckets. IF_MultiAlloc * m_pBlockAllocator; // Fixed size allocators for cache blocks F_BlockRelocator m_blockRelocator; // Relocator for cache blocks FLMBOOL m_bReduceInProgress; #ifdef FLM_DEBUG FLMBOOL m_bDebug; // Enables checksumming and cache use // monitoring. Only available when // debug is compiled in. #endif friend class F_CachedBlock; friend class F_GlobalCacheMgr; friend class F_Database; friend class F_Db; friend class F_DbSystem; friend class F_BlockRelocator; }; #ifdef FLM_DEBUG /**************************************************************************** Struct: SCACHE_USE (Cache Block Use) Desc: This is a debug only structure that is used to keep track of the threads that are currently using a block. ****************************************************************************/ typedef struct SCache_Use { SCache_Use * pNext; // Pointer to next SCACHE_USE structure in // the list. FLMUINT uiThreadId; // Thread ID of thread using the block. FLMUINT uiUseCount; // Use count for this particular thread. } SCACHE_USE; #endif // Flags for m_ui16Flags field in F_CachedBlock #define CA_DIRTY 0x0001 // This bit indicates that the block is // dirty and needs to be flushed to disk. // NOTE: For 3.x files, this bit may remain // set on prior versions of blocks until the // current transaction commits. #define CA_WRITE_INHIBIT 0x0002 // Must not write block until use count // goes to zero. NOTE: Can ignore when // in the checkpoint thread. #define CA_READ_PENDING 0x0004 // This bit indicates that the block is // currently being read in from disk. #define CA_WRITE_TO_LOG 0x0008 // This bit indicates that this version of // the block should be written to the // rollback log before being replaced. // During an update transaction, the first // time a block is updated, FLAIM will // create a new version of the block and // insert it into cache. The prior version // of the block is marked with this flag // to indicate that it needs to be written // to the log before it can be replaced. #define CA_LOG_FOR_CP 0x0010 // This bit indicates that this version of // the block needs to be logged to the // physical rollback in order to restore // the last checkpoint. This is only // applicable to 3.x files. #define CA_WAS_DIRTY 0x0020 // This bit indicates that this version of // the block was dirty before the newer // version of the block was created. // Its dirty state should be restored if // the current transaction aborts. This // flag is only used for 3.x files. #define CA_WRITE_PENDING 0x0040 // This bit indicates that a block is in // the process of being written out to // disk. #define CA_IN_WRITE_PENDING_LIST 0x0080 // This bit indicates that a block is in // the write pending list. #define CA_FREE 0x0100 // The block has been linked to the free // list (and unlinked from all other lists) #define CA_IN_FILE_LOG_LIST 0x0200 // Block is in the list of blocks that may // have one or more versions that need to // be logged #define CA_IN_NEW_LIST 0x0400 // Dirty block that is beyond the last CP EOF #define CA_DUMMY_FLAG 0x0800 // Used to prevent blocks from being linked // into the replace list in cases where // they will be removed immediately (because // a bit is going to being set) /**************************************************************************** Desc: This is the header structure for a cached data block. ****************************************************************************/ class F_CachedBlock : public F_CachedItem { public: F_CachedBlock( FLMUINT uiBlockSize); ~F_CachedBlock(); FINLINE FLMUINT memSize( void) { return( gv_XFlmSysData.pBlockCacheMgr->m_pBlockAllocator->getTrueSize( (FLMBYTE *)this)); } FINLINE FLMUINT blkAddress( void) { return( m_uiBlkAddress); } FINLINE F_BLK_HDR * getBlockPtr( void) { return( m_pBlkHdr); } FINLINE F_Database * getDatabase( void) { return( m_pDatabase); } FINLINE FLMUINT16 getModeFlags( void) { return( m_ui16Flags); } FINLINE FLMUINT getUseCount( void) { return( m_uiUseCount); } // Gets the prior image block address from the block header. // NOTE: This function assumes that the block cache mutex is locked. FINLINE FLMUINT getPriorImageAddress( void) { return( (FLMUINT)m_pBlkHdr->ui32PriorBlkImgAddr); } // Gets the transaction ID from the block header. NOTE: This function // assumes that the block cache mutex is locked. FINLINE FLMUINT64 getLowTransID( void) { return( m_pBlkHdr->ui64TransID); } // Set the high transaction ID for a cache block. // NOTE: This function assumes that the block cache mutex is locked. FINLINE void setTransID( FLMUINT64 ui64NewTransID) { if (m_ui64HighTransID == FLM_MAX_UINT64 && ui64NewTransID != FLM_MAX_UINT64) { gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes += memSize(); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount++; } else if (m_ui64HighTransID != FLM_MAX_UINT64 && ui64NewTransID == FLM_MAX_UINT64) { FLMUINT uiSize = memSize(); flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes >= uiSize); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes -= uiSize; flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount--; } m_ui64HighTransID = ui64NewTransID; } // Determines if a cache block is needed by a read transaction. FINLINE FLMBOOL neededByReadTrans( void) { return( m_pDatabase->neededByReadTrans( getLowTransID(), m_ui64HighTransID)); } // Link a cache block into the replace list as the MRU item. This routine // assumes that the block cache mutex has already been locked. FINLINE void linkToReplaceListAsMRU( void) { flmAssert( !m_ui16Flags); if ((m_pNextInReplaceList = gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace) != NULL) { m_pNextInReplaceList->m_pPrevInReplaceList = this; } else { gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = this; } m_pPrevInReplaceList = NULL; gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = this; gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount++; gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes += memSize(); } // Link a cache block into the replace list as the LRU item. This routine // assumes that the block cache mutex has already been locked. FINLINE void linkToReplaceListAsLRU( void) { flmAssert( !m_ui16Flags); if ((m_pPrevInReplaceList = gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace) != NULL) { m_pPrevInReplaceList->m_pNextInReplaceList = this; } else { gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = this; } m_pNextInReplaceList = NULL; gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = this; gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount++; gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes += memSize(); } // Moves a block one step closer to the MRU slot in the replace list. // This routine assumes that the block cache mutex has already been locked. FINLINE void stepUpInReplaceList( void) { F_CachedBlock * pPrevSCache; flmAssert( !m_ui16Flags); if( (pPrevSCache = m_pPrevInReplaceList) != NULL) { if( pPrevSCache->m_pPrevInReplaceList) { pPrevSCache->m_pPrevInReplaceList->m_pNextInReplaceList = this; } else { gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = this; } m_pPrevInReplaceList = pPrevSCache->m_pPrevInReplaceList; pPrevSCache->m_pPrevInReplaceList = this; pPrevSCache->m_pNextInReplaceList = m_pNextInReplaceList; if( m_pNextInReplaceList) { m_pNextInReplaceList->m_pPrevInReplaceList = pPrevSCache; } else { gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = pPrevSCache; } m_pNextInReplaceList = pPrevSCache; } } // Clears the passed-in flags from the F_CachedBlock object // This routine assumes that the block cache mutex is locked. FINLINE void clearFlags( FLMUINT16 ui16FlagsToClear) { if( m_ui16Flags) { if( (m_ui16Flags &= ~ui16FlagsToClear) == 0) { if( !m_pPrevInGlobal || m_ui64HighTransID == ~((FLMUINT64)0) || neededByReadTrans()) { linkToReplaceListAsMRU(); } else { linkToReplaceListAsLRU(); } } } } // Sets the passed-in flags on the object // This routine assumes that the block cache mutex is locked. FINLINE void setFlags( FLMUINT16 ui16FlagsToSet) { flmAssert( ui16FlagsToSet); if( !m_ui16Flags) { unlinkFromReplaceList(); } m_ui16Flags |= ui16FlagsToSet; } // Set the dirty flag on a cache block. // This routine assumes that the block cache mutex is locked. FINLINE void setDirtyFlag( F_Database * pDatabase) { flmAssert( !(m_ui16Flags & (CA_DIRTY | CA_WRITE_PENDING | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); setFlags( CA_DIRTY); pDatabase->incrementDirtyCacheCount(); } // Unset the dirty flag on a cache block. // This routine assumes that the block cache mutex is locked. FINLINE void unsetDirtyFlag( void) { flmAssert( m_ui16Flags & CA_DIRTY); flmAssert( m_pDatabase->getDirtyCacheCount()); if (m_ui16Flags & CA_IN_FILE_LOG_LIST) { unlinkFromLogList(); } else if (m_ui16Flags & CA_IN_NEW_LIST) { unlinkFromNewList(); } clearFlags( CA_DIRTY); m_pDatabase->decrementDirtyCacheCount(); } FINLINE FLMUINT getBlkSize( void) { return( (FLMUINT)m_ui16BlkSize); } #ifdef FLM_DBG_LOG void logFlgChange( FLMUINT16 ui16OldFlags, char cPlace); #endif void linkToLogList( void); void unlinkFromLogList( void); void linkToNewList( void); void unlinkFromNewList( void); void linkToDatabase( F_Database * pDatabase); void unlinkFromDatabase( void); void unlinkFromTransLogList( void); // Increment the use count on a cache block for a particular // thread. NOTE: This routine assumes that the block cache mutex // is locked. FINLINE void useForThread( #ifdef FLM_DEBUG FLMUINT uiThreadId) #else FLMUINT) // uiThreadId) #endif { #ifdef FLM_DEBUG if (m_pUseList || (gv_XFlmSysData.pBlockCacheMgr->m_bDebug && !m_uiUseCount)) { dbgUseForThread( uiThreadId); } else #endif { if (!m_uiUseCount) { gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed++; } m_uiUseCount++; gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses++; } } // Decrement the use count on a cache block for a particular // thread. NOTE: This routine assumes that the block cache mutex // is locked. FINLINE void releaseForThread( void) { if (!m_uiUseCount) { return; } #ifdef FLM_DEBUG if (m_pUseList) { dbgReleaseForThread(); } else #endif { #ifdef FLM_DEBUG // If count is one, it will be decremented to zero. if (m_uiUseCount == 1) { m_uiChecksum = computeChecksum(); } #endif m_uiUseCount--; gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses--; if (!m_uiUseCount) { gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed--; } } } // Tests if a block can be freed from cache. // NOTE: This routine assumes that the block cache mutex is locked. FINLINE FLMBOOL canBeFreed( void) { if (!m_uiUseCount && !m_ui16Flags) { F_CachedBlock * pNewerSCache = m_pPrevInVersionList; // The following code is attempting to ensure that newer // versions of the block have had the prior block address // properly transferred to them from an older version of // the block. If not, we cannot remove the current version // of the block (pointed to by pSCache), because it is // the older version that needs to be logged in order for // the prior block address to be properly transferred to // the newer version of the block. // If there is no newer version of the block, we can remove // this block, because it means that there was at one point // in time a newer version, the prior block address was // safely transferred - otherwise, the newer version would // still be in cache. // If there is a newer version of the block, but it is in the // process of being read in from disk (CA_READ_PENDING bit is // set), we can know that the prior block address has been // properly transferred to the block being read in. // Explanation: If the CA_READ_PENDING bit it set, the block // had to have been written out to disk at some prior time. The // rules for writing out a block to disk are such that it is // impossible for a block to be written out without having a // pointer to some prior version of the block. The only // exception to this is a newly created block - but in that // case, the block does not need to have a prior version pointer - // because there are none! // This assertion is obvious for a version of a block that is // being read from the rollback log - it would be impossible // to be reading such a block from the rollback log if it hadn't // been part of a version chain! As for the current version of a // block, it cannot be written out and removed from cache without // having a pointer to the chain of older versions that may still // be needed (by a read transactions, for rollback, or to recover // a checkpoint). // NOTE: Although we know that a block being read in from disk // has to already have a prior block address, we cannot just // look at the block header, because it is being read in from // disk and the prior block address is not yet there. Usually, // it will still be zeroes - making it look as though the block // does not have a prior block address when, in fact, it does. // Thus, we look at the CA_READ_PENDING bit first. If that // is not set, we can safely look at the prior block address. // Note also that even if there is a newer block that doesn't // have a prior block address, we may still be able to remove // the current block (pSCache) if it is not needed by any // read transactions. if (!pNewerSCache || (pNewerSCache->m_ui16Flags & CA_READ_PENDING) || pNewerSCache->getPriorImageAddress() != 0 || !m_pDatabase || !neededByReadTrans()) { return( TRUE); } } return( FALSE); } void linkToFreeList( FLMUINT uiFreeTime); void unlinkFromFreeList( void); void * operator new( FLMSIZET uiSize, FLMUINT uiBlockSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new( FLMSIZET uiSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new( FLMSIZET uiSize, const char * pszFile, int iLine) #if !defined( FLM_NLM) throw() #endif ; void * operator new[]( FLMSIZET uiSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new[]( FLMSIZET uiSize, const char * pszFile, int iLine) #if !defined( FLM_NLM) throw() #endif ; void operator delete( void * ptr); void operator delete[]( void * ptr); private: void unlinkFromReplaceList( void); #ifdef FLM_DEBUG void dbgUseForThread( FLMUINT uiThreadId); void dbgReleaseForThread( void); FLMUINT computeChecksum( void); #endif #ifdef SCACHE_LINK_CHECKING void verifyCache( int iPlace); #else FINLINE void verifyCache( int) { } #endif // Link a cached block into the global list as the MRU item. This routine // assumes that the block cache mutex has already been locked. FINLINE void linkToGlobalListAsMRU( void) { if( (m_pBlkHdr->ui8BlkType & BT_FREE) || (m_pBlkHdr->ui8BlkType & BT_LEAF) || (m_pBlkHdr->ui8BlkType & BT_LEAF_DATA) || (m_pBlkHdr->ui8BlkType & BT_DATA_ONLY)) { gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLastMRU( (F_CachedItem *)this); } else { gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsMRU( (F_CachedItem *)this); } if( !m_ui16Flags) { linkToReplaceListAsMRU(); } } // Link a cached block into the global list as the LRU item. This routine // assumes that the block cache mutex has already been locked. FINLINE void linkToGlobalListAsLRU( void) { if( (m_pBlkHdr->ui8BlkType & BT_FREE) || (m_pBlkHdr->ui8BlkType & BT_LEAF) || (m_pBlkHdr->ui8BlkType & BT_LEAF_DATA) || (m_pBlkHdr->ui8BlkType & BT_DATA_ONLY)) { gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLRU( (F_CachedItem *)this); } else { gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLastMRU( (F_CachedItem *)this); } if( !m_ui16Flags) { linkToReplaceListAsLRU(); } } // Unlink a cache block from the global list. This routine // assumes that the block cache mutex has already been locked. FINLINE void unlinkFromGlobalList( void) { gv_XFlmSysData.pBlockCacheMgr->m_MRUList.unlinkGlobal( (F_CachedItem *)this); if( !m_ui16Flags) { unlinkFromReplaceList(); } } // Moves a block one step closer to the MRU slot in the global list. This // routine assumes that the block cache mutex has already been locked. FINLINE void stepUpInGlobalList( void) { gv_XFlmSysData.pBlockCacheMgr->m_MRUList.stepUpInGlobal( (F_CachedItem *)this); if( !m_ui16Flags) { stepUpInReplaceList(); } } #ifdef SCACHE_LINK_CHECKING void checkHashLinks( F_CachedBlock ** ppSCacheBucket); void checkHashUnlinks( F_CachedBlock ** ppSCacheBucket); #endif // Link a cache block to its hash bucket. This routine assumes // that the block cache mutex has already been locked. FINLINE void linkToHashBucket( F_CachedBlock ** ppSCacheBucket) { #ifdef SCACHE_LINK_CHECKING checkHashLinks( ppSCacheBucket); #endif m_pPrevInHashBucket = NULL; if ((m_pNextInHashBucket = *ppSCacheBucket) != NULL) { m_pNextInHashBucket->m_pPrevInHashBucket = this; } *ppSCacheBucket = this; } // Unlink a cache block from its hash bucket. This routine assumes // that the block cache mutex has already been locked. FINLINE void unlinkFromHashBucket( F_CachedBlock ** ppSCacheBucket) { #ifdef SCACHE_LINK_CHECKING checkHashUnlinks( ppSCacheBucket); #endif // Make sure it is not in the list of log blocks. flmAssert( !(m_ui16Flags & CA_WRITE_TO_LOG)); if (m_pNextInHashBucket) { m_pNextInHashBucket->m_pPrevInHashBucket = m_pPrevInHashBucket; } if (m_pPrevInHashBucket) { m_pPrevInHashBucket->m_pNextInHashBucket = m_pNextInHashBucket; } else { *ppSCacheBucket = m_pNextInHashBucket; } m_pNextInHashBucket = NULL; m_pPrevInHashBucket = NULL; } static void FLMAPI objectAllocInit( void * pvAlloc, FLMUINT uiSize); void unlinkCache( FLMBOOL bFreeIt, RCODE NotifyRc); void savePrevBlkAddress( void); F_CachedBlock * m_pPrevInDatabase; // This is a pointer to the previous block // in the linked list of blocks that are // in the same database. F_CachedBlock * m_pNextInDatabase; // This is a pointer to the next block in // the linked list of blocks that are in // the same database. F_BLK_HDR * m_pBlkHdr; // Pointer to this block's header and data. F_Database * m_pDatabase; // Pointer to the database this data block // belongs to. FLMUINT m_uiBlkAddress; // Block address. F_CachedBlock * m_pNextInReplaceList;// This is a pointer to the next block in // the global linked list of cache blocks // that have a flags value of zero. F_CachedBlock * m_pPrevInReplaceList;// This is a pointer to the previous block in // the global linked list of cache blocks // that have a flags value of zero. F_CachedBlock * m_pPrevInHashBucket; // This is a pointer to the previous block // in the linked list of blocks that are // in the same hash bucket. F_CachedBlock * m_pNextInHashBucket; // This is a pointer to the next block in // the linked list of blocks that are in // the same hash bucket. F_CachedBlock * m_pPrevInVersionList;// This is a pointer to the previous block // in the linked list of blocks that are // all just different versions of the // same block. The previous block is a // more recent version of the block. F_CachedBlock * m_pNextInVersionList;// This is a pointer to the next block in // the linked list of blocks that are all // just different versions of the same // block. The next block is an older // version of the block. F_NOTIFY_LIST_ITEM * m_pNotifyList; // This is a pointer to a list of threads // that want to be notified when a pending // I/O is complete. This pointer is only // non-null if the block is currently being // read from disk and there are multiple // threads all waiting for the block to // be read in. FLMUINT64 m_ui64HighTransID; // This indicates the highest known moment // in the file's update history when this // version of the block was the active // block. // A block's low transaction ID and high // transaction ID indicate a span of // transactions where this version of the // block was the active version of the // block. FLMUINT m_uiUseCount; // Number of times this block has been // retrieved for use by threads. A use // count of zero indicates that no thread // is currently using the block. Note that // a block cannot be replaced when its use // count is non-zero. FLMUINT16 m_ui16Flags; // This is a set of flags for the block // that indicate various things about the // block's current state. FLMUINT16 m_ui16BlkSize; // Block size FLMBOOL m_bCanRelocate; // Can the block object be relocated // if defragmenting memory? // NOTE: Keep debug items at the END of the structure. #ifdef FLM_DEBUG FLMUINT m_uiChecksum; // Checksum for the block and header. SCACHE_USE * m_pUseList; // This is a pointer to a list of threads // that are currently using the block. #endif friend class F_BlockCacheMgr; friend class F_GlobalCacheMgr; friend class F_Database; friend class F_DbSystem; friend class F_Db; friend class F_Btree; friend class F_BTreeInfo; friend class F_BlockRelocator; }; /**************************************************************************** Desc: Class for moving nodes in cache. ****************************************************************************/ class F_NodeRelocator : public IF_Relocator { public: F_NodeRelocator() { } virtual ~F_NodeRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: Class for moving node data buffers in cache. ****************************************************************************/ class F_NodeDataRelocator : public IF_Relocator { public: F_NodeDataRelocator() { } virtual ~F_NodeDataRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: Class for moving node lists in cache. ****************************************************************************/ class F_NodeListRelocator : public IF_Relocator { public: F_NodeListRelocator() { } virtual ~F_NodeListRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: Class for moving attr lists in cache. ****************************************************************************/ class F_AttrListRelocator : public IF_Relocator { public: F_AttrListRelocator() { } virtual ~F_AttrListRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: Class for moving attr items in cache. ****************************************************************************/ class F_AttrItemRelocator : public IF_Relocator { public: F_AttrItemRelocator() { } virtual ~F_AttrItemRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Desc: Class for moving attr buffers in cache. ****************************************************************************/ class F_AttrBufferRelocator : public IF_Relocator { public: F_AttrBufferRelocator() { } virtual ~F_AttrBufferRelocator() { } void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc); FLMBOOL FLMAPI canRelocate( void * pvOldAlloc); }; /**************************************************************************** Struct: NODE_CACHE_MGR (XFLAIM Node Cache Manager) Desc: This structure defines the header information that is used to control the FLAIM record cache. This structure will be embedded in the FLMSYSDATA structure. ****************************************************************************/ class F_NodeCacheMgr : public F_Object { public: F_NodeCacheMgr(); ~F_NodeCacheMgr(); void insertDOMNode( F_DOMNode * pNode); RCODE initCache( void); void cleanupOldCache( void); void cleanupPurgedCache( void); void reduceCache( void); FINLINE void setDebugMode( FLMBOOL bDebug) { #ifdef FLM_DEBUG m_bDebug = bDebug; #else (void)bDebug; #endif } RCODE allocNode( F_CachedNode ** ppNode, FLMBOOL bMutexLocked); RCODE allocDOMNode( F_DOMNode ** ppDOMNode); RCODE retrieveNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_DOMNode ** ppDOMNode); RCODE createNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_DOMNode ** ppDOMNode); RCODE makeWriteCopy( F_Db * pDb, F_CachedNode ** ppCachedNode); void removeNode( F_Db * pDb, F_CachedNode * pNode, FLMBOOL bDecrementUseCount, FLMBOOL bMutexLocked = FALSE); void removeNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId); FINLINE void defragmentMemory( FLMBOOL bMutexLocked = FALSE) { if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } m_pNodeAllocator->defragmentMemory(); m_pBufAllocator->defragmentMemory(); m_pAttrItemAllocator->defragmentMemory(); if( !bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } } private: // Hash function for hashing to nodes in node cache. // Assumes that the node cache mutex has already been locked. FINLINE F_CachedNode ** nodeHash( FLMUINT64 ui64NodeId) { return( &m_ppHashBuckets[(FLMUINT)ui64NodeId & m_uiHashMask]); } RCODE rehash( void); RCODE waitNotify( F_Db * pDb, F_CachedNode ** ppNode); void notifyWaiters( F_NOTIFY_LIST_ITEM * pNotify, F_CachedNode * pUseNode, RCODE NotifyRc); void linkIntoNodeCache( F_CachedNode * pNewerNode, F_CachedNode * pOlderNode, F_CachedNode * pNode, FLMBOOL bLinkAsMRU); void findNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64VersionNeeded, FLMBOOL bDontPoisonCache, FLMUINT * puiNumLooks, F_CachedNode ** ppNode, F_CachedNode ** ppNewerNode, F_CachedNode ** ppOlderNode); RCODE readNodeFromDisk( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_CachedNode * pNode, FLMUINT64 * pui64LowTransId, FLMBOOL * pbMostCurrent); RCODE _makeWriteCopy( F_Db * pDb, F_CachedNode ** ppCachedNode); // Private Data F_CacheList m_MRUList; // List of all node objects in MRU order. F_CachedNode * m_pPurgeList; // List of F_CachedNode objects that // should be deleted when the use count // goes to zero. F_CachedNode * m_pHeapList; // List of nodes with heap allocations F_CachedNode * m_pOldList; // List of old versions XFLM_CACHE_USAGE m_Usage; // Contains maximum, bytes used, etc. F_CachedNode ** m_ppHashBuckets; // Array of hash buckets. FLMUINT m_uiNumBuckets; // Total number of hash buckets. // must be an exponent of 2. FLMUINT m_uiHashFailTime; // Last time we tried to rehash and // failed. Want to wait before we // retry again. FLMUINT m_uiHashMask; // Hash mask mask for hashing a // node id to a hash bucket. FLMUINT m_uiPendingReads; // Total reads currently pending. FLMUINT m_uiIoWaits; // Number of times multiple threads // were reading the same node from // disk at the same time. IF_FixedAlloc * m_pNodeAllocator; // Fixed size allocator for F_CachedNode // objects IF_BufferAlloc * m_pBufAllocator; // Buffer allocator for buffers in // F_CachedNode objects IF_FixedAlloc * m_pAttrItemAllocator; // Allocator for attribute list items F_NodeRelocator m_nodeRelocator; F_NodeDataRelocator m_nodeDataRelocator; F_NodeListRelocator m_nodeListRelocator; F_AttrListRelocator m_attrListRelocator; F_AttrItemRelocator m_attrItemRelocator; F_AttrBufferRelocator m_attrBufferRelocator; F_DOMNode * m_pFirstNode; // List of DOM Nodes in a pool FLMBOOL m_bReduceInProgress; #ifdef FLM_DEBUG FLMBOOL m_bDebug; // Debug mode? #endif friend class F_CachedNode; friend class F_GlobalCacheMgr; friend class F_Database; friend class F_DbSystem; friend class F_DOMNode; friend class F_AttrItem; friend class F_NodeRelocator; friend class F_NodeDataRelocator; friend class F_NodeListRelocator; friend class F_AttrListRelocator; friend class F_AttrItemRelocator; friend class F_AttrBufferRelocator; }; // Bits for m_uiCacheFlags field in F_CachedNode #define NCA_READING_IN 0x80000000 #define NCA_UNCOMMITTED 0x40000000 #define NCA_LATEST_VER 0x20000000 #define NCA_PURGED 0x10000000 #define NCA_LINKED_TO_DATABASE 0x08000000 #define NCA_COUNTER_BITS (~(NCA_READING_IN | NCA_UNCOMMITTED | \ NCA_LATEST_VER | NCA_PURGED | \ NCA_LINKED_TO_DATABASE)) // In-memory node flags. // // Flags that are not actually written to disk - only held in memory // Reserve upper byte for these. These flags need to be here so that // we don't have to worry about locking the mutex to access them. #define FDOM_READ_ONLY 0x00000001 #define FDOM_CANNOT_DELETE 0x00000002 #define FDOM_QUARANTINED 0x00000004 #define FDOM_VALUE_ON_DISK 0x00000008 #define FDOM_SIGNED_QUICK_VAL 0x00000010 #define FDOM_UNSIGNED_QUICK_VAL 0x00000020 #define FDOM_DIRTY 0x00000040 #define FDOM_NEW 0x00000080 #define FDOM_HEAP_ALLOC 0x00000100 #define FDOM_HAVE_CELM_LIST 0x00000200 #define FDOM_NAMESPACE_DECL 0x00000400 #define FDOM_FIXED_SIZE_HEADER 0x00000800 #define FDOM_PERSISTENT_FLAGS (FDOM_READ_ONLY | \ FDOM_CANNOT_DELETE | \ FDOM_QUARANTINED | \ FDOM_NAMESPACE_DECL) /***************************************************************************** Desc: Node storage flags ******************************************************************************/ #define NSF_HAVE_BASE_ID_BIT 0x0001 #define NSF_HAVE_META_VALUE_BIT 0x0002 #define NSF_HAVE_SIBLINGS_BIT 0x0004 #define NSF_HAVE_CHILDREN_BIT 0x0008 #define NSF_HAVE_ATTR_LIST_BIT 0x0010 #define NSF_HAVE_CELM_LIST_BIT 0x0020 #define NSF_HAVE_DATA_LEN_BIT 0x0040 #define NSF_EXT_HAVE_DCHILD_COUNT_BIT 0x0080 #define NSF_EXT_READ_ONLY_BIT 0x0100 #define NSF_EXT_CANNOT_DELETE_BIT 0x0200 #define NSF_EXT_HAVE_PREFIX_BIT 0x0400 #define NSF_EXT_ENCRYPTED_BIT 0x0800 #define NSF_EXT_NAMESPACE_DECL_BIT 0x1000 #define NSF_EXT_ANNOTATION_BIT 0x2000 #define NSF_EXT_QUARANTINED_BIT 0x4000 /***************************************************************************** Desc: Attribute storage flags and masks ******************************************************************************/ #define ASF_PAYLOAD_LEN_MASK 0x0000000F #define ASF_MAX_EMBEDDED_PAYLOAD_LEN 0x0000000E #define ASF_HAVE_PAYLOAD_LEN_SEN 0x0000000F #define ASF_HAVE_PREFIX_BIT 0x00000010 #define ASF_READ_ONLY_BIT 0x00000020 #define ASF_CANNOT_DELETE_BIT 0x00000040 #define ASF_ENCRYPTED_BIT 0x00000080 /***************************************************************************** Desc: ******************************************************************************/ typedef struct NODE_ITEM { FLMUINT uiNameId; FLMUINT64 ui64NodeId; } NODE_ITEM; /***************************************************************************** Desc: ******************************************************************************/ class F_AttrItem { public: F_AttrItem() { m_pCachedNode = NULL; m_pucPayload = NULL; m_uiPayloadLen = 0; m_uiDataType = 0; m_uiNameId = 0; m_uiFlags = 0; m_uiPrefixId = 0; m_ui64QuickVal = 0; m_uiEncDefId = 0; m_uiIVLen = 0; m_uiDecryptedDataLen = 0; } ~F_AttrItem(); RCODE resizePayloadBuffer( FLMUINT uiTotalNeeded, FLMBOOL bMutexAlreadyLocked); RCODE setupAttribute( F_Db * pDb, FLMUINT uiEncDefId, FLMUINT uiSizeNeeded, FLMBOOL bOkToGenerateIV, FLMBOOL bMutexAlreadyLocked); void getAttrSizeNeeded( FLMUINT uiBaseNameId, XFLM_NODE_INFO * pNodeInfo, FLMUINT * puiSaveStorageFlags, FLMUINT * puiSizeNeeded); FINLINE void copyFrom( F_AttrItem * pSrcAttrItem) { m_pCachedNode = pSrcAttrItem->m_pCachedNode; m_pucPayload = pSrcAttrItem->m_pucPayload; m_uiPayloadLen = pSrcAttrItem->m_uiPayloadLen; m_uiDataType = pSrcAttrItem->m_uiDataType; m_uiNameId = pSrcAttrItem->m_uiNameId; m_uiFlags = pSrcAttrItem->m_uiFlags; m_uiPrefixId = pSrcAttrItem->m_uiPrefixId; m_ui64QuickVal = pSrcAttrItem->m_ui64QuickVal; m_uiEncDefId = pSrcAttrItem->m_uiEncDefId; m_uiIVLen = pSrcAttrItem->m_uiIVLen; m_uiDecryptedDataLen = pSrcAttrItem->m_uiDecryptedDataLen; if( m_uiPayloadLen > sizeof( FLMBYTE *)) { m_pucPayload = NULL; m_uiPayloadLen = 0; m_uiEncDefId = 0; } } FINLINE FLMBYTE * getAttrDataPtr( void) { if( m_uiPayloadLen <= sizeof( FLMBYTE *)) { return( (FLMBYTE *)&m_pucPayload); } return( m_pucPayload + m_uiIVLen); } FINLINE FLMUINT getAttrDataLength( void) { if( m_uiEncDefId) { return( m_uiDecryptedDataLen); } return( m_uiPayloadLen - m_uiIVLen); } FINLINE FLMUINT getAttrDataBufferSize( void) { return( m_uiPayloadLen - m_uiIVLen); } FINLINE FLMBYTE * getAttrIVPtr( void) { if( m_uiPayloadLen <= sizeof( FLMBYTE *)) { flmAssert( !m_uiIVLen); return( NULL); } return( m_pucPayload); } FINLINE FLMBYTE * getAttrPayloadPtr( void) { if( m_uiPayloadLen <= sizeof( FLMBYTE *)) { return( (FLMBYTE *)&m_pucPayload); } return( m_pucPayload); } FINLINE FLMUINT getAttrPayloadSize( void) { return( m_uiPayloadLen); } FINLINE FLMUINT getAttrModeFlags( void) { return( m_uiFlags & FDOM_PERSISTENT_FLAGS); } FINLINE FLMUINT getAttrEncDefId( void) { return( m_uiEncDefId); } FINLINE FLMUINT memSize( void) { FLMUINT uiSize = gv_XFlmSysData.pNodeCacheMgr->m_pAttrItemAllocator->getCellSize(); f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); if( m_uiPayloadLen > sizeof( FLMBYTE *)) { uiSize += gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->getTrueSize( m_uiPayloadLen + sizeof( F_AttrItem *), m_pucPayload - sizeof( F_AttrItem *)); } return( uiSize); } FINLINE FLMUINT getAttrStorageFlags( void) { FLMUINT uiFlags = 0; if( m_uiPayloadLen <= ASF_MAX_EMBEDDED_PAYLOAD_LEN) { uiFlags |= (FLMBYTE)m_uiPayloadLen; } else { uiFlags |= ASF_HAVE_PAYLOAD_LEN_SEN; } if( m_uiPayloadLen) { if( m_uiEncDefId) { uiFlags |= ASF_ENCRYPTED_BIT; } } if( m_uiPrefixId) { uiFlags |= ASF_HAVE_PREFIX_BIT; } if( m_uiFlags & FDOM_READ_ONLY) { uiFlags |= ASF_READ_ONLY_BIT; } if( m_uiFlags & FDOM_CANNOT_DELETE) { uiFlags |= ASF_CANNOT_DELETE_BIT; } return( uiFlags); } void * operator new( FLMSIZET uiSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new( FLMSIZET uiSize, const char * pszFile, int iLine) #if !defined( FLM_NLM) throw() #endif ; void * operator new[]( FLMSIZET uiSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new[]( FLMSIZET uiSize, const char * pszFile, int iLine) #if !defined( FLM_NLM) throw() #endif ; void operator delete( void * ptr); void operator delete[]( void * ptr); private: F_CachedNode * m_pCachedNode; FLMBYTE * m_pucPayload; FLMUINT m_uiPayloadLen; FLMUINT m_uiDataType; FLMUINT m_uiNameId; FLMUINT m_uiFlags; FLMUINT m_uiPrefixId; FLMUINT64 m_ui64QuickVal; FLMUINT m_uiEncDefId; FLMUINT m_uiIVLen; FLMUINT m_uiDecryptedDataLen; friend class F_DOMNode; friend class F_Rfl; friend class F_Db; friend class F_DbRebuild; friend class F_CachedNode; friend class F_NodeCacheMgr; friend class F_NodeRelocator; friend class F_NodeDataRelocator; friend class F_NodeListRelocator; friend class F_AttrListRelocator; friend class F_AttrItemRelocator; friend class F_AttrBufferRelocator; friend class F_NodeInfo; }; /***************************************************************************** Desc: ******************************************************************************/ FINLINE FLMUINT allocOverhead( void) { // Round sizeof( F_CachedNode *) + 1 to nearest 8 byte boundary. return( (sizeof( F_CachedNode *) + 9) & (~((FLMUINT)7))); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE FLMBYTE * getActualPointer( void * pvPtr) { if (pvPtr) { return( (FLMBYTE *)pvPtr - allocOverhead()); } else { return( NULL); } } /***************************************************************************** Desc: ******************************************************************************/ FINLINE FLMUINT calcNodeListBufSize( FLMUINT uiNodeCount) { return( uiNodeCount * sizeof( NODE_ITEM) + allocOverhead()); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE FLMUINT calcAttrListBufSize( FLMUINT uiAttrCount) { return( uiAttrCount * sizeof( F_AttrItem *) + allocOverhead()); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE FLMUINT calcDataBufSize( FLMUINT uiDataSize) { // Leave room for in buffer for a pointer back to the F_CachedNode. // We reserve enough so that the actual allocation will start on // the next eight byte boundary. return( uiDataSize + allocOverhead()); } /***************************************************************************** Desc: ******************************************************************************/ typedef struct { FLMUINT64 ui64NodeId; FLMUINT64 ui64DocumentId; FLMUINT64 ui64ParentId; FLMUINT64 ui64MetaValue; FLMUINT64 ui64FirstChildId; FLMUINT64 ui64LastChildId; FLMUINT64 ui64PrevSibId; FLMUINT64 ui64NextSibId; FLMUINT64 ui64AnnotationId; eDomNodeType eNodeType; FLMUINT uiCollection; FLMUINT uiChildElmCount; FLMUINT uiDataLength; FLMUINT uiDataType; FLMUINT uiPrefixId; FLMUINT uiNameId; FLMUINT uiDataChildCount; FLMUINT uiEncDefId; } F_NODE_INFO; /***************************************************************************** Desc: Cached DOM NODE ******************************************************************************/ class F_CachedNode : public F_CachedItem { public: F_CachedNode(); ~F_CachedNode(); // This method assumes that the node cache mutex has been locked. FINLINE FLMBOOL canBeFreed( void) { return( (!nodeInUse() && !readingInNode() && !nodeIsDirty()) ? TRUE : FALSE); } // This method assumes that the node cache mutex has been locked. FINLINE void freeNode( void) { if (nodePurged()) { freePurged(); } else { freeCache( FALSE); } } FINLINE FLMUINT64 getLowTransId( void) { return( m_ui64LowTransId); } FINLINE FLMUINT64 getHighTransId( void) { return( m_ui64HighTransId); } RCODE getIStream( F_Db * pDb, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiDataType = NULL, FLMUINT * puiDataLength = NULL); RCODE headerToBuf( FLMBOOL bFixedSizeHeader, FLMBYTE * pucBuf, FLMUINT * puiHeaderSize, XFLM_NODE_INFO * pNodeInfo, F_Db * pDb); RCODE readNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, IF_IStream * pIStream, FLMUINT uiOverallLength, FLMBYTE * pucIV); RCODE getRawIStream( F_Db * pDb, IF_PosIStream ** ppIStream); RCODE openPendingInput( F_Db * pDb, FLMUINT uiNewDataType); RCODE flushPendingInput( F_Db * pDb, FLMBOOL bLast); RCODE resizeChildElmList( FLMUINT uiChildElmCount, FLMBOOL bMutexAlreadyLocked); RCODE resizeAttrList( FLMUINT uiAttrCount, FLMBOOL bMutexAlreadyLocked); RCODE resizeDataBuffer( FLMUINT uiSize, FLMBOOL bMutexAlreadyLocked); FINLINE void setNodeAndDataPtr( FLMBYTE * pucActualAlloc) { *((F_CachedNode **)(pucActualAlloc)) = this; m_pucData = pucActualAlloc + allocOverhead(); } FINLINE void setNodeListPtr( FLMBYTE * pucActualAlloc) { *((F_CachedNode **)(pucActualAlloc)) = this; m_pNodeList = (NODE_ITEM *)(pucActualAlloc + allocOverhead()); } FINLINE void setAttrListPtr( FLMBYTE * pucActualAlloc) { *((F_CachedNode **)(pucActualAlloc)) = this; m_ppAttrList = (F_AttrItem **)(pucActualAlloc + allocOverhead()); } FINLINE FLMBYTE * getDataPtr( void) { return( m_pucData); } FINLINE FLMUINT getDataBufSize( void) { return( m_uiDataBufSize); } FINLINE FLMUINT getModeFlags( void) { return( m_uiFlags); } FINLINE FLMUINT getPersistentFlags( void) { return( m_uiFlags & FDOM_PERSISTENT_FLAGS); } FINLINE void setFlags( FLMUINT uiFlags) { m_uiFlags |= uiFlags; } FINLINE void unsetFlags( FLMUINT uiFlags) { m_uiFlags &= ~uiFlags; } FINLINE FLMUINT64 getNodeId( void) { return( m_nodeInfo.ui64NodeId); } FINLINE F_Database * getDatabase( void) { return( m_pDatabase); } FINLINE FLMUINT getCollection( void) { return( m_nodeInfo.uiCollection); } FINLINE eDomNodeType getNodeType( void) { return( m_nodeInfo.eNodeType); } FINLINE void setNodeType( eDomNodeType eNodeType) { m_nodeInfo.eNodeType = eNodeType; } FINLINE FLMUINT getDataType( void) { return( m_nodeInfo.uiDataType); } FINLINE void setDataType( FLMUINT uiDataType) { m_nodeInfo.uiDataType = uiDataType; } FINLINE FLMUINT getNameId( void) { return( m_nodeInfo.uiNameId); } FINLINE void setNameId( FLMUINT uiNameId) { m_nodeInfo.uiNameId = uiNameId; } FINLINE FLMUINT getPrefixId( void) { return( m_nodeInfo.uiPrefixId); } FINLINE void setPrefixId( FLMUINT uiPrefixId) { m_nodeInfo.uiPrefixId = uiPrefixId; } FINLINE FLMUINT64 getDocumentId( void) { return( m_nodeInfo.ui64DocumentId); } FINLINE FLMBOOL isRootNode( void) { return( m_nodeInfo.ui64NodeId == m_nodeInfo.ui64DocumentId ? TRUE : FALSE); } FINLINE void setDocumentId( FLMUINT64 ui64DocumentId) { m_nodeInfo.ui64DocumentId = ui64DocumentId; } FINLINE FLMUINT64 getParentId( void) { return( m_nodeInfo.ui64ParentId); } FINLINE void setParentId( FLMUINT64 ui64ParentId) { m_nodeInfo.ui64ParentId = ui64ParentId; } FINLINE FLMUINT64 getNextSibId( void) { return( m_nodeInfo.ui64NextSibId); } FINLINE void setNextSibId( FLMUINT64 ui64NextSibId) { m_nodeInfo.ui64NextSibId = ui64NextSibId; } FINLINE FLMUINT64 getPrevSibId( void) { return( m_nodeInfo.ui64PrevSibId); } FINLINE void setPrevSibId( FLMUINT64 ui64PrevSibId) { m_nodeInfo.ui64PrevSibId = ui64PrevSibId; } FINLINE void setSibIds( FLMUINT64 ui64PrevSibId, FLMUINT64 ui64NextSibId) { m_nodeInfo.ui64NextSibId = ui64NextSibId; m_nodeInfo.ui64PrevSibId = ui64PrevSibId; } FINLINE FLMUINT64 getFirstChildId( void) { return( m_nodeInfo.ui64FirstChildId); } FINLINE void setFirstChildId( FLMUINT64 ui64FirstChildId) { m_nodeInfo.ui64FirstChildId = ui64FirstChildId; } FINLINE FLMUINT64 getLastChildId( void) { return( m_nodeInfo.ui64LastChildId); } FINLINE void setLastChildId( FLMUINT64 ui64LastChildId) { m_nodeInfo.ui64LastChildId = ui64LastChildId; } FINLINE void setChildIds( FLMUINT64 ui64FirstChildId, FLMUINT64 ui64LastChildId) { m_nodeInfo.ui64FirstChildId = ui64FirstChildId; m_nodeInfo.ui64LastChildId = ui64LastChildId; } FINLINE FLMUINT getDataChildCount( void) { return( m_nodeInfo.uiDataChildCount); } FINLINE void setDataChildCount( FLMUINT uiDataChildCount) { flmAssert( m_nodeInfo.ui64FirstChildId); m_nodeInfo.uiDataChildCount = uiDataChildCount; } FINLINE FLMUINT64 getAnnotationId( void) { return( m_nodeInfo.ui64AnnotationId); } FINLINE void setAnnotationId( FLMUINT64 ui64AnnotationId) { m_nodeInfo.ui64AnnotationId = ui64AnnotationId; } FINLINE FLMBOOL hasAttributes( void) { return( m_uiAttrCount ? TRUE : FALSE); } FINLINE FLMUINT getChildElmCount( void) { flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); return( m_nodeInfo.uiChildElmCount); } FINLINE FLMUINT getChildElmNameId( FLMUINT uiChildElmOffset) { flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); return( m_pNodeList [ uiChildElmOffset].uiNameId); } FINLINE FLMUINT64 getChildElmNodeId( FLMUINT uiChildElmOffset) { flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); return( m_pNodeList [ uiChildElmOffset].ui64NodeId); } FLMBOOL findChildElm( FLMUINT uiChildElmNameId, FLMUINT * puiInsertPos); FINLINE RCODE removeChildElm( FLMUINT uiChildElmOffset) { flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); if (uiChildElmOffset < m_nodeInfo.uiChildElmCount - 1) { f_memmove( &m_pNodeList [ uiChildElmOffset], &m_pNodeList [ uiChildElmOffset + 1], sizeof( NODE_ITEM) * (m_nodeInfo.uiChildElmCount - uiChildElmOffset - 1)); } return( resizeChildElmList( m_nodeInfo.uiChildElmCount - 1, FALSE)); } RCODE insertChildElm( FLMUINT uiChildElmOffset, FLMUINT uiChildElmNameId, FLMUINT64 ui64ChildElmNodeId); FINLINE FLMUINT getDataLength( void) { return( m_nodeInfo.uiDataLength); } FINLINE void setDataLength( FLMUINT uiDataLength) { m_nodeInfo.uiDataLength = uiDataLength; } FINLINE FLMUINT getEncDefId( void) { return( m_nodeInfo.uiEncDefId); } FINLINE void setEncDefId( FLMUINT uiEncDefId) { m_nodeInfo.uiEncDefId = uiEncDefId; } FINLINE FLMBOOL getQuickNumber64( FLMUINT64 * pui64Num, FLMBOOL * pbNeg) { if (m_uiFlags & FDOM_UNSIGNED_QUICK_VAL) { *pui64Num = m_numberVal.ui64Val; *pbNeg = FALSE; return( TRUE); } if (m_uiFlags & FDOM_SIGNED_QUICK_VAL) { if( m_numberVal.i64Val < 0) { *pui64Num = (FLMUINT64)(-m_numberVal.i64Val); *pbNeg = TRUE; } else { *pui64Num = (FLMUINT64)m_numberVal.i64Val; *pbNeg = FALSE; } return( TRUE); } *pui64Num = 0; *pbNeg = FALSE; return( FALSE); } FINLINE FLMINT64 getQuickINT64( void) { flmAssert( m_uiFlags & FDOM_SIGNED_QUICK_VAL); return( m_numberVal.i64Val); } FINLINE FLMUINT64 getQuickUINT64( void) { flmAssert( m_uiFlags & FDOM_UNSIGNED_QUICK_VAL); return( m_numberVal.ui64Val); } FINLINE void setUINT64( FLMUINT64 ui64Value) { m_numberVal.ui64Val = ui64Value; m_uiFlags &= ~FDOM_SIGNED_QUICK_VAL; m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL; } FINLINE void setINT64( FLMINT64 i64Value) { m_numberVal.i64Val = i64Value; m_uiFlags &= ~FDOM_UNSIGNED_QUICK_VAL; m_uiFlags |= FDOM_SIGNED_QUICK_VAL; } FINLINE void setMetaValue( FLMUINT64 ui64Value) { m_nodeInfo.ui64MetaValue = ui64Value; } FINLINE FLMUINT64 getMetaValue( void) { return( m_nodeInfo.ui64MetaValue); } FINLINE FLMUINT getOffsetIndex( void) { return( m_uiOffsetIndex); } FINLINE void setOffsetIndex( FLMUINT uiOffsetIndex) { m_uiOffsetIndex = uiOffsetIndex; } FINLINE FLMUINT32 getBlkAddr( void) { return( m_ui32BlkAddr); } FINLINE void setBlkAddr( FLMUINT32 ui32BlkAddr) { m_ui32BlkAddr = ui32BlkAddr; } FINLINE FLMBOOL isRightVersion( FLMUINT64 ui64TransId) { return( (ui64TransId >= m_ui64LowTransId && ui64TransId <= m_ui64HighTransId) ? TRUE : FALSE); } // Generally, assumes that the node cache mutex has already been locked. // There is one case where it is not locked, but it is not // critical that it be locked - inside syncFromDb. FINLINE FLMBOOL nodePurged( void) { return( (m_uiCacheFlags & NCA_PURGED ) ? TRUE : FALSE); } #ifdef FLM_DEBUG void checkReadFromDisk( F_Db * pDb); #endif void * operator new( FLMSIZET uiSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new( FLMSIZET uiSize, const char * pszFile, int iLine) #if !defined( FLM_NLM) throw() #endif ; void * operator new[]( FLMSIZET uiSize) #if !defined( FLM_NLM) throw() #endif ; void * operator new[]( FLMSIZET uiSize, const char * pszFile, int iLine) #if !defined( FLM_NLM) throw() #endif ; void operator delete( void * ptr); void operator delete[]( void * ptr); // Assumes that the node cache mutex has already been locked. FINLINE void incrNodeUseCount( void) { m_uiCacheFlags = (m_uiCacheFlags & (~(NCA_COUNTER_BITS))) | (((m_uiCacheFlags & NCA_COUNTER_BITS) + 1)); } // Assumes that the node cache mutex has already been locked. FINLINE void decrNodeUseCount( void) { m_uiCacheFlags = (m_uiCacheFlags & (~(NCA_COUNTER_BITS))) | (((m_uiCacheFlags & NCA_COUNTER_BITS) - 1)); } // Assumes that the node cache mutex has already been locked. FINLINE void incrStreamUseCount( void) { m_uiStreamUseCount++; } // Assumes that the node cache mutex has already been locked. FINLINE void decrStreamUseCount( void) { flmAssert( m_uiStreamUseCount); m_uiStreamUseCount--; } FINLINE FLMUINT getStreamUseCount( void) { return( m_uiStreamUseCount); } void setNodeDirty( F_Db * pDb, FLMBOOL bNew); void unsetNodeDirtyAndNew( F_Db * pDb, FLMBOOL bMutexAlreadyLocked = FALSE); FINLINE FLMBOOL nodeIsDirty( void) { return( (m_uiFlags & FDOM_DIRTY) ? TRUE : FALSE); } FINLINE FLMBOOL nodeIsNew( void) { return( (m_uiFlags & FDOM_NEW) ? TRUE : FALSE); } // Assumes that the node cache mutex has already been locked. FINLINE FLMBOOL nodeUncommitted( void) { return( (m_uiCacheFlags & NCA_UNCOMMITTED ) ? TRUE : FALSE); } void freeCache( FLMBOOL bPutInPurgeList); // Attribute functions RCODE setNumber64( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 ui64Value, FLMBOOL bNeg, FLMUINT uiEncDefId); RCODE getNumber64( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 * pui64Value, FLMBOOL * pbNeg); RCODE setUTF8( F_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiNumBytesInValue, FLMUINT uiNumCharsInValue, FLMUINT uiEncDefId); RCODE setBinary( F_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiNumBytesInBuffer, FLMUINT uiEncDefId); RCODE getBinary( F_Db * pDb, FLMUINT uiAttrNameId, void * pvBuffer, FLMUINT uiBufferLen, FLMUINT * puiDataLen); RCODE setStorageValue( F_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiValueLen, FLMUINT uiEncDefId); F_AttrItem * getAttribute( FLMUINT uiAttrNameId, FLMUINT * puiInsertPos); FINLINE F_AttrItem * getFirstAttribute( void) { return( m_uiAttrCount ? m_ppAttrList [0] : NULL); } FINLINE F_AttrItem * getLastAttribute( void) { return( m_uiAttrCount ? m_ppAttrList [m_uiAttrCount - 1] : NULL); } RCODE getPrevSiblingNode( FLMUINT uiCurrentNameId, IF_DOMNode ** ppSib); RCODE getNextSiblingNode( FLMUINT uiCurrentNameId, IF_DOMNode ** ppSib); RCODE allocAttribute( F_Db * pDb, FLMUINT uiAttrNameId, F_AttrItem * pSrcAttrItem, FLMUINT uiInsertPos, F_AttrItem ** ppAttrItem, FLMBOOL bMutexAlreadyLocked); RCODE freeAttribute( F_AttrItem * pAttrItem, FLMUINT uiPos); RCODE createAttribute( F_Db * pDb, FLMUINT uiAttrNameId, F_AttrItem ** ppAttrItem); RCODE removeAttribute( F_Db * pDb, FLMUINT uiAttrNameId); RCODE exportAttributeList( F_Db * pDb, F_DynaBuf * pDynaBuf, XFLM_NODE_INFO * pNodeInfo); RCODE addModeFlags( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiFlags); RCODE removeModeFlags( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiFlags); RCODE setPrefixId( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiPrefixId); FINLINE F_AttrItem * getPrevSibling( FLMUINT uiAttrNameId) { F_AttrItem * pAttrItem; FLMUINT uiPos; if ((pAttrItem = getAttribute( uiAttrNameId, &uiPos)) != NULL) { pAttrItem = uiPos ? m_ppAttrList [uiPos - 1] : NULL; } return( pAttrItem); } FINLINE F_AttrItem * getNextSibling( FLMUINT uiAttrNameId) { F_AttrItem * pAttrItem; FLMUINT uiPos; if( (pAttrItem = getAttribute( uiAttrNameId, &uiPos)) != NULL) { pAttrItem = uiPos < m_uiAttrCount - 1 ? m_ppAttrList [uiPos + 1] : NULL; } return( pAttrItem); } FINLINE FLMUINT getModeFlags( FLMUINT uiAttrNameId) { F_AttrItem * pAttrItem; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { return( 0); } return( pAttrItem->getAttrModeFlags()); } FINLINE RCODE getPrefixId( FLMUINT uiAttrNameId, FLMUINT * puiPrefixId) { F_AttrItem * pAttrItem; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { return( RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND)); } *puiPrefixId = pAttrItem->m_uiPrefixId; return( NE_XFLM_OK); } FINLINE RCODE getDataLength( FLMUINT uiAttrNameId, FLMUINT * puiDataLength) { F_AttrItem * pAttrItem; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { return( RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND)); } *puiDataLength = pAttrItem->getAttrDataLength(); return( NE_XFLM_OK); } FINLINE RCODE getDataType( FLMUINT uiAttrNameId, FLMUINT * puiDataType) { F_AttrItem * pAttrItem; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { return( RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND)); } *puiDataType = pAttrItem->m_uiDataType; return( NE_XFLM_OK); } FINLINE RCODE getEncDefId( FLMUINT uiAttrNameId, FLMUINT * puiEncDefId) { F_AttrItem * pAttrItem; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { return( RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND)); } *puiEncDefId = pAttrItem->m_uiEncDefId; return( NE_XFLM_OK); } RCODE getIStream( F_Db * pDb, FLMUINT uiAttrNameId, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiDataType = NULL, FLMUINT * puiDataLength = NULL); private: // Assumes that the node cache mutex has already been locked. FINLINE FLMBOOL readingInNode( void) { return( (m_uiCacheFlags & NCA_READING_IN ) ? TRUE : FALSE); } // Assumes that the node cache mutex has already been locked. FINLINE void setReadingIn( void) { m_uiCacheFlags |= NCA_READING_IN; } // Assumes that the node cache mutex has already been locked. FINLINE void unsetReadingIn( void) { m_uiCacheFlags &= (~(NCA_READING_IN)); } // Assumes that the node cache mutex has already been locked. FINLINE void setUncommitted( void) { m_uiCacheFlags |= NCA_UNCOMMITTED; } // Assumes that the node cache mutex has already been locked. FINLINE void unsetUncommitted( void) { m_uiCacheFlags &= (~(NCA_UNCOMMITTED)); } // Assumes that the node cache mutex has already been locked. FINLINE FLMBOOL nodeIsLatestVer( void) { return( (m_uiCacheFlags & NCA_LATEST_VER ) ? TRUE : FALSE); } // Assumes that the node cache mutex has already been locked. FINLINE void setLatestVer( void) { m_uiCacheFlags |= NCA_LATEST_VER; } // Assumes that the node cache mutex has already been locked. FINLINE void unsetLatestVer( void) { m_uiCacheFlags &= (~(NCA_LATEST_VER)); } // Assumes that the node cache mutex has already been locked. FINLINE void setPurged( void) { m_uiCacheFlags |= NCA_PURGED; } // Assumes that the node cache mutex has already been locked. FINLINE void unsetPurged( void) { m_uiCacheFlags &= (~(NCA_PURGED)); } // Assumes that the node cache mutex has already been locked. FINLINE FLMBOOL nodeLinkedToDatabase( void) { return( (m_uiCacheFlags & NCA_LINKED_TO_DATABASE ) ? TRUE : FALSE); } // Assumes that the node cache mutex has already been locked. FINLINE void setLinkedToDatabase( void) { m_uiCacheFlags |= NCA_LINKED_TO_DATABASE; } // Assumes that the node cache mutex has already been locked. FINLINE void unsetLinkedToDatabase( void) { m_uiCacheFlags &= (~(NCA_LINKED_TO_DATABASE)); } // Assumes that the node cache mutex has already been locked. FINLINE FLMBOOL nodeInUse( void) { return( (m_uiCacheFlags & NCA_COUNTER_BITS ) ? TRUE : FALSE); } // Unlink a node from the global purged list. // Assumes that the node cache mutex has already been locked. FINLINE void unlinkFromPurged( void) { if (m_pNextInGlobal) { m_pNextInGlobal->m_pPrevInGlobal = m_pPrevInGlobal; } if (m_pPrevInGlobal) { m_pPrevInGlobal->m_pNextInGlobal = m_pNextInGlobal; } else { gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList = (F_CachedNode *)m_pNextInGlobal; } m_pPrevInGlobal = NULL; m_pNextInGlobal = NULL; } // Link a node to an F_Database list at the head of the list. // Assumes that the node cache mutex has already been locked. FINLINE void linkToDatabaseAtHead( F_Database * pDatabase) { if (!pDatabase->m_pLastDirtyNode || nodeIsDirty()) { m_pPrevInDatabase = NULL; if ((m_pNextInDatabase = pDatabase->m_pFirstNode) != NULL) { pDatabase->m_pFirstNode->m_pPrevInDatabase = this; } else { pDatabase->m_pLastNode = this; } pDatabase->m_pFirstNode = this; if (nodeIsDirty() && !pDatabase->m_pLastDirtyNode) { pDatabase->m_pLastDirtyNode = this; } } else { // pDatabase->m_pLastDirtyNode is guaranteed to be non-NULL, // Hence, m_pPrevInDatabase will be non-NULL. // We are also guaranteed that the node is not dirty. m_pPrevInDatabase = pDatabase->m_pLastDirtyNode; m_pNextInDatabase = m_pPrevInDatabase->m_pNextInDatabase; m_pPrevInDatabase->m_pNextInDatabase = this; if (m_pNextInDatabase) { m_pNextInDatabase->m_pPrevInDatabase = this; } else { pDatabase->m_pLastNode = this; } } m_pDatabase = pDatabase; setLinkedToDatabase(); } // Link a node to an F_Database list at the end of the list. // Assumes that the node cache mutex has already been locked. FINLINE void linkToDatabaseAtEnd( F_Database * pDatabase) { // Node cannot be a dirty node. flmAssert( !nodeIsDirty()); m_pNextInDatabase = NULL; if( (m_pPrevInDatabase = pDatabase->m_pLastNode) != NULL) { pDatabase->m_pLastNode->m_pNextInDatabase = this; } else { pDatabase->m_pFirstNode = this; } pDatabase->m_pLastNode = this; m_pDatabase = pDatabase; setLinkedToDatabase(); } // Unlink a node from its F_Database list. // Assumes that the node cache mutex has already been locked. FINLINE void unlinkFromDatabase( void) { if( nodeLinkedToDatabase()) { // If this is the last dirty node, change the database's // last dirty pointer to point to the previous node, if any. if (m_pDatabase->m_pLastDirtyNode == this) { flmAssert( nodeIsDirty()); m_pDatabase->m_pLastDirtyNode = m_pPrevInDatabase; } // Remove the node from the database's list. if( m_pNextInDatabase) { m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; } else { m_pDatabase->m_pLastNode = m_pPrevInDatabase; } if( m_pPrevInDatabase) { m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; } else { m_pDatabase->m_pFirstNode = m_pNextInDatabase; } m_pPrevInDatabase = NULL; m_pNextInDatabase = NULL; m_pDatabase = NULL; unsetLinkedToDatabase(); } } // Link a node into its hash bucket. // Assumes that the node cache mutex has already been locked. FINLINE void linkToHashBucket( void) { F_CachedNode ** ppHashBucket = gv_XFlmSysData.pNodeCacheMgr->nodeHash( m_nodeInfo.ui64NodeId); flmAssert( m_pNewerVersion == NULL); m_pPrevInBucket = NULL; if ((m_pNextInBucket = *ppHashBucket) != NULL) { m_pNextInBucket->m_pPrevInBucket = this; } *ppHashBucket = this; } // Unlink a node from its hash bucket. // Assumes that the node cache mutex has already been locked. FINLINE void unlinkFromHashBucket( void) { flmAssert( m_pNewerVersion == NULL); if (m_pNextInBucket) { m_pNextInBucket->m_pPrevInBucket = m_pPrevInBucket; } if (m_pPrevInBucket) { m_pPrevInBucket->m_pNextInBucket = m_pNextInBucket; } else { F_CachedNode ** ppHashBucket = gv_XFlmSysData.pNodeCacheMgr->nodeHash( m_nodeInfo.ui64NodeId); *ppHashBucket = m_pNextInBucket; } m_pPrevInBucket = NULL; m_pNextInBucket = NULL; } // Unlink a node from its version list. // Assumes that the node cache mutex has already been locked. FINLINE void linkToVerList( F_CachedNode * pNewerVer, F_CachedNode * pOlderVer) { if( (m_pNewerVersion = pNewerVer) != NULL) { pNewerVer->m_pOlderVersion = this; } if ((m_pOlderVersion = pOlderVer) != NULL) { pOlderVer->m_pNewerVersion = this; } } // Unlink a node from its version list. This routine // Assumes that the node cache mutex has already been locked. FINLINE void unlinkFromVerList( void) { if (m_pNewerVersion) { m_pNewerVersion->m_pOlderVersion = m_pOlderVersion; } if (m_pOlderVersion) { m_pOlderVersion->m_pNewerVersion = m_pNewerVersion; } m_pNewerVersion = NULL; m_pOlderVersion = NULL; } // Link a node into the heap list // Assumes that the node cache mutex has already been locked. FINLINE void linkToHeapList( void) { flmAssert( !m_pPrevInHeapList); flmAssert( (m_uiFlags & FDOM_HEAP_ALLOC) == 0); if( (m_pNextInHeapList = gv_XFlmSysData.pNodeCacheMgr->m_pHeapList) != NULL) { gv_XFlmSysData.pNodeCacheMgr->m_pHeapList->m_pPrevInHeapList = this; } gv_XFlmSysData.pNodeCacheMgr->m_pHeapList = this; m_uiFlags |= FDOM_HEAP_ALLOC; } // Unlink a node from the heap list // Assumes that the node cache mutex has already been locked. FINLINE void unlinkFromHeapList( void) { flmAssert( m_uiFlags & FDOM_HEAP_ALLOC); if (m_pNextInHeapList) { m_pNextInHeapList->m_pPrevInHeapList = m_pPrevInHeapList; } if (m_pPrevInHeapList) { m_pPrevInHeapList->m_pNextInHeapList = m_pNextInHeapList; } else { gv_XFlmSysData.pNodeCacheMgr->m_pHeapList = m_pNextInHeapList; } m_pPrevInHeapList = NULL; m_pNextInHeapList = NULL; m_uiFlags &= ~FDOM_HEAP_ALLOC; } // Assumes that the node cache mutex has already been locked. FINLINE void linkToOldList( void) { flmAssert( !m_pPrevInOldList); if( (m_pNextInOldList = gv_XFlmSysData.pNodeCacheMgr->m_pOldList) != NULL) { gv_XFlmSysData.pNodeCacheMgr->m_pOldList->m_pPrevInOldList = this; } gv_XFlmSysData.pNodeCacheMgr->m_pOldList = this; } // Assumes that the node cache mutex has already been locked. FINLINE void unlinkFromOldList( void) { if (m_pNextInOldList) { m_pNextInOldList->m_pPrevInOldList = m_pPrevInOldList; } if (m_pPrevInOldList) { m_pPrevInOldList->m_pNextInOldList = m_pNextInOldList; } else { gv_XFlmSysData.pNodeCacheMgr->m_pOldList = m_pNextInOldList; } m_pPrevInOldList = NULL; m_pNextInOldList = NULL; } FINLINE FLMUINT memSize( void) { FLMUINT uiSize = gv_XFlmSysData.pNodeCacheMgr->m_pNodeAllocator->getCellSize(); f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); if (m_pucData) { uiSize += gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->getTrueSize( m_uiDataBufSize, getActualPointer( m_pucData)); } if( m_pNodeList) { uiSize += gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->getTrueSize( calcNodeListBufSize( m_nodeInfo.uiChildElmCount), getActualPointer( m_pNodeList)); } if( m_ppAttrList) { uiSize += gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->getTrueSize( calcAttrListBufSize( m_uiAttrCount), getActualPointer( m_ppAttrList)); } return( uiSize + m_uiTotalAttrSize); } // Assumes that the node cache mutex is locked, because // it potentially updates the cache usage statistics. FINLINE void setTransID( FLMUINT64 ui64NewTransID) { FLMUINT uiSize; if (m_ui64HighTransId == FLM_MAX_UINT64 && ui64NewTransID != FLM_MAX_UINT64) { uiSize = memSize(); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount++; linkToOldList(); } else if (m_ui64HighTransId != FLM_MAX_UINT64 && ui64NewTransID == FLM_MAX_UINT64) { uiSize = memSize(); flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize); flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount--; unlinkFromOldList(); } m_ui64HighTransId = ui64NewTransID; } void freePurged( void); void linkToDatabase( F_Database * pDatabase, F_Db * pDb, FLMUINT64 ui64LowTransId, FLMBOOL bMostCurrent); RCODE importAttributeList( F_Db * pDb, IF_IStream * pIStream, FLMBOOL bMutexAlreadyLocked); RCODE importAttributeList( F_Db * pDb, F_CachedNode * pSourceNode, FLMBOOL bMutexAlreadyLocked); void resetNode( void); typedef union { FLMUINT64 ui64Val; FLMINT64 i64Val; } FLMNUMBER; // Things that manage the node's place in node cache. These are // always initialized in the constructor F_CachedNode * m_pPrevInBucket; F_CachedNode * m_pNextInBucket; F_CachedNode * m_pPrevInDatabase; F_CachedNode * m_pNextInDatabase; F_CachedNode * m_pOlderVersion; F_CachedNode * m_pNewerVersion; F_CachedNode * m_pPrevInHeapList; F_CachedNode * m_pNextInHeapList; F_CachedNode * m_pPrevInOldList; F_CachedNode * m_pNextInOldList; FLMUINT64 m_ui64LowTransId; FLMUINT64 m_ui64HighTransId; F_NOTIFY_LIST_ITEM * m_pNotifyList; FLMUINT m_uiCacheFlags; FLMUINT m_uiStreamUseCount; // Things we hash on - initialized by caller of constructor F_Database * m_pDatabase; // Items initialized in constructor F_NODE_INFO m_nodeInfo; FLMUINT m_uiFlags; FLMBYTE * m_pucData; FLMUINT m_uiDataBufSize; NODE_ITEM * m_pNodeList; F_AttrItem ** m_ppAttrList; FLMUINT m_uiAttrCount; FLMUINT m_uiTotalAttrSize; // Items initialized by caller of constructor, but not // in constructor - for performance reasons - so we don't // end up setting them twice. FLMUINT m_uiOffsetIndex; FLMUINT32 m_ui32BlkAddr; // Items that m_uiFlags indicates whether they are present FLMNUMBER m_numberVal; // Valid only if FDOM_SIGNED_QUICK_VAL // or FDOM_UNSIGNED_QUICK_VAL is set on m_uiFlags friend class F_NodeCacheMgr; friend class F_GlobalCacheMgr; friend class F_Database; friend class F_Db; friend class F_DbSystem; friend class F_BTreeIStream; friend class F_LocalNodeCache; friend class F_DOMNode; friend class F_Rfl; friend class F_NodeInfo; friend class F_RebuildNodeIStream; friend class F_DbRebuild; friend class F_AttrItem; friend class F_NodeRelocator; friend class F_NodeDataRelocator; friend class F_NodeListRelocator; friend class F_AttrListRelocator; friend class F_AttrItemRelocator; friend class F_AttrBufferRelocator; }; RCODE flmReadNodeInfo( FLMUINT uiCollection, FLMUINT64 ui64NodeId, IF_IStream * pIStream, FLMUINT uiOverallLength, FLMBOOL bAssertOnCorruption, F_NODE_INFO * pNodeInfo, FLMUINT * puiStorageFlags, FLMBOOL * pbFixedSizeHeader = NULL); #endif // FCACHE_H libxflaim-5.1.969/src/fquery.h0000644000175000017500000002250510511001742017514 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains defines, structures and prototypes for FLAIM cursors. // // Tabs: 3 // // Copyright (c) 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fquery.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FQUERY_H #define FQUERY_H #define FLM_MAX_POS_KEYS 1000 #define FLM_MIN_POS_KEYS 250 #define FLM_ADDR_GROW_SIZE 100 #define FLM_KEYS_GROW_SIZE 100 /**************************************************************************** Macro: Desc: Macros used for determining the nature and precedence of OP codes. ****************************************************************************/ class F_DataVector; class FSIndexCursor; class FSDataCursor; class F_CollIStream; typedef struct QueryValue * FQVALUE_p; typedef struct QueryXPathPath * FXPATH_p; typedef struct XPathComponent * XPATH_COMPONENT_p; typedef struct QueryExpr * FQEXPR_p; typedef struct QueryNode * FQNODE_p; typedef enum QueryNodeTypes { FLM_OPERATOR_NODE = 0, FLM_VALUE_NODE, FLM_XPATH_NODE, FLM_FUNCTION_NODE } eNodeTypes; /**************************************************************************** Structures used for the query tree and other stuff ****************************************************************************/ typedef struct QueryValue { eValTypes eValType; FLMUINT uiFlags; #define VAL_IS_STREAM 0x0001 #define VAL_IS_CONSTANT 0x0002 // During query evaluation, this indicates // that this value is a constant. If it // is a FLM_UTF8_VAL, then asterisks will // be treated as a wildcard, unless // escaped (\*). If the value is NOT // a constant, the asterisk is NEVER // treated as a wildcard, and the // backslash is NEVER treated as an // escape character. #define VAL_HAS_WILDCARDS 0x0004 // This is only set if the value is a // constant, FLM_UTF8_VAL, that has // wildcards. FLMUINT uiDataLen; // Length in bytes if the type is text // or binary union { XFlmBoolType eBool; FLMUINT uiVal; FLMUINT64 ui64Val; FLMINT iVal; FLMINT64 i64Val; FLMBYTE * pucBuf; IF_PosIStream * pIStream; } val; // Holds or points to the atom value. } FQVALUE; /*************************************************************************** Desc: Can two values be compared? ***************************************************************************/ FINLINE FLMBOOL fqCanCompare( FQVALUE * pValue1, FQVALUE * pValue2 ) { if (!pValue1 || !pValue2 || pValue1->eValType == pValue2->eValType) { return( TRUE); } else { switch (pValue1->eValType) { case XFLM_UINT_VAL: case XFLM_UINT64_VAL: case XFLM_INT_VAL: case XFLM_INT64_VAL: return( (FLMBOOL)(pValue2->eValType == XFLM_UINT_VAL || pValue2->eValType == XFLM_UINT64_VAL || pValue2->eValType == XFLM_INT_VAL || pValue2->eValType == XFLM_INT64_VAL ? TRUE : FALSE)); default: return( FALSE); } } } typedef struct QueryExpr { FQNODE_p pExpr; FQEXPR_p pNext; FQEXPR_p pPrev; } FQEXPR; typedef struct PathPred * PATH_PRED_p; typedef struct PathPredNode * PATH_PRED_NODE_p; typedef struct XPathComponent { FLMBOOL bIsSource; // Indicates component is query source PATH_PRED_p pOptPred; // Optimization predicate IF_DOMNode * pCurrNode; // Used when evaluating expressions IF_DOMNode * pKeyNode; XPATH_COMPONENT_p pXPathContext; XPATH_COMPONENT_p pNext; XPATH_COMPONENT_p pPrev; FQNODE_p pXPathNode; eXPathAxisTypes eXPathAxis; eDomNodeType eNodeType; IF_QueryNodeSource * pNodeSource; FLMUINT uiDictNum; FLMUINT uiContextPosNeeded; FQNODE_p pContextPosExpr; FQNODE_p pExpr; FQNODE_p pExprXPathSource; // XPATH node that is expression's source } XPATH_COMPONENT; typedef struct QueryXPath { FLMBOOL bGettingNodes; // Used when evaluating expressions FLMBOOL bIsSource; // Indicates XPATH is query source XPATH_COMPONENT_p pSourceComponent; // Used when XPATH is query source FLMBOOL bHavePassingNode; XPATH_COMPONENT_p pFirstComponent; XPATH_COMPONENT_p pLastComponent; } FXPATH; typedef struct PathPredNode { FQNODE_p pXPathNode; PATH_PRED_NODE_p pNext; } PATH_PRED_NODE; typedef struct PathPred { PATH_PRED_NODE * pXPathNodeList;// List of XPATHs sharing this predicate. eQueryOperators eOperator; // Operator of the predicate FLMUINT uiCompareRules;// Comparison rules IF_OperandComparer * pOpComparer; // Function to perform comparison FQNODE_p pContextNode; // Context node for this predicate, if // any FLMBOOL bNotted; // Has operator been notted? FQVALUE * pFromValue; // Points to FQVALUE that has the FROM value for // this predicate. Will be NULL for unary // operators such as exists FLMBOOL bInclFrom; // Flag indicating if the from value is // inclusive. FQVALUE * pUntilValue; // Points to FQValue that has the UNTIL value // for this predicate. FLMBOOL bInclUntil; // Flag indicating if until value is // inclusive. XFLM_OPT_INFO OptInfo; // Optimization information. FSIndexCursor * pFSIndexCursor;// Used if OptInfo.eOptType is // QOPT_USING_INDEX FSCollectionCursor * pFSCollectionCursor;// Used if OptInfo.eOptType is // QOPT_USING_NODE_ID IF_QueryNodeSource * pNodeSource; // Used if OptInfo.eOptType is // QOPT_USING_APP_SOURCE PATH_PRED_p pNext; PATH_PRED_p pPrev; } PATH_PRED; typedef struct CONTEXT_PATH { XPATH_COMPONENT * pXPathComponent; FLMUINT uiCost; FLMBOOL bMustScan; PATH_PRED * pSelectedPred; // Only used for intersect contexts PATH_PRED * pFirstPred; PATH_PRED * pLastPred; CONTEXT_PATH * pNext; CONTEXT_PATH * pPrev; } CONTEXT_PATH; typedef struct OP_CONTEXT { FLMBOOL bIntersect; FLMBOOL bMustScan; FLMBOOL bForceOptToScan; FQNODE_p pQRootNode; // Root node of this context. FLMUINT uiCost; OP_CONTEXT * pSelectedChild; // Only used for intersect contexts CONTEXT_PATH * pSelectedPath; // Only used for intersect contexts OP_CONTEXT * pParent; OP_CONTEXT * pFirstChild; OP_CONTEXT * pLastChild; OP_CONTEXT * pNextSib; OP_CONTEXT * pPrevSib; CONTEXT_PATH * pFirstPath; CONTEXT_PATH * pLastPath; } OP_CONTEXT; typedef struct QueryFunction { eQueryFunctions eFunction; IF_QueryValFunc * pFuncObj; FQEXPR_p pFirstArg; FQEXPR_p pLastArg; } FQFUNCTION; typedef struct QueryOperator { eQueryOperators eOperator; FLMUINT uiCompareRules; IF_OperandComparer * pOpComparer; } FQOPERATOR; typedef struct QueryNode { eNodeTypes eNodeType; // Type of node this is FLMUINT uiNestLevel; // Nesting level of node - only used when // setting up the query OP_CONTEXT * pContext; FQVALUE currVal; // Current value - used during evaluation // and for value types FLMBOOL bUsedValue; // Used during evaluation FLMBOOL bLastValue; // Used during evaluation FLMBOOL bNotted; FQNODE_p pParent; // Parent of this query node FQNODE_p pPrevSib; // Previous sibling of this query node FQNODE_p pNextSib; // Next sibling of this query node FQNODE_p pFirstChild; // First child of this query node FQNODE_p pLastChild; // Last child of this query node union { FQOPERATOR op; FQFUNCTION * pQFunction; FXPATH * pXPath; } nd; } FQNODE; RCODE fqCompare( // fqeval.cpp FQVALUE * pValue1, FQVALUE * pValue2, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FLMUINT uiLanguage, FLMINT * piCmp); RCODE fqCompareOperands( // fqeval.cpp FLMUINT uiLanguage, FQVALUE * pLValue, FQVALUE * pRValue, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FLMBOOL bNotted, XFlmBoolType * peBool); RCODE fqArithmeticOperator( // fqeval.cpp FQVALUE * pLValue, FQVALUE * pRValue, eQueryOperators eOperator, FQVALUE * pResult); RCODE fqCompareCollStreams( // fqeval.cpp F_CollIStream * pLStream, F_CollIStream * pRStream, FLMBOOL bOpIsMatch, FLMUINT uiLanguage, FLMINT * piResult); RCODE flmBuildFromAndUntilKeys( // kybldkey.cpp IXD * pIxd, PATH_PRED * pPred, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, F_DataVector * pUntilSearchKey, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbDoNodeMatch, FLMBOOL * pbCanCompareOnKey); #endif // FQUERY_H libxflaim-5.1.969/src/fltrcmit.cpp0000644000175000017500000004031510511001742020357 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains routines for committing a transaction. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fltrcmit.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: This routine commits an active transaction for a particular database. ****************************************************************************/ RCODE F_Db::commitTrans( FLMUINT uiNewLogicalEOF, // New logical end-of-file. This is only // set by the reduceSize function when // it is truncating the file. FLMBOOL bForceCheckpoint, // Force a checkpoint? FLMBOOL * pbEmpty ) { RCODE rc = NE_XFLM_OK; XFLM_DB_HDR * pUncommittedDbHdr; FLMUINT uiCPFileNum = 0; FLMUINT uiCPOffset = 0; FLMUINT64 ui64TransId = 0; FLMBOOL bTransEndLogged; FLMBOOL bForceCloseOnError = FALSE; eDbTransType eSaveTransType; FLMBOOL bOkToLogAbort = TRUE; FLMUINT uiCollection; FLMUINT64 ui64DocId; FLMUINT64 ui64NodeId; FLMBOOL bIndexAfterCommit = FALSE; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; F_COLLECTION * pCollection; FLMUINT uiLfNum; // Not allowed to commit temporary databases. flmAssert( !m_pDatabase->m_bTempDb); // See if we even have a transaction going. if (m_eTransType == XFLM_NO_TRANS) { goto Exit; // Will return NE_XFLM_OK. } // See if we have a transaction going which should be aborted. if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit1; } // If we are in a read transaction we can skip most of the stuff // below because no updates would have occurred. This will help // improve performance. if (m_eTransType == XFLM_READ_TRANS) { if (m_bKrefSetup) { // krefCntrlFree could be called w/o checking bKrefSetup because // it checks the flag, but it is more optimal to check the // flag before making the call because most of the time it will // be false. krefCntrlFree(); } goto Exit1; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Disable DB header writes pRfl->clearDbHdrs(); // At this point, we know we have an update transaction. ui64TransId = m_ui64CurrTransID; #ifdef FLM_DBG_LOG flmDbgLogUpdate( m_pDatabase, ui64TransId, 0, 0, NE_XFLM_OK, "TCmit"); #endif // End any pending input operations if( m_pDatabase->m_pPendingInput) { rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); goto Exit1; } // Call documentDone for any documents in the document list for (;;) { m_pDatabase->m_DocumentList.getNode( 0, &uiCollection, &ui64DocId, &ui64NodeId); if (!uiCollection) { break; } if (RC_BAD( rc = documentDone( uiCollection, ui64DocId))) { goto Exit1; } } // Flush any dirty cache nodes if( RC_BAD( rc = flushDirtyNodes())) { goto Exit1; } // Write out any LFILE changes for collections flmAssert( m_pDict); // Only need to do collections. Nothing from the LFH of // an index is stored in memory except for the root block // address, and whenever that is changed, we get a new // dictionary. Since the new dictionary will be discarded // in that case, there is nothing to restore for an index. uiLfNum = 0; while ((pCollection = m_pDict->getNextCollection( uiLfNum, TRUE)) != NULL) { if (pCollection->bNeedToUpdateNodes) { if (RC_BAD( rc = m_pDatabase->lFileWrite( this, pCollection, &pCollection->lfInfo))) { goto Exit; } } uiLfNum = pCollection->lfInfo.uiLfNum; } // Commit any keys in the KREF buffers. if (RC_BAD( rc = keysCommit( TRUE))) { flmLogError( rc, "calling keysCommit from commitTrans"); goto Exit1; } // If the transaction had no update operations, restore it // to its pre-transaction state - make it appear that no // transaction ever happened. if (!m_bHadUpdOper) { bOkToLogAbort = FALSE; pRfl->enableLogging( &uiRflToken); rc = pRfl->logEndTransaction( this, RFL_TRNS_COMMIT_PACKET, TRUE); // Even though we didn't have any update operations, there may have // been operations during the transaction (i.e., query operations) // that initialized the KREF in order to generate keys. krefCntrlFree(); // Restore everything as if the transaction never happened. if (pbEmpty) { *pbEmpty = TRUE; } // Even if the transaction is empty, there could be "uncommitted" nodes that // were created, but never set to "dirty" - hence the m_bHadUpdOper flag // would never have been set. So we need to call freeModifiedNodes to get // rid of them. m_pDatabase->freeModifiedNodes( this, ui64TransId - 1); goto Exit1; } // Re-enable RFL logging pRfl->enableLogging( &uiRflToken); // Log commit record to roll-forward log bOkToLogAbort = FALSE; if (RC_BAD( rc = pRfl->logEndTransaction( this, RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged))) { goto Exit1; } bForceCloseOnError = TRUE; // Reinitialize the log header. If the local dictionary was updated // during the transaction, increment the local dictionary ID so that // other concurrent users will know that it has been modified and // that they need to re-read it into memory. // If we are in recovery mode, see if we need to force // a checkpoint with what we have so far. We force a // checkpoint on one of two conditions: // 1. If it appears that we have a buildup of dirty cache // blocks. We force a checkpoint on this condition // because it will be more efficient than replacing // cache blocks one at a time. // We check for this condition by looking to see if // our LRU block is not used and it is dirty. That is // a pretty good indicator that we have a buildup // of dirty cache blocks. // 2. We are at the end of the roll-forward log. We // want to force a checkpoint here to complete the // recovery phase. if (m_uiFlags & FDB_REPLAYING_RFL) { // If we are in the middle of upgrading, and are forcing // a checkpoint, use the file number and offset that were // set in the F_Db. if ((m_uiFlags & FDB_UPGRADING) && bForceCheckpoint) { uiCPFileNum = m_uiUpgradeCPFileNum; uiCPOffset = m_uiUpgradeCPOffset; } else { FLMUINT uiCurrTime; uiCurrTime = (FLMUINT)FLM_GET_TIMER(); f_mutexLock( gv_XFlmSysData.hShareMutex); if (FLM_ELAPSED_TIME( uiCurrTime, m_pDatabase->m_uiLastCheckpointTime) >= gv_XFlmSysData.uiMaxCPInterval || !gv_XFlmSysData.uiMaxCPInterval || (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache && (m_pDatabase->m_uiDirtyCacheCount + m_pDatabase->m_uiLogCacheCount) * m_pDatabase->m_uiBlockSize > gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache) || pRfl->atEndOfLog() || bForceCheckpoint) { bForceCheckpoint = TRUE; uiCPFileNum = pRfl->getCurrFileNum(); uiCPOffset = pRfl->getCurrReadOffset(); } f_mutexUnlock( gv_XFlmSysData.hShareMutex); } } // Move information collected in the pDb into the // uncommitted DB header. Other things that need to be // set have already been set in the uncommitted log header // at various places in the code. // Mutex does not have to be locked while we do this because // the update transaction is the only one that ever accesses // the uncommitted log header buffer. pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; // Set the new logical EOF if passed in. if (uiNewLogicalEOF) { m_uiLogicalEOF = uiNewLogicalEOF; } pUncommittedDbHdr->ui32LogicalEOF = (FLMUINT32)m_uiLogicalEOF; // Increment the commit counter. pUncommittedDbHdr->ui64TransCommitCnt++; // Set the last committed transaction ID if (bTransEndLogged || (m_uiFlags & FDB_REPLAYING_COMMIT)) { pUncommittedDbHdr->ui64LastRflCommitID = ui64TransId; } // Write the header pRfl->commitDbHdrs( pUncommittedDbHdr, &m_pDatabase->m_checkpointDbHdr); // Commit any node cache. m_pDatabase->commitNodeCache(); // Push the IXD_FIXUP values back into the IXD if (m_pIxdFixups) { IXD_FIXUP * pIxdFixup; IXD_FIXUP * pDeleteIxdFixup; IXD * pIxd; RCODE tmpRc; pIxdFixup = m_pIxdFixups; while (pIxdFixup) { if (RC_BAD( tmpRc = m_pDict->getIndex( pIxdFixup->uiIndexNum, NULL, &pIxd, TRUE))) { RC_UNEXPECTED_ASSERT( tmpRc); pIxd = NULL; } if (pIxd) { pIxd->ui64LastDocIndexed = pIxdFixup->ui64LastDocIndexed; } pDeleteIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; f_free( &pDeleteIxdFixup); } m_pIxdFixups = NULL; } // Set the update transaction ID back to zero only // AFTER we know the transaction has safely committed. m_pDatabase->lockMutex(); f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pUncommittedDbHdr, sizeof( XFLM_DB_HDR)); if (m_uiFlags & FDB_UPDATED_DICTIONARY) { // Link the new local dictionary to its file. // Since the new local dictionary will be linked at the head // of the list of FDICT structures, see if the FDICT currently // at the head of the list is unused and can be unlinked. if (m_pDatabase->m_pDictList && !m_pDatabase->m_pDictList->getUseCount()) { m_pDatabase->m_pDictList->unlinkFromDatabase(); } m_pDict->linkToDatabase( m_pDatabase); } m_pDatabase->unlockMutex(); // Log blocks must be released after the last committed database header // has been updated. If done before, there is a slight window of // opportunity for a caller to attempt to start a read transaction. Between // the time releaseLogBlocks unlocks the block cache mutex and when // the committed header is copied into m_pDatabase, the read transaction // could see the prior committed transaction ID and use that as the basis // for its transaction. In the mean time, releaseLogBlocks may release // versions of the blocks that would be needed to satisfy the new reader's // (incorrect) view of the database. m_pDatabase->releaseLogBlocks(); Exit1: if (RC_BAD( rc)) { // Since we failed to commit, do an abort. We are purposely not // checking the return code from flmAbortDbTrans because we already // have an error return code. If we attempted to log the transaction // to the RFL and failed, we don't want to try to log an abort packet. // The RFL code has already reset the log back to the starting point // of the transaction, thereby discarding all operations. (void)abortTrans( bOkToLogAbort); eSaveTransType = XFLM_NO_TRANS; // Do we need to force all handles to close? if (bForceCloseOnError) { // Since the commit packet has already been logged to the RFL, // we must have failed when trying to write the log header. The // database is in a bad state and must be closed. // Set the "must close" flag on all FDBs linked to the FFILE // and set the FFILE's "must close" flag. This will cause any // subsequent operations on the database to fail until all // handles have been closed. m_pDatabase->setMustCloseFlags( rc, FALSE); } } else { eSaveTransType = m_eTransType; if (m_eTransType == XFLM_UPDATE_TRANS) { if (gv_XFlmSysData.EventHdrs [XFLM_EVENT_UPDATES].pEventCBList) { flmTransEventCallback( XFLM_EVENT_COMMIT_TRANS, this, rc, ui64TransId); } // Do the indexing work before we unlock the db. if (m_pIxStopList || m_pIxStartList) { // Must not call indexingAfterCommit until after // completeTransWrites. Otherwise, there is a potential // deadlock condition where indexingAfterCommit is // waiting on an indexing thread to quit, but that // thread is waiting to be signaled by this thread that // writes are completed. However, indexingAfterCommit // also must only be called while the database is still // locked. If we were to leave the database locked for // every call to completeTransWrites, however, we would // lose the group commit capability. Hence, we opt to // only lose it when there are actual indexing operations // to start or stop - which should be very few transactions. // That is what the bIndexAfterCommit flag is for. bIndexAfterCommit = TRUE; } } } // Unlock the database, if the update transaction is still going. // NOTE: We check m_eTransType because it may have been reset // to XFLM_NO_TRANS up above if abortTrans was called. if (m_eTransType == XFLM_UPDATE_TRANS) { if (RC_BAD( rc)) { // SHOULD NEVER HAPPEN - because it would have been taken // care of above - abortTrans would have been called and // m_eTransType would no longer be XFLM_UPDATE_TRANS. RC_UNEXPECTED_ASSERT( rc); pRfl->completeTransWrites( this, FALSE, TRUE); } else if (!bForceCheckpoint) { if (bIndexAfterCommit) { rc = pRfl->completeTransWrites( this, TRUE, FALSE); indexingAfterCommit(); unlinkFromTransList( TRUE); } else { rc = pRfl->completeTransWrites( this, TRUE, TRUE); } } else { // Do checkpoint, if forcing. Before doing the checkpoint // we have to make sure the roll-forward log writes // complete. We don't want to unlock the DB while the // writes are happening in this case - thus, the FALSE // parameter to completeTransWrites. if (RC_OK( rc = pRfl->completeTransWrites( this, TRUE, FALSE))) { bForceCloseOnError = FALSE; rc = m_pDatabase->doCheckpoint( m_hWaitSem, m_pDbStats, m_pSFileHdl, (m_uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE, TRUE, XFLM_CP_TIME_INTERVAL_REASON, uiCPFileNum, uiCPOffset); } if (bIndexAfterCommit) { indexingAfterCommit(); } unlinkFromTransList( TRUE); } if (RC_BAD( rc) && bForceCloseOnError) { // Since the commit packet has already been logged to the RFL, // we must have failed when trying to write the log header. The // database is in a bad state and must be closed. // Set the "must close" flag on all F_Db objects linked to the // F_Database object and set the F_Database object's "must close" // flag. This will cause any subsequent operations on the // database to fail until all handles have been closed. m_pDatabase->setMustCloseFlags( rc, FALSE); } } else { // Unlink the database from the transaction list unlinkFromTransList( FALSE); } if (m_pDbStats && eSaveTransType != XFLM_NO_TRANS) { FLMUINT64 ui64ElapMilli = 0; flmAddElapTime( &m_TransStartTime, &ui64ElapMilli); m_pDbStats->bHaveStats = TRUE; if (eSaveTransType == XFLM_READ_TRANS) { m_pDbStats->ReadTransStats.CommittedTrans.ui64Count++; m_pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli += ui64ElapMilli; } else { m_pDbStats->UpdateTransStats.CommittedTrans.ui64Count++; m_pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli += ui64ElapMilli; } } // Update stats if (m_pStats) { (void)flmStatUpdate( &m_Stats); } Exit: if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Commits an active transaction. *END************************************************************************/ RCODE FLMAPI F_Db::transCommit( FLMBOOL * pbEmpty) // may be NULL { RCODE rc = NE_XFLM_OK; if (m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (pbEmpty) { *pbEmpty = FALSE; } rc = commitTrans( 0, FALSE, pbEmpty); Exit: if( RC_OK( rc)) { rc = checkState( __FILE__, __LINE__); } return( rc); } libxflaim-5.1.969/src/flverify.cpp0000644000175000017500000033330310511001742020363 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Verify data in a database. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flverify.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC FLMBYTE * getEntryEnd( FLMBYTE * pucEntry, FLMUINT uiBlkType); FLMUINT64 getLinkVal( FLMUINT uiBMId, NODE_RS_ENTRY * pRSEntry); FSTATIC RCODE flmVerifyKeyOrder( STATE_INFO * pStateInfo, LFILE * pLFile, IXD * pIxd, F_BLK_HDR * pBlkHdr, FLMBYTE * pucElmKey, FLMUINT uiElmKeyLen, FLMUINT uiElmOffset); FSTATIC FLMBOOL flmVerifyElementChain( STATE_INFO * pStateInfo, LFILE * pLFile); FSTATIC RCODE verifyRootLink( NODE_RS_ENTRY * pRSEntry, FLMUINT uiRSEntrySize, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); FSTATIC RCODE verifyParentLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); FSTATIC RCODE verifyFirstChildLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); FSTATIC RCODE verifyLastChildLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); FSTATIC RCODE verifyPrevSiblingLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); FSTATIC RCODE verifyNextSiblingLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); FSTATIC RCODE verifyAnnotationLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode); /******************************************************************** Desc: Verifies a block's header and sets up the STATE_INFO structure to verify the rest of the block. *********************************************************************/ FLMINT32 flmVerifyBlockHeader( STATE_INFO * pStateInfo, BLOCK_INFO * pBlockInfo, FLMUINT uiBlockSize, FLMUINT uiExpNextBlkAddr, FLMUINT uiExpPrevBlkAddr, FLMBOOL bCheckEOF) { F_BLK_HDR * pBlkHdr = pStateInfo->pBlkHdr; if (pBlockInfo) { pBlockInfo->uiBlockCount++; } pStateInfo->ui32NextBlkAddr = pBlkHdr->ui32NextBlkInChain; if( (FLMUINT)pBlkHdr->ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pBlkHdr)) { return( FLM_BAD_BLK_HDR_BLK_END); } else { if( pBlockInfo) { pBlockInfo->ui64BytesUsed += (FLMUINT64)(uiBlockSize - pBlkHdr->ui16BlkBytesAvail - blkHdrSize( pBlkHdr)); } } // Verify the block address. if ((FLMUINT)pBlkHdr->ui32BlkAddr != pStateInfo->ui32BlkAddress) { return( FLM_BAD_BLK_HDR_ADDR); } // Verify that block address is below the logical EOF // Rebuild passes in FALSE for bCheckEOF if( bCheckEOF && pStateInfo->pDb) { if( !FSAddrIsBelow( pStateInfo->ui32BlkAddress, pStateInfo->pDb->getLogicalEOF())) { return( FLM_BAD_FILE_SIZE); } } // Verify the block type. if( pStateInfo->uiBlkType != 0xFF && pStateInfo->uiBlkType != (FLMUINT)pBlkHdr->ui8BlkType) { return( FLM_BAD_BLK_HDR_TYPE); } // Verify the block level - if there is one if( pStateInfo->uiBlkType != BT_DATA_ONLY && pStateInfo->uiLevel != 0xFF && blkIsBTree( pBlkHdr) && pStateInfo->uiLevel != (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel)) { return( FLM_BAD_BLK_HDR_LEVEL); } // Verify the previous block address. If uiExpPrevBlkAddr is 0xFFFFFFFF, // we do not know what the previous address should be, so we don't verify. if (uiExpPrevBlkAddr != 0xFFFFFFFF && uiExpPrevBlkAddr != (FLMUINT)pBlkHdr->ui32PrevBlkInChain) { return( FLM_BAD_BLK_HDR_PREV); } // Verify the next block address. If uiExpNextBlkAddr is 0xFFFFFFFF, // we do not know what the next address should be, se we don't verify. if( uiExpNextBlkAddr != 0xFFFFFFFF && uiExpNextBlkAddr != pStateInfo->ui32NextBlkAddr) { return( FLM_BAD_BLK_HDR_NEXT); } // Verify that if it is a root block, the root bit flags is set, // or if it is NOT a root block, that the root bit flag is NOT set. // Note that Data Only blocks don't have the extended header, so we // can't do this check... if( pStateInfo->pCollection && (pStateInfo->uiBlkType != BT_DATA_ONLY) ) { if( pStateInfo->uiLevel != 0xFF) { F_BTREE_BLK_HDR * pBTreeBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; FLMBOOL bShouldBeRootBlk = (pStateInfo->uiLevel == pStateInfo->uiRootLevel) ? TRUE : FALSE; if ((bShouldBeRootBlk && !isRootBlk( pBTreeBlkHdr)) || (!bShouldBeRootBlk && isRootBlk( pBTreeBlkHdr))) { return( FLM_BAD_BLK_HDR_ROOT_BIT); } } // Verify the logical file number - if any. if( (pBlkHdr->ui8BlkType != BT_DATA_ONLY) && (pStateInfo->pCollection->lfInfo.uiLfNum != (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile)) ) { return( FLM_BAD_BLK_HDR_LF_NUM); } } return( 0); } /******************************************************************** Desc: Verify an index or data element in a b-tree and set pStateInfo. *********************************************************************/ RCODE flmVerifyElement( STATE_INFO * pStateInfo, LFILE * pLFile, IXD * pIxd, FLMINT32 * pi32ErrCode) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry; FLMUINT32 ui32DOAddr = 0; FLMUINT32 ui32ChildAddr = 0; FLMUINT uiCounts = 0; *pi32ErrCode = 0; // Get the pointer to the element. pucEntry = BtEntry( (FLMBYTE *)pStateInfo->pBlkHdr, pStateInfo->uiElmOffset); pStateInfo->pucElm = pucEntry; // Verify that the address of the entry is within the block. if ((FLMUINT)pucEntry > (FLMUINT)pStateInfo->pBlkHdr + pStateInfo->pDb->getDatabase()->getBlockSize()) { *pi32ErrCode = FLM_BAD_ELM_OFFSET; goto Exit; } switch( pStateInfo->pBlkHdr->ui8BlkType) { case BT_LEAF: { pStateInfo->uiElmKeyLen = FB2UW( pucEntry); f_memcpy( pStateInfo->pucElmKey, pucEntry + 2, pStateInfo->uiElmKeyLen); pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 2; break; } case BT_LEAF_DATA: { FLMBYTE ucFlags = *pucEntry; FLMBYTE * pucPtr = pucEntry + 1; FLMUINT uiElmLen = 1; if( ucFlags & BTE_FLAG_KEY_LEN) { // 2 byte key length pStateInfo->uiElmKeyLen = FB2UW( pucPtr); pucPtr += 2; uiElmLen += 2; } else { // 1 byte key length pStateInfo->uiElmKeyLen = (FLMUINT)*pucPtr; pucPtr++; uiElmLen++; } if( ucFlags & BTE_FLAG_DATA_LEN) { // 2 byte data length pStateInfo->uiElmDataLen = FB2UW( pucPtr); pucPtr += 2; uiElmLen += 2; } else { // 1 byte data length pStateInfo->uiElmDataLen = (FLMUINT)*pucPtr; pucPtr++; uiElmLen++; } if( ucFlags & BTE_FLAG_OA_DATA_LEN) { pStateInfo->uiElmOADataLen = FB2UD( pucPtr); pucPtr += 4; uiElmLen += 4; } f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen); pucPtr += pStateInfo->uiElmKeyLen; uiElmLen += pStateInfo->uiElmKeyLen; pStateInfo->pucElmData = pucPtr; uiElmLen += pStateInfo->uiElmDataLen; if( ucFlags & BTE_FLAG_DATA_BLOCK) { ui32DOAddr = FB2UD( pucPtr); } pStateInfo->uiElmLen = uiElmLen; break; } case BT_NON_LEAF: { FLMBYTE * pucPtr = pucEntry; ui32ChildAddr = FB2UD( pucPtr); pucPtr += 4; pStateInfo->uiElmKeyLen = FB2UW( pucPtr); pucPtr += 2; f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen); pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 6; break; } case BT_NON_LEAF_COUNTS: { FLMBYTE * pucPtr = pucEntry; ui32ChildAddr = FB2UD( pucPtr); pucPtr += 4; uiCounts = FB2UD( pucPtr); pucPtr += 4; pStateInfo->uiElmKeyLen = FB2UW( pucPtr); pucPtr += 2; f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen); pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 10; break; } default: { *pi32ErrCode = FLM_BAD_BLK_TYPE; goto Exit; } } // Verify that the end of the element is not past the end of the block. if( pStateInfo->pucElm + pStateInfo->uiElmLen > (FLMBYTE *)pStateInfo->pBlkHdr + pStateInfo->pDb->getDatabase()->getBlockSize()) { *pi32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } // Verify the key length is not too big if( pStateInfo->uiElmKeyLen > XFLM_MAX_KEY_SIZE) { *pi32ErrCode = FLM_BAD_ELM_KEY_SIZE; goto Exit; } // Verify the key order. First the previous key, then the next key. if( RC_BAD( rc = flmVerifyKeyOrder( pStateInfo, pLFile, pIxd, pStateInfo->pBlkHdr, pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen, pStateInfo->uiElmOffset))) { *pi32ErrCode = FLM_BAD_ELM_KEY_ORDER; goto Exit; } pStateInfo->bValidKey = TRUE; if( !isIndexBlk( (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr)) { switch( pStateInfo->pBlkHdr->ui8BlkType) { case BT_LEAF: case BT_LEAF_DATA: { FLMBOOL bNeg; FLMUINT uiBytesProcessed; if( pStateInfo->pucElm[0] & BTE_FLAG_FIRST_ELEMENT) { if( !flmVerifyElementChain( pStateInfo, pLFile)) { *pi32ErrCode = FLM_BAD_ELEMENT_CHAIN; goto Exit; } } // The key length may be zero on a LEM if( pStateInfo->uiElmKeyLen) { if( RC_BAD( rc = flmCollation2Number( pStateInfo->uiElmKeyLen, pStateInfo->pucElmKey, &pStateInfo->ui64ElmNodeId, &bNeg, &uiBytesProcessed))) { *pi32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } if( bNeg || uiBytesProcessed != pStateInfo->uiElmKeyLen) { *pi32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } if( !pStateInfo->ui64ElmNodeId) { flmAssert( 0); *pi32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } } else { // If the key length is zero, then this MUST be the last block! if( pStateInfo->pBlkHdr->ui32NextBlkInChain) { *pi32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } } break; } case BT_NON_LEAF: { break; } case BT_NON_LEAF_COUNTS: { break; } } } Exit: return( rc); } /*************************************************************************** Desc: Verify that the key order is correct. *****************************************************************************/ FSTATIC RCODE flmVerifyKeyOrder( STATE_INFO * pStateInfo, LFILE * pLFile, IXD * pIxd, F_BLK_HDR * pBlkHdr, FLMBYTE * pucElmKey, FLMUINT uiElmKeyLen, FLMUINT uiElmOffset) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pPrevSCache = NULL; F_CachedBlock * pNextSCache = NULL; F_BTREE_BLK_HDR * pPrevBlk; F_BTREE_BLK_HDR * pNextBlk; FLMBYTE * pucEntry = NULL; FLMBYTE * pucKey = NULL; FLMUINT uiKeyLen = 0; FLMBOOL bCheckPrev = FALSE; FLMBOOL bCheckNext = FALSE; FLMUINT uiBlockSize; uiBlockSize = pStateInfo->pDb->getDatabase()->getBlockSize(); // Get the previous key if( uiElmOffset) { flmAssert( uiElmOffset < ((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys); pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset - 1); if( (FLMUINT)pucEntry > (FLMUINT)pBlkHdr + uiBlockSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } bCheckPrev = TRUE; } else if( uiElmOffset == 0 && pBlkHdr->ui32PrevBlkInChain) { if( pLFile) { // Need to get the previous block. if( RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock( pStateInfo->pDb, pLFile, pBlkHdr->ui32PrevBlkInChain, NULL, &pPrevSCache))) { goto Exit; } pPrevBlk = (F_BTREE_BLK_HDR *)pPrevSCache->getBlockPtr(); pucEntry = BtEntry( (FLMBYTE *)pPrevBlk, pPrevBlk->ui16NumKeys - 1); if( (FLMUINT)pucEntry > (FLMUINT)pPrevBlk + uiBlockSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } if( pBlkHdr->ui8BlkType != pPrevBlk->stdBlkHdr.ui8BlkType) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } bCheckPrev = TRUE; } } if( bCheckPrev) { // Get the key switch (pBlkHdr->ui8BlkType) { case BT_LEAF: { uiKeyLen = FB2UW( pucEntry); pucKey = pucEntry + 2; break; } case BT_LEAF_DATA: { FLMBYTE ucFlags = *pucEntry; FLMBYTE * pucPtr = pucEntry + 1; flmAssert( (*pucEntry & 0x03) == 0); if (ucFlags & BTE_FLAG_KEY_LEN) { uiKeyLen = FB2UW( pucPtr); pucPtr += 2; } else { uiKeyLen = *pucPtr; pucPtr++; } if( ucFlags & BTE_FLAG_DATA_LEN) { pucPtr += 2; } else { pucPtr++; } if (ucFlags & BTE_FLAG_OA_DATA_LEN) { pucPtr += 4; } pucKey = pucPtr; break; } case BT_NON_LEAF: { FLMBYTE * pucPtr = pucEntry + 4; uiKeyLen = FB2UW( pucPtr); pucKey = pucPtr + 2; break; } case BT_NON_LEAF_COUNTS: { FLMBYTE * pucPtr = pucEntry + 8; uiKeyLen = FB2UW( pucPtr); pucKey = pucPtr + 2; break; } } if( uiKeyLen) { if( !pLFile || pLFile->eLfType == XFLM_LF_COLLECTION) { if( f_memcmp( pucElmKey, pucKey, uiElmKeyLen < uiKeyLen ? uiElmKeyLen : uiKeyLen) < 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } else { // If uiElmKeyLen == 0, it is the LEM, in which case we won't // do the comparison, because pucElmKey is greater than pucKey // by definition. if( uiElmKeyLen) { FLMINT iCompare; if( RC_BAD( rc = ixKeyCompare( pStateInfo->pDb, pIxd, NULL, NULL, NULL, TRUE, TRUE, pucElmKey, uiElmKeyLen, pucKey, uiKeyLen, &iCompare))) { goto Exit; } if( iCompare < 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } // Get the next key if( uiElmOffset < (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys - 1) { pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset + 1); if( pucEntry > (FLMBYTE *)pBlkHdr + uiBlockSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } bCheckNext = TRUE; } else if( uiElmOffset == (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys - 1 && pBlkHdr->ui32NextBlkInChain) { if( pLFile) { if( RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock( pStateInfo->pDb, pLFile, pBlkHdr->ui32NextBlkInChain, NULL, &pNextSCache))) { goto Exit; } pNextBlk = (F_BTREE_BLK_HDR *)pNextSCache->getBlockPtr(); pucEntry = BtEntry( (FLMBYTE *)pNextBlk, 0); if( pucEntry > (FLMBYTE *)pNextBlk + uiBlockSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } if( pBlkHdr->ui8BlkType != pNextBlk->stdBlkHdr.ui8BlkType) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } bCheckNext = TRUE; } } if( bCheckNext) { switch (pBlkHdr->ui8BlkType) { case BT_LEAF: { uiKeyLen = FB2UW( pucEntry); pucKey = pucEntry + 2; break; } case BT_LEAF_DATA: { FLMBYTE ucFlags = *pucEntry; FLMBYTE * pucPtr = pucEntry + 1; if( (*pucEntry & 0x03) != 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( ucFlags & BTE_FLAG_KEY_LEN) { uiKeyLen = FB2UW( pucPtr); pucPtr += 2; } else { uiKeyLen = *pucPtr; pucPtr++; } if( ucFlags & BTE_FLAG_DATA_LEN) { pucPtr += 2; } else { pucPtr++; } if( ucFlags & BTE_FLAG_OA_DATA_LEN) { pucPtr += 4; } pucKey = pucPtr; break; } case BT_NON_LEAF: { FLMBYTE * pucPtr = pucEntry + 4; uiKeyLen = FB2UW( pucPtr); pucKey = pucPtr + 2; break; } case BT_NON_LEAF_COUNTS: { FLMBYTE * pucPtr = pucEntry + 8; uiKeyLen = FB2UW( pucPtr); pucKey = pucPtr + 2; break; } } if (uiKeyLen) { if (!pLFile || pLFile->eLfType == XFLM_LF_COLLECTION) { if (f_memcmp( pucKey, pucElmKey, uiElmKeyLen < uiKeyLen ? uiElmKeyLen : uiKeyLen) < 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } else { // If uiElmKeyLen == 0, it is the LEM, in which case we won't // do the comparison, because pucKey will always be less than // the LEM, but it is not an error in that case. if( uiElmKeyLen) { FLMINT iCompare; if (RC_BAD( rc = ixKeyCompare( pStateInfo->pDb, pIxd, NULL, NULL, NULL, TRUE, TRUE, pucKey, uiKeyLen, pucElmKey, uiElmKeyLen, &iCompare))) { goto Exit; } if( iCompare < 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } } } else { // A zero length key should only occur on the last block in the chain. if( pBlkHdr->ui32NextBlkInChain) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } } Exit: if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } if( pNextSCache) { ScaReleaseCache( pNextSCache, FALSE); } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ FSTATIC FLMBOOL flmVerifyElementChain( STATE_INFO * pStateInfo, LFILE * pLFile) { RCODE rc = NE_XFLM_OK; FLMBOOL bResult = TRUE; F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr; FLMBYTE * pucElmKey = pStateInfo->pucElmKey; FLMUINT uiElmKeyLen = pStateInfo->uiElmKeyLen; FLMUINT uiElmOffset = pStateInfo->uiElmOffset; FLMBYTE * pucNextKey = NULL; FLMUINT uiNextKeySize; FLMBYTE ucFlags = pStateInfo->pucElm[ 0]; F_CachedBlock * pSCache = NULL; if (pLFile) { while (!(ucFlags & BTE_FLAG_LAST_ELEMENT)) { // Get the next element. if (uiElmOffset >= (FLMUINT)(pBlkHdr->ui16NumKeys - 1)) { if (pSCache) { ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } // Will have to go the the next block to find the key we want. if (RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock( pStateInfo->pDb, pLFile, pBlkHdr->stdBlkHdr.ui32NextBlkInChain, NULL, &pSCache))) { RC_UNEXPECTED_ASSERT( rc); bResult = FALSE; goto Exit; } flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType == pSCache->getBlockPtr()->ui8BlkType); pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->getBlockPtr(); uiElmOffset = 0; // Get the first element... } else { uiElmOffset++; } pucNextKey = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset); // Update the flag for the next iteration ucFlags = pucNextKey[ 0]; if (ucFlags & BTE_FLAG_FIRST_ELEMENT) { flmAssert( 0); bResult = FALSE; goto Exit; } // Find the key. pucNextKey++; if (ucFlags & BTE_FLAG_KEY_LEN) { uiNextKeySize = FB2UW( pucNextKey); pucNextKey += 2; } else { uiNextKeySize = *pucNextKey; pucNextKey++; } // Key size is now set. They should match. if (uiElmKeyLen != uiNextKeySize) { flmAssert( 0); bResult = FALSE; goto Exit; } // Scoot past the info we don't need if (ucFlags & BTE_FLAG_DATA_LEN) { pucNextKey += 2; } else { pucNextKey++; } if (ucFlags & BTE_FLAG_OA_DATA_LEN) { pucNextKey += 4; } // Make sure the keys match. if ( f_memcmp( pucElmKey, pucNextKey, uiElmKeyLen) != 0) { flmAssert( 0); bResult = FALSE; goto Exit; } } } Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } return TRUE; } /******************************************************************** Desc: Build the result set of all keys in the database. *********************************************************************/ RCODE F_DbCheck::buildIndexKeyList( FLMUINT64 * pui64TotalKeys) { RCODE rc = NE_XFLM_OK; F_KeyCollector * pKeyColl = NULL; DOC_IXD_XREF XRef; IXD * pIxd; LFILE * pLFile; F_DOMNode * pDocNode = NULL; F_Dict * pDict; FLMBOOL bUpdTranStarted = FALSE; RCODE tmpRc = NE_XFLM_OK; FLMUINT uiSizeRV; F_Btree * pBTree = NULL; // Set information for the result set sort phase. m_Progress.i32CheckPhase = XFLM_CHECK_RS_SORT; m_Progress.bStartFlag = TRUE; if ((pKeyColl = f_new F_KeyCollector( this)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } // Save the key collector in the m_pDb to redirect the // keys generated into the key result set. m_pDb->setKeyCollector( pKeyColl); // Start an update transaction, end the read trans. if (m_pDb->getTransType() == XFLM_READ_TRANS) { if (RC_OK( rc = m_pDb->transCommit())) { if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bUpdTranStarted = TRUE; } else { goto Exit; } } if (RC_BAD( rc = m_pDb->getDictionary( &pDict))) { goto Exit; } // For each entry in the Xref result set, call indexDocument to generate the // keys. if (RC_BAD( rc = m_pXRefRS->getBTree( NULL, NULL, &pBTree))) { goto Exit; } if( RC_BAD( rc = m_pXRefRS->getFirst( NULL, NULL, pBTree, (FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL, 0, NULL))) { goto Exit; } for( ;;) { if( RC_BAD( rc = m_pDb->getNode( XRef.uiCollection, XRef.ui64DocId, &pDocNode))) { goto Exit; } if( pDocNode->getDocumentId() == XRef.ui64DocId) { if( RC_BAD( rc = pDict->getIndex( XRef.uiIndexNum, &pLFile, &pIxd))) { // If the index is offline, skip it and move to the next, if any if( rc == NE_XFLM_INDEX_OFFLINE) { if( RC_BAD( rc = m_pXRefRS->getNext( NULL, NULL, pBTree, (FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL, 0, NULL))) { if( rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } break; } continue; } goto Exit; } if (RC_BAD( rc = m_pDb->indexDocument( pIxd, pDocNode))) { goto Exit; } m_Progress.ui64NumKeys++; // += ui64KeysProcessed; } // Get the next document... if (RC_BAD( rc = m_pXRefRS->getNext( NULL, NULL, pBTree, (FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL, 0, NULL))) { if (rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } else { goto Exit; } } if( RC_BAD( rc = chkCallProgFunc())) { goto Exit; } } m_pXRefRS->freeBTree( &pBTree); if( bUpdTranStarted) { if (RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } bUpdTranStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS))) { goto Exit; } } *pui64TotalKeys = pKeyColl->getTotalKeys(); Exit: if( pBTree) { m_pXRefRS->freeBTree( &pBTree); } if( bUpdTranStarted) { // If we fail here, the whole thing should abort. if( RC_OK( rc)) { if( RC_OK ( rc = m_pDb->transCommit())) { rc = m_pDb->transBegin( XFLM_READ_TRANS); } else { m_pDb->transAbort(); } } else { m_pDb->transAbort(); if( RC_BAD( tmpRc = m_pDb->transBegin( XFLM_READ_TRANS))) { rc = tmpRc; } } } if( pDocNode) { pDocNode->Release(); } // Be sure we don't leave it this way. m_pDb->setKeyCollector( NULL); if( pKeyColl) { pKeyColl->Release(); } m_Progress.bStartFlag = TRUE; return( rc); } /*************************************************************************** Desc: This routine checks all of the B-TREES in the database -- all indexes and containers. *****************************************************************************/ RCODE F_DbCheck::verifyBTrees( FLMBOOL * pbStartOverRV) { RCODE rc = NE_XFLM_OK; FLMUINT uiCurrLf; FLMUINT uiCurrLevel; FLMBYTE * pucKeyBuffer = NULL; FLMUINT uiKeysAllocated = 0; STATE_INFO State [BH_MAX_LEVELS]; FLMBOOL bStateInitialized [BH_MAX_LEVELS]; FLMBYTE * pucResetKey = NULL; FLMUINT uiResetKeyLen = ~(FLMUINT)0; FLMUINT64 ui64ResetNodeId = 0; LF_HDR * pLogicalFile; FLMUINT uiSaveDictSeq; FLMBOOL bRSFinalized = FALSE; char szTmpIoPath [F_PATH_MAX_SIZE]; char szBaseName [F_FILENAME_SIZE]; F_NodeVerifier * pNodeVerifier = NULL; F_BLK_HDR * pBlkHdr = NULL; F_CachedBlock * pSCache = NULL; f_memset( State, 0, sizeof( State)); // The StateInfo structs may have pointer to this object, // but we only need one instance, so do the new here, rather // than inside the loop where we initialize the StateInfo structs. if( (pNodeVerifier = f_new F_NodeVerifier) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } // szTmpIoPath is the directory where the result sets containing // node data will be stored. if (RC_BAD( rc = gv_pXFlmDbSystem->getTempDir( szTmpIoPath))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_pDb->m_pDatabase->m_pszDbPath, szTmpIoPath, szBaseName))) { goto Exit; } } else { goto Exit; } } for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++) { bStateInitialized [uiCurrLevel] = FALSE; } if (*pbStartOverRV) { goto Exit; } uiSaveDictSeq = m_pDb->m_pDict->getDictSeq(); if (RC_BAD( rc = setupLfTable())) { goto Exit; } if (m_uiFlags & XFLM_DO_LOGICAL_CHECK) { if (RC_BAD( rc = setupIxInfo())) { goto Exit; } } // Loop through all of the logical files in the database // and perform a structural and logical check. m_pIxd = NULL; uiCurrLf = 0; while (uiCurrLf < m_pDbInfo->m_uiNumLogicalFiles) { m_Progress.ui32CurrLF = (FLMUINT32)(uiCurrLf + 1); pLogicalFile = &m_pDbInfo->m_pLogicalFiles[uiCurrLf]; if (pLogicalFile->eLfType == XFLM_LF_COLLECTION) { if (RC_BAD( rc = m_pDb->m_pDict->getCollection( pLogicalFile->uiLfNum, &m_pCollection, TRUE))) { goto Exit; } m_pLFile = &m_pCollection->lfInfo; m_pIxd = NULL; } else { // If this is our first index, and we are doing a logical check, // create the index key result set from all of the documents we // have created. if (!m_bPhysicalCorrupt && (m_uiFlags & XFLM_DO_LOGICAL_CHECK) && !bRSFinalized) { FLMUINT64 ui64NumRSKeys = 0; if (RC_BAD( rc = buildIndexKeyList( &ui64NumRSKeys))) { if (rc == NE_XFLM_EOF_HIT && ui64NumRSKeys == 0) { rc = NE_XFLM_OK; } else { goto Exit; } } // Reset uiNumKeys to reflect the number of keys // in the result set now that all duplicates have // been eliminated. if (m_Progress.ui64NumKeys > ui64NumRSKeys) { m_Progress.ui64NumDuplicateKeys = m_Progress.ui64NumKeys - ui64NumRSKeys; } m_Progress.ui64NumKeys = ui64NumRSKeys; // Set bRSFinalized to TRUE so that subsequent passes will not // attempt to finalize the result set again. bRSFinalized = TRUE; } if (RC_BAD( rc = m_pDb->m_pDict->getIndex( pLogicalFile->uiLfNum, &m_pLFile, &m_pIxd, TRUE))) { goto Exit; } m_pCollection = NULL; } pLogicalFile->uiRootBlk = m_pLFile->uiRootBlk; flmAssert( m_pLFile->uiRootBlk); // Allocate space to hold the keys, if not already allocated. if (uiKeysAllocated < pLogicalFile->uiNumLevels) { if (RC_BAD( rc = f_realloc( pLogicalFile->uiNumLevels * XFLM_MAX_KEY_SIZE, &pucKeyBuffer))) { goto Exit; } uiKeysAllocated = pLogicalFile->uiNumLevels; } // Setup XFLM_PROGRESS_CHECK_INFO structure m_Progress.i32CheckPhase = XFLM_CHECK_B_TREE; m_Progress.bStartFlag = TRUE; m_Progress.ui32LfNumber = (FLMUINT32)m_pLFile->uiLfNum; m_Progress.ui32LfType = (FLMUINT32)m_pLFile->eLfType; if (RC_BAD( rc = chkCallProgFunc())) { break; } m_Progress.bStartFlag = FALSE; f_yieldCPU(); // Initialize the state information for each level of the B-TREE. for (uiCurrLevel = 0; uiCurrLevel < pLogicalFile->uiNumLevels; uiCurrLevel++) { FLMUINT uiExpectedBlkType; // If we are resetting to a particular key, save the statistics // which were gathered so far. if (uiResetKeyLen != ~(FLMUINT)0) { // Save the statistics which were gathered. pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount = State [uiCurrLevel].ui64KeyCount; f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, &State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO)); } if (m_pLFile->eLfType == XFLM_LF_INDEX) { if (uiCurrLevel == 0) { if (m_pIxd->uiNumDataComponents) { uiExpectedBlkType = BT_LEAF_DATA; } else { uiExpectedBlkType = BT_LEAF; } } else if (m_pIxd->uiFlags & IXD_ABS_POS) { uiExpectedBlkType = BT_NON_LEAF_COUNTS; } else { uiExpectedBlkType = BT_NON_LEAF; } } else // collection BTree... { if (uiCurrLevel == 0) { uiExpectedBlkType = BT_LEAF_DATA; } else { uiExpectedBlkType = BT_NON_LEAF; } } flmInitReadState( &State [uiCurrLevel], &bStateInitialized [uiCurrLevel], (FLMUINT)m_pDb-> m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion, m_pDb, pLogicalFile, uiCurrLevel, uiExpectedBlkType, &pucKeyBuffer [uiCurrLevel * XFLM_MAX_KEY_SIZE]); State[ uiCurrLevel].pCollection = m_pCollection; State[ uiCurrLevel].uiRootLevel = pLogicalFile->uiNumLevels - 1; State[ uiCurrLevel].uiCurrLf = uiCurrLf; if (uiResetKeyLen == ~(FLMUINT)0) { State [uiCurrLevel].ui32LastChildAddr = 0; State [uiCurrLevel].uiElmLastFlag = TRUE; } else { // Restore the statistics which were gathered so far. State [uiCurrLevel].ui64KeyCount = pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount; f_memcpy( &State [uiCurrLevel].BlkInfo, &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, sizeof( BLOCK_INFO)); } } if (m_pLFile->eLfType == XFLM_LF_COLLECTION) { // Only leaf blocks of collections need a NodeVerifier object State[0].pNodeVerifier = pNodeVerifier; // If this is a collection BTree, create a result set to hold the pointer // information from all the nodes in this btree if (RC_BAD( rc = getBtResultSet( &State[ 0].pNodeRS))) { goto Exit; } // The nodeVerifier will setup the Node Result Set etc.. if (pNodeVerifier) { pNodeVerifier->setupNodeRS( State[ 0].pNodeRS); } } if ((m_uiFlags & XFLM_DO_LOGICAL_CHECK) && State[ 0].pXRefRS == NULL) { if (m_pXRefRS == NULL) { if (RC_BAD( rc = getBtResultSet( &m_pXRefRS))) { goto Exit; } } State[ 0].pXRefRS = m_pXRefRS; // The nodeVerifier will setup the Node Result Set etc.. if (pNodeVerifier) { pNodeVerifier->setupXRefRS( State[ 0].pXRefRS); } } // Call verifySubTree to check the B-TREE starting at the // root block. Reset: rc = verifySubTree( NULL, &State [pLogicalFile->uiNumLevels - 1], m_pLFile->uiRootBlk, &pucResetKey, uiResetKeyLen, ui64ResetNodeId); if (rc == NE_XFLM_RESET_NEEDED || rc == NE_XFLM_OLD_VIEW) { FLMUINT uiNumLevels; if (rc == NE_XFLM_RESET_NEEDED) { m_LastStatusRc = NE_XFLM_OK; } // If it is a read transaction, reset. if (m_pDb->getTransType() == XFLM_READ_TRANS) { // Free the KrefCntrl m_pDb->krefCntrlFree(); // Abort the read transaction if (RC_BAD( rc = m_pDb->transAbort())) { goto Exit; } // Try to start a new read transaction if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { goto Exit; } } // If we already have a reset key buffer, we need to free it. // We will start by repositioning to the root level key we were // last on. if (pucResetKey) { f_free( &pucResetKey); } uiResetKeyLen = State[ pLogicalFile->uiNumLevels - 1].uiElmKeyLen; ui64ResetNodeId = State[ pLogicalFile->uiNumLevels - 1].ui64ElmNodeId; if (RC_BAD( rc = f_calloc( uiResetKeyLen + 1, &pucResetKey))) { goto Exit; } f_memcpy( pucResetKey, State[ pLogicalFile->uiNumLevels - 1].pucElmKey, uiResetKeyLen); // On Reset, we may need to reget the LFILE and IXD. uiNumLevels = pLogicalFile->uiNumLevels; if (pLogicalFile->eLfType == XFLM_LF_COLLECTION) { if (RC_BAD( rc = m_pDb->m_pDict->getCollection( pLogicalFile->uiLfNum, &m_pCollection, TRUE))) { goto Exit; } m_pIxd = NULL; m_pLFile = &m_pCollection->lfInfo; } else { if (RC_BAD( rc = m_pDb->m_pDict->getIndex( pLogicalFile->uiLfNum, &m_pLFile, &m_pIxd, TRUE))) { goto Exit; } m_pCollection = NULL; } if (RC_BAD( rc = getLfInfo( pLogicalFile, m_pLFile))) { goto Exit; } if (uiNumLevels != pLogicalFile->uiNumLevels) { // Since the block structure of the BTree has been changed, we have // to begin our scan again from the top. We need to gather stats too. uiResetKeyLen = ~(FLMUINT)0; if (pucResetKey) { f_free( &pucResetKey); } ui64ResetNodeId = 0; // Initialize the state information for each level of the B-TREE. for (uiCurrLevel = 0; uiCurrLevel < pLogicalFile->uiNumLevels; uiCurrLevel++) { FLMUINT uiExpectedBlkType; // If we are resetting to a particular key, save the statistics // which were gathered so far. if (uiResetKeyLen != ~(FLMUINT)0) { // Save the statistics which were gathered. pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount = State [uiCurrLevel].ui64KeyCount; f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, &State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO)); } if (m_pLFile->eLfType == XFLM_LF_INDEX) { if (uiCurrLevel == 0) { if (m_pIxd->uiNumDataComponents) { uiExpectedBlkType = BT_LEAF_DATA; } else { uiExpectedBlkType = BT_LEAF; } } else if (m_pIxd->uiFlags & IXD_ABS_POS) { uiExpectedBlkType = BT_NON_LEAF_COUNTS; } else { uiExpectedBlkType = BT_NON_LEAF; } } else // collection BTree... { if (uiCurrLevel == 0) { uiExpectedBlkType = BT_LEAF_DATA; } else { uiExpectedBlkType = BT_NON_LEAF; } } flmInitReadState( &State [uiCurrLevel], &bStateInitialized [uiCurrLevel], (FLMUINT)m_pDb-> m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion, m_pDb, pLogicalFile, uiCurrLevel, uiExpectedBlkType, &pucKeyBuffer [uiCurrLevel * XFLM_MAX_KEY_SIZE]); State[ uiCurrLevel].pCollection = m_pCollection; State[ uiCurrLevel].uiRootLevel = pLogicalFile->uiNumLevels - 1; State[ uiCurrLevel].uiCurrLf = uiCurrLf; if (uiResetKeyLen == ~(FLMUINT)0) { State [uiCurrLevel].ui32LastChildAddr = 0; State [uiCurrLevel].uiElmLastFlag = TRUE; } else { // Restore the statistics which were gathered so far. State [uiCurrLevel].ui64KeyCount = pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount; f_memcpy( &State [uiCurrLevel].BlkInfo, &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, sizeof( BLOCK_INFO)); } } } goto Reset; } if (RC_BAD( rc)) { goto Exit; } // If this was a collection, then go through the result set and verify // all of the node pointers... if (State[ 0].pNodeRS) { FLMINT32 i32ErrCode; // Setup the current progress phase m_Progress.i32CheckPhase = XFLM_CHECK_DOM_LINKS; m_Progress.bStartFlag = TRUE; m_Progress.ui32LfNumber = (FLMUINT32)m_pLFile->uiLfNum; m_Progress.ui32LfType = (FLMUINT32)m_pLFile->eLfType; if (RC_BAD( rc = chkCallProgFunc())) { break; } m_Progress.bStartFlag = FALSE; f_yieldCPU(); m_LastStatusRc = verifyNodePointers( &State[ 0], &i32ErrCode); if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)State[ 0].uiLevel, (FLMUINT32)m_pLFile->uiBlkAddress, 0, 0, 0); } State[ 0].pNodeRS->Release(); State[ 0].pNodeRS = NULL; if (pNodeVerifier) { // Resets the Node verifier RS to NULL. pNodeVerifier->setupNodeRS( State[ 0].pNodeRS); } } // Verify that all of the levels' next block address's are 0. if (RC_OK( m_LastStatusRc)) { for (uiCurrLevel = 0; uiCurrLevel < pLogicalFile->uiNumLevels; uiCurrLevel++) { // Save the statistics which were gathered. pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount = State [uiCurrLevel].ui64KeyCount; f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, &State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO)); // Make sure the last block had a NEXT block address of 0. if (State [uiCurrLevel].ui32NextBlkAddr != 0xFFFFFFFF && State [uiCurrLevel].ui32NextBlkAddr != 0) { FLMINT32 i32BlkErrCode; // Verify our finding. Get the block in question and see // if it realy has a problem. if (RC_BAD( rc = blkRead( State[ uiCurrLevel].ui32BlkAddress, &pBlkHdr, &pSCache, &i32BlkErrCode))) { // Log the error. if (i32BlkErrCode) { chkReportError( i32BlkErrCode, XFLM_LOCALE_LFH_LIST, 0, 0, 0xFF, State[ uiCurrLevel].ui32BlkAddress, 0, 0, 0); } goto Exit; } if (pBlkHdr->ui32NextBlkInChain != 0) { chkReportError( FLM_BAD_LAST_BLK_NEXT, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiCurrLevel, 0, 0, 0, 0); } ScaReleaseCache( pSCache, FALSE); pSCache = NULL; pBlkHdr = NULL; } } } if (RC_BAD( m_LastStatusRc)) { break; } // If we are doing a logical index check, need to see if we used up // all of the keys in the result set for this index. if (pLogicalFile->eLfType == XFLM_LF_INDEX && !m_bPhysicalCorrupt && (m_uiFlags & XFLM_DO_LOGICAL_CHECK) && bRSFinalized) { for (;;) { if (RC_BAD( rc = chkGetNextRSKey())) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; break; } goto Exit; } else { // Updated statistics m_Progress.ui64NumKeysExamined++; if (RC_BAD( rc = resolveIXMissingKey( &(State[ 0])))) { goto Exit; } } } } uiCurrLf++; if (pucResetKey) { f_free( &pucResetKey); } pucResetKey = NULL; uiResetKeyLen = ~(FLMUINT)0; ui64ResetNodeId = 0; } Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } else if (pBlkHdr) { f_free( &pBlkHdr); } if (pucKeyBuffer) { f_free( &pucKeyBuffer); } if (pucResetKey) { f_free( &pucResetKey); } if (State[ 0].pNodeRS) { State[ 0].pNodeRS->Release(); State[ 0].pNodeRS = NULL; if (pNodeVerifier) { pNodeVerifier->setupNodeRS( NULL); } } // Clean up the NodeVerifier instance... if (pNodeVerifier) { pNodeVerifier->Release(); } // Cleanup any temporary index check files if (m_pIxRSet) { m_pIxRSet->Release(); m_pIxRSet = NULL; } if (m_puiIxArray) { f_free( &m_puiIxArray); } if (RC_OK( rc) && RC_BAD( m_LastStatusRc)) { rc = m_LastStatusRc; } return( rc); } /*************************************************************************** Desc: This routine allocates and initializes the LF table (array of LF_HDR structures). *****************************************************************************/ RCODE F_DbCheck::setupLfTable() { RCODE rc = NE_XFLM_OK; FLMUINT uiLfHdrOffset; IXD * pIxd; F_COLLECTION * pCollection; F_Dict * pDict = m_pDb->m_pDict; FLMUINT uiIndexNum; FLMUINT uiCollectionNum; // Set up the table such that the dictionary is checked first, // followed by data containers, and then indexes. This is // necessary for the logical (index) check to work. The // data records must be extracted before the indexes are // checked so that the temporary result set, used during // the logical check, can be built. m_pDbInfo->freeLogicalFiles(); m_Progress.ui32NumLFs = 0; if (pDict) { // Count the indexes. uiIndexNum = 0; for (;;) { if ((pIxd = pDict->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; m_pDbInfo->m_uiNumIndexes++; } // Count the collections uiCollectionNum = 0; for (;;) { if ((pCollection = pDict->getNextCollection( uiCollectionNum, TRUE)) == NULL) { break; } uiCollectionNum = pCollection->lfInfo.uiLfNum; m_pDbInfo->m_uiNumCollections++; } m_pDbInfo->m_uiNumLogicalFiles = m_pDbInfo->m_uiNumIndexes + m_pDbInfo->m_uiNumCollections; m_Progress.ui32NumLFs = (FLMUINT32)m_pDbInfo->m_uiNumLogicalFiles; // Allocate memory for each collection and index, then set up each // collection and index if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( LF_HDR) * m_pDbInfo->m_uiNumLogicalFiles), (void **)&m_pDbInfo->m_pLogicalFiles))) { goto Exit; } uiLfHdrOffset = 0; // Add in our dictionary collection first. Do field // definitions first, then collection definitions, then index // definitions. if (RC_BAD( rc = pDict->getCollection( XFLM_DICT_COLLECTION, &pCollection))) { goto Exit; } if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], &pCollection->lfInfo))) { goto Exit; } uiLfHdrOffset++; // Add in default data collection if (RC_BAD( rc = pDict->getCollection( XFLM_DATA_COLLECTION, &pCollection))) { goto Exit; } if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], &pCollection->lfInfo))) { goto Exit; } uiLfHdrOffset++; // Add in the maintenance collection if (RC_BAD( rc = pDict->getCollection( XFLM_MAINT_COLLECTION, &pCollection))) { goto Exit; } if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], &pCollection->lfInfo))) { goto Exit; } uiLfHdrOffset++; // Add in user defined collections uiCollectionNum = 0; for (;;) { if ((pCollection = pDict->getNextCollection( uiCollectionNum, FALSE)) == NULL) { break; } uiCollectionNum = pCollection->lfInfo.uiLfNum; if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], &pCollection->lfInfo))) { goto Exit; } uiLfHdrOffset++; } // Indexes need to be in order from lowest to highest // because the result set is ordered that way. uiIndexNum = 0; for (;;) { if ((pIxd = pDict->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], &pIxd->lfInfo))) { goto Exit; } uiLfHdrOffset++; } } Exit: return( rc); } /*************************************************************************** Desc: Initializes index checking information. *****************************************************************************/ RCODE F_DbCheck::setupIxInfo( void) { RCODE rc = NE_XFLM_OK; LF_HDR * pLogicalFile; FLMUINT uiLoop; FLMUINT uiIxCount; // Initialize the result set. The result set will be used // to build an ordered list of keys for comparison to // the database's indexes. if (RC_BAD( rc = getBtResultSet( &m_pIxRSet))) { goto Exit; } // Build list of all indexes if (m_pDbInfo->m_uiNumIndexes) { // Allocate memory to save each index number if (RC_BAD( rc = f_alloc( (FLMUINT)(sizeof( FLMUINT) * m_pDbInfo->m_uiNumIndexes), &m_puiIxArray))) { goto Exit; } // Save the index numbers into the array. uiIxCount = 0; pLogicalFile = m_pDbInfo->m_pLogicalFiles; for( uiLoop = 0; uiLoop < m_pDbInfo->m_uiNumLogicalFiles; uiLoop++, pLogicalFile++) { if (pLogicalFile->eLfType == XFLM_LF_INDEX) { m_puiIxArray[ uiIxCount] = pLogicalFile->uiLfNum; uiIxCount++; } } } m_bGetNextRSKey = TRUE; Exit: // Clean up any memory on error exit. if (RC_BAD( rc)) { if (m_pIxRSet) { m_pIxRSet->Release(); m_pIxRSet = NULL; } if (m_puiIxArray) { f_free( &m_puiIxArray); } } return( rc); } /*************************************************************************** Desc: This routine reads the LFH areas from disk to make sure they are up to date in memory. *****************************************************************************/ RCODE F_DbCheck::getLfInfo( LF_HDR * pLogicalFile, LFILE * pLFile ) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = NULL; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiSaveLevel; FLMINT32 i32BlkErrCode; pLogicalFile->eLfType = pLFile->eLfType; pLogicalFile->uiLfNum = pLFile->uiLfNum; pLogicalFile->uiRootBlk = pLFile->uiRootBlk; // Read in the block containing the logical file header. if (RC_BAD( rc = blkRead( pLFile->uiBlkAddress, &pBlkHdr, &pSCache, &i32BlkErrCode))) { // Log the error. if (i32BlkErrCode) { chkReportError( i32BlkErrCode, XFLM_LOCALE_LFH_LIST, 0, 0, 0xFF, (FLMUINT32)pLFile->uiBlkAddress, 0, 0, 0); } goto Exit; } uiSaveLevel = pLogicalFile->uiNumLevels; // Read root block to get the number of levels in the B-TREE flmAssert( pLFile->uiRootBlk); if (RC_BAD( rc = blkRead( pLFile->uiRootBlk, &pBlkHdr, &pSCache, &i32BlkErrCode))) { if (i32BlkErrCode) { chkReportError( i32BlkErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)pLFile->uiLfNum, (FLMUINT32)pLFile->eLfType, 0xFF, (FLMUINT32)pLFile->uiRootBlk, 0, 0, 0); } goto Exit; } pLogicalFile->uiNumLevels = (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel) + 1; // Need to make sure that the level extracted from // the block is valid. if (((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel >= BH_MAX_LEVELS) { chkReportError( FLM_BAD_BLK_HDR_LEVEL, XFLM_LOCALE_B_TREE, (FLMUINT32)pLFile->uiLfNum, (FLMUINT32)pLFile->eLfType, (FLMUINT32)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel), (FLMUINT32)pLFile->uiRootBlk, 0, 0, 0); // Force pLogicalFile->uiNumLevels to 1 so that we don't crash pLogicalFile->uiNumLevels = 1; } // If the number of levels changed, reset the level information // on this logical file. if (uiSaveLevel != pLogicalFile->uiNumLevels && pLogicalFile->uiNumLevels) { if (pLogicalFile->uiNumLevels > uiSaveLevel) { if ( pLogicalFile->pLevelInfo) { f_free( &pLogicalFile->pLevelInfo); } if (RC_BAD( rc = f_calloc( (sizeof( LEVEL_INFO) * pLogicalFile->uiNumLevels), (void **)&pLogicalFile->pLevelInfo))) { goto Exit; } } } Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } else if (pBlkHdr) { f_free( &pBlkHdr); } return( rc); } /*************************************************************************** Desc: Goes throught the (finalized) result set and validates that all the node pointers point to the right nodes *****************************************************************************/ RCODE F_DbCheck::verifyNodePointers( STATE_INFO * pStateInfo, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; NODE_RS_ENTRY * pRSEntry = NULL; NODE_RS_ENTRY * pTmpRSEntry = NULL; F_BtResultSet * pResult = pStateInfo->pNodeRS; FLMUINT uiRSEntrySize = sizeof( NODE_RS_ENTRY); FLMBOOL bFirst = TRUE; FLMBYTE pucKey[ XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLength = XFLM_MAX_KEY_SIZE; F_Btree * pBTree = NULL; FLMINT32 i32ErrCode = 0; *pi32ErrCode = 0; if (RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pRSEntry))) { goto Exit; } if (RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pTmpRSEntry))) { goto Exit; } for (;;) { m_Progress.ui64NumDomNodes++;; if (bFirst) { if (RC_BAD( rc = pResult->getBTree( NULL, NULL, &pBTree))) { goto Exit; } if (RC_BAD( rc = pResult->getFirst( NULL, NULL, pBTree, pucKey, XFLM_MAX_KEY_SIZE, &uiKeyLength, (FLMBYTE *)pRSEntry, sizeof( NODE_RS_ENTRY), &uiRSEntrySize))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) { rc = NE_XFLM_OK; break; } goto Exit; } } else { if (RC_BAD( rc = pResult->getNext( NULL, NULL, pBTree, pucKey, XFLM_MAX_KEY_SIZE, &uiKeyLength, (FLMBYTE *)pRSEntry, sizeof( NODE_RS_ENTRY), &uiRSEntrySize))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) { rc = NE_XFLM_OK; break; } goto Exit; } } bFirst = FALSE; if (RC_BAD( rc = verifyRootLink( pRSEntry, uiRSEntrySize, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if (RC_BAD( rc = verifyParentLink( pRSEntry, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if( RC_BAD( rc = verifyFirstChildLink( pRSEntry, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if( i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if (RC_BAD( rc = verifyLastChildLink( pRSEntry, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if (RC_BAD( rc = verifyPrevSiblingLink( pRSEntry, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if (RC_BAD( rc = verifyNextSiblingLink( pRSEntry, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if (RC_BAD( rc = verifyAnnotationLink( pRSEntry, pTmpRSEntry, pResult, &i32ErrCode))) { goto Exit; } if (i32ErrCode) { chkReportError( i32ErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, 0, 0, 0, FLM_MAX_UINT32, pRSEntry->hdr.ui64NodeId); i32ErrCode = 0; m_Progress.ui64NumBrokenDomLinks++; } else { m_Progress.ui64NumDomLinksVerified++; } if (RC_BAD( rc = chkCallProgFunc())) { break; } f_yieldCPU(); } Exit: if (pBTree) { pResult->freeBTree( &pBTree); } if (pRSEntry) { f_free( &pRSEntry); } if (pTmpRSEntry) { f_free( &pTmpRSEntry); } return rc; } /******************************************************************** Desc: Verify that the Root Id field points to a valid node. ********************************************************************/ FSTATIC RCODE verifyRootLink( NODE_RS_ENTRY * pRSEntry, FLMUINT uiRSEntrySize, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64RootId) { // Returns NE_XFLM_OK goto Exit; } if( ui64RootId != pRSEntry->hdr.ui64NodeId) { pTmpRSEntry->hdr.ui64NodeId = ui64RootId; if (RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_ROOT_LINK; goto Exit; } // Found it! // Make sure this Root is a "True" root node. // Cannot have a parent node if (pTmpRSEntry->hdr.ui16BitMap & CHK_BM_PARENT_ID) { *pi32ErrCode = FLM_BAD_ROOT_PARENT; goto Exit; } // Cannot be another node child, or attribute or annotation node. if (pTmpRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED || pTmpRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED || pTmpRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) { *pi32ErrCode = FLM_BAD_ROOT_LINK; goto Exit; } pTmpRSEntry->hdr.ui16Flags |= CHK_ROOT_VERIFIED; if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } } else { // If the node we were passed IS the root node... // Cannot have a parent node if (pRSEntry->hdr.ui16BitMap & CHK_BM_PARENT_ID) { *pi32ErrCode = FLM_BAD_ROOT_LINK; goto Exit; } // Cannot be another node child, or attribute or annotation node. if (pRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED || pRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED || pRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) { *pi32ErrCode = FLM_BAD_ROOT_LINK; goto Exit; } pRSEntry->hdr.ui16Flags |= CHK_ROOT_VERIFIED; if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pRSEntry, uiRSEntrySize))) { goto Exit; } } Exit: return rc; } /******************************************************************** Desc: Verify that the parent Id field points to a valid node. ********************************************************************/ FSTATIC RCODE verifyParentLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry); FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT64 ui64TmpRootId; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64ParentId) { // With no parent node, this node cannot have any child // or attribute or annotation nodes. if (pRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED || pRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED || pRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) { *pi32ErrCode = FLM_BAD_PARENT_LINK; } goto Exit; } if (ui64ParentId == pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_PARENT_LINK; goto Exit; } pTmpRSEntry->hdr.ui64NodeId = ui64ParentId; if (RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_PARENT_LINK; goto Exit; } // Verify that they are in the same document... ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); if (ui64TmpRootId) { if (ui64RootId != ui64TmpRootId) { *pi32ErrCode = FLM_BAD_PARENT_LINK; goto Exit; } } else { if (ui64ParentId != ui64RootId) { *pi32ErrCode = FLM_BAD_PARENT_LINK; goto Exit; } } pTmpRSEntry->hdr.ui16Flags |= CHK_PARENT_VERIFIED; if( RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Verify that the First Child link points to a vaild node. ********************************************************************/ FSTATIC RCODE verifyFirstChildLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64FirstChildId = getLinkVal( CHK_BM_FIRST_CHILD, pRSEntry); FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT64 ui64TmpRootId; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64FirstChildId) { // Better not have a last child either. if (getLinkVal( CHK_BM_LAST_CHILD, pRSEntry)) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; } if (pRSEntry->hdr.ui16Flags & CHK_PARENT_VERIFIED) { if( !getLinkVal( CHK_BM_ANNOTATION, pRSEntry)) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; } } goto Exit; } if (ui64FirstChildId == pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; goto Exit; } pTmpRSEntry->hdr.ui64NodeId = ui64FirstChildId; if (RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; goto Exit; } // Must belong to the same document / root ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); if (ui64RootId) { if (ui64RootId != ui64TmpRootId) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; goto Exit; } } else { if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; goto Exit; } } // Make sure this child has not been visited as a first child already. if (pTmpRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; goto Exit; } // Does this child reference the correct parent? if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; goto Exit; } // Mark the child as visited as a first child. pTmpRSEntry->hdr.ui16Flags |= CHK_FIRST_CHILD_VERIFIED; if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } Exit: return rc; } /******************************************************************** Desc: Verify that the Last Child link points to a vaild node. ********************************************************************/ FSTATIC RCODE verifyLastChildLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64LastChildId = getLinkVal( CHK_BM_LAST_CHILD, pRSEntry); FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT64 ui64TmpRootId; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64LastChildId) { // Better not have a first child either. if (getLinkVal( CHK_BM_FIRST_CHILD, pRSEntry)) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; } if (pRSEntry->hdr.ui16Flags & CHK_PARENT_VERIFIED) { if( !getLinkVal( CHK_BM_ANNOTATION, pRSEntry)) { *pi32ErrCode = FLM_BAD_FIRST_CHILD_LINK; } } goto Exit; } if (ui64LastChildId == pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; goto Exit; } pTmpRSEntry->hdr.ui64NodeId = ui64LastChildId; if( RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; goto Exit; } // Must belong to the same document / root ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); if (ui64RootId) { if (ui64RootId != ui64TmpRootId) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; goto Exit; } } else { if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; goto Exit; } } // Make sure this child has not been visited as a last child already. if (pTmpRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; goto Exit; } // Does this child reference the correct parent? if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_LAST_CHILD_LINK; goto Exit; } // Mark the child as visited as a last child. pTmpRSEntry->hdr.ui16Flags |= CHK_LAST_CHILD_VERIFIED; if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } Exit: return rc; } /******************************************************************** Desc: Verify that the Prev Sibling link points to a valid node. ********************************************************************/ FSTATIC RCODE verifyPrevSiblingLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64PrevSibling = getLinkVal( CHK_BM_PREV_SIBLING, pRSEntry); FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64PrevSibling) { // Should not be a Next Sibling to anyone. if (pRSEntry->hdr.ui16Flags & CHK_NEXT_SIBLING_VERIFIED) { *pi32ErrCode = FLM_BAD_PREV_SIBLING_LINK; } // Must also verify that this node is the first child of the parent node // - if there is a parent. if (ui64ParentId) { FLMUINT64 ui64FirstChild; pTmpRSEntry->hdr.ui64NodeId = ui64ParentId; if( RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_PARENT_LINK; goto Exit; } ui64FirstChild = getLinkVal( CHK_BM_FIRST_CHILD, pTmpRSEntry); if (ui64FirstChild != pRSEntry->hdr.ui64NodeId) { FLMUINT64 ui64Annot; // It may be an annotation Node. ui64Annot = getLinkVal( CHK_BM_ANNOTATION, pTmpRSEntry); if (ui64Annot != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_PREV_SIBLING_LINK; goto Exit; } } } goto Exit; } pTmpRSEntry->hdr.ui64NodeId = ui64PrevSibling; if( RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_PREV_SIBLING_LINK; goto Exit; } // Must belong to the same document unless both nodes are // document roots if( ui64RootId != getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry)) { if( ui64ParentId || getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry)) { *pi32ErrCode = FLM_BAD_PREV_SIBLING_LINK; goto Exit; } } // The previous sibling should not have been visited as a previous // sibling before now. if (pTmpRSEntry->hdr.ui16Flags & CHK_PREV_SIBLING_VERIFIED) { *pi32ErrCode = FLM_BAD_PREV_SIBLING_LINK; goto Exit; } // Should point to "this" node. if (getLinkVal( CHK_BM_NEXT_SIBLING, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_PREV_SIBLING_LINK; goto Exit; } // Mark the previous sibling as being visited as such. pTmpRSEntry->hdr.ui16Flags |= CHK_PREV_SIBLING_VERIFIED; if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } Exit: return rc; } /******************************************************************** Desc: Verify that the Next Sibling link points to a valid node. ********************************************************************/ FSTATIC RCODE verifyNextSiblingLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NextSibling = getLinkVal( CHK_BM_NEXT_SIBLING, pRSEntry); FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64NextSibling) { // Should not be a Prev Sibling to anyone. if (pRSEntry->hdr.ui16Flags & CHK_PREV_SIBLING_VERIFIED) { *pi32ErrCode = FLM_BAD_NEXT_SIBLING_LINK; } // Must also verify that this node is the last child of the parent node // - if there is a parent. if (ui64ParentId) { FLMUINT64 ui64LastChild; pTmpRSEntry->hdr.ui64NodeId = ui64ParentId; if( RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_PARENT_LINK; goto Exit; } ui64LastChild = getLinkVal( CHK_BM_LAST_CHILD, pTmpRSEntry); if (ui64LastChild != pRSEntry->hdr.ui64NodeId) { FLMUINT64 ui64Annot; // It may be an annotation Node. ui64Annot = getLinkVal( CHK_BM_ANNOTATION, pTmpRSEntry); if (ui64Annot != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_NEXT_SIBLING_LINK; goto Exit; } } } goto Exit; } pTmpRSEntry->hdr.ui64NodeId = ui64NextSibling; if( RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_NEXT_SIBLING_LINK; goto Exit; } // Must belong to the same document unless both nodes are // document roots if( ui64RootId != getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry)) { if( ui64ParentId || getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry)) { *pi32ErrCode = FLM_BAD_NEXT_SIBLING_LINK; goto Exit; } } // The next sibling should not have been visited as a next // sibling before now. if( pTmpRSEntry->hdr.ui16Flags & CHK_NEXT_SIBLING_VERIFIED) { *pi32ErrCode = FLM_BAD_NEXT_SIBLING_LINK; goto Exit; } // Should point to "this" node. if( getLinkVal( CHK_BM_PREV_SIBLING, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_NEXT_SIBLING_LINK; goto Exit; } // Mark the previous sibling as being visited as such. pTmpRSEntry->hdr.ui16Flags |= CHK_NEXT_SIBLING_VERIFIED; if( RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: ********************************************************************/ FSTATIC RCODE verifyAnnotationLink( NODE_RS_ENTRY * pRSEntry, NODE_RS_ENTRY * pTmpRSEntry, F_BtResultSet * pResult, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Annotation = getLinkVal( CHK_BM_ANNOTATION, pRSEntry); FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); FLMUINT uiTmpRSEntrySize; FLMUINT64 ui64TmpRootId; FLMUINT uiKeySize = sizeof( FLMUINT64); f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); if (!ui64Annotation) { goto Exit; } pTmpRSEntry->hdr.ui64NodeId = ui64Annotation; if( RC_BAD( rc = pResult->findEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), &uiTmpRSEntrySize))) { *pi32ErrCode = FLM_BAD_ANNOTATION_LINK; goto Exit; } // Must belong to the same document / root // Annotations may belong to a node that does not have a Root Id. If // that is the case, the parent and root of the annotaion should match and // the parent should point to the source node. ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); if( ui64RootId) { if (ui64RootId != ui64TmpRootId) { *pi32ErrCode = FLM_BAD_ANNOTATION_LINK; goto Exit; } } else { // With no root, the temporary root must point to "this" node. if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_ANNOTATION_LINK; goto Exit; } } // The annotation should not have been visited as such before now. if (pTmpRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) { *pi32ErrCode = FLM_BAD_ANNOTATION_LINK; goto Exit; } // Parent should point to "this" node. if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) { *pi32ErrCode = FLM_BAD_ANNOTATION_LINK; goto Exit; } // Mark the last attr as being visited as such. pTmpRSEntry->hdr.ui16Flags |= CHK_ANNOTATION_VERIFIED; if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) { goto Exit; } Exit: return rc; } /*************************************************************************** Desc: This routine does for chains of data-only blocks what verifySubTree does for the other blocks. Basically, it's going to read in each block in the chain, perform some basic verification on the header and add the data to the NodeVerifier object. *****************************************************************************/ RCODE F_DbCheck::verifyDOChain( STATE_INFO * pParentState, FLMUINT uiBlkAddr, FLMINT32 * pi32ElmErrCode) { RCODE rc = NE_XFLM_OK; F_NodeVerifier * pNodeVerifier = pParentState->pNodeVerifier; F_CachedBlock * pSCache = NULL; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiParentBlkAddr = pParentState->pBlkHdr->ui32BlkAddr; FLMUINT uiPrevNextBlkAddr; // The ui32NextBlkInChain field from the previous block FLMUINT uiNumErrors = 0; FLMUINT uiNumBlksRead = 0; FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); FLMBYTE * pucData = NULL; FLMUINT uiDataLen = 0; BLOCK_INFO * pBlkInfo = &pParentState->BlkInfo; STATE_INFO StateInfo; // All leaf nodes are level 0, and only leaf nodes can point // to data only blocks... if (pParentState->uiLevel != 0) { *pi32ElmErrCode = FLM_BAD_ELM_INVALID_LEVEL; rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } //Initialize the StateInfo struct f_memset( &StateInfo, 0, sizeof( STATE_INFO)); StateInfo.pCollection = pParentState->pCollection; StateInfo.pDb = pParentState->pDb; StateInfo.ui64ElmNodeId = pParentState->ui64ElmNodeId; // Initialize the NodeVerifier object if (pNodeVerifier) { pNodeVerifier->Reset( pParentState); } // Now, loop through every block in the chain... uiPrevNextBlkAddr = 0; while (uiBlkAddr) { // Read in the next block in the chain f_yieldCPU(); if (RC_BAD( rc = blkRead( uiBlkAddr, &pBlkHdr, &pSCache, pi32ElmErrCode))) { if (*pi32ElmErrCode) { uiNumErrors++; chkReportError( *pi32ElmErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)StateInfo.uiLevel, (FLMUINT32)uiBlkAddr, (FLMUINT32)uiParentBlkAddr, 0, 0); } else if (rc == NE_XFLM_OLD_VIEW) { // We're going to have to reset. Unfortunately, // we don't know how to position ourselves into the middle // of a record, so we'll have to reset back to the beginning // of the record. We should still be able to finish processing. // We only need to see enough of the DOM node to build the header. // It's the header that gives us the DOM link information. if (pNodeVerifier) { pNodeVerifier->Reset( pParentState); } m_Progress.ui64BytesExamined -= (FLMUINT64)(uiBlockSize*uiNumBlksRead); chkCallProgFunc(); } goto Exit; } // Chains of data only blocks should always have at least 2 blocks... if ((uiNumBlksRead == 0) && (pBlkHdr->ui32NextBlkInChain == 0)) { *pi32ElmErrCode = FLM_BAD_DATA_BLOCK_COUNT; rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } // Record the progress that we're making uiNumBlksRead++; m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; chkCallProgFunc(); // Check the block header. StateInfo.pBlkHdr = pBlkHdr; StateInfo.uiBlkType = BT_DATA_ONLY; StateInfo.ui32BlkAddress = (FLMUINT32)uiBlkAddr; *pi32ElmErrCode = flmVerifyBlockHeader( &StateInfo, pBlkInfo, uiBlockSize, 0xFFFFFFFF, (uiNumBlksRead > 1) ? uiPrevNextBlkAddr : 0, TRUE); if (*pi32ElmErrCode != 0) { uiNumErrors++; chkReportError( *pi32ElmErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)StateInfo.uiLevel, (FLMUINT32)uiBlkAddr, (FLMUINT32)uiParentBlkAddr, 0, 0); } // Verify that the ui16BlkBytesAvail is a reasonable size... if( (pBlkHdr->ui32NextBlkInChain != 0) && (pBlkHdr->ui16BlkBytesAvail != 0)) { *pi32ElmErrCode = FLM_BAD_AVAIL_SIZE; rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } // Add the current data to the verifier.... // We really only should need to do this if this is the first block // in the chain. if( !pBlkHdr->ui32PrevBlkInChain) { FLMBYTE * pucPtr = (FLMBYTE *)pBlkHdr + sizeof( F_BLK_HDR); FLMUINT uiKeySize = (FLMUINT)FB2UW( pucPtr); pucData = pucPtr + uiKeySize + 2; uiDataLen = uiBlockSize - sizeof( F_BLK_HDR) - pBlkHdr->ui16BlkBytesAvail - uiKeySize - 2; if (pNodeVerifier) { if (RC_BAD( rc = pNodeVerifier->AddData( StateInfo.ui64ElmNodeId, pucData, uiDataLen))) { goto Exit; } } } uiPrevNextBlkAddr = uiBlkAddr; uiBlkAddr = pBlkHdr->ui32NextBlkInChain; } // end of while (uiNextBlkAddress) Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } else if (pBlkHdr) { // The reason for the else is that pBlkHdr will normally be // pointing into pSCache. Only if the call to getBlock() // inside of blkRead() fails will memory be allocated // explicitly for pBlkHdr f_free( &pBlkHdr); } return rc; } /*************************************************************************** Desc: This routine checks all of the blocks/links in a sub-tree of a B-TREE. It calls itself recursively whenever it descends a level in the tree. *****************************************************************************/ RCODE F_DbCheck::verifySubTree( STATE_INFO * pParentState, STATE_INFO * pStateInfo, FLMUINT uiBlkAddress, FLMBYTE ** ppucResetKey, FLMUINT uiResetKeyLen, FLMUINT64 ui64ResetNodeId) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = NULL; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiLevel = pStateInfo->uiLevel; FLMUINT uiBlkType = pStateInfo->uiBlkType; FLMUINT uiLfType = m_pLFile->eLfType; FLMUINT uiBlockSize = m_pDb->m_pDatabase->m_uiBlockSize; FLMUINT uiParentBlkAddress; FLMUINT uiChildBlkAddress; FLMUINT uiPrevNextBlkAddress; FLMINT32 i32ElmErrCode; FLMINT32 i32BlkErrCode = 0; FLMINT32 i32LastErrCode = 0; FLMUINT uiNumErrors = 0; FLMUINT64 ui64SaveKeyCount = 0; FLMUINT64 ui64SaveKeyRefs = 0; BLOCK_INFO SaveBlkInfo; BLOCK_INFO * pBlkInfo; FLMBOOL bProcessElm; FLMBOOL bCountElm; FLMBOOL bDescendToChildBlocks; FLMINT iCompareStatus; FLMINT32 i32HdrErrCode; F_NodeVerifier * pNodeVerifier = pStateInfo->pNodeVerifier; STATE_INFO * pChildStateInfo = NULL; F_CachedBlock * pTmpSCache = NULL; F_BLK_HDR * pTmpBlkHdr = NULL; // Setup the state information. pStateInfo->pBlkHdr = NULL; pStateInfo->ui32BlkAddress = (FLMUINT32)uiBlkAddress; uiPrevNextBlkAddress = pStateInfo->ui32NextBlkAddr; uiParentBlkAddress = (pParentState) ? pParentState->ui32BlkAddress : 0xFFFFFFFF; // Read the sub-tree block into memory bDescendToChildBlocks = TRUE; if (RC_BAD( rc = blkRead( uiBlkAddress, &pBlkHdr, &pSCache, &i32BlkErrCode))) { if (i32BlkErrCode) { uiNumErrors++; i32LastErrCode = i32BlkErrCode; chkReportError( i32BlkErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, 0, 0); if( i32BlkErrCode == FLM_BAD_BLK_CHECKSUM) { bDescendToChildBlocks = FALSE; // Allow to continue the check, but if this is a non-leaf block // a non-zero i32BlkErrCode will prevent us from descending to // child blocks. Set rc to SUCCESS so we won't goto Exit below. rc = NE_XFLM_OK; } else if (i32BlkErrCode == FLM_COULD_NOT_SYNC_BLK) { i32LastErrCode = i32BlkErrCode; // Need the goto here, because rc is changed to SUCCESS, // and the goto below would get skipped. rc = NE_XFLM_OK; goto fix_state; } } // If rc was not changed to SUCCESS above, goto Exit. if (RC_BAD( rc)) { goto Exit; } } pStateInfo->pBlkHdr = pBlkHdr; // Verify the block header // Don't recount the block if we are resetting. if (uiResetKeyLen == ~(FLMUINT)0) { m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; pBlkInfo = &pStateInfo->BlkInfo; } else { pBlkInfo = NULL; } chkCallProgFunc(); // Check the block header. if ((i32HdrErrCode = flmVerifyBlockHeader( pStateInfo, pBlkInfo, uiBlockSize, (pParentState == NULL) ? 0 : 0xFFFFFFFF, (pParentState == NULL) ? 0 : pParentState->ui32LastChildAddr, TRUE)) == 0) { // Verify the previous block's next block address -- it should // equal the current block's address. if (uiPrevNextBlkAddress != 0xFFFFFFFF && uiPrevNextBlkAddress != uiBlkAddress && (uiResetKeyLen == ~(FLMUINT)0)) { i32HdrErrCode = FLM_BAD_PREV_BLK_NEXT; } } if (i32HdrErrCode != 0) { // Check to see if the previous block is still valid. // It may be that the block has gone away, and so is no longer valid. if (i32HdrErrCode == FLM_BAD_BLK_HDR_PREV) { flmAssert( pParentState); if (RC_BAD( rc = blkRead( pParentState->ui32LastChildAddr, &pTmpBlkHdr, &pTmpSCache, &i32BlkErrCode))) { i32LastErrCode = i32BlkErrCode; uiNumErrors++; chkReportError( i32BlkErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, 0, 0); } else { // If the block is free, it means that somewhere in our check, we // deleted entries that resulted in this block being emptied, // thus freed. if (pTmpBlkHdr->ui8BlkType == BT_FREE) { i32HdrErrCode = 0; } else { i32LastErrCode = i32HdrErrCode; uiNumErrors++; chkReportError( i32HdrErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, 0, 0); } } } else { i32LastErrCode = i32HdrErrCode; uiNumErrors++; chkReportError( i32HdrErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, 0, 0); } if (pTmpSCache) { ScaReleaseCache( pTmpSCache, FALSE); pTmpSCache = NULL; pTmpBlkHdr = NULL; } } // Verify the structure of the block if( RC_BAD( rc = verifyBlockStructure( uiBlockSize, (F_BTREE_BLK_HDR *)pBlkHdr))) { if (rc == NE_XFLM_BTREE_ERROR) { i32BlkErrCode = FLM_BAD_BLOCK_STRUCTURE; rc = NE_XFLM_OK; goto fix_state; } else { goto Exit; } } // Go through the elements in the block. for ( pStateInfo->uiElmOffset=0; (pStateInfo->uiElmOffset < ((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys && RC_OK( m_LastStatusRc)); pStateInfo->uiElmOffset++) { // If we are resetting, save any statistical information so we // can back it out if we need to. if (uiResetKeyLen != ~(FLMUINT)0) { ui64SaveKeyCount = pStateInfo->ui64KeyCount; ui64SaveKeyRefs = pStateInfo->ui64KeyRefs; f_memcpy( &SaveBlkInfo, &pStateInfo->BlkInfo, sizeof( BLOCK_INFO)); bCountElm = FALSE; bProcessElm = FALSE; } else { bCountElm = TRUE; bProcessElm = TRUE; } // Verify the element first, then check if we are restting... m_LastStatusRc = flmVerifyElement( pStateInfo, m_pLFile, m_pIxd, &i32ElmErrCode); if (i32ElmErrCode) { // Report any errors in the element. i32LastErrCode = i32ElmErrCode; uiNumErrors++; if (RC_BAD( rc = chkReportError( i32ElmErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, (FLMUINT32)pStateInfo->uiElmOffset, pStateInfo->ui64ElmNodeId))) { break; } } // See if we are resetting iCompareStatus = 0; if ((uiResetKeyLen != ~(FLMUINT)0) && pStateInfo->bValidKey && (!pStateInfo->uiElmKeyLen || ((iCompareStatus = f_memcmp( pStateInfo->pucElmKey, *ppucResetKey, pStateInfo->uiElmKeyLen < uiResetKeyLen ? pStateInfo->uiElmKeyLen : uiResetKeyLen)) >= 0))) { // A key length of 0 is valid. It refers to the LEM. All keys are // less than the LEM if their length is > 0. if ((uiResetKeyLen && pStateInfo->uiElmKeyLen || !pStateInfo->uiElmKeyLen)) { // If we passed the target key, or we are on the last element // then count it. bProcessElm = TRUE; if ((iCompareStatus > 0) || !(pStateInfo->uiElmKeyLen)) { if ( (uiBlkType == BT_LEAF_DATA) || (uiBlkType == BT_LEAF) ) { bCountElm = TRUE; uiResetKeyLen = ~(FLMUINT)0; if (*ppucResetKey) { f_free( ppucResetKey); } *ppucResetKey = NULL; ui64ResetNodeId = 0; } } else if ( uiLfType == XFLM_LF_INDEX) { bCountElm = TRUE; } } } if (bCountElm) { pStateInfo->BlkInfo.ui64ElementCount++; } // Check for index keys that can be verified on leaf level blocks. if (uiResetKeyLen == ~(FLMUINT)0) { if ( isIndexBlk( (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr) && (pStateInfo->pBlkHdr->ui8BlkType == BT_LEAF || pStateInfo->pBlkHdr->ui8BlkType == BT_LEAF_DATA) && pStateInfo->uiElmKeyLen) { FLMUINT64 ui64CurrTransId = pStateInfo->pDb->m_ui64CurrTransID; if (RC_BAD( rc = verifyIXRefs( pStateInfo, ui64ResetNodeId))) { goto Exit; } // Make sure we resynchronize when we make changes to // blocks we are looking at. if (pStateInfo->pDb->m_ui64CurrTransID != ui64CurrTransId) { if (m_bPhysicalCorrupt) { m_bPhysicalCorrupt = FALSE; m_uiFlags |= XFLM_DO_LOGICAL_CHECK; } rc = m_LastStatusRc = RC_SET( NE_XFLM_RESET_NEEDED); goto Exit; } } if (RC_BAD( m_LastStatusRc)) { break; } // Keep track of the number of continuation elements. if( (uiBlkType == BT_LEAF_DATA) && ((*pStateInfo->pucElm & BTE_FLAG_FIRST_ELEMENT) == 0)) { pStateInfo->BlkInfo.ui64ContElementCount++; pStateInfo->BlkInfo.ui64ContElmBytes += pStateInfo->uiElmLen; } } // Do some further checking. if (i32ElmErrCode == 0) { if (bProcessElm && (uiBlkType == BT_LEAF_DATA || uiBlkType == BT_LEAF) && uiLfType == XFLM_LF_COLLECTION) { // No need to process LEM element if ((pStateInfo->uiElmKeyLen != 0) && (pStateInfo->bValidKey)) { // Is this record stored in a chain of DO blocks...? if ((*pStateInfo->pucElm & BTE_FLAG_DATA_BLOCK) != 0) { flmAssert( pStateInfo->uiElmDataLen == 4); if( RC_BAD( rc = verifyDOChain( pStateInfo, FB2UD( pStateInfo->pucElmData), &i32ElmErrCode))) { goto Exit; } } else { // Since DOM nodes may be spread across multiple entries // in the Btree block, it may be impractical to read in // the entire node all at once, we need a way of doing the // verification a little at a time. The NodeVerifier // object handles this. We pass in the data as we get it, // it appends the data to any data it had left over from // a previous call, and then verifies as much as it can. // Any "left over" data is saved for the next call. // If the "first" flag is set on this element, twe need // to reset the verifier if( *pStateInfo->pucElm & BTE_FLAG_FIRST_ELEMENT && pNodeVerifier) { pNodeVerifier->Reset( pStateInfo); } // Add the current data to the verifier.... flmAssert( pStateInfo->ui64ElmNodeId); if (pNodeVerifier) { if (RC_BAD( rc = pNodeVerifier->AddData( pStateInfo->ui64ElmNodeId, pStateInfo->pucElmData, pStateInfo->uiElmDataLen))) { goto Exit; } } } // If this is the last entry for this element, then we can // finalize the node verifier. This entails assembling // the DOM node, and possibly adding required information // to a result set for later DOM link verification. We also // add the node Id to the index result set if we are // checking/verifying indexes. if( *pStateInfo->pucElm & BTE_FLAG_LAST_ELEMENT) { if( pNodeVerifier) { if( RC_BAD( rc = pNodeVerifier->finalize( m_pDb, m_pDb->m_pDict, pStateInfo->pCollection->lfInfo.uiLfNum, pStateInfo->ui64ElmNodeId, m_bSkipDOMLinkCheck, &i32ElmErrCode))) { goto Exit; } } } } if (bProcessElm) { if (i32ElmErrCode != 0) { // Report any errors in the element. i32LastErrCode = i32ElmErrCode; uiNumErrors++; chkReportError( i32ElmErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, (FLMUINT32)pStateInfo->uiElmOffset, pStateInfo->ui64ElmNodeId); if (RC_BAD( m_LastStatusRc)) { break; } } } } else if (uiBlkType != BT_LEAF_DATA && uiBlkType != BT_LEAF) { flmAssert( uiBlkType != BT_DATA_ONLY); uiChildBlkAddress = (FLMUINT)FB2UD(pStateInfo->pucElm); // Check the child sub-tree -- NOTE, this is a recursive call. if (bProcessElm) { if (!bDescendToChildBlocks) { rc = NE_XFLM_OK; } else { pChildStateInfo = (pStateInfo - 1); if (pChildStateInfo->uiElmKeyLen && (uiResetKeyLen != ~(FLMUINT)0)) { uiResetKeyLen = pChildStateInfo->uiElmKeyLen; ui64ResetNodeId = pChildStateInfo->ui64ElmNodeId; if (*ppucResetKey) { f_free( ppucResetKey); } if (RC_BAD( rc = f_calloc( uiResetKeyLen + 1, ppucResetKey))) { goto Exit; } f_memcpy( *ppucResetKey, pChildStateInfo->pucElmKey, uiResetKeyLen); } rc = verifySubTree( pStateInfo, (pStateInfo - 1), uiChildBlkAddress, ppucResetKey, uiResetKeyLen, ui64ResetNodeId); } if (RC_BAD( rc) || RC_BAD( m_LastStatusRc)) { goto Exit; } // Once we reach the key, set it to an empty to key so that // we will always descend to the child block after this point. uiResetKeyLen = ~(FLMUINT)0; if (*ppucResetKey) { f_free( ppucResetKey); } *ppucResetKey = NULL; ui64ResetNodeId = 0; } // Save the child block address in the level information pStateInfo->ui32LastChildAddr = (FLMUINT32)uiChildBlkAddress; } } // If we were resetting on this element, restore the statistics to what // they were before. if( !bCountElm) { pStateInfo->ui64KeyCount = ui64SaveKeyCount; pStateInfo->ui64KeyRefs = ui64SaveKeyRefs; f_memcpy( &pStateInfo->BlkInfo, &SaveBlkInfo, sizeof( BLOCK_INFO)); } if (RC_BAD( rc = chkCallProgFunc())) { break; } } // Verify that the last key in the block matches the parent's key. if (i32LastErrCode == 0 && pParentState && RC_OK( m_LastStatusRc)) { if (pStateInfo->bValidKey && pParentState->bValidKey && f_memcmp( pStateInfo->pucElmKey, pParentState->pucElmKey, pStateInfo->uiElmKeyLen < pParentState->uiElmKeyLen ? pStateInfo->uiElmKeyLen : pParentState->uiElmKeyLen) != 0) { i32LastErrCode = FLM_BAD_PARENT_KEY; uiNumErrors++; chkReportError( i32LastErrCode, XFLM_LOCALE_B_TREE, (FLMUINT32)m_Progress.ui32LfNumber, (FLMUINT32)m_Progress.ui32LfType, (FLMUINT32)uiLevel, (FLMUINT32)uiBlkAddress, (FLMUINT32)uiParentBlkAddress, 0, 0); } } fix_state: // If the block could not be verified, set the level's next block // address and last child address to zero to indicate that we really // aren't sure we're at the right place in this level in the B-TREE. if (i32LastErrCode != 0) { pStateInfo->BlkInfo.i32ErrCode = i32LastErrCode; pStateInfo->BlkInfo.uiNumErrors += uiNumErrors; // Reset all child block states. for (;;) { pStateInfo->ui32NextBlkAddr = 0xFFFFFFFF; pStateInfo->ui32LastChildAddr = 0xFFFFFFFF; pStateInfo->bValidKey = FALSE; pStateInfo->uiElmLastFlag = 0xFF; // Quit when the leaf level has been reached. if (pStateInfo->uiLevel == 0) { break; } pStateInfo--; } } Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } else if (pBlkHdr) { f_free( &pBlkHdr); } pStateInfo->pBlkHdr = NULL; return( rc); } /******************************************************************** Desc: *********************************************************************/ FSTATIC FLMBYTE * getEntryEnd( FLMBYTE * pucEntry, FLMUINT uiBlkType) { FLMBYTE * pucEnd = pucEntry; switch (uiBlkType) { case BT_LEAF: { FLMUINT uiKL = (FLMUINT)FB2UW( pucEnd); pucEnd += (uiKL + 2); break; } case BT_LEAF_DATA: { FLMUINT uiKL; FLMUINT uiDL; FLMUINT ucFlags = *pucEnd; pucEnd++; if (ucFlags & BTE_FLAG_KEY_LEN) { uiKL = (FLMUINT)FB2UW( pucEnd); pucEnd += 2; } else { uiKL = *pucEnd; pucEnd++; } if (ucFlags & BTE_FLAG_DATA_LEN) { uiDL = FB2UW( pucEnd); pucEnd += 2; } else { uiDL = *pucEnd; pucEnd++; } if (ucFlags & BTE_FLAG_OA_DATA_LEN) { pucEnd += 4; } pucEnd += (uiKL + uiDL); break; } case BT_NON_LEAF: { FLMUINT uiKL; pucEnd += 4; uiKL = (FLMUINT)FB2UW( pucEnd); pucEnd += (uiKL + 2); break; } case BT_NON_LEAF_COUNTS: { FLMUINT uiKL; pucEnd += 8; uiKL = (FLMUINT)FB2UW( pucEnd); pucEnd += (uiKL + 2); break; } } return( pucEnd); } /*************************************************************************** Desc: Compare two cache blocks during a sort to determine which one has lower address. *****************************************************************************/ FINLINE FLMINT FLMAPI blkSortCompare( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2) { BlkStruct * pBlk1 = ((BlkStruct *)pvBuffer) + uiPos1; BlkStruct * pBlk2 = ((BlkStruct *)pvBuffer) + uiPos2; if (pBlk1->uiStartOfEntry < pBlk2->uiStartOfEntry) { return( -1); } else if (pBlk1->uiStartOfEntry > pBlk2->uiStartOfEntry) { return( 1); } else { return( 0); } } /*************************************************************************** Desc: Swap two entries in cache table during sort. *****************************************************************************/ FINLINE void FLMAPI blkSortSwap( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2) { BlkStruct * pBlkEntryTable = (BlkStruct *)pvBuffer; BlkStruct TmpEntry = pBlkEntryTable [uiPos1]; pBlkEntryTable[ uiPos1] = pBlkEntryTable[ uiPos2]; pBlkEntryTable[ uiPos2] = TmpEntry; } /******************************************************************** Desc: *********************************************************************/ RCODE F_DbCheck::verifyBlockStructure( FLMUINT uiBlockSize, F_BTREE_BLK_HDR * pBlkHdr) { RCODE rc = NE_XFLM_OK; FLMUINT uiIndex; FLMBYTE * pucEntry; FLMBYTE * pucEnd; BlkStruct * pCurBlk; FLMUINT uiNumEntries; // No need to check anything in the block if there are less than 2 entries. if ((uiNumEntries = pBlkHdr->ui16NumKeys) < 2) { goto Exit; } if (uiNumEntries > m_uiBlkEntryArraySize) { FLMUINT uiNewEntryArraySize = uiNumEntries + 200; if (RC_BAD( rc = f_realloc( sizeof( BlkStruct) * uiNewEntryArraySize, &m_pBlkEntries))) { goto Exit; } f_memset( &m_pBlkEntries [m_uiBlkEntryArraySize], 0, sizeof( BlkStruct) * (uiNewEntryArraySize - m_uiBlkEntryArraySize)); m_uiBlkEntryArraySize = uiNewEntryArraySize; } for (uiIndex = 0, pCurBlk = m_pBlkEntries; uiIndex < uiNumEntries; uiIndex++, pCurBlk++) { pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiIndex); if( pucEntry > (FLMBYTE *)pBlkHdr + uiBlockSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } pucEnd = getEntryEnd( pucEntry, pBlkHdr->stdBlkHdr.ui8BlkType); pCurBlk->uiStartOfEntry = (FLMUINT)pucEntry; pCurBlk->uiEndOfEntry = (FLMUINT)pucEnd; } // Now sort the entries and check the offsets. f_qsort( m_pBlkEntries, 0, uiNumEntries - 1, blkSortCompare, blkSortSwap); for (uiIndex = 1; uiIndex < uiNumEntries; uiIndex++) { if (m_pBlkEntries [uiIndex - 1].uiEndOfEntry > m_pBlkEntries [uiIndex].uiStartOfEntry) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } Exit: return( rc); } /******************************************************************** Desc: Verify the reference set for a key *********************************************************************/ RCODE F_DbCheck::verifyIXRefs( STATE_INFO * pStateInfo, FLMUINT64 ui64ResetNodeId ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NodeId; // Get the NodeId for the element. ui64NodeId = pStateInfo->ui64ElmNodeId; if (ui64NodeId <= ui64ResetNodeId) { ui64ResetNodeId = 0; } if (!ui64ResetNodeId && !m_bPhysicalCorrupt) { if (RC_BAD( rc = verifyIXRSet( pStateInfo))) { goto Exit; } } pStateInfo->ui64KeyRefs++; Exit: return( rc); } /******************************************************************** Desc: Initialize the STATE_INFO in preparation to do checking. *********************************************************************/ void flmInitReadState( STATE_INFO * pStateInfo, FLMBOOL * pbStateInitialized, FLMUINT, //uiVersionNum, F_Db * pDb, // May be NULL. LF_HDR *, //pLogicalFile, FLMUINT uiLevel, FLMUINT uiExpectedBlkType, FLMBYTE * pucKeyBuffer) { f_memset( pStateInfo, 0, sizeof( STATE_INFO)); *pbStateInitialized = TRUE; pStateInfo->pDb = pDb; pStateInfo->uiLevel = uiLevel; pStateInfo->uiBlkType = uiExpectedBlkType; pStateInfo->pucElmKey = pucKeyBuffer; pStateInfo->uiElmLastFlag = 0xFF; pStateInfo->ui32NextBlkAddr = 0xFFFFFFFF; pStateInfo->ui32LastChildAddr = 0xFFFFFFFF; } /****************************************************************************** Desc: Constructor ******************************************************************************/ F_NodeVerifier::F_NodeVerifier() { m_pucBuf = &m_ucBuf [0]; m_uiBufSize = sizeof( m_ucBuf); m_uiBytesInBuf = 0; m_pXRefRS = NULL; m_bFinalizeCalled = FALSE; m_pRS = NULL; f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); } /****************************************************************************** Desc: Destructor ******************************************************************************/ F_NodeVerifier::~F_NodeVerifier() { if (m_pRS) { m_pRS->Release(); } if (m_pucBuf != &m_ucBuf [0]) { f_free( &m_pucBuf); } } /****************************************************************************** Desc: Reset ******************************************************************************/ void F_NodeVerifier::Reset( STATE_INFO * pStateInfo) { m_uiBytesInBuf = 0; m_bFinalizeCalled = FALSE; f_memset( m_pucBuf, 0, m_uiBufSize); f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); if( *pStateInfo->pucElm & BTE_FLAG_OA_DATA_LEN) { m_uiOverallLength = pStateInfo->uiElmOADataLen; } else { m_uiOverallLength = pStateInfo->uiElmDataLen; } } /****************************************************************************** Desc: ******************************************************************************/ RCODE F_NodeVerifier::AddData( FLMUINT64 ui64NodeId, void * pucData, FLMUINT uiDataLen) { RCODE rc = NE_XFLM_OK; // Verify the node Id or Save it (first time) if( m_nodeInfo.ui64NodeId) { if (m_nodeInfo.ui64NodeId != ui64NodeId) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } } else { m_nodeInfo.ui64NodeId = ui64NodeId; } // Copy the entry data into the local buffer. At most, // we only need MAX_DOM_HEADER_SIZE bytes to make a // complete DOM node header - unless we have an // element node, in which case we want to get all of // the attribute nodes. if (uiDataLen + m_uiBytesInBuf > m_uiBufSize) { eDomNodeType eNodeType; // Node type should be in the first byte of data we have for // the node. if (!m_uiBytesInBuf) { eNodeType = (eDomNodeType)((*((FLMBYTE *)pucData)) & 0x0F); } else { eNodeType = (eDomNodeType)((*m_pucBuf) & 0x0F); } if (eNodeType != ELEMENT_NODE) { flmAssert( m_uiBufSize >= MAX_DOM_HEADER_SIZE); // Only copy enough to fill up the current buffer - don't // really need any more than the header for non-element nodes. uiDataLen = m_uiBufSize - m_uiBytesInBuf; } else { // Must get everything for element nodes if (m_pucBuf != &m_ucBuf [0]) { if (RC_BAD( rc = f_realloc( uiDataLen + m_uiBytesInBuf, &m_pucBuf))) { goto Exit; } } else { FLMBYTE * pucNew; if (RC_BAD( rc = f_alloc( uiDataLen + m_uiBytesInBuf, &pucNew))) { goto Exit; } if (m_uiBytesInBuf) { f_memcpy( pucNew, m_pucBuf, m_uiBytesInBuf); } m_pucBuf = pucNew; } m_uiBufSize = uiDataLen + m_uiBytesInBuf; } } f_memcpy( &m_pucBuf[m_uiBytesInBuf], pucData, uiDataLen); m_uiBytesInBuf += uiDataLen; Exit: return rc; } /******************************************************************** Desc: Do the final setup to store the node information in the Node Result Set. *********************************************************************/ RCODE F_NodeVerifier::finalize( F_Db * pDb, F_Dict * pDict, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMBOOL bSkipDOMLinkCheck, FLMINT32 * pi32ElmErrCodeRV) { RCODE rc = NE_XFLM_OK; NODE_RS_ENTRY * pRSEntry = NULL; FLMUINT uiRSBufIndex; FLMUINT uiStorageFlags; F_NameTable * pNameTable = pDict->getNameTable(); IF_BufferIStream * pBufferStream = NULL; *pi32ElmErrCodeRV = 0; if( m_bFinalizeCalled) { flmAssert( 0); goto Exit; } f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferStream))) { goto Exit; } if( RC_BAD( rc = pBufferStream->openStream( (const char *)m_pucBuf, m_uiBytesInBuf))) { goto Exit; } if( RC_BAD( rc = flmReadNodeInfo( uiCollection, ui64NodeId, pBufferStream, m_uiOverallLength, FALSE, &m_nodeInfo, &uiStorageFlags))) { goto Exit; } // See if child element count is set. if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT) { // This bit should only be set for elements. if( m_nodeInfo.eNodeType != ELEMENT_NODE) { *pi32ElmErrCodeRV = FLM_BAD_NODE_TYPE; goto Exit; } // If the count > 0, the NSF_HAVE_CHILDREN_BIT better also be set. if( m_nodeInfo.uiChildElmCount) { if( !(uiStorageFlags & NSF_HAVE_CHILDREN_BIT)) { *pi32ElmErrCodeRV = FLM_BAD_CHILD_ELM_COUNT; goto Exit; } } } // Verify the Name and Prefix Ids. if( RC_BAD( rc = verifyNameId( pDb, m_nodeInfo.eNodeType, m_nodeInfo.uiNameId, pNameTable, pi32ElmErrCodeRV))) { goto Exit; } if( *pi32ElmErrCodeRV) { goto Exit; } if( RC_BAD( rc = verifyPrefixId( pDb, m_nodeInfo.uiPrefixId, pNameTable, pi32ElmErrCodeRV))) { goto Exit; } if( *pi32ElmErrCodeRV) { goto Exit; } if( !bSkipDOMLinkCheck) { FLMUINT16 ui16BitMap = 0; // Build a buffer to set into the Result Set for later verification... if( RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pRSEntry))) { goto Exit; } uiRSBufIndex = 0; if( m_nodeInfo.ui64DocumentId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64DocumentId; ui16BitMap |= CHK_BM_ROOT_ID; } if( m_nodeInfo.ui64ParentId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64ParentId; ui16BitMap |= CHK_BM_PARENT_ID; } if( m_nodeInfo.ui64PrevSibId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64PrevSibId; ui16BitMap |= CHK_BM_PREV_SIBLING; } if( m_nodeInfo.ui64NextSibId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64NextSibId; ui16BitMap |= CHK_BM_NEXT_SIBLING; } if( m_nodeInfo.ui64FirstChildId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64FirstChildId; ui16BitMap |= CHK_BM_FIRST_CHILD; } if( m_nodeInfo.ui64LastChildId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64LastChildId; ui16BitMap |= CHK_BM_LAST_CHILD; } if( m_nodeInfo.ui64AnnotationId) { pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64AnnotationId; ui16BitMap |= CHK_BM_ANNOTATION; } pRSEntry->hdr.ui64NodeId = m_nodeInfo.ui64NodeId; pRSEntry->hdr.ui16BitMap = ui16BitMap; m_bFinalizeCalled = TRUE; // Finally add the result to the result set for later evaluation. if( RC_BAD( rc = m_pRS->addEntry( NULL, NULL, (FLMBYTE *)&(pRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), (FLMBYTE *)pRSEntry, sizeof( NODE_RS_HDR) + (uiRSBufIndex * sizeof( FLMUINT64))))) { *pi32ElmErrCodeRV = -1; goto Exit; } } if( m_pXRefRS) { if (RC_BAD( rc = checkForIndexes( pDb, pDict, uiCollection))) { goto Exit; } } Exit: if( pBufferStream) { pBufferStream->Release(); } if( pRSEntry) { f_free( &pRSEntry); } return( rc); } /****************************************************************************** Desc: Method to add any indexes to the document list that qualify. ******************************************************************************/ RCODE F_NodeVerifier::checkForIndexes( F_Db * pDb, F_Dict * pDict, FLMUINT uiCollection) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64DocumentId; DOC_IXD_XREF DocXRef; ICD * pIcd; F_AttrElmInfo defInfo; // Determine if there are any indexs that qualify to be included in the // index list. // // If there is no document id, then this node is the the root node? ui64DocumentId = m_nodeInfo.ui64DocumentId; if( !ui64DocumentId) { if( !m_nodeInfo.ui64ParentId) { ui64DocumentId = m_nodeInfo.ui64NodeId; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); } } switch( m_nodeInfo.eNodeType) { case ELEMENT_NODE: case DATA_NODE: { if( RC_BAD( rc = pDict->getElement( pDb, m_nodeInfo.uiNameId, &defInfo))) { goto Exit; } break; } case ATTRIBUTE_NODE: { if (RC_BAD( rc = pDict->getAttribute( pDb, m_nodeInfo.uiNameId, &defInfo))) { goto Exit; } break; } default: { goto Exit; } } pIcd = defInfo.m_pFirstIcd; // Do we have any qualifying indexes? while (pIcd) { // Only process if the target collection matches. if( pIcd->pIxd->uiCollectionNum == uiCollection) { if( pIcd->uiFlags & (ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET)) { FLMBYTE ucDummy = 0; // Get the document list entry for this node. // Build a buffer to set into the Result Set for later verification... DocXRef.uiIndexNum = pIcd->pIxd->uiIndexNum; DocXRef.ui64DocId = m_nodeInfo.ui64DocumentId; DocXRef.uiCollection = uiCollection; if (RC_BAD( rc = m_pXRefRS->addEntry( NULL, NULL, (FLMBYTE *)&DocXRef, sizeof( DOC_IXD_XREF), &ucDummy, 1))) { // It's okay if we get a duplicate entry, only one will be saved in // the result set. if (rc == NE_XFLM_NOT_UNIQUE) { rc = NE_XFLM_OK; } else { goto Exit; } } } } pIcd = pIcd->pNextInChain; } Exit: return rc; } /****************************************************************************** Desc: ******************************************************************************/ FLMUINT64 getLinkVal( FLMUINT uiBMId, NODE_RS_ENTRY * pRSEntry ) { FLMUINT64 * pui64Ptr = pRSEntry->ui64FieldArray; FLMUINT16 ui16BitMap = pRSEntry->hdr.ui16BitMap; FLMUINT64 ui64Link = 0; if (ui16BitMap & CHK_BM_ROOT_ID) { if (uiBMId == CHK_BM_ROOT_ID) { ui64Link = *pui64Ptr; goto Exit; } pui64Ptr++; } if (ui16BitMap & CHK_BM_PARENT_ID) { if (uiBMId == CHK_BM_PARENT_ID) { ui64Link = *pui64Ptr; goto Exit; } pui64Ptr++; } if (ui16BitMap & CHK_BM_PREV_SIBLING) { if (uiBMId == CHK_BM_PREV_SIBLING) { ui64Link = *pui64Ptr; goto Exit; } pui64Ptr++; } if (ui16BitMap & CHK_BM_NEXT_SIBLING) { if (uiBMId == CHK_BM_NEXT_SIBLING) { ui64Link = *pui64Ptr; goto Exit; } pui64Ptr++; } if (ui16BitMap & CHK_BM_FIRST_CHILD) { if (uiBMId == CHK_BM_FIRST_CHILD) { ui64Link = *pui64Ptr; goto Exit; } pui64Ptr++; } if (ui16BitMap & CHK_BM_LAST_CHILD) { if (uiBMId == CHK_BM_LAST_CHILD) { ui64Link = *pui64Ptr; goto Exit; } pui64Ptr++; } if (ui16BitMap & CHK_BM_ANNOTATION) { if (uiBMId == CHK_BM_ANNOTATION) { ui64Link = *pui64Ptr; goto Exit; } } Exit: return ui64Link; } /****************************************************************************** Desc: Verify that the nameId is in the dictionary. ******************************************************************************/ RCODE F_NodeVerifier::verifyNameId( F_Db * pDb, eDomNodeType eNodeType, FLMUINT uiNameId, F_NameTable * pNameTable, FLMINT32 * pi32ErrCode) { RCODE rc = NE_XFLM_OK; FLMUINT uiType; FLMUINT uiLen; if( !uiNameId) { goto Exit; } switch (eNodeType) { case DOCUMENT_NODE: case ELEMENT_NODE: case DATA_NODE: case COMMENT_NODE: case CDATA_SECTION_NODE: case ANNOTATION_NODE: { uiType = ELM_ELEMENT_TAG; break; } case ATTRIBUTE_NODE: { uiType = ELM_ATTRIBUTE_TAG; break; } default: { flmAssert( 0); *pi32ErrCode = FLM_UNSUPPORTED_NODE_TYPE; goto Exit; } } if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, uiType, uiNameId, NULL, NULL, &uiLen, NULL, NULL, NULL, NULL, TRUE))) { *pi32ErrCode = FLM_BAD_INVALID_NAME_ID; goto Exit; } Exit: return rc; } /****************************************************************************** Desc: Verify that the prefixId is in the dictionary. ******************************************************************************/ RCODE F_NodeVerifier::verifyPrefixId( F_Db * pDb, FLMUINT uiPrefixId, F_NameTable * pNameTable, FLMINT32 * pi32ErrCode ) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen; if (!uiPrefixId) { goto Exit; // Ok. } if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, ELM_PREFIX_TAG, uiPrefixId, NULL, NULL, &uiLen))) { *pi32ErrCode = FLM_BAD_INVALID_PREFIX_ID; goto Exit; } Exit: return rc; } /****************************************************************************** Desc: Add a key to the result set. ******************************************************************************/ RCODE F_KeyCollector::addKey( F_Db * pDb, IXD * pIxd, KREF_ENTRY * pKref ) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucKey = (FLMBYTE *)&pKref[1]; FLMBYTE * pucData = pucKey + pKref->ui16KeyLen; FLMUINT uiDataLen; flmAssert( pKref->ui16KeyLen <= XFLM_MAX_KEY_SIZE); // Can't store an entry with zero length data. if ((uiDataLen = pKref->uiDataLen) == 0) { *pucData = 0; uiDataLen = 1; } // Save the key in the result set. if (RC_BAD( rc = m_pDbCheck->m_pIxRSet->addEntry( pDb, pIxd, pucKey, (FLMUINT)pKref->ui16KeyLen, pucData, uiDataLen))) { if (rc == NE_XFLM_NOT_UNIQUE) { rc = NE_XFLM_OK; } goto Exit; } m_ui64TotalKeys++; Exit: return( rc); } libxflaim-5.1.969/src/fdict.cpp0000644000175000017500000065643710511001742017646 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines to access anything in the dictionary // // Tabs: 3 // // Copyright (c) 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdict.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" // Data type strings const char * fdictDataTypes[ XFLM_NUM_OF_TYPES + 1] = { XFLM_NODATA_OPTION_STR, XFLM_STRING_OPTION_STR, XFLM_INTEGER_OPTION_STR, XFLM_BINARY_OPTION_STR, NULL }; extern RESERVED_TAG_NAME FlmReservedElementTags[]; extern RESERVED_TAG_NAME FlmReservedAttributeTags[]; FSTATIC void fdictInsertInIcdChain( ICD ** ppFirstIcd, ICD * pIcd); FSTATIC void fdictRemoveFromIcdChain( ICD ** ppFirstIcd, ICD * pIcd); FSTATIC RCODE fdictCopyCollection( F_Pool * pDictPool, F_COLLECTION ** ppDestCollection, F_COLLECTION * pSrcCollection); FSTATIC RCODE fdictCopyPrefix( F_Pool * pDictPool, F_PREFIX ** ppDestPrefix, F_PREFIX * pSrcPrefix); FSTATIC RCODE fdictCopyEncDef( F_Pool * pDictPool, F_ENCDEF ** ppDestEncDef, F_ENCDEF * pSrcEncDef); FSTATIC char * fdictGetOption( char ** ppszSrc); FSTATIC RCODE isIndexComponent( F_Db * pDb, F_DOMNode * pNode, FLMBOOL * pbIsIndexComponent, FLMUINT * puiElementId); FSTATIC FLMBOOL indexDefsSame( IXD * pOldIxd, IXD * pNewIxd); #define MAX_ENC_TYPES 2 // NOTE: If you change the arrangement of the values in this array, make sure // you search the entire codebase for references to DDEncOpts and fdictLegalEncDefTypes // and verify that the changes won't cause problems. const char * DDEncOpts[MAX_ENC_TYPES] = { XFLM_ENC_AES_OPTION_STR, /* AES */ XFLM_ENC_DES3_OPTION_STR /* Triple DES */ }; /*************************************************************************** Desc: Constructor ***************************************************************************/ F_Dict::F_Dict() { m_pNext = NULL; m_pPrev = NULL; m_pDatabase = NULL; m_uiDictSeq = 0; m_dictPool.poolInit( 1024); m_pElementDefTbl = NULL; m_uiLowestElementNum = 0; m_uiHighestElementNum = 0; m_pReservedElementDefTbl = NULL; m_pExtElementDefTbl = NULL; m_uiExtElementDefTblSize = 0; m_hExtElementDefMutex = F_MUTEX_NULL; m_pIxElementTbl = NULL; m_uiIxElementTblSize = 0; m_uiNumIxElements = 0; m_pAttributeDefTbl = NULL; m_uiLowestAttributeNum = 0; m_uiHighestAttributeNum = 0; m_pReservedAttributeDefTbl = NULL; m_pExtAttributeDefTbl = NULL; m_uiExtAttributeDefTblSize = 0; m_hExtAttributeDefMutex = F_MUTEX_NULL; m_pIxAttributeTbl = NULL; m_uiIxAttributeTblSize = 0; m_uiNumIxAttributes = 0; m_pDictCollection = NULL; m_pDataCollection = NULL; m_pMaintCollection = NULL; m_ppCollectionTbl = NULL; m_uiLowestCollectionNum = 0; m_uiHighestCollectionNum = 0; m_ppPrefixTbl = NULL; m_uiLowestPrefixNum = 0; m_uiHighestPrefixNum = 0; m_ppEncDefTbl = NULL; m_uiLowestEncDefNum = 0; m_uiHighestEncDefNum = 0; m_pNameIndex = NULL; m_pNumberIndex = NULL; m_ppIxdTbl = NULL; m_uiLowestIxNum = 0; m_uiHighestIxNum = 0; m_pNameTable = NULL; m_pRootIcdList = NULL; // Whenever an F_Dict is allocated, it is always immediately // used by an F_Db. m_uiUseCount = 1; } /*************************************************************************** Desc: ***************************************************************************/ F_Dict::~F_Dict() { resetDict(); } /*************************************************************************** Desc: Clear the dictionary object so it can be reused. NOTE: This function also needs to do all the freeing up that the destructor would do because it is called by the destructor. ***************************************************************************/ void F_Dict::resetDict( void) { FLMUINT uiLoop; f_free( &m_pElementDefTbl); m_uiLowestElementNum = 0; m_uiHighestElementNum = 0; f_free( &m_pExtElementDefTbl); m_uiExtElementDefTblSize = 0; if (m_hExtElementDefMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hExtElementDefMutex); } f_free( &m_pReservedElementDefTbl); f_free( &m_pIxElementTbl); m_uiIxElementTblSize = 0; m_uiNumIxElements = 0; f_free( &m_pAttributeDefTbl); m_uiLowestAttributeNum = 0; m_uiHighestAttributeNum = 0; f_free( &m_pExtAttributeDefTbl); m_uiExtAttributeDefTblSize = 0; if (m_hExtAttributeDefMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hExtAttributeDefMutex); } f_free( &m_pReservedAttributeDefTbl); f_free( &m_pIxAttributeTbl); m_uiIxAttributeTblSize = 0; m_uiNumIxAttributes = 0; f_free( &m_ppIxdTbl); m_uiLowestIxNum = 0; m_uiHighestIxNum = 0; m_pNameIndex = NULL; m_pNumberIndex = NULL; f_free( &m_ppCollectionTbl); m_uiLowestCollectionNum = 0; m_uiHighestCollectionNum = 0; f_free( &m_ppPrefixTbl); m_uiLowestPrefixNum = 0; m_uiHighestPrefixNum = 0; for( uiLoop = 0; uiLoop <= (m_uiHighestEncDefNum - m_uiLowestEncDefNum); uiLoop++) { if (m_ppEncDefTbl && m_ppEncDefTbl[ uiLoop] && (*m_ppEncDefTbl[ uiLoop]).pCcs) { (*m_ppEncDefTbl[ uiLoop]).pCcs->Release(); } } f_free( &m_ppEncDefTbl); m_uiLowestEncDefNum = 0; m_uiHighestEncDefNum = 0; m_pDictCollection = NULL; m_pDataCollection = NULL; m_pMaintCollection = NULL; m_dictPool.poolFree(); m_dictPool.poolInit( 1024); if (m_pNameTable) { m_pNameTable->Release(); m_pNameTable = NULL; } m_pRootIcdList = NULL; } /*************************************************************************** Desc: ***************************************************************************/ RCODE F_Dict::allocNameTable( void) { if ((m_pNameTable = f_new F_NameTable) == NULL) { return( RC_SET( NE_XFLM_MEM)); } return( NE_XFLM_OK); } /*************************************************************************** Desc: Get the first ICD, if any for an element. NOTE: This is only called for element numbers greater or equal to FLM_LOW_EXT_ELEMENT_NUM, so we have to look in the m_pIxElementTbl table. ***************************************************************************/ IX_ITEM * F_Dict::findIxItem( IX_ITEM * pIxTbl, FLMUINT uiNumItems, FLMUINT uiDictNum, FLMUINT * puiInsertPos ) { IX_ITEM * pIxItem = NULL; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblDictNum; // Do binary search in the table if (!uiNumItems) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiNumItems; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblDictNum = pIxTbl [uiMid].uiDictNum; if (uiTblDictNum == uiDictNum) { // Found Match if (puiInsertPos) { *puiInsertPos = uiMid; } pIxItem = &pIxTbl [uiMid]; goto Exit; } // Check if we are done if (uiLow >= uiHigh) { if (puiInsertPos) { *puiInsertPos = (uiDictNum < uiTblDictNum) ? uiMid : uiMid + 1; } goto Exit; } if (uiDictNum < uiTblDictNum) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiNumItems) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( pIxItem); } /*************************************************************************** Desc: Get information for an extended element - which are elements whose tag number is greater than or equal to FLM_LOW_EXT_ELEMENT_NUM Note: Populates data type, first Icd, and state ***************************************************************************/ RCODE F_Dict::getExtElement( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiElementNum, F_AttrElmInfo * pElmInfo) { RCODE rc = NE_XFLM_OK; EXT_ATTR_ELM_DEF * pExtElementDef; IX_ITEM * pIxItem; FLMBOOL bMutexLocked; // See if the element is in our extended list. If not, bring it in. f_mutexLock( m_hExtElementDefMutex); bMutexLocked = TRUE; pExtElementDef = getExtElementDef( uiElementNum); if (pExtElementDef->uiDictNum != uiElementNum) { f_mutexUnlock( m_hExtElementDefMutex); bMutexLocked = FALSE; if (!ui64DocumentID) { F_DataVector searchKey; F_DataVector foundKey; // Find and read the element definition document // Need to lookup the document's ID, using the element number if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ELEMENT_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUINT( 1, uiElementNum))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } ui64DocumentID = foundKey.getDocumentID(); } // Must read the element definition document if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ELEMENT_TAG, ui64DocumentID, pElmInfo, TRUE, FALSE))) { goto Exit; } // See if the element is indexed. pIxItem = findIxElement( uiElementNum); // Relock the mutex and populate the extended structure // with the information we found. f_mutexLock( m_hExtElementDefMutex); bMutexLocked = TRUE; pExtElementDef->uiDictNum = uiElementNum; pExtElementDef->attrElmDef.uiFlags = (pElmInfo->m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (pElmInfo->m_uiState & ATTR_ELM_STATE_MASK); pExtElementDef->attrElmDef.pFirstIcd = pIxItem ? pIxItem->pFirstIcd : NULL; } pElmInfo->m_uiDataType = attrElmGetType( &pExtElementDef->attrElmDef); pElmInfo->m_pFirstIcd = pExtElementDef->attrElmDef.pFirstIcd; pElmInfo->m_uiState = attrElmGetState( &pExtElementDef->attrElmDef); Exit: if (bMutexLocked) { f_mutexUnlock( m_hExtElementDefMutex); } return( rc); } /*************************************************************************** Desc: Get information for an extended attribute - which are attributes whose tag number is greater than or equal to FLM_LOW_EXT_ATTRIBUTE_NUM ***************************************************************************/ RCODE F_Dict::getExtAttribute( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiAttributeNum, F_AttrElmInfo * pAttrInfo) { RCODE rc = NE_XFLM_OK; EXT_ATTR_ELM_DEF * pExtAttributeDef; IX_ITEM * pIxItem; FLMBOOL bMutexLocked; // See if the attribute is in our extended list. If not, bring it in. f_mutexLock( m_hExtAttributeDefMutex); bMutexLocked = TRUE; pExtAttributeDef = getExtAttributeDef( uiAttributeNum); if (pExtAttributeDef->uiDictNum != uiAttributeNum) { f_mutexUnlock( m_hExtAttributeDefMutex); bMutexLocked = FALSE; if (!ui64DocumentID) { F_DataVector searchKey; F_DataVector foundKey; // Find and read the element definition document // Need to lookup the document's ID, using the element number if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ATTRIBUTE_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUINT( 1, uiAttributeNum))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } ui64DocumentID = foundKey.getDocumentID(); } // Must read the attribute definition document if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ATTRIBUTE_TAG, ui64DocumentID, pAttrInfo, TRUE, FALSE))) { goto Exit; } // See if the attribute is indexed. pIxItem = findIxAttribute( uiAttributeNum); // Relock the mutex and populate the extended structure // with the information we found. f_mutexLock( m_hExtAttributeDefMutex); bMutexLocked = TRUE; pExtAttributeDef->uiDictNum = uiAttributeNum; pExtAttributeDef->attrElmDef.uiFlags = (pAttrInfo->m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (pAttrInfo->m_uiState & ATTR_ELM_STATE_MASK); pExtAttributeDef->attrElmDef.pFirstIcd = pIxItem ? pIxItem->pFirstIcd : NULL; } pAttrInfo->m_uiDataType = attrElmGetType( &pExtAttributeDef->attrElmDef); pAttrInfo->m_pFirstIcd = pExtAttributeDef->attrElmDef.pFirstIcd; pAttrInfo->m_uiState = attrElmGetState( &pExtAttributeDef->attrElmDef); pAttrInfo->m_uiFlags = attrElmGetFlags( &pExtAttributeDef->attrElmDef); Exit: if (bMutexLocked) { f_mutexUnlock( m_hExtAttributeDefMutex); } return( rc); } /*************************************************************************** Desc: Get the element information. Note: Populates data type, first ICD, and state ***************************************************************************/ RCODE F_Dict::getElement( F_Db * pDb, FLMUINT uiElementNum, // [in] Element Number to look up F_AttrElmInfo * pElmInfo) { RCODE rc = NE_XFLM_OK; ATTR_ELM_DEF * pElementDef; if (elementIsReservedTag( uiElementNum)) { if ((pElementDef = getReservedElementDef( uiElementNum)) != NULL) { goto Get_Type_And_State; } else { rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); goto Exit; } } else if (uiElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { if ((pElementDef = getElementDef( uiElementNum)) != NULL) { Get_Type_And_State: pElmInfo->m_uiDataType = attrElmGetType( pElementDef); pElmInfo->m_pFirstIcd = pElementDef->pFirstIcd; pElmInfo->m_uiState = attrElmGetState( pElementDef); pElmInfo->m_uiFlags = attrElmGetFlags( pElementDef); } else { rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); goto Exit; } } else if (uiElementNum >= FLM_LOW_EXT_ELEMENT_NUM && m_pExtElementDefTbl) { if (RC_BAD( rc = getExtElement( pDb, 0, uiElementNum, pElmInfo))) { goto Exit; } } else { rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the attribute information. Note: Populates data type, first ICD, and state ***************************************************************************/ RCODE F_Dict::getAttribute( F_Db * pDb, FLMUINT uiAttributeNum, F_AttrElmInfo * pAttrInfo) { RCODE rc = NE_XFLM_OK; ATTR_ELM_DEF * pAttributeDef; if( attributeIsReservedTag( uiAttributeNum)) { if ((pAttributeDef = getReservedAttributeDef( uiAttributeNum)) != NULL) { goto Get_Type_And_State; } else { rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); goto Exit; } } else if (uiAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { if ((pAttributeDef = getAttributeDef( uiAttributeNum)) != NULL) { Get_Type_And_State: pAttrInfo->m_uiDataType = attrElmGetType( pAttributeDef); pAttrInfo->m_pFirstIcd = pAttributeDef->pFirstIcd; pAttrInfo->m_uiState = attrElmGetState( pAttributeDef); pAttrInfo->m_uiFlags = attrElmGetFlags( pAttributeDef); } else { rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); goto Exit; } } else if (uiAttributeNum >= FLM_LOW_EXT_ATTRIBUTE_NUM && m_pExtAttributeDefTbl) { if (RC_BAD( rc = getExtAttribute( pDb, 0, uiAttributeNum, pAttrInfo))) { goto Exit; } } else { rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the data type for any given element or attribute in the current db ***************************************************************************/ RCODE F_Db::getDataType( FLMUINT uiDictType, FLMUINT uiNameId, FLMUINT * puiDataType) { RCODE rc = NE_XFLM_OK; F_AttrElmInfo defInfo; flmAssert( uiDictType == ELM_ELEMENT_TAG || uiDictType == ELM_ATTRIBUTE_TAG); if( RC_BAD( rc = (uiDictType == ELM_ELEMENT_TAG ? m_pDict->getElement( this, uiNameId, &defInfo) : m_pDict->getAttribute( this, uiNameId, &defInfo)))) { goto Exit; } *puiDataType = defInfo.m_uiDataType; Exit: return( rc); } /*************************************************************************** Desc: Get the next element defined in the dictionary after the one that is passed in. Note: Populates data type, first ICD, and state ***************************************************************************/ RCODE F_Dict::getNextElement( F_Db * pDb, FLMUINT * puiElementNum, F_AttrElmInfo * pElmInfo) { RCODE rc = NE_XFLM_OK; ATTR_ELM_DEF * pElementDef = NULL; FLMUINT uiElementNum = *puiElementNum; if (uiElementNum < m_uiLowestElementNum) { uiElementNum = m_uiLowestElementNum; } else { uiElementNum++; } while (uiElementNum >= m_uiLowestElementNum && uiElementNum <= m_uiHighestElementNum) { pElementDef = &m_pElementDefTbl [uiElementNum - m_uiLowestElementNum]; if (attrElmGetState( pElementDef)) { break; } else { pElementDef = NULL; } uiElementNum++; } if (pElementDef) { // At this point we know we have an element. *puiElementNum = uiElementNum; pElmInfo->m_uiDataType = attrElmGetType( pElementDef); pElmInfo->m_pFirstIcd = pElementDef->pFirstIcd; pElmInfo->m_uiState = attrElmGetState( pElementDef); } else { F_DataVector searchKey; F_DataVector foundKey; FLMUINT uiType; // See if it is perchance in the extended list, if there // is one if (!m_pExtElementDefTbl) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } searchKey.reset(); foundKey.reset(); if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ELEMENT_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUINT( 1, uiElementNum))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXCL, &foundKey))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } // If it is not an element definition document, skip it // we are done - there are no more. if (RC_BAD( rc = foundKey.getUINT( 0, &uiType))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } if (uiType != ELM_ELEMENT_TAG) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // Get the element number. if (RC_BAD( rc = foundKey.getUINT( 1, &uiElementNum))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } // At this point, we know we found one, so we will populate // the extended element structure. if (RC_BAD( rc = getExtElement( pDb, foundKey.getDocumentID(), uiElementNum, pElmInfo))) { goto Exit; } *puiElementNum = uiElementNum; } Exit: return( rc); } /*************************************************************************** Desc: Get the next attribute defined in the dictionary after the one that is passed in. Note: Populates data type, first ICD, state, and flags ***************************************************************************/ RCODE F_Dict::getNextAttribute( F_Db * pDb, FLMUINT * puiAttributeNum, F_AttrElmInfo * pAttrInfo) { RCODE rc = NE_XFLM_OK; ATTR_ELM_DEF * pAttributeDef = NULL; FLMUINT uiAttributeNum = *puiAttributeNum; if (uiAttributeNum < m_uiLowestAttributeNum) { uiAttributeNum = m_uiLowestAttributeNum; } else { uiAttributeNum++; } while (uiAttributeNum >= m_uiLowestAttributeNum && uiAttributeNum <= m_uiHighestAttributeNum) { pAttributeDef = &m_pAttributeDefTbl [uiAttributeNum - m_uiLowestAttributeNum]; if (attrElmGetState( pAttributeDef)) { break; } else { pAttributeDef = NULL; } uiAttributeNum++; } if (pAttributeDef) { // At this point we know we have an attribute. *puiAttributeNum = uiAttributeNum; pAttrInfo->m_uiDataType = attrElmGetType( pAttributeDef); pAttrInfo->m_pFirstIcd = pAttributeDef->pFirstIcd; pAttrInfo->m_uiState = attrElmGetState( pAttributeDef); pAttrInfo->m_uiFlags = attrElmGetFlags( pAttributeDef); } else { F_DataVector searchKey; F_DataVector foundKey; FLMUINT uiType; // See if it is perchance in the extended list, if there // is one if (!m_pExtAttributeDefTbl) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } searchKey.reset(); foundKey.reset(); if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ATTRIBUTE_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUINT( 1, uiAttributeNum))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXCL, &foundKey))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } // If it is not an attribute definition document, skip it // we are done - there are no more. if (RC_BAD( rc = foundKey.getUINT( 0, &uiType))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } if (uiType != ELM_ATTRIBUTE_TAG) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // Get the attribute number. if (RC_BAD( rc = foundKey.getUINT( 1, &uiAttributeNum))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } // At this point, we know we found one, so we will populate // the extended attribute structure. if (RC_BAD( rc = getExtAttribute( pDb, foundKey.getDocumentID(), uiAttributeNum, pAttrInfo))) { goto Exit; } *puiAttributeNum = uiAttributeNum; } Exit: return( rc); } /*************************************************************************** Desc: Get the collection given a collection number. ***************************************************************************/ RCODE F_Dict::getCollection( FLMUINT uiCollectionNum, F_COLLECTION ** ppCollection, // [out] optional FLMBOOL bOfflineOk ) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection = NULL; // Try most commonly used one first - default data collection if( uiCollectionNum == XFLM_DATA_COLLECTION) { pCollection = m_pDataCollection; } else if( uiCollectionNum && uiCollectionNum >= m_uiLowestCollectionNum && uiCollectionNum <= m_uiHighestCollectionNum) { pCollection = m_ppCollectionTbl [uiCollectionNum - m_uiLowestCollectionNum]; } else { switch (uiCollectionNum) { case XFLM_DICT_COLLECTION: pCollection = m_pDictCollection; break; case XFLM_MAINT_COLLECTION: pCollection = m_pMaintCollection; break; } } // If the collection is encrypted, and we are in limited mode, then it must // be offline. if (!pCollection) { if (ppCollection) { *ppCollection = NULL; } rc = RC_SET( NE_XFLM_BAD_COLLECTION); goto Exit; } else { if ((pCollection->lfInfo.uiEncId && m_bInLimitedMode) && !bOfflineOk) { rc = RC_SET( NE_XFLM_COLLECTION_OFFLINE); } if (ppCollection) { *ppCollection = pCollection; } } Exit: return rc; } /*************************************************************************** Desc: Returns the ID of a prefix (unicode buffer) ***************************************************************************/ RCODE F_Dict::getPrefixId( F_Db * pDb, const FLMUNICODE * puzPrefix, FLMUINT * puiPrefixId) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey; F_DataVector foundKey; // Get the prefix number - look up in the index if (RC_BAD( rc = searchKey.setUINT( 0, ELM_PREFIX_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUnicode( 1, puzPrefix))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } // Data part of the retrieved key has the dictionary number // for this prefix if (RC_BAD( rc = foundKey.getUINT( 3, puiPrefixId))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); *puiPrefixId = 0; rc = NE_XFLM_OK; } else { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Returns the ID of a prefix (native buffer) ***************************************************************************/ RCODE F_Dict::getPrefixId( F_Db * pDb, const char * pszPrefix, FLMUINT * puiPrefixId) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey; F_DataVector foundKey; // Get the prefix number - look up in the index if (RC_BAD( rc = searchKey.setUINT( 0, ELM_PREFIX_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszPrefix))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } // Data part of the retrieved key has the dictionary number // for this prefix if (RC_BAD( rc = foundKey.getUINT( 3, puiPrefixId))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); *puiPrefixId = 0; rc = NE_XFLM_OK; } else { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Returns the ID of an encryption definition (unicode buffer) ***************************************************************************/ RCODE F_Dict::getEncDefId( F_Db * pDb, const FLMUNICODE * puzEncDef, FLMUINT * puiEncDefId) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey; F_DataVector foundKey; // Get the encdef number - look up in the name index if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ENCDEF_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUnicode( 1, puzEncDef))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } // Data part of the retrieved key has the dictionary number // for this encdef if (RC_BAD( rc = foundKey.getUINT( 3, puiEncDefId))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); *puiEncDefId = 0; rc = NE_XFLM_OK; } else { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Returns the ID of an encryption definition (native buffer) ***************************************************************************/ RCODE F_Dict::getEncDefId( F_Db * pDb, const char * pszEncDef, FLMUINT * puiEncDefId) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey; F_DataVector foundKey; // Get the encdef number - look up in the name index if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ENCDEF_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszEncDef))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } // Data part of the retrieved key has the dictionary number // for this encdef if (RC_BAD( rc = foundKey.getUINT( 3, puiEncDefId))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); *puiEncDefId = 0; rc = NE_XFLM_OK; } else { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Returns the name of a prefix, either Unicode or Native. ***************************************************************************/ RCODE F_Dict::getPrefix( FLMBOOL bUnicode, FLMUINT uiPrefixId, void * pvPrefixBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_PREFIX * pPrefix; FLMUNICODE * puzName; FLMUINT uiOffset = 0; FLMUNICODE * puzPrefixBuf = (FLMUNICODE *)pvPrefixBuf; char * pszPrefixBuf = (char *)pvPrefixBuf; FLMUINT uiBufChars = (bUnicode ? uiBufSize / sizeof(FLMUNICODE) : uiBufSize); if( RC_BAD( rc = getPrefix( uiPrefixId, &pPrefix))) { goto Exit; } if( (puzName = pPrefix->puzPrefixName) != NULL) { if( bUnicode && puzPrefixBuf) { if( !uiBufChars) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } while( *puzName) { if( uiBufChars == 1) { puzPrefixBuf[ uiOffset] = 0; rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } puzPrefixBuf[ uiOffset++] = *puzName; puzName++; uiBufChars--; } puzPrefixBuf[ uiOffset] = 0; } else if (pszPrefixBuf) { if (!uiBufChars) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } while ( *puzName) { if (uiBufChars == 1) { pszPrefixBuf[ uiOffset] = 0; rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Is the character convertable? if (*puzName > 127) { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } else { pszPrefixBuf[ uiOffset] = (char)*puzName; uiOffset++; } puzName++; } pszPrefixBuf[ uiOffset] = 0; } else if( puiCharsReturned) { uiOffset = f_unilen( puzName); } } Exit: if( puiCharsReturned) { *puiCharsReturned = uiOffset; } return( rc); } /*************************************************************************** Desc: Returns prefix object ***************************************************************************/ RCODE F_Dict::getPrefix( FLMUINT uiPrefixNum, F_PREFIX ** ppPrefix) { F_PREFIX * pPrefix = NULL; if( uiPrefixNum) { if( uiPrefixNum >= m_uiLowestPrefixNum && uiPrefixNum <= m_uiHighestPrefixNum) { pPrefix = m_ppPrefixTbl[ uiPrefixNum - m_uiLowestPrefixNum]; } } if( ppPrefix) { *ppPrefix = pPrefix; } return( pPrefix ? NE_XFLM_OK : RC_SET( NE_XFLM_BAD_PREFIX)); } /*************************************************************************** Desc: Returns the name of an encdef, either Unicode or Native. ***************************************************************************/ RCODE F_Dict::getEncDef( FLMBOOL bUnicode, FLMUINT uiEncDefId, void * pvEncDefBuf, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_ENCDEF * pEncDef; FLMUNICODE * puzName; FLMUINT uiOffset = 0; FLMUNICODE * puzEncDefBuf = (FLMUNICODE *)pvEncDefBuf; char * pszEncDefBuf = (char *)pvEncDefBuf; FLMUINT uiBufChars = (bUnicode ? uiBufSize / sizeof(FLMUNICODE) : uiBufSize); if( RC_BAD( rc = getEncDef( uiEncDefId, &pEncDef))) { goto Exit; } if( (puzName = pEncDef->puzEncDefName) != NULL) { if( bUnicode && puzEncDefBuf) { if( !uiBufChars) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } while( *puzName) { if( uiBufChars == 1) { puzEncDefBuf[ uiOffset] = 0; rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } puzEncDefBuf[ uiOffset++] = *puzName; puzName++; uiBufChars--; } puzEncDefBuf[ uiOffset] = 0; } else if (pszEncDefBuf) { if (!uiBufChars) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } while ( *puzName) { if (uiBufChars == 1) { pszEncDefBuf[ uiOffset] = 0; rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Is the character convertable? if (*puzName > 127) { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } else { pszEncDefBuf[ uiOffset] = (char)*puzName; uiOffset++; } puzName++; } pszEncDefBuf[ uiOffset] = 0; } else if( puiCharsReturned) { uiOffset = f_unilen( puzName); } } Exit: if( puiCharsReturned) { *puiCharsReturned = uiOffset; } return( rc); } /*************************************************************************** Desc: Returns encdef object ***************************************************************************/ RCODE F_Dict::getEncDef( FLMUINT uiEncDefNum, F_ENCDEF ** ppEncDef) { F_ENCDEF * pEncDef = NULL; if( uiEncDefNum) { if( uiEncDefNum >= m_uiLowestEncDefNum && uiEncDefNum <= m_uiHighestEncDefNum) { pEncDef = m_ppEncDefTbl[ uiEncDefNum - m_uiLowestEncDefNum]; } } if( ppEncDef) { *ppEncDef = pEncDef; } return( pEncDef ? NE_XFLM_OK : RC_SET( NE_XFLM_BAD_ENCDEF_NUM)); } /*************************************************************************** Desc: Returns the root node of the specified attribute definition ***************************************************************************/ RCODE F_Dict::getDefinitionDoc( F_Db * pDb, FLMUINT uiTag, FLMUINT uiDictId, F_DOMNode ** ppDoc) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey; F_DataVector foundKey; // Need to lookup the document's ID, using its dictionary number // attribute if (RC_BAD( rc = searchKey.setUINT( 0, uiTag))) { goto Exit; } if (RC_BAD( rc = searchKey.setUINT( 1, uiDictId))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, foundKey.getDocumentID(), ppDoc))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the IXD and LFILE information given an index number. ***************************************************************************/ RCODE F_Dict::getIndex( FLMUINT uiIndexNum, LFILE ** ppLFile, // [out] optional IXD ** ppIxd, // [out] optional FLMBOOL bOfflineOk) { RCODE rc = NE_XFLM_OK; IXD * pIxd = NULL; if (uiIndexNum >= m_uiLowestIxNum && uiIndexNum <= m_uiHighestIxNum) { pIxd = m_ppIxdTbl [uiIndexNum - m_uiLowestIxNum]; } else { switch (uiIndexNum) { case XFLM_DICT_NUMBER_INDEX: pIxd = m_pNumberIndex; break; case XFLM_DICT_NAME_INDEX: pIxd = m_pNameIndex; break; } } if (ppIxd) { *ppIxd = pIxd; } if (!pIxd) { if (ppLFile) { *ppLFile = NULL; } rc = RC_SET( NE_XFLM_BAD_IX); goto Exit; } else { if (ppLFile) { *ppLFile = &pIxd->lfInfo; } // If the index is suspended the IXD_OFFLINE flag // will be set, so it is sufficient to just test // the IXD_OFFLINE for both suspended and offline // conditions. if ((pIxd->uiFlags & IXD_OFFLINE) && !bOfflineOk) { rc = RC_SET( NE_XFLM_INDEX_OFFLINE); goto Exit; } // An encrypted index is offline if we are in limited mode. if (pIxd->lfInfo.uiEncId && m_bInLimitedMode && !bOfflineOk) { rc = RC_SET( NE_XFLM_INDEX_OFFLINE); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Given an index number, returns the next index after that. These should be returned in ascending numeric order ALWAYS - there are routines that are depending on this. ***************************************************************************/ IXD * F_Dict::getNextIndex( FLMUINT uiIndexNum, FLMBOOL bOkToGetPredefined) { IXD * pIxd = NULL; if (uiIndexNum < m_uiLowestIxNum) { uiIndexNum = m_uiLowestIxNum; } else { uiIndexNum++; } while (uiIndexNum >= m_uiLowestIxNum && uiIndexNum <= m_uiHighestIxNum) { if ((pIxd = m_ppIxdTbl [uiIndexNum - m_uiLowestIxNum]) != NULL) { goto Exit; } uiIndexNum++; } // pIxd better be NULL at this point flmAssert( pIxd == NULL); if (bOkToGetPredefined) { if (uiIndexNum <= XFLM_DICT_NUMBER_INDEX) { pIxd = m_pNumberIndex; } else if (uiIndexNum <= XFLM_DICT_NAME_INDEX) { pIxd = m_pNameIndex; } } Exit: return( pIxd); } /*************************************************************************** Desc: Given a collection number, returns the next collection after that. These should be returned in ascending numeric order ALWAYS - there are routines that are depending on this. ***************************************************************************/ F_COLLECTION * F_Dict::getNextCollection( FLMUINT uiCollectionNum, FLMBOOL bOkToGetPredefined) { F_COLLECTION * pCollection = NULL; if (uiCollectionNum < m_uiLowestCollectionNum) { uiCollectionNum = m_uiLowestCollectionNum; } else { uiCollectionNum++; } while (uiCollectionNum >= m_uiLowestCollectionNum && uiCollectionNum <= m_uiHighestCollectionNum) { if ((pCollection = m_ppCollectionTbl [uiCollectionNum - m_uiLowestCollectionNum]) != NULL) { goto Exit; // Will return pLFile } uiCollectionNum++; } // pCollection better be NULL at this point flmAssert( pCollection == NULL); if (bOkToGetPredefined) { if( uiCollectionNum <= XFLM_MAINT_COLLECTION) { pCollection = m_pMaintCollection; } else if( uiCollectionNum <= XFLM_DATA_COLLECTION) { pCollection = m_pDataCollection; } else if (uiCollectionNum <= XFLM_DICT_COLLECTION) { pCollection = m_pDictCollection; } } Exit: return( pCollection); } /**************************************************************************** Desc: Link the dictionary to an F_Database object NOTE: This routine assumes the global mutex is locked. ****************************************************************************/ void F_Dict::linkToDatabase( F_Database * pDatabase) { if ((m_pNext = pDatabase->m_pDictList) != NULL) { m_uiDictSeq = m_pNext->m_uiDictSeq + 1; m_pNext->m_pPrev = this; } else { m_uiDictSeq = 1; } pDatabase->m_pDictList = this; m_pDatabase = pDatabase; } /**************************************************************************** Desc: Unlink the dictionary from its F_Database object NOTE: This routine assumes the database mutex is locked. ****************************************************************************/ void F_Dict::unlinkFromDatabase( void) { // Unlink the local dictionary from its database - if it is connected // to one. if (m_pDatabase) { if (m_pPrev) { m_pPrev->m_pNext = m_pNext; } else { m_pDatabase->m_pDictList = m_pNext; } if (m_pNext) { m_pNext->m_pPrev = m_pPrev; } } // Free the local dictionary and its associated tables. Release(); } /**************************************************************************** Desc: Insert an IFD into the chain of IFDs that is headed by ppFirstIfd. Alter ppFirstIfd if linking at head of chain. ****************************************************************************/ FSTATIC void fdictInsertInIcdChain( ICD ** ppFirstIcd, ICD * pIcd) { ICD * pTempIcd; ICD * pPrevInChain; if (!(*ppFirstIcd)) { *ppFirstIcd = pIcd; } else { // Follow the chain and index at the front or rear depending on // if the attribute or element is required within the set. pTempIcd = *ppFirstIcd; if ((pIcd->uiFlags & ICD_REQUIRED_IN_SET) || !(pTempIcd->uiFlags & ICD_REQUIRED_IN_SET)) { pIcd->pNextInChain = pTempIcd; *ppFirstIcd = pIcd; } else { // Not required in set and first ICD is required in set. // Look for first not required ICD in the chain. pPrevInChain = pTempIcd; pTempIcd = pTempIcd->pNextInChain; for (; pTempIcd; pTempIcd = pTempIcd->pNextInChain) { if (!(pTempIcd->uiFlags & ICD_REQUIRED_IN_SET)) { break; } pPrevInChain = pTempIcd; } pIcd->pNextInChain = pPrevInChain->pNextInChain; pPrevInChain->pNextInChain = pIcd; } } } /**************************************************************************** Desc: Remove an ICD from the chain of ICDs that is headed by ppFirstIcd. Alter ppFirstIcd if it was at the head of the chain. ****************************************************************************/ FSTATIC void fdictRemoveFromIcdChain( ICD ** ppFirstIcd, ICD * pIcd) { ICD * pTmpIcd; ICD * pPrevInChain; pPrevInChain = NULL; pTmpIcd = *ppFirstIcd; while (pTmpIcd != pIcd) { pPrevInChain = pTmpIcd; pTmpIcd = pTmpIcd->pNextInChain; } // Better have found it! flmAssert( pTmpIcd == pIcd); // Unlink from the chain if (!pPrevInChain) { *ppFirstIcd = pIcd->pNextInChain; } else { pPrevInChain->pNextInChain = pIcd->pNextInChain; } } /**************************************************************************** Desc: Set the first ICD pointer for an extended element - if the element is currently in memory. Should not need to lock the mutex to do this because the dictionary should not be in a shared state - only one F_Db should be pointing to it, and it should be in the middle of an update transaction where an index definition is being added, modified, or deleted. ****************************************************************************/ void F_Dict::setExtElementFirstIcd( FLMUINT uiElementNum, ICD * pFirstIcd) { EXT_ATTR_ELM_DEF * pExtElementDef; pExtElementDef = getExtElementDef( uiElementNum); if (pExtElementDef->uiDictNum == uiElementNum) { pExtElementDef->attrElmDef.pFirstIcd = pFirstIcd; } } /**************************************************************************** Desc: Set the first ICD pointer for an extended attribute - if the attribute is currently in memory. Should not need to lock the mutex to do this because the dictionary should not be in a shared state - only one F_Db should be pointing to it, and it should be in the middle of an update transaction where an index definition is being added, modified, or deleted. ****************************************************************************/ void F_Dict::setExtAttributeFirstIcd( FLMUINT uiAttributeNum, ICD * pFirstIcd) { EXT_ATTR_ELM_DEF * pExtAttributeDef; pExtAttributeDef = getExtAttributeDef( uiAttributeNum); if (pExtAttributeDef->uiDictNum == uiAttributeNum) { pExtAttributeDef->attrElmDef.pFirstIcd = pFirstIcd; } } /**************************************************************************** Desc: Link an ICD into its ICD chain. ****************************************************************************/ RCODE F_Dict::linkIcdInChain( ICD * pIcd) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLMUINT uiInsertPos; IX_ITEM * pIxItem; if (pIcd->uiFlags & ICD_IS_ATTRIBUTE) { ATTR_ELM_DEF * pAttributeDef; if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { // Link the ICD into its chain, making sure that required attributes // are first in the chain. pAttributeDef = getAttributeDef( pIcd->uiDictNum); // Attribute had better be defined at this point. We should have already // verified them. flmAssert( pAttributeDef != NULL); fdictInsertInIcdChain( &pAttributeDef->pFirstIcd, pIcd); } else if (attributeIsReservedTag( pIcd->uiDictNum)) { // Link the ICD into its chain, making sure that required attributes // are first in the chain. pAttributeDef = getReservedAttributeDef( pIcd->uiDictNum); // Attribute had better be defined at this point. We should have already // verified them. flmAssert( pAttributeDef); fdictInsertInIcdChain( &pAttributeDef->pFirstIcd, pIcd); } else { // Better be one of our extended attributes flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM); if ((pIxItem = findIxAttribute( pIcd->uiDictNum, &uiInsertPos)) == NULL) { // Expand the table size if necessary - by 50 elements at a time. if (m_uiNumIxAttributes == m_uiIxAttributeTblSize) { FLMUINT uiNewTblSize = m_uiIxAttributeTblSize + 50; IX_ITEM * pNewTbl; if (RC_BAD( rc = f_calloc( sizeof( IX_ITEM) * uiNewTblSize, &pNewTbl))) { goto Exit; } if (m_uiIxAttributeTblSize) { f_memcpy( pNewTbl, m_pIxAttributeTbl, sizeof( IX_ITEM) * m_uiIxAttributeTblSize); f_free( &m_pIxAttributeTbl); } m_pIxAttributeTbl = pNewTbl; m_uiIxAttributeTblSize = uiNewTblSize; } // Insert a new IX_ITEM at the specified position. uiLoop = m_uiNumIxAttributes; while (uiLoop > uiInsertPos) { f_memcpy( &m_pIxAttributeTbl [uiLoop], &m_pIxAttributeTbl [uiLoop - 1], sizeof( IX_ITEM)); uiLoop--; } pIxItem = &m_pIxAttributeTbl [uiInsertPos]; pIxItem->uiDictNum = pIcd->uiDictNum; pIxItem->pFirstIcd = NULL; m_uiNumIxAttributes++; } fdictInsertInIcdChain( &pIxItem->pFirstIcd, pIcd); setExtAttributeFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); } } else if (pIcd->uiDictNum == ELM_ROOT_TAG) { fdictInsertInIcdChain( &m_pRootIcdList, pIcd); } else { ATTR_ELM_DEF * pElementDef; if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { // Link the ICD into its chain, making sure that required elements // are first in the chain. pElementDef = getElementDef( pIcd->uiDictNum); // Element had better be defined at this point. We should have already // verified them. flmAssert( pElementDef != NULL); fdictInsertInIcdChain( &pElementDef->pFirstIcd, pIcd); } else if (elementIsReservedTag( pIcd->uiDictNum)) { // Link the ICD into its chain, making sure that required elements // are first in the chain. pElementDef = getReservedElementDef( pIcd->uiDictNum); // Element had better be defined at this point. We should have already // verified them. flmAssert( pElementDef); fdictInsertInIcdChain( &pElementDef->pFirstIcd, pIcd); } else { // Better be one of our extended elements flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ELEMENT_NUM); if ((pIxItem = findIxElement( pIcd->uiDictNum, &uiInsertPos)) == NULL) { // Expand the table size if necessary - by 50 elements at a time. if (m_uiNumIxElements == m_uiIxElementTblSize) { FLMUINT uiNewTblSize = m_uiIxElementTblSize + 50; IX_ITEM * pNewTbl; if (RC_BAD( rc = f_calloc( sizeof( IX_ITEM) * uiNewTblSize, &pNewTbl))) { goto Exit; } if (m_uiIxElementTblSize) { f_memcpy( pNewTbl, m_pIxElementTbl, sizeof( IX_ITEM) * m_uiIxElementTblSize); f_free( &m_pIxElementTbl); } m_pIxElementTbl = pNewTbl; m_uiIxElementTblSize = uiNewTblSize; } // Insert a new IX_ITEM at the specified position. uiLoop = m_uiNumIxElements; while (uiLoop > uiInsertPos) { f_memcpy( &m_pIxElementTbl [uiLoop], &m_pIxElementTbl [uiLoop - 1], sizeof( IX_ITEM)); uiLoop--; } pIxItem = &m_pIxElementTbl [uiInsertPos]; pIxItem->uiDictNum = pIcd->uiDictNum; pIxItem->pFirstIcd = NULL; m_uiNumIxElements++; } fdictInsertInIcdChain( &pIxItem->pFirstIcd, pIcd); setExtElementFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); } } Exit: return( rc); } /*************************************************************************** Desc: Link all of the ICDs of a tree into their proper chains from the IX_ITEM array or ATTR_ELM_DEF array. ***************************************************************************/ RCODE F_Dict::linkIcds( ICD * pIcdTree) { RCODE rc = NE_XFLM_OK; ICD * pIcd = pIcdTree; while (pIcd) { if (RC_BAD( rc = linkIcdInChain( pIcd))) { goto Exit; } if (pIcd->pFirstChild) { pIcd = pIcd->pFirstChild; } else { while (pIcd && !pIcd->pNextSibling) { pIcd = pIcd->pParent; } if (!pIcd) { break; } pIcd = pIcd->pNextSibling; } } Exit: return( rc); } /**************************************************************************** Desc: Unlink an ICD from the ICD chain it is in. ****************************************************************************/ void F_Dict::unlinkIcdFromChain( ICD * pIcd) { IX_ITEM * pIxItem; if (pIcd->uiFlags & ICD_IS_ATTRIBUTE) { ATTR_ELM_DEF * pAttributeDef; // Unlink the ICD from its chain - must find its previous ICD. if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { pAttributeDef = getAttributeDef( pIcd->uiDictNum); // Attribute had better be defined at this point. flmAssert( pAttributeDef != NULL && pAttributeDef->pFirstIcd); fdictRemoveFromIcdChain( &pAttributeDef->pFirstIcd, pIcd); } else if (attributeIsReservedTag( pIcd->uiDictNum)) { pAttributeDef = getReservedAttributeDef( pIcd->uiDictNum); // Attribute had better be defined at this point. flmAssert( pAttributeDef != NULL && pAttributeDef->pFirstIcd); fdictRemoveFromIcdChain( &pAttributeDef->pFirstIcd, pIcd); } else { // Better be one of our extended attributes flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM); pIxItem = findIxAttribute( pIcd->uiDictNum); // If there is an ICD, it better be in the IX_ITEM list! flmAssert( pIxItem && pIxItem->pFirstIcd); fdictRemoveFromIcdChain( &pIxItem->pFirstIcd, pIcd); setExtAttributeFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); } } else if (pIcd->uiDictNum == ELM_ROOT_TAG) { fdictRemoveFromIcdChain( &m_pRootIcdList, pIcd); } else { ATTR_ELM_DEF * pElementDef; // Unlink the ICD from its chain - must find its previous ICD. if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { pElementDef = getElementDef( pIcd->uiDictNum); // Element had better be defined at this point. flmAssert( pElementDef != NULL && pElementDef->pFirstIcd); fdictRemoveFromIcdChain( &pElementDef->pFirstIcd, pIcd); } else if (elementIsReservedTag( pIcd->uiDictNum)) { pElementDef = getReservedElementDef( pIcd->uiDictNum); // Element had better be defined at this point. flmAssert( pElementDef != NULL && pElementDef->pFirstIcd); fdictRemoveFromIcdChain( &pElementDef->pFirstIcd, pIcd); } else { // Better be one of our extended elements flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM); pIxItem = findIxElement( pIcd->uiDictNum); // If there is an ICD, it better be in the IX_ITEM list! flmAssert( pIxItem && pIxItem->pFirstIcd); fdictRemoveFromIcdChain( &pIxItem->pFirstIcd, pIcd); setExtElementFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); } } } /**************************************************************************** Desc: Unlink all ICDs in an ICD tree from the chains they are in. ****************************************************************************/ void F_Dict::unlinkIcds( ICD * pIcdTree) { ICD * pIcd = pIcdTree; while (pIcd) { unlinkIcdFromChain( pIcd); if (pIcd->pFirstChild) { pIcd = pIcd->pFirstChild; } else { while (pIcd && !pIcd->pNextSibling) { pIcd = pIcd->pParent; } if (!pIcd) { break; } pIcd = pIcd->pNextSibling; } } } /**************************************************************************** Desc: Copies an IXD and all of its ICDs. ****************************************************************************/ RCODE F_Dict::copyIXD( IXD ** ppDestIxd, IXD * pSrcIxd) { RCODE rc = NE_XFLM_OK; IXD * pDestIxd; ICD * pSrcIcd; ICD * pDestIcd; ICD * pLastIcd = NULL; ICD * pTmpIcd; FLMBOOL bLinkAsChild = FALSE; if (!pSrcIxd) { *ppDestIxd = NULL; goto Exit; } // Allocate the IXD structure if (RC_BAD( rc = m_dictPool.poolAlloc( sizeof( IXD), (void **)&pDestIxd))) { goto Exit; } *ppDestIxd = pDestIxd; f_memcpy( pDestIxd, pSrcIxd, sizeof( IXD)); // Null out the pointers to the ICDs. pDestIxd->pIcdTree = NULL; pDestIxd->pFirstKey = NULL; pDestIxd->pLastKey = NULL; pDestIxd->pFirstData = NULL; pDestIxd->pLastData = NULL; pDestIxd->pFirstContext = NULL; pDestIxd->pLastContext = NULL; // Copy the ICDs pSrcIcd = pSrcIxd->pIcdTree; while (pSrcIcd) { if (RC_BAD( rc = m_dictPool.poolAlloc( sizeof( ICD), (void **)&pDestIcd))) { goto Exit; } f_memcpy( pDestIcd, pSrcIcd, sizeof( ICD)); pDestIcd->pNextInChain = NULL; pDestIcd->pIxd = pDestIxd; // Link into key component list if (pDestIcd->uiKeyComponent) { pTmpIcd = pDestIxd->pFirstKey; while (pTmpIcd && pDestIcd->uiKeyComponent > pTmpIcd->uiKeyComponent) { pTmpIcd = pTmpIcd->pNextKeyComponent; } if ((pDestIcd->pNextKeyComponent = pTmpIcd) == NULL) { // Link at end of list. if ((pDestIcd->pPrevKeyComponent = pDestIxd->pLastKey) != NULL) { pDestIxd->pLastKey->pNextKeyComponent = pDestIcd; } else { pDestIxd->pFirstKey = pDestIcd; } pDestIxd->pLastKey = pDestIcd; } else { // Link in front of pTmpIcd flmAssert( pDestIcd->uiKeyComponent < pTmpIcd->uiKeyComponent); if ((pDestIcd->pPrevKeyComponent = pTmpIcd->pPrevKeyComponent) == NULL) { pDestIxd->pFirstKey = pDestIcd; } else { pTmpIcd->pPrevKeyComponent->pNextKeyComponent = pDestIcd; } pTmpIcd->pPrevKeyComponent = pDestIcd; } } // Link into the data component list if (pDestIcd->uiDataComponent) { pTmpIcd = pDestIxd->pFirstData; while (pTmpIcd && pDestIcd->uiDataComponent > pTmpIcd->uiDataComponent) { pTmpIcd = pTmpIcd->pNextDataComponent; } if ((pDestIcd->pNextDataComponent = pTmpIcd) == NULL) { // Link at end of list if ((pDestIcd->pPrevDataComponent = pDestIxd->pLastData) != NULL) { pDestIxd->pLastData->pNextDataComponent = pDestIcd; } else { pDestIxd->pFirstData = pDestIcd; } pDestIxd->pLastData = pDestIcd; } else { // Link in front of pTmpIcd flmAssert( pDestIcd->uiDataComponent != pTmpIcd->uiDataComponent); if ((pDestIcd->pPrevDataComponent = pTmpIcd->pPrevDataComponent) == NULL) { // Link at very front of list pDestIxd->pFirstData = pDestIcd; } else { pTmpIcd->pPrevDataComponent->pNextDataComponent = pDestIcd; } pTmpIcd->pPrevDataComponent = pDestIcd; } } // Link into the context component list if not a key or data component if (!pDestIcd->uiDataComponent && !pDestIcd->uiKeyComponent) { pTmpIcd = pDestIxd->pFirstContext; while (pTmpIcd && pDestIcd->uiCdl > pTmpIcd->uiCdl) { pTmpIcd = pTmpIcd->pNextKeyComponent; } if ((pDestIcd->pNextKeyComponent = pTmpIcd) == NULL) { // Link at end of list if ((pDestIcd->pPrevKeyComponent = pDestIxd->pLastContext) != NULL) { pDestIxd->pLastContext->pNextKeyComponent = pDestIcd; } else { pDestIxd->pFirstContext = pDestIcd; } pDestIxd->pLastContext = pDestIcd; } else { // Link in front of pTmpIcd flmAssert( pDestIcd->uiCdl != pTmpIcd->uiCdl); if ((pDestIcd->pPrevKeyComponent = pTmpIcd->pPrevKeyComponent) == NULL) { // Link at very front of list pDestIxd->pFirstContext = pDestIcd; } else { pTmpIcd->pPrevKeyComponent->pNextKeyComponent = pDestIcd; } pTmpIcd->pPrevKeyComponent = pDestIcd; } } // Link the ICD into the destination ICD tree pDestIcd->pFirstChild = NULL; if (!pDestIxd->pIcdTree) { pDestIxd->pIcdTree = pDestIcd; pDestIcd->pParent = NULL; pDestIcd->pPrevSibling = NULL; pDestIcd->pNextSibling = NULL; } else if (bLinkAsChild) { // link as child pLastIcd->pFirstChild = pDestIcd; pDestIcd->pParent = pLastIcd; pDestIcd->pPrevSibling = NULL; pDestIcd->pNextSibling = NULL; } else { // link as sibling pLastIcd->pNextSibling = pDestIcd; pDestIcd->pPrevSibling = pLastIcd; pDestIcd->pNextSibling = NULL; pDestIcd->pParent = pLastIcd->pParent; } // Go to the next source ICD pLastIcd = pDestIcd; if (pSrcIcd->pFirstChild) { pSrcIcd = pSrcIcd->pFirstChild; bLinkAsChild = TRUE; } else { while (pSrcIcd && !pSrcIcd->pNextSibling) { pLastIcd = pLastIcd->pParent; pSrcIcd = pSrcIcd->pParent; } if (!pSrcIcd) { break; } bLinkAsChild = FALSE; pSrcIcd = pSrcIcd->pNextSibling; } } // Put the ICDs into their appropriate ICD chains. if (RC_BAD( rc = linkIcds( pDestIxd->pIcdTree))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Copies a collection ****************************************************************************/ FSTATIC RCODE fdictCopyCollection( F_Pool * pDictPool, F_COLLECTION ** ppDestCollection, F_COLLECTION * pSrcCollection) { RCODE rc = NE_XFLM_OK; if (!pSrcCollection) { *ppDestCollection = NULL; } else { if (RC_BAD( rc = pDictPool->poolAlloc( sizeof( F_COLLECTION), (void **)ppDestCollection))) { goto Exit; } f_memcpy( *ppDestCollection, pSrcCollection, sizeof( F_COLLECTION)); } Exit: return( rc); } /**************************************************************************** Desc: Copies a prefix ****************************************************************************/ FSTATIC RCODE fdictCopyPrefix( F_Pool * pDictPool, F_PREFIX ** ppDestPrefix, F_PREFIX * pSrcPrefix) { RCODE rc = NE_XFLM_OK; FLMUINT uiPrefixLen; if (!pSrcPrefix) { *ppDestPrefix = NULL; } else { if (RC_BAD( rc = pDictPool->poolAlloc( sizeof( F_PREFIX), (void **)ppDestPrefix))) { goto Exit; } (*ppDestPrefix)->ui64PrefixId = pSrcPrefix->ui64PrefixId; uiPrefixLen = f_unilen( pSrcPrefix->puzPrefixName); if( RC_BAD( rc = pDictPool->poolAlloc( sizeof( FLMUNICODE) * (uiPrefixLen + 1), (void **)&(*ppDestPrefix)->puzPrefixName))) { goto Exit; } f_memcpy( (*ppDestPrefix)->puzPrefixName, pSrcPrefix->puzPrefixName, (uiPrefixLen + 1) * sizeof( FLMUNICODE)); } Exit: return( rc); } /**************************************************************************** Desc: Copies an encryption def (F_ENCDEF) ****************************************************************************/ FSTATIC RCODE fdictCopyEncDef( F_Pool * pDictPool, F_ENCDEF ** ppDestEncDef, F_ENCDEF * pSrcEncDef) { RCODE rc = NE_XFLM_OK; FLMUINT uiEncDefLen; if (!pSrcEncDef) { *ppDestEncDef = NULL; } else { if (RC_BAD( rc = pDictPool->poolAlloc( sizeof( F_ENCDEF), (void **)ppDestEncDef))) { goto Exit; } (*ppDestEncDef)->ui64EncDefId = pSrcEncDef->ui64EncDefId; (*ppDestEncDef)->ui64DocumentId = pSrcEncDef->ui64DocumentId; uiEncDefLen = f_unilen( pSrcEncDef->puzEncDefName); if( RC_BAD( rc = pDictPool->poolAlloc( sizeof( FLMUNICODE) * (uiEncDefLen + 1), (void **)&(*ppDestEncDef)->puzEncDefName))) { goto Exit; } f_memcpy( (*ppDestEncDef)->puzEncDefName, pSrcEncDef->puzEncDefName, (uiEncDefLen + 1) * sizeof( FLMUNICODE)); (*ppDestEncDef)->pCcs = pSrcEncDef->pCcs; (*ppDestEncDef)->pCcs->AddRef(); (*ppDestEncDef)->uiEncKeySize = pSrcEncDef->uiEncKeySize; } Exit: return( rc); } /**************************************************************************** Desc: Clones a dictionary from another one. ****************************************************************************/ RCODE F_Dict::cloneDict( F_Dict * pSrcDict) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; FLMUINT uiLoop; FLMBOOL bElementMutexLocked = FALSE; FLMBOOL bAttributeMutexLocked = FALSE; resetDict(); // Copy the element definitions. m_uiLowestElementNum = pSrcDict->m_uiLowestElementNum; m_uiHighestElementNum = pSrcDict->m_uiHighestElementNum; if (m_uiHighestElementNum) { uiCount = m_uiHighestElementNum - m_uiLowestElementNum + 1; if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), &m_pElementDefTbl))) { goto Exit; } f_memcpy( m_pElementDefTbl, pSrcDict->m_pElementDefTbl, uiCount * sizeof( ATTR_ELM_DEF)); // NULL out every element's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { m_pElementDefTbl[ uiLoop].pFirstIcd = NULL; } } // Copy the reserved element definitions. uiCount = XFLM_LAST_RESERVED_ELEMENT_TAG - XFLM_FIRST_RESERVED_ELEMENT_TAG + 1; if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), &m_pReservedElementDefTbl))) { goto Exit; } f_memcpy( m_pReservedElementDefTbl, pSrcDict->m_pReservedElementDefTbl, uiCount * sizeof( ATTR_ELM_DEF)); // NULL out every element's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { m_pReservedElementDefTbl [uiLoop].pFirstIcd = NULL; } // Copy the extended element definitions, if any if (pSrcDict->m_pExtElementDefTbl) { // Source table's mutex must be locked while copying f_mutexLock( pSrcDict->m_hExtElementDefMutex); bElementMutexLocked = TRUE; m_uiExtElementDefTblSize = pSrcDict->m_uiExtElementDefTblSize; // Allocate a mutex if (RC_BAD( rc = f_mutexCreate( &m_hExtElementDefMutex))) { goto Exit; } // Allocate the array if (RC_BAD( rc = f_alloc( m_uiExtElementDefTblSize * sizeof( EXT_ATTR_ELM_DEF), &m_pExtElementDefTbl))) { goto Exit; } f_memcpy( m_pExtElementDefTbl, pSrcDict->m_pExtElementDefTbl, m_uiExtElementDefTblSize * sizeof( EXT_ATTR_ELM_DEF)); f_mutexUnlock( pSrcDict->m_hExtElementDefMutex); bElementMutexLocked = FALSE; // NULL out every element's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < m_uiExtElementDefTblSize; uiLoop++) { m_pExtElementDefTbl [uiLoop].attrElmDef.pFirstIcd = NULL; } } // Copy the extended element IX_ITEM structures for indexing. if (pSrcDict->m_pIxElementTbl) { m_uiIxElementTblSize = pSrcDict->m_uiIxElementTblSize; m_uiNumIxElements = pSrcDict->m_uiNumIxElements; if (RC_BAD( rc = f_alloc( sizeof( IX_ITEM) * m_uiIxElementTblSize, &m_pIxElementTbl))) { goto Exit; } f_memcpy( m_pIxElementTbl, pSrcDict->m_pIxElementTbl, sizeof( IX_ITEM) * m_uiIxElementTblSize); // NULL out every element's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < m_uiNumIxElements; uiLoop++) { m_pIxElementTbl [uiLoop].pFirstIcd = NULL; } } // Copy the attribute definitions. m_uiLowestAttributeNum = pSrcDict->m_uiLowestAttributeNum; m_uiHighestAttributeNum = pSrcDict->m_uiHighestAttributeNum; if (m_uiHighestAttributeNum) { uiCount = m_uiHighestAttributeNum - m_uiLowestAttributeNum + 1; if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), &m_pAttributeDefTbl))) { goto Exit; } f_memcpy( m_pAttributeDefTbl, pSrcDict->m_pAttributeDefTbl, uiCount * sizeof( ATTR_ELM_DEF)); // NULL out every attribute's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { m_pAttributeDefTbl[ uiLoop].pFirstIcd = NULL; } } // Copy the reserved attribute definitions. uiCount = XFLM_LAST_RESERVED_ATTRIBUTE_TAG - XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1; if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), &m_pReservedAttributeDefTbl))) { goto Exit; } f_memcpy( m_pReservedAttributeDefTbl, pSrcDict->m_pReservedAttributeDefTbl, uiCount * sizeof( ATTR_ELM_DEF)); // NULL out every attribute's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { m_pReservedAttributeDefTbl [uiLoop].pFirstIcd = NULL; } // Copy the extended attribute definitions, if any if (pSrcDict->m_pExtAttributeDefTbl) { // Source table's mutex must be locked while copying f_mutexLock( pSrcDict->m_hExtAttributeDefMutex); bAttributeMutexLocked = TRUE; m_uiExtAttributeDefTblSize = pSrcDict->m_uiExtAttributeDefTblSize; // Allocate a mutex if (RC_BAD( rc = f_mutexCreate( &m_hExtAttributeDefMutex))) { goto Exit; } // Allocate the array if (RC_BAD( rc = f_alloc( m_uiExtAttributeDefTblSize * sizeof( EXT_ATTR_ELM_DEF), &m_pExtAttributeDefTbl))) { goto Exit; } f_memcpy( m_pExtAttributeDefTbl, pSrcDict->m_pExtAttributeDefTbl, m_uiExtAttributeDefTblSize * sizeof( EXT_ATTR_ELM_DEF)); f_mutexUnlock( pSrcDict->m_hExtAttributeDefMutex); bAttributeMutexLocked = FALSE; // NULL out every attribute's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < m_uiExtAttributeDefTblSize; uiLoop++) { m_pExtAttributeDefTbl[ uiLoop].attrElmDef.pFirstIcd = NULL; } } // Copy the extended attribute IX_ITEM structures for indexing. if (pSrcDict->m_pIxAttributeTbl) { m_uiIxAttributeTblSize = pSrcDict->m_uiIxAttributeTblSize; m_uiNumIxAttributes = pSrcDict->m_uiNumIxAttributes; if (RC_BAD( rc = f_alloc( sizeof( IX_ITEM) * m_uiIxAttributeTblSize, &m_pIxAttributeTbl))) { goto Exit; } f_memcpy( m_pIxAttributeTbl, pSrcDict->m_pIxAttributeTbl, sizeof( IX_ITEM) * m_uiIxAttributeTblSize); // NULL out every attribute's pFirstIcd pointer. These will // be fixed up as indexes are added. for (uiLoop = 0; uiLoop < m_uiNumIxAttributes; uiLoop++) { m_pIxAttributeTbl [uiLoop].pFirstIcd = NULL; } } // Copy the pre-defined collections if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, &m_pDictCollection, pSrcDict->m_pDictCollection))) { goto Exit; } if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, &m_pDataCollection, pSrcDict->m_pDataCollection))) { goto Exit; } if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, &m_pMaintCollection, pSrcDict->m_pMaintCollection))) { goto Exit; } // Collection Table if ((uiCount = pSrcDict->getCollectionCount( FALSE)) > 0) { m_uiLowestCollectionNum = pSrcDict->m_uiLowestCollectionNum; m_uiHighestCollectionNum = pSrcDict->m_uiHighestCollectionNum; if (RC_BAD( rc = f_alloc( uiCount * sizeof( F_COLLECTION *), &m_ppCollectionTbl))) { goto Exit; } // Copy each F_COLLECTION in the table. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, &m_ppCollectionTbl [uiLoop], pSrcDict->m_ppCollectionTbl [uiLoop]))) { goto Exit; } } } // Copy the user-defined prefixes if ((uiCount = pSrcDict->getPrefixCount()) > 0) { m_uiLowestPrefixNum = pSrcDict->m_uiLowestPrefixNum; m_uiHighestPrefixNum = pSrcDict->m_uiHighestPrefixNum; if (RC_BAD( rc = f_alloc( uiCount * sizeof( F_PREFIX *), &m_ppPrefixTbl))) { goto Exit; } // Copy each F_PREFIX in the table. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { if (RC_BAD( rc = fdictCopyPrefix( &m_dictPool, &m_ppPrefixTbl [uiLoop], pSrcDict->m_ppPrefixTbl [uiLoop]))) { goto Exit; } } } // Copy the encryption definitions if ((uiCount = pSrcDict->getEncDefCount()) > 0) { m_uiLowestEncDefNum = pSrcDict->m_uiLowestEncDefNum; m_uiHighestEncDefNum = pSrcDict->m_uiHighestEncDefNum; if (RC_BAD( rc = f_alloc( uiCount * sizeof( F_ENCDEF *), &m_ppEncDefTbl))) { goto Exit; } // Copy each F_ENCDEF in the table. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { if (RC_BAD( rc = fdictCopyEncDef( &m_dictPool, &m_ppEncDefTbl [uiLoop], pSrcDict->m_ppEncDefTbl [uiLoop]))) { goto Exit; } } } // Copy the hard-coded indexes if (RC_BAD( rc = copyIXD( &m_pNameIndex, pSrcDict->m_pNameIndex))) { goto Exit; } if (RC_BAD( rc = copyIXD( &m_pNumberIndex, pSrcDict->m_pNumberIndex))) { goto Exit; } // IXD Table m_uiLowestIxNum = pSrcDict->m_uiLowestIxNum; m_uiHighestIxNum = pSrcDict->m_uiHighestIxNum; // Array of IXD pointers - for user defined indexes only if ((uiCount = pSrcDict->getIndexCount( FALSE)) > 0) { if (RC_BAD( rc = f_alloc( uiCount * sizeof( IXD *), &m_ppIxdTbl))) { goto Exit; } // Copy each IXD in the table. for (uiLoop = 0; uiLoop < uiCount; uiLoop++) { if (RC_BAD( rc = copyIXD( &m_ppIxdTbl [uiLoop], pSrcDict->m_ppIxdTbl [uiLoop]))) { goto Exit; } } } // Copy the name table if (RC_BAD( rc = allocNameTable())) { goto Exit; } if (RC_BAD( rc = m_pNameTable->cloneNameTable( pSrcDict->m_pNameTable))) { goto Exit; } // See if we can get the limited mode flag from the database file instead // of copying it from the source dictionary. if (pSrcDict->m_pDatabase) { m_bInLimitedMode = pSrcDict->m_pDatabase->inLimitedMode(); } else { m_bInLimitedMode = pSrcDict->m_bInLimitedMode; } Exit: if (bElementMutexLocked) { f_mutexUnlock( pSrcDict->m_hExtElementDefMutex); } if (bAttributeMutexLocked) { f_mutexUnlock( pSrcDict->m_hExtAttributeDefMutex); } return( rc); } /*************************************************************************** Desc: This routine checks to see if an element is referenced in an index definition. ***************************************************************************/ RCODE F_Dict::checkElementReferences( FLMUINT uiElementNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiIxdCount; FLMUINT uiIxdLoop; IXD * pIxd; ICD * pIcd; // Look through all ICDs in all IXDs. uiIxdCount = getIndexCount( FALSE); for (uiIxdLoop = 0; uiIxdLoop < uiIxdCount; uiIxdLoop++) { if ((pIxd = m_ppIxdTbl [uiIxdLoop]) != NULL) { // Loop through all of the index's ICDs. pIcd = pIxd->pIcdTree; while (pIcd) { if (!(pIcd->uiFlags & ICD_IS_ATTRIBUTE) && pIcd->uiDictNum == uiElementNum) { rc = RC_SET( NE_XFLM_CANNOT_DEL_ELEMENT); goto Exit; } if (pIcd->pFirstChild) { pIcd = pIcd->pFirstChild; } else { while (pIcd && !pIcd->pNextSibling) { pIcd = pIcd->pParent; } if (!pIcd) { break; } pIcd = pIcd->pNextSibling; } } } } Exit: return( rc); } /*************************************************************************** Desc: This routine checks to see if an attribute is referenced in an index definition. ***************************************************************************/ RCODE F_Dict::checkAttributeReferences( FLMUINT uiAttributeNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiIxdCount; FLMUINT uiIxdLoop; IXD * pIxd; ICD * pIcd; // Look through all ICDs in all IXDs. uiIxdCount = getIndexCount( FALSE); for (uiIxdLoop = 0; uiIxdLoop < uiIxdCount; uiIxdLoop++) { if ((pIxd = m_ppIxdTbl [uiIxdLoop]) != NULL) { // Loop through all of the index's ICDs. pIcd = pIxd->pIcdTree; while (pIcd) { if ((pIcd->uiFlags & ICD_IS_ATTRIBUTE) && pIcd->uiDictNum == uiAttributeNum) { rc = RC_SET( NE_XFLM_CANNOT_DEL_ATTRIBUTE); goto Exit; } if (pIcd->pFirstChild) { pIcd = pIcd->pFirstChild; } else { while (pIcd && !pIcd->pNextSibling) { pIcd = pIcd->pParent; } if (!pIcd) { break; } pIcd = pIcd->pNextSibling; } } } } Exit: return( rc); } /*************************************************************************** Desc: This routine checks to see if a collection is referenced in an index definition. ***************************************************************************/ RCODE F_Dict::checkCollectionReferences( FLMUINT uiCollectionNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiIxdCount; FLMUINT uiIxdLoop; IXD * pIxd; // Look through all IXDs uiIxdCount = getIndexCount( FALSE); for (uiIxdLoop = 0; uiIxdLoop < uiIxdCount; uiIxdLoop++) { if ((pIxd = m_ppIxdTbl [uiIxdLoop]) != NULL) { if (pIxd->uiCollectionNum == uiCollectionNum) { rc = RC_SET( NE_XFLM_MUST_DELETE_INDEXES); goto Exit; } } } Exit: return( rc); } /*************************************************************************** Desc: Reallocate a field, index, or collection table. Note that any new slots in the table will be zeroed out. ***************************************************************************/ RCODE F_Dict::reallocTbl( FLMUINT uiNewId, FLMUINT uiElementSize, void ** ppvTbl, FLMUINT * puiLowest, FLMUINT * puiHighest, FLMUINT uiAdjustFactor, FLMUINT uiMaxId ) { RCODE rc = NE_XFLM_OK; void * pvNewTbl; FLMUINT uiLowest = *puiLowest; FLMUINT uiOldLowest = uiLowest; FLMUINT uiHighest = *puiHighest; FLMUINT uiOldCount; FLMUINT uiNewCount; if (!uiHighest) { uiOldCount = 0; if (uiNewId <= uiAdjustFactor) { uiLowest = 1; } else { uiLowest = uiNewId - uiAdjustFactor; } if (uiNewId >= uiMaxId - uiAdjustFactor) { uiHighest = uiMaxId; } else { uiHighest = uiNewId + uiAdjustFactor; } } else { uiOldCount = uiHighest - uiLowest + 1; if (uiNewId < uiLowest) { if (uiNewId <= uiAdjustFactor) { uiLowest = 1; } else { uiLowest = uiNewId - uiAdjustFactor; } } else { if (uiNewId >= uiMaxId - uiAdjustFactor) { uiHighest = uiMaxId; } else { uiHighest = uiNewId + uiAdjustFactor; } } } // Reallocate the table. uiNewCount = uiHighest - uiLowest + 1; if (RC_BAD( rc = f_calloc( uiNewCount * uiElementSize, &pvNewTbl))) { goto Exit; } if (uiOldCount) { f_memcpy( (FLMBYTE *)pvNewTbl + uiElementSize * (uiOldLowest - uiLowest), *ppvTbl, uiOldCount * uiElementSize); } f_free( ppvTbl); *ppvTbl = pvNewTbl; *puiLowest = uiLowest; *puiHighest = uiHighest; Exit: return( rc); } /*************************************************************************** Desc: Maps a string to an element or attribute data type. ***************************************************************************/ RCODE fdictGetDataType( char * pszDataType, FLMUINT * puiDataType) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataType; // Parse the type keyword - only one type allowed. for (uiDataType = 0; uiDataType < XFLM_NUM_OF_TYPES; uiDataType++) { if (f_stricmp( pszDataType, fdictDataTypes [uiDataType]) == 0) { *puiDataType = uiDataType; goto Exit; // Will return NE_XFLM_OK } } rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_DATA_TYPE); Exit: return( rc); } /*************************************************************************** Desc: Maps a string to an element or attribute data type. ***************************************************************************/ const char * fdictGetDataTypeStr( FLMUINT uiDataType ) { if( uiDataType > XFLM_NUM_OF_TYPES) { return( NULL); } return( fdictDataTypes[ uiDataType]); } /*************************************************************************** Desc: Maps a string to an element or attribute state. ***************************************************************************/ RCODE fdictGetState( const char * pszState, FLMUINT * puiState) { RCODE rc = NE_XFLM_OK; if (f_stricmp( pszState, XFLM_CHECKING_OPTION_STR) == 0) { *puiState = ATTR_ELM_STATE_CHECKING; } else if (f_stricmp( pszState, XFLM_PURGE_OPTION_STR) == 0) { *puiState = ATTR_ELM_STATE_PURGE; } else if (f_stricmp( pszState, XFLM_ACTIVE_OPTION_STR) == 0) { *puiState = ATTR_ELM_STATE_ACTIVE; } else { rc = RC_SET( NE_XFLM_ILLEGAL_STATE); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Maps a string to an index state. ***************************************************************************/ RCODE fdictGetIndexState( const char * pszState, FLMUINT * puiState) { RCODE rc = NE_XFLM_OK; if (f_stricmp( pszState, XFLM_INDEX_SUSPENDED_STR) == 0) { *puiState = IXD_SUSPENDED | IXD_OFFLINE; } else if (f_stricmp( pszState, XFLM_INDEX_OFFLINE_STR) == 0) { *puiState = IXD_OFFLINE; } else if (!pszState [0] || f_stricmp( pszState, XFLM_INDEX_ONLINE_STR) == 0) { *puiState = 0; } else { rc = RC_SET( NE_XFLM_ILLEGAL_STATE); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Determine if an element name is legal - formatwise ***************************************************************************/ FINLINE RCODE fdictLegalElementName( FLMUNICODE * // puzElementName ) { //VISIT: Need to fill this out - could return NE_XFLM_ILLEGAL_ELEMENT_NAME return( NE_XFLM_OK); } /*************************************************************************** Desc: Determine if an attribute name is legal - formatwise ***************************************************************************/ FINLINE RCODE fdictLegalAttributeName( FLMUNICODE * puzAttributeName ) { if (*puzAttributeName == '/' && *(puzAttributeName + 1) == 0) { return( RC_SET( NE_XFLM_ILLEGAL_ATTRIBUTE_NAME)); } //VISIT: Need to fill this out - could return NE_XFLM_ILLEGAL_ATTRIBUTE_NAME return( NE_XFLM_OK); } /*************************************************************************** Desc: Determine if an element number is legal ***************************************************************************/ FINLINE RCODE fdictLegalElementNumber( FLMUINT uiElementNumber, FLMBOOL bAllowReserved) { return( (uiElementNumber > 0 && uiElementNumber <= XFLM_MAX_ELEMENT_NUM) || (bAllowReserved && uiElementNumber >= XFLM_FIRST_RESERVED_ELEMENT_TAG && uiElementNumber <= XFLM_LAST_RESERVED_ELEMENT_TAG) ? NE_XFLM_OK : RC_SET( NE_XFLM_ILLEGAL_ELEMENT_NUMBER)); } /*************************************************************************** Desc: Determine if an attribute number is legal ***************************************************************************/ FINLINE RCODE fdictLegalAttributeNumber( FLMUINT uiAttributeNumber, FLMBOOL bAllowReserved) { return( (uiAttributeNumber > 0 && uiAttributeNumber <= XFLM_MAX_ATTRIBUTE_NUM) || (bAllowReserved && uiAttributeNumber >= XFLM_FIRST_RESERVED_ATTRIBUTE_TAG && uiAttributeNumber <= XFLM_LAST_RESERVED_ATTRIBUTE_TAG) ? NE_XFLM_OK : RC_SET( NE_XFLM_ILLEGAL_ATTRIBUTE_NUMBER)); } /*************************************************************************** Desc: Retrieve a dictionary. If there is no dictionary, open it first. ***************************************************************************/ RCODE F_Db::getDictionary( F_Dict ** ppDict) { RCODE rc = NE_XFLM_OK; if (!m_pDict) { if (RC_BAD( rc = dictOpen())) { goto Exit; } } *ppDict = m_pDict; Exit: return rc; } /*************************************************************************** Desc: Change an element's or attribute's state. ***************************************************************************/ RCODE F_Db::changeItemState( FLMUINT uiDictType, FLMUINT uiDictNum, const char * pszState) { RCODE rc = NE_XFLM_OK; FLMUINT uiNewState; FLMBOOL bStartedTrans = FALSE; F_DataVector srchKey; F_DataVector foundKey; F_DOMNode * pNode = NULL; F_DOMNode * pAttr = NULL; F_DOMNode * pChangeCountAttr = NULL; F_DOMNode * pTrackerDoc = NULL; FLMUINT64 ui64DocumentId; FLMUINT64 ui64Count; FLMBOOL bMustAbortOnError = FALSE; F_AttrElmInfo defInfo; if (uiDictType != ELM_ELEMENT_TAG && uiDictType != ELM_ATTRIBUTE_TAG) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } if (RC_BAD( rc = fdictGetState( pszState, &uiNewState))) { goto Exit; } if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } else if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } } else { // Need to have an update transaction going. if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // Commit any keys in the KREF buffers. if (RC_BAD( rc = keysCommit( FALSE))) { goto Exit; } // See if the element or attribute is defined, and get its // current state if (uiDictType == ELM_ELEMENT_TAG) { if (RC_BAD( rc = m_pDict->getElement( this, uiDictNum, &defInfo))) { goto Exit; } // Nothing to change if the state does not change. if (uiNewState == defInfo.m_uiState) { goto Exit; } if (elementIsReservedTag( uiDictNum)) { rc = RC_SET( NE_XFLM_CANNOT_MOD_ELEMENT_STATE); goto Exit; } } else { if (RC_BAD( rc = m_pDict->getAttribute( this, uiDictNum, &defInfo))) { goto Exit; } // Nothing to change if the state does not change. if (uiNewState == defInfo.m_uiState) { goto Exit; } if (attributeIsReservedTag( uiDictNum)) { rc = RC_SET( NE_XFLM_CANNOT_MOD_ATTRIBUTE_STATE); goto Exit; } } // Check the legal state changes that can be made by external callers // of this routine if (!m_bItemStateUpdOk) { switch (defInfo.m_uiState) { case ATTR_ELM_STATE_ACTIVE: { if( uiNewState != ATTR_ELM_STATE_CHECKING && uiNewState != ATTR_ELM_STATE_PURGE) { rc = RC_SET( NE_XFLM_ILLEGAL_STATE_CHANGE); goto Exit; } // Add a task to the tracker container so the maintenance thread // will sweep the database if( !(m_uiFlags & FDB_SWEEP_SCHEDULED)) { if( RC_BAD( rc = createRootNode( XFLM_MAINT_COLLECTION, ELM_SWEEP_TAG, ELEMENT_NODE, &pTrackerDoc))) { goto Exit; } if( RC_BAD( rc = pTrackerDoc->setAttributeValueUINT64( this, ATTR_TRANSACTION_TAG, m_ui64CurrTransID))) { goto Exit; } if( RC_BAD( rc = pTrackerDoc->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = documentDone( pTrackerDoc))) { goto Exit; } f_semSignal( m_pDatabase->m_hMaintSem); m_uiFlags |= FDB_SWEEP_SCHEDULED; } break; } case ATTR_ELM_STATE_CHECKING: case ATTR_ELM_STATE_PURGE: { if (uiNewState != ATTR_ELM_STATE_ACTIVE) { rc = RC_SET( NE_XFLM_ILLEGAL_STATE_CHANGE); goto Exit; } break; } default: { // Only other old state is ATTR_ELM_STATE_UNUSED, and it // can be changed to any other state. break; } } } bMustAbortOnError = TRUE; // To do a state change, must create a new dictionary if( !(m_uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = dictClone())) { goto Exit; } } // Find the element's document if( RC_BAD( rc = srchKey.setUINT( 0, uiDictType))) { goto Exit; } if( RC_BAD( rc = srchKey.setUINT( 1, uiDictNum))) { goto Exit; } if( RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, &srchKey, XFLM_EXACT, &foundKey))) { if( rc == NE_XFLM_NOT_FOUND) { // We should have found the thing! It should have // already been indexed! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } // Read the root node of the document ui64DocumentId = foundKey.getDocumentID(); if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // We should have found the thing! It should have // already been indexed! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } // Find the state attribute if (RC_BAD( rc = pNode->getAttribute( this, ATTR_STATE_TAG, (IF_DOMNode **)&pAttr))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // Create the attribute since it was not found if (RC_BAD( rc = pNode->createAttribute( this, ATTR_STATE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } } else { // Unfreeze the attribute state if (RC_BAD( rc = pAttr->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } // Set the attribute state if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pszState))) { goto Exit; } // Freeze the attribute state if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Find the state change count attribute if (RC_BAD( rc = pNode->getAttribute( this, ATTR_STATE_CHANGE_COUNT_TAG, (IF_DOMNode **)&pChangeCountAttr))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // Create the attribute since it was not found if (RC_BAD( rc = pNode->createAttribute( this, ATTR_STATE_CHANGE_COUNT_TAG, (IF_DOMNode **)&pChangeCountAttr))) { goto Exit; } ui64Count = 0; } else { // Unfreeze the attribute state if (RC_BAD( rc = pChangeCountAttr->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if (RC_BAD( rc = pChangeCountAttr->getUINT64( this, &ui64Count))) { goto Exit; } } // Increment the state change count. if (RC_BAD( rc = pChangeCountAttr->setUINT64( this, ui64Count + 1))) { goto Exit; } // Freeze the attribute state change count. if( RC_BAD( rc = pChangeCountAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Set the state in the dictionary if (uiDictType == ELM_ELEMENT_TAG) { if (uiDictNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { ATTR_ELM_DEF * pElementDef; if ((pElementDef = m_pDict->getElementDef( uiDictNum)) != NULL) { attrElmSetState( pElementDef, uiNewState); } } else { EXT_ATTR_ELM_DEF * pExtElementDef; // Better be one of our extended elements - cannot change // state on reserved elements. flmAssert( elementIsUserDefined( uiDictNum)); // No need to lock the mutex, because no other threads can // be accessing it right now. if (m_pDict->m_pExtElementDefTbl) { pExtElementDef = m_pDict->getExtElementDef( uiDictNum); if (pExtElementDef->uiDictNum == uiDictNum) { attrElmSetState( &pExtElementDef->attrElmDef, uiNewState); } } } } else { if (uiDictNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { ATTR_ELM_DEF * pAttributeDef; if ((pAttributeDef = m_pDict->getAttributeDef( uiDictNum)) != NULL) { attrElmSetState( pAttributeDef, uiNewState); } } else { EXT_ATTR_ELM_DEF * pExtAttributeDef; // Better be one of our extended attributes - cannot change // state on reserved attributes. flmAssert( attributeIsUserDefined( uiDictNum)); // No need to lock the mutex, because no other threads can // be accessing it right now. if (m_pDict->m_pExtAttributeDefTbl) { pExtAttributeDef = m_pDict->getExtAttributeDef( uiDictNum); if (pExtAttributeDef->uiDictNum == uiDictNum) { attrElmSetState( &pExtAttributeDef->attrElmDef, uiNewState); } } } } Exit: if( pNode) { pNode->Release(); } if( pAttr) { pAttr->Release(); } if( pTrackerDoc) { pTrackerDoc->Release(); } if( pChangeCountAttr) { pChangeCountAttr->Release(); } if( bStartedTrans) { rc = commitTrans( 0, FALSE); } else if( RC_BAD( rc) && bMustAbortOnError) { setMustAbortTrans( rc); } return( rc); } /*************************************************************************** Desc: Retrieve an element or attribute definition - get name, data type, state, and namespace. ***************************************************************************/ RCODE F_Db::getElmAttrInfo( FLMUINT uiType, FLMUINT64 ui64DocumentID, F_AttrElmInfo * pDefInfo, FLMBOOL bOpeningDict, FLMBOOL bDeleting) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDocNode = NULL; F_DOMNode * pAttr = NULL; F_CachedNode * pCachedDocNode; char szTmpBuf[ 80]; FLMUINT uiNameId; FLMUNICODE * puzName = NULL; FLMBOOL bNamespaceDecl = FALSE; FLMBOOL bHadUniqueSubElementTag = FALSE; // Retrieve the root element of the definition if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pDocNode))) { goto Exit; } flmAssert( pDocNode->getNodeType() == ELEMENT_NODE); pDefInfo->m_pDocNode = (IF_DOMNode *)pDocNode; pDocNode->AddRef(); pCachedDocNode = pDocNode->m_pCachedNode; uiNameId = pCachedDocNode->getNameId(); if( uiType != uiNameId) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Cycle through the attributes on the root of the definition if( pCachedDocNode->hasAttributes()) { if( RC_BAD( rc = pDocNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch( uiNameId) { case ATTR_NAME_TAG: { pDefInfo->m_pNameAttr = (IF_DOMNode *)pAttr; pAttr->AddRef(); if( RC_BAD( rc = pAttr->getUnicode( this, &puzName))) { goto Exit; } if( isXMLNS( puzName) && (puzName [5] == 0 || (puzName [5] == ':' && puzName [6] != 0))) { if( uiType == ELM_ATTRIBUTE_TAG) { pDefInfo->m_uiFlags |= ATTR_ELM_NS_DECL; bNamespaceDecl = TRUE; } else { rc = RC_SET( NE_XFLM_ILLEGAL_ELEMENT_NAME); goto Exit; } } if (uiType == ELM_ELEMENT_TAG) { if (RC_BAD( rc = fdictLegalElementName( puzName))) { goto Exit; } } else { if (RC_BAD( rc = fdictLegalAttributeName( puzName))) { goto Exit; } } // Put a freeze on name if we are not deleting. // Modify is allowed, but delete is not. if( !bDeleting && !bOpeningDict) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_CANNOT_DELETE))) { goto Exit; } } break; } case ATTR_TARGET_NAMESPACE_TAG: { pDefInfo->m_pTargetNamespaceAttr = (IF_DOMNode *)pAttr; pAttr->AddRef(); break; } case ATTR_TYPE_TAG: { if( RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (RC_BAD( rc = fdictGetDataType( (char *)szTmpBuf, &pDefInfo->m_uiDataType))) { goto Exit; } if (uiType == ELM_ATTRIBUTE_TAG && pDefInfo->m_uiDataType == XFLM_NODATA_TYPE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_DATA_TYPE); goto Exit; } break; } case ATTR_STATE_TAG: { if (RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (RC_BAD( rc = fdictGetState( (char *)szTmpBuf, &pDefInfo->m_uiState))) { goto Exit; } // Freeze the state - can only be changed by an explicit // call to changeItemState. if( !bOpeningDict) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } break; } case ATTR_DICT_NUMBER_TAG: { if (RC_BAD( rc = pAttr->getUINT( this, &pDefInfo->m_uiDictNum))) { goto Exit; } if (uiType == ELM_ELEMENT_TAG) { if (RC_BAD( rc = fdictLegalElementNumber( pDefInfo->m_uiDictNum, FALSE))) { goto Exit; } } else { if (RC_BAD( rc = fdictLegalAttributeNumber( pDefInfo->m_uiDictNum, FALSE))) { goto Exit; } } break; } case ATTR_UNIQUE_SUB_ELEMENTS_TAG: { // This one is only allowed on element definitions. if (uiType != ELM_ELEMENT_TAG) { rc = RC_SET( NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE); goto Exit; } bHadUniqueSubElementTag = TRUE; if( RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (f_stricmp( szTmpBuf, "yes") == 0 || f_stricmp( szTmpBuf, "true") == 0 || f_stricmp( szTmpBuf, "1") == 0 || f_stricmp( szTmpBuf, "on") == 0 || f_stricmp( szTmpBuf, "enable") == 0) { pDefInfo->m_uiFlags |= ATTR_ELM_UNIQUE_SUBELMS; } else if (f_stricmp( szTmpBuf, "no") != 0 && f_stricmp( szTmpBuf, "false") != 0 && f_stricmp( szTmpBuf, "0") != 0 && f_stricmp( szTmpBuf, "off") != 0 && f_stricmp( szTmpBuf, "disable") != 0) { rc = RC_SET( NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE); goto Exit; } // Freeze the state - cannot be changed or deleted. if( !bDeleting && !bOpeningDict) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } break; } default: { // Ignore all other attributes break; } } if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } } // Make sure we had both a name and number specified if (!pDefInfo->m_pNameAttr) { rc = (RCODE)(uiType == ELM_ELEMENT_TAG ? RC_SET( NE_XFLM_MISSING_ELEMENT_NAME) : RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NAME)); goto Exit; } if (!pDefInfo->m_uiDictNum) { rc = (RCODE)(uiType == ELM_ELEMENT_TAG ? RC_SET( NE_XFLM_MISSING_ELEMENT_NUMBER) : RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NUMBER)); goto Exit; } if (!bDeleting && bNamespaceDecl && pDefInfo->m_uiDataType != XFLM_TEXT_TYPE) { rc = RC_SET( NE_XFLM_ILLEGAL_NAMESPACE_DECL_DATATYPE); goto Exit; } // If we didn't have a unique sub-element tag, set one - better only // be missing when we first add the element definition. if (!bHadUniqueSubElementTag && uiType == ELM_ELEMENT_TAG) { flmAssert( !bOpeningDict && !bDeleting); // Set attribute and freeze it if it was missing. if (RC_BAD( rc = pDocNode->createAttribute( this, ATTR_UNIQUE_SUB_ELEMENTS_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)"no"))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } else if (pDefInfo->m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) { // If sub-elements must be unique, the data type has to be // nodata. if (pDefInfo->m_uiDataType != XFLM_NODATA_TYPE) { rc = RC_SET( NE_XFLM_DATA_TYPE_MUST_BE_NO_DATA); goto Exit; } } Exit: if( pDocNode) { pDocNode->Release(); } if( pAttr) { pAttr->Release(); } if( puzName) { f_free( &puzName); } return( rc); } /*************************************************************************** Desc: Add, modify, or delete an element definition in the dictionary. ***************************************************************************/ RCODE F_Dict::updateElementDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiElementNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting ) { RCODE rc = NE_XFLM_OK; ATTR_ELM_DEF * pElementDef; EXT_ATTR_ELM_DEF * pExtElementDef; FLMUNICODE * puzElementName = NULL; FLMUNICODE * puzNamespace = NULL; IX_ITEM * pIxElement; F_AttrElmInfo defInfo; F_DOMNode * pTmpNode = NULL; if (bDeleting) { flmAssert( uiElementNum); // NOTE: It is possible that the element may not be in the // element table yet, because dictDocumentDone had not been // called to put it in there, but we are calling dictDocumentDone // to remove it. // Remove the tag number from the name table m_pNameTable->removeTag( ELM_ELEMENT_TAG, uiElementNum); if (uiElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { if ((pElementDef = getElementDef( uiElementNum)) != NULL) { pElementDef->uiFlags = 0; // Unused slot pElementDef->pFirstIcd = NULL; } } else { // Better be one of our extended elements - cannot delete // reserved elements. flmAssert( elementIsUserDefined( uiElementNum)); // No need to lock the mutex, because no other threads can // be accessing it right now. if (m_pExtElementDefTbl) { pExtElementDef = getExtElementDef( uiElementNum); if (pExtElementDef->uiDictNum == uiElementNum) { pExtElementDef->uiDictNum = 0; pExtElementDef->attrElmDef.uiFlags = 0; pExtElementDef->attrElmDef.pFirstIcd = NULL; } } } goto Exit; } if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ELEMENT_TAG, ui64DocumentID, &defInfo, bOpeningDict, bDeleting))) { goto Exit; } if (!uiElementNum) { uiElementNum = defInfo.m_uiDictNum; } flmAssert( uiElementNum == defInfo.m_uiDictNum); // Get the element name if( RC_BAD( rc = defInfo.m_pNameAttr->getUnicode( pDb, &puzElementName))) { goto Exit; } if( RC_BAD( rc = fdictLegalElementName( puzElementName))) { goto Exit; } // Get the element's namespace if( defInfo.m_pTargetNamespaceAttr) { if( RC_BAD( rc = defInfo.m_pTargetNamespaceAttr->getUnicode( pDb, &puzNamespace))) { goto Exit; } } // Add to the tag table, unless we already have our quota of // element names. Remove the tag by number first, in case // it was renamed. if (!bOpeningDict) { m_pNameTable->removeTag( ELM_ELEMENT_TAG, uiElementNum); } if (RC_BAD( rc = m_pNameTable->addTag( ELM_ELEMENT_TAG, puzElementName, NULL, uiElementNum, defInfo.m_uiDataType, puzNamespace, bOpeningDict ? FALSE : TRUE))) { goto Exit; } if (uiElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { // See if we are updating an existing element, in which case the // data type cannot be changed. if ((pElementDef = getElementDef( uiElementNum)) != NULL) { if (attrElmGetType( pElementDef) != defInfo.m_uiDataType) { rc = RC_SET( NE_XFLM_CANNOT_MOD_DATA_TYPE); goto Exit; } // Still need to assign uiFldType because state could change. pElementDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); } else { // If it will fit in the range of elements, simply set it. // Otherwise, we will need to reallocate the array to make // room for it. if (uiElementNum < m_uiLowestElementNum || uiElementNum > m_uiHighestElementNum) { if (RC_BAD( rc = reallocTbl( uiElementNum, sizeof( ATTR_ELM_DEF), (void **)&m_pElementDefTbl, &m_uiLowestElementNum, &m_uiHighestElementNum, 200, XFLM_MAX_ELEMENT_NUM))) { goto Exit; } } pElementDef = &m_pElementDefTbl [uiElementNum - m_uiLowestElementNum]; pElementDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); } } else { // Cannot be modifying or adding in the reserved range. Element // number must be user defined. flmAssert( elementIsUserDefined( uiElementNum)); // See if we should increase the extended table size. Do so if: // 1. It has not yet been allocated, or // 2. It has not yet reached its maximum size and this field // number would hash to a slot beyond that maximum. if (!m_pExtElementDefTbl || (m_uiExtElementDefTblSize < MAX_EXT_ATTR_ELM_ARRAY_SIZE && uiElementNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE > m_uiExtElementDefTblSize - 1)) { FLMUINT uiNewSize = uiElementNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; FLMUINT uiOldSize = m_uiExtElementDefTblSize; EXT_ATTR_ELM_DEF * pNewTbl; EXT_ATTR_ELM_DEF * pOldTbl = m_pExtElementDefTbl; FLMUINT uiLoop; if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) { uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; } // If we had no array, allocate a mutex too. if (!pOldTbl) { // Better not be a mutex allocated at this point either. flmAssert( m_hExtElementDefMutex == F_MUTEX_NULL); if (RC_BAD( rc = f_mutexCreate( &m_hExtElementDefMutex))) { goto Exit; } } // Allocate a new array if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, &pNewTbl))) { goto Exit; } m_pExtElementDefTbl = pNewTbl; m_uiExtElementDefTblSize = uiNewSize; // Rehash everything from the old table into the new table and then // free the old one. if (pOldTbl) { for (uiLoop = 0; uiLoop < uiOldSize; uiLoop++) { if (pOldTbl [uiLoop].uiDictNum) { pExtElementDef = getExtElementDef( pOldTbl [uiLoop].uiDictNum); f_memcpy( pExtElementDef, &pOldTbl [uiLoop], sizeof( EXT_ATTR_ELM_DEF)); } } f_free( &pOldTbl); } } // Put the new or modified field into the table. No need to lock the // mutex to do this because no other threads can be accessing this table // at this time. pExtElementDef = getExtElementDef( uiElementNum); pExtElementDef->uiDictNum = uiElementNum; pIxElement = findIxElement( uiElementNum); pExtElementDef->attrElmDef.pFirstIcd = pIxElement ? pIxElement->pFirstIcd : NULL; pExtElementDef->attrElmDef.uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); } Exit: if (puzElementName) { f_free( &puzElementName); } if (puzNamespace) { f_free( &puzNamespace); } if( pTmpNode) { pTmpNode->Release(); } return( rc); } /*************************************************************************** Desc: Add, modify, or delete an attribute definition in the dictionary. ***************************************************************************/ RCODE F_Dict::updateAttributeDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiAttributeNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting ) { RCODE rc = NE_XFLM_OK; ATTR_ELM_DEF * pAttributeDef; EXT_ATTR_ELM_DEF * pExtAttributeDef; FLMUNICODE * puzAttributeName = NULL; FLMUNICODE * puzNamespace = NULL; IX_ITEM * pIxAttribute; F_AttrElmInfo defInfo; if (bDeleting) { flmAssert( uiAttributeNum); // NOTE: It is possible that the attribute may not be in the // attribute table yet, because dictDocumentDone had not been // called to put it in there, but we are calling dictDocumentDone // to remove it. // Remove the tag number from the name table m_pNameTable->removeTag( ELM_ATTRIBUTE_TAG, uiAttributeNum); if (uiAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { if ((pAttributeDef = getAttributeDef( uiAttributeNum)) != NULL) { pAttributeDef->uiFlags = 0; // Unused slot pAttributeDef->pFirstIcd = NULL; } } else { // Better be one of our extended attributes - cannot delete // reserved attributes. flmAssert( attributeIsUserDefined( uiAttributeNum)); // No need to lock the mutex, because no other threads can // be accessing it right now. if (m_pExtAttributeDefTbl) { pExtAttributeDef = getExtAttributeDef( uiAttributeNum); if (pExtAttributeDef->uiDictNum == uiAttributeNum) { pExtAttributeDef->uiDictNum = 0; pExtAttributeDef->attrElmDef.uiFlags = 0; pExtAttributeDef->attrElmDef.pFirstIcd = NULL; } } } goto Exit; } if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ATTRIBUTE_TAG, ui64DocumentID, &defInfo, bOpeningDict, bDeleting))) { goto Exit; } if (!uiAttributeNum) { uiAttributeNum = defInfo.m_uiDictNum; } flmAssert( uiAttributeNum == defInfo.m_uiDictNum); // Get the attribute name if( RC_BAD( rc = defInfo.m_pNameAttr->getUnicode( pDb, &puzAttributeName))) { goto Exit; } if (RC_BAD( rc = fdictLegalAttributeName( puzAttributeName))) { goto Exit; } // Get the attribute's namespace if( defInfo.m_pTargetNamespaceAttr) { if( RC_BAD( rc = defInfo.m_pTargetNamespaceAttr->getUnicode( pDb, &puzNamespace))) { goto Exit; } } // Add to the tag table, unless we already have our quota of // attribute names. Remove the tag by number first, in case // it was renamed. if (!bOpeningDict) { m_pNameTable->removeTag( ELM_ATTRIBUTE_TAG, uiAttributeNum); } if (RC_BAD( rc = m_pNameTable->addTag( ELM_ATTRIBUTE_TAG, puzAttributeName, NULL, uiAttributeNum, defInfo.m_uiDataType, puzNamespace, bOpeningDict ? FALSE : TRUE))) { goto Exit; } if (uiAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { // See if we are updating an existing attribute, in which case the // data type cannot be changed. if ((pAttributeDef = getAttributeDef( uiAttributeNum)) != NULL) { if ( attrElmGetType( pAttributeDef) != defInfo.m_uiDataType) { rc = RC_SET( NE_XFLM_CANNOT_MOD_DATA_TYPE); goto Exit; } // Still need to assign uiFldType because state could change. pAttributeDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); } else { // If it will fit in the range of attributes, simply set it. // Otherwise, we will need to reallocate the array to make // room for it. if (uiAttributeNum < m_uiLowestAttributeNum || uiAttributeNum > m_uiHighestAttributeNum) { if (RC_BAD( rc = reallocTbl( uiAttributeNum, sizeof( ATTR_ELM_DEF), (void **)&m_pAttributeDefTbl, &m_uiLowestAttributeNum, &m_uiHighestAttributeNum, 200, XFLM_MAX_ATTRIBUTE_NUM))) { goto Exit; } } pAttributeDef = &m_pAttributeDefTbl [uiAttributeNum - m_uiLowestAttributeNum]; pAttributeDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); } } else { // Cannot be modifying or adding in the reserved range. Attribute // number must be user defined. flmAssert( attributeIsUserDefined( uiAttributeNum)); // See if we should increase the extended table size. Do so if: // 1. It has not yet been allocated, or // 2. It has not yet reached its maximum size and this field // number would hash to a slot beyond that maximum. if (!m_pExtAttributeDefTbl || (m_uiExtAttributeDefTblSize < MAX_EXT_ATTR_ELM_ARRAY_SIZE && uiAttributeNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE > m_uiExtAttributeDefTblSize - 1)) { FLMUINT uiNewSize = uiAttributeNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; FLMUINT uiOldSize = m_uiExtAttributeDefTblSize; EXT_ATTR_ELM_DEF * pNewTbl; EXT_ATTR_ELM_DEF * pOldTbl = m_pExtAttributeDefTbl; FLMUINT uiLoop; if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) { uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; } // If we had no array, allocate a mutex too. if (!pOldTbl) { // Better not be a mutex allocated at this point either. flmAssert( m_hExtAttributeDefMutex == F_MUTEX_NULL); if (RC_BAD( rc = f_mutexCreate( &m_hExtAttributeDefMutex))) { goto Exit; } } // Allocate a new array if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, &pNewTbl))) { goto Exit; } m_pExtAttributeDefTbl = pNewTbl; m_uiExtAttributeDefTblSize = uiNewSize; // Rehash everything from the old table into the new table and then // free the old one. if (pOldTbl) { for (uiLoop = 0; uiLoop < uiOldSize; uiLoop++) { if (pOldTbl [uiLoop].uiDictNum) { pExtAttributeDef = getExtAttributeDef( pOldTbl [uiLoop].uiDictNum); f_memcpy( pExtAttributeDef, &pOldTbl [uiLoop], sizeof( EXT_ATTR_ELM_DEF)); } } f_free( &pOldTbl); } } // Put the new or modified field into the table. No need to lock the // mutex to do this because no other threads can be accessing this table // at this time. pExtAttributeDef = getExtAttributeDef( uiAttributeNum); pExtAttributeDef->uiDictNum = uiAttributeNum; pIxAttribute = findIxAttribute( uiAttributeNum); pExtAttributeDef->attrElmDef.pFirstIcd = pIxAttribute ? pIxAttribute->pFirstIcd : NULL; pExtAttributeDef->attrElmDef.uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); } Exit: if (puzAttributeName) { f_free( &puzAttributeName); } if (puzNamespace) { f_free( &puzNamespace); } return( rc); } /*************************************************************************** Desc: Determine if an index name is legal - formatwise ***************************************************************************/ FINLINE RCODE fdictLegalIndexName( FLMUNICODE * // puzIndexName ) { //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_INDEX_NAME return( NE_XFLM_OK); } /*************************************************************************** Desc: Determine if an index number is legal ***************************************************************************/ FINLINE RCODE fdictLegalIndexNumber( FLMUINT uiIndexNumber ) { return( (uiIndexNumber > 0 && uiIndexNumber <= XFLM_MAX_INDEX_NUM) ? NE_XFLM_OK : RC_SET( NE_XFLM_ILLEGAL_INDEX_NUMBER)); } /*************************************************************************** Desc: Determine if a collection name is legal - formatwise ***************************************************************************/ FINLINE RCODE fdictLegalCollectionName( FLMUNICODE * // puzCollectionName ) { //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_COLLECTION_NAME return( NE_XFLM_OK); } /*************************************************************************** Desc: Determine if a collection number is legal ***************************************************************************/ FINLINE RCODE fdictLegalCollectionNumber( FLMUINT uiCollectionNumber ) { return( ( (uiCollectionNumber > 0 && uiCollectionNumber <= XFLM_MAX_COLLECTION_NUM) || (uiCollectionNumber == XFLM_DATA_COLLECTION)) ? NE_XFLM_OK : RC_SET( NE_XFLM_ILLEGAL_COLLECTION_NUMBER)); } /*************************************************************************** Desc: Determine if a prefix name is legal - formatwise ***************************************************************************/ FINLINE RCODE fdictLegalPrefixName( FLMUNICODE * // puzPrefixName ) { //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_PREFIX_NAME return( NE_XFLM_OK); } /*************************************************************************** Desc: Determine if a prefix number is legal ***************************************************************************/ FINLINE RCODE fdictLegalPrefixNumber( FLMUINT uiPrefixNumber ) { return( (uiPrefixNumber > 0 && uiPrefixNumber <= XFLM_MAX_PREFIX_NUM) ? NE_XFLM_OK : RC_SET( NE_XFLM_ILLEGAL_PREFIX_NUMBER)); } /*************************************************************************** Desc: Determine if an encryption def name is legal - formatwise ***************************************************************************/ FINLINE RCODE fdictLegalEncDefName( FLMUNICODE * // puzEncDefName ) { //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_ENCDEF_NAME return( NE_XFLM_OK); } /*************************************************************************** Desc: Determine if an encryption def number is legal ***************************************************************************/ FINLINE RCODE fdictLegalEncDefNumber( FLMUINT uiEncDefNumber ) { return( (uiEncDefNumber > 0 && uiEncDefNumber <= XFLM_MAX_ENCDEF_NUM) ? NE_XFLM_OK : RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_ENCDEF_NUMBER)); } /*************************************************************************** Desc: Determine if an encryption def type (algorithm) is legal ***************************************************************************/ FINLINE RCODE fdictLegalEncDefType( char * pszEncDefType, FLMUINT * puiEncDefType ) { RCODE rc = NE_XFLM_OK; FLMUINT uiType; // Parse the type keyword - only one type allowed. for( uiType = 0; uiType < MAX_ENC_TYPES ; uiType++) { if( f_strnicmp( pszEncDefType, DDEncOpts[ uiType], f_strlen(DDEncOpts[ uiType])) == 0) { *puiEncDefType = uiType; goto Exit; } } rc = RC_SET( NE_XFLM_INVALID_ENC_ALGORITHM); Exit: return( rc); } /*************************************************************************** Desc: Determine if a specified encryption key size is legal ***************************************************************************/ FINLINE RCODE fdictLegalEncKeySize( FLMUINT uiEncType, FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; switch( uiEncType) { case FLM_NICI_AES: { if (uiEncKeySize != XFLM_NICI_AES128 && uiEncKeySize != XFLM_NICI_AES192 && uiEncKeySize != XFLM_NICI_AES256) { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } break; } case FLM_NICI_DES3: { if (uiEncKeySize != XFLM_NICI_DES3X) { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } break; } default: { rc = RC_SET( NE_XFLM_INVALID_ENC_ALGORITHM); goto Exit; } } Exit: return rc; } /*************************************************************************** Desc: return a legal key size, based on the encryption algorithm. ***************************************************************************/ FINLINE FLMBOOL fdictGetLegalKeySize( FLMUINT uiEncType, FLMUINT * puiEncKeySize ) { FLMBOOL bSizeOk = FALSE; // Note: The uiEncType should have already been validated. switch( uiEncType) { case FLM_NICI_AES: { if ( *puiEncKeySize == 0) { *puiEncKeySize = XFLM_NICI_AES256; bSizeOk = TRUE; } else if ( *puiEncKeySize == XFLM_NICI_AES256) { *puiEncKeySize = XFLM_NICI_AES192; bSizeOk = TRUE; } else if ( *puiEncKeySize == XFLM_NICI_AES192) { *puiEncKeySize = XFLM_NICI_AES128; bSizeOk = TRUE; } break; } case FLM_NICI_DES3: { if ( *puiEncKeySize == 0) { *puiEncKeySize = XFLM_NICI_DES3X; bSizeOk = TRUE; } break; } } return bSizeOk; } /*************************************************************************** Desc: Parse an option from a string. Function returns pointer to beginning of option. Parameter is moved to just after where that option started. ***************************************************************************/ FSTATIC char * fdictGetOption( char ** ppszSrc ) { char * pszMatch = NULL; char * pszSrc = *ppszSrc; while (*pszSrc == NATIVE_SPACE) { pszSrc++; } // See if at end of string. if (!(*pszSrc)) { goto Exit; } pszMatch = pszSrc; while (*pszSrc && *pszSrc != NATIVE_SPACE) { pszSrc++; } if (*pszSrc) { *pszSrc = 0; pszSrc++; } Exit: *ppszSrc = pszSrc; return( pszMatch); } /*************************************************************************** Desc: Retrieve an index definition. ***************************************************************************/ RCODE F_Db::getIndexDef( FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzIndexName, FLMUINT * puiIndexNumber, FLMUINT * puiCollectionNumber, FLMUINT * puiLanguage, FLMUINT * puiFlags, FLMUINT64 * pui64LastDocIndexed, FLMUINT * puiEncId, F_DOMNode ** ppNode, FLMBOOL bOpeningDict, FLMBOOL bDeleting) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_CachedNode * pCachedNode; F_DOMNode * pAttr = NULL; FLMBOOL bHadIndexNumber = FALSE; FLMBOOL bHadIndexName = FALSE; char szTmpBuf[ 80]; FLMUNICODE * puzCollectionName = NULL; FLMUINT uiNameCollectionNum = 0; FLMUINT uiNameId; FLMBOOL bHadLastDocIndexed = FALSE; FLMBOOL bHadState = FALSE; // Set up defaults *ppuzIndexName = NULL; *puiIndexNumber = 0; *puiCollectionNumber = XFLM_DATA_COLLECTION; *puiLanguage = m_pDatabase->m_uiDefaultLanguage; *puiFlags = 0; *puiEncId = 0; *ppNode = NULL; // Retrieve the root element of the index definition if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) { goto Exit; } flmAssert( pNode->getNodeType() == ELEMENT_NODE); pCachedNode = pNode->m_pCachedNode; // Cycle through the attributes if( !pCachedNode->hasAttributes()) { rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME); goto Exit; } if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch (uiNameId) { case ATTR_NAME_TAG: { if (RC_BAD( rc = pAttr->getUnicode( this, ppuzIndexName))) { goto Exit; } if (RC_BAD( rc = fdictLegalIndexName( *ppuzIndexName))) { goto Exit; } bHadIndexName = TRUE; break; } case ATTR_DICT_NUMBER_TAG: { if (RC_BAD( rc = pAttr->getUINT( this, puiIndexNumber))) { goto Exit; } if (RC_BAD( rc = fdictLegalIndexNumber( *puiIndexNumber))) { goto Exit; } bHadIndexNumber = TRUE; break; } case ATTR_COLLECTION_NUMBER_TAG: { if (RC_BAD( rc = pAttr->getUINT( this, puiCollectionNumber))) { goto Exit; } if (RC_BAD( rc = fdictLegalCollectionNumber( *puiCollectionNumber))) { goto Exit; } if (uiNameCollectionNum && *puiCollectionNumber != uiNameCollectionNum) { rc = RC_SET( NE_XFLM_COLLECTION_NAME_MISMATCH); goto Exit; } break; } case ATTR_COLLECTION_NAME_TAG: { F_DataVector searchKey; F_DataVector dataPart; if (RC_BAD( rc = pAttr->getUnicode( this, &puzCollectionName))) { goto Exit; } if (RC_BAD( rc = fdictLegalCollectionName( puzCollectionName))) { goto Exit; } // Get the collection number - look up in the index if (RC_BAD( rc = searchKey.setUINT( 0, ELM_COLLECTION_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUnicode( 1, puzCollectionName))) { goto Exit; } if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &dataPart))) { goto Exit; } // Data part of the retrieved key has the dictionary number // for this collection. if (RC_BAD( rc = dataPart.getUINT( 3, &uiNameCollectionNum))) { if (rc == NE_XFLM_NOT_FOUND) { uiNameCollectionNum = 0; rc = NE_XFLM_OK; } else { goto Exit; } } flmAssert( uiNameCollectionNum); if (*puiCollectionNumber && uiNameCollectionNum != *puiCollectionNumber) { rc = RC_SET( NE_XFLM_COLLECTION_NAME_MISMATCH); goto Exit; } *puiCollectionNumber = uiNameCollectionNum; break; } case ATTR_LANGUAGE_TAG: { if( RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } *puiLanguage = f_languageToNum( (char *)szTmpBuf); break; } case ATTR_INDEX_OPTIONS_TAG: { char * pszTmp; char * pszOption; if( RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } pszTmp = &szTmpBuf [0]; while ((pszOption = fdictGetOption( (char **)&pszTmp)) != NULL) { if (f_stricmp( pszOption, XFLM_ABS_POS_OPTION_STR) == 0) { (*puiFlags) |= IXD_ABS_POS; } else { rc = RC_SET( NE_XFLM_INVALID_INDEX_OPTION); goto Exit; } } break; } case ATTR_STATE_TAG: { FLMUINT uiState; if (RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (RC_BAD( rc = fdictGetIndexState( szTmpBuf, &uiState))) { goto Exit; } *puiFlags = (*puiFlags & (~(IXD_SUSPENDED | IXD_OFFLINE))) | uiState; // Freeze the state - can only be changed by an explicit // call to indexSuspend or indexResume. if (!bOpeningDict) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } bHadState = TRUE; break; } case ATTR_LAST_DOC_INDEXED_TAG: { if (RC_BAD( rc = pAttr->getUINT64( this, pui64LastDocIndexed))) { goto Exit; } bHadLastDocIndexed = TRUE; break; } case ATTR_ENCRYPTION_ID_TAG: { if (RC_BAD( rc = pAttr->getUINT( this, puiEncId))) { goto Exit; } if (RC_BAD( rc = fdictLegalEncDefNumber( *puiEncId))) { goto Exit; } break; } default: { // Ignore all other attributes break; } } if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } if (!bHadLastDocIndexed) { *pui64LastDocIndexed = (FLMUINT64)(((*puiFlags) & (IXD_SUSPENDED | IXD_OFFLINE)) ? (FLMUINT64)0 : ~((FLMUINT64)0)); } // Make sure we had both a name and number specified if (!bHadIndexName) { rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME); goto Exit; } if (!bHadIndexNumber) { rc = RC_SET( NE_XFLM_MISSING_INDEX_NUMBER); goto Exit; } // Set a state attribute and freeze it if it was missing. if (!bOpeningDict && !bDeleting && !bHadState) { if (RC_BAD( rc = pNode->createAttribute( this, ATTR_STATE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)XFLM_INDEX_ONLINE_STR))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } *ppNode = pNode; // Set to NULL so it will not be released at Exit. pNode = NULL; Exit: if (pNode) { pNode->Release(); } if (pAttr) { pAttr->Release(); } if (puzCollectionName) { f_free( &puzCollectionName); } return( rc); } /*************************************************************************** Desc: Get the information for an index component. ***************************************************************************/ RCODE F_Db::getIndexComponentDef( F_Dict * pDict, F_DOMNode * pElementNode, FLMUINT uiElementId, IXD * pIxd, ICD * pIcd ) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pAttr = NULL; F_CachedNode * pCachedNode; ICD * pTmpIcd; ICD * pPrevIcd; char szTmpBuf[ 200]; char * pszTmp; char * pszOption; FLMUNICODE * puzName = NULL; FLMUNICODE * puzNamespace = NULL; FLMUINT uiKeyComponent; FLMUINT uiDataComponent; FLMUINT uiNameDictNumber = 0; FLMUINT uiDataType= 0; FLMBOOL bLimitSet = FALSE; FLMBOOL bRequiredSet = FALSE; FLMBOOL bIndexOnSet = FALSE; FLMBOOL bCompareRuleSet = FALSE; FLMBOOL bIsAttr = FALSE; FLMBOOL bHadDictNumber = FALSE; F_NameTable * pNameTable = NULL; FLMUINT uiNameId; if (uiElementId == ELM_ATTRIBUTE_COMPONENT_TAG) { bIsAttr = TRUE; pIcd->uiFlags |= ICD_IS_ATTRIBUTE; } pCachedNode = pElementNode->m_pCachedNode; if( !pCachedNode->hasAttributes()) { rc = (RCODE)(bIsAttr ? RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NUMBER) : RC_SET( NE_XFLM_MISSING_ELEMENT_NUMBER)); goto Exit; } if( RC_BAD( rc = pElementNode->getFirstAttribute( this, &pAttr))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch (uiNameId) { case ATTR_DICT_NUMBER_TAG: if (RC_BAD( rc = pAttr->getUINT( this, &pIcd->uiDictNum))) { goto Exit; } if (bIsAttr) { if (RC_BAD( rc = fdictLegalAttributeNumber( pIcd->uiDictNum, TRUE))) { goto Exit; } } else { if (RC_BAD( rc = fdictLegalElementNumber( pIcd->uiDictNum, TRUE))) { goto Exit; } } bHadDictNumber = TRUE; break; case ATTR_NAME_TAG: if (RC_BAD( rc = pAttr->getUnicode( this, &puzName))) { goto Exit; } if (bIsAttr) { if (RC_BAD( rc = fdictLegalAttributeName( puzName))) { goto Exit; } } else { if (RC_BAD( rc = fdictLegalElementName( puzName))) { goto Exit; } } break; case ATTR_TARGET_NAMESPACE_TAG: if (RC_BAD( rc = pAttr->getUnicode( this, &puzNamespace))) { goto Exit; } break; case ATTR_INDEX_ON_TAG: if( RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (f_stricmp( szTmpBuf, XFLM_VALUE_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_VALUE; pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_SUBSTRING | ICD_EACHWORD | ICD_METAPHONE)); } else if (f_stricmp( szTmpBuf, XFLM_PRESENCE_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_PRESENCE; pIcd->uiFlags &= (~(ICD_VALUE | ICD_SUBSTRING | ICD_EACHWORD | ICD_METAPHONE)); } else if (f_stricmp( szTmpBuf, XFLM_SUBSTRING_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_SUBSTRING; pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_VALUE | ICD_EACHWORD | ICD_METAPHONE)); } else if (f_stricmp( szTmpBuf, XFLM_EACHWORD_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_EACHWORD; pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_VALUE | ICD_SUBSTRING | ICD_METAPHONE)); } else if (f_stricmp( szTmpBuf, XFLM_METAPHONE_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_METAPHONE; pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_VALUE | ICD_SUBSTRING | ICD_EACHWORD)); } else { rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_ON); goto Exit; } bIndexOnSet = TRUE; break; case ATTR_REQUIRED_TAG: if( RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (f_stricmp( szTmpBuf, "yes") == 0 || f_stricmp( szTmpBuf, "true") == 0 || f_stricmp( szTmpBuf, "1") == 0 || f_stricmp( szTmpBuf, "on") == 0 || f_stricmp( szTmpBuf, "enable") == 0) { pIcd->uiFlags |= ICD_REQUIRED_PIECE; } else if (f_stricmp( szTmpBuf, "no") != 0 && f_stricmp( szTmpBuf, "false") != 0 && f_stricmp( szTmpBuf, "0") != 0 && f_stricmp( szTmpBuf, "off") != 0 && f_stricmp( szTmpBuf, "disable") != 0) { rc = RC_SET( NE_XFLM_ILLEGAL_REQUIRED_VALUE); goto Exit; } bRequiredSet = TRUE; break; case ATTR_LIMIT_TAG: if (RC_BAD( rc = pAttr->getUINT( this, &pIcd->uiLimit))) { goto Exit; } bLimitSet = TRUE; break; case ATTR_COMPARE_RULES_TAG: if (RC_BAD( rc = pAttr->getUTF8( this, (FLMBYTE *)szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } pszTmp = &szTmpBuf [0]; while ((pszOption = fdictGetOption( &pszTmp)) != NULL) { if (f_stricmp( pszOption, XFLM_CASE_INSENSITIVE_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_CASE_INSENSITIVE; } else if (f_stricmp( pszOption, XFLM_MINSPACES_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_COMPRESS_WHITESPACE; } else if (f_stricmp( pszOption, XFLM_WHITESPACE_AS_SPACE_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_WHITESPACE_AS_SPACE; } else if (f_stricmp( pszOption, XFLM_IGNORE_LEADINGSPACES_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_IGNORE_LEADING_SPACE; } else if (f_stricmp( pszOption, XFLM_IGNORE_TRAILINGSPACES_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_IGNORE_TRAILING_SPACE; } else if (f_stricmp( pszOption, XFLM_NOUNDERSCORE_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_NO_UNDERSCORES; } else if (f_stricmp( pszOption, XFLM_NOSPACE_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_NO_WHITESPACE; } else if (f_stricmp( pszOption, XFLM_NODASH_OPTION_STR) == 0) { pIcd->uiCompareRules |= XFLM_COMP_NO_DASHES; } else if (f_stricmp( pszOption, XFLM_DESCENDING_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_DESCENDING; } else if (f_stricmp( pszOption, XFLM_MISSING_HIGH_OPTION_STR) == 0) { pIcd->uiFlags |= ICD_MISSING_HIGH; } else { rc = RC_SET( NE_XFLM_INVALID_COMPARE_RULE); goto Exit; } } bCompareRuleSet = TRUE; break; case ATTR_KEY_COMPONENT_TAG: if( RC_BAD( rc = pAttr->getUINT( this, &uiKeyComponent))) { goto Exit; } if (!uiKeyComponent) { rc = RC_SET( NE_XFLM_ILLEGAL_KEY_COMPONENT_NUM); goto Exit; } // Insert into its place in the list of key components pPrevIcd = NULL; pTmpIcd = pIxd->pFirstKey; while (pTmpIcd && uiKeyComponent > pTmpIcd->uiKeyComponent) { pPrevIcd = pTmpIcd; pTmpIcd = pTmpIcd->pNextKeyComponent; } // Make sure not already in the list if (pTmpIcd && pTmpIcd->uiKeyComponent == uiKeyComponent) { rc = RC_SET( NE_XFLM_DUPLICATE_KEY_COMPONENT); goto Exit; } // Link after pPrevIcd if (pPrevIcd) { if ((pIcd->pNextKeyComponent = pPrevIcd->pNextKeyComponent) != NULL) { pIcd->pNextKeyComponent->pPrevKeyComponent = pIcd; } else { pIxd->pLastKey = pIcd; } pPrevIcd->pNextKeyComponent = pIcd; pIcd->pPrevKeyComponent = pPrevIcd; } else { if ((pIcd->pNextKeyComponent = pIxd->pFirstKey) != NULL) { pIxd->pFirstKey->pPrevKeyComponent = pIcd; } else { pIxd->pLastKey = pIcd; } pIxd->pFirstKey = pIcd; } pIcd->uiKeyComponent = uiKeyComponent; pIxd->uiNumKeyComponents++; break; case ATTR_DATA_COMPONENT_TAG: if( RC_BAD( rc = pAttr->getUINT( this, &uiDataComponent))) { goto Exit; } if (!uiDataComponent) { rc = RC_SET( NE_XFLM_ILLEGAL_DATA_COMPONENT_NUM); goto Exit; } // Insert into its place in the list of data components pPrevIcd = NULL; pTmpIcd = pIxd->pFirstData; while (pTmpIcd && uiDataComponent > pTmpIcd->uiDataComponent) { pPrevIcd = pTmpIcd; pTmpIcd = pTmpIcd->pNextDataComponent; } // Make sure not already in the list if (pTmpIcd && pTmpIcd->uiDataComponent == uiDataComponent) { rc = RC_SET( NE_XFLM_DUPLICATE_DATA_COMPONENT); goto Exit; } // Link after pPrevIcd if (pPrevIcd) { if ((pIcd->pNextDataComponent = pPrevIcd->pNextDataComponent) != NULL) { pIcd->pNextDataComponent->pPrevDataComponent = pIcd; } else { pIxd->pLastData = pIcd; } pPrevIcd->pNextDataComponent = pIcd; pIcd->pPrevDataComponent = pPrevIcd; } else { if ((pIcd->pNextDataComponent = pIxd->pFirstData) != NULL) { pIxd->pFirstData->pPrevDataComponent = pIcd; } else { pIxd->pLastData = pIcd; } pIxd->pFirstData = pIcd; } pIcd->uiDataComponent = uiDataComponent; pIxd->uiNumDataComponents++; break; default: // Ignore all other attributes break; } if( RC_BAD( rc = pAttr->getNextSibling( this, &pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } // If they specified a name, see if we can find it if (puzName) { if (*puzName == '/' && *(puzName + 1) == 0) { uiNameDictNumber = ELM_ROOT_TAG; // For now, we can only allow presence indexing on // ELM_ROOT_TAG. That is because we don't yet // allow specifying of data type per ICD if (!(pIcd->uiFlags & ICD_PRESENCE)) { rc = RC_SET( NE_XFLM_MUST_INDEX_ON_PRESENCE); goto Exit; } // ELM_ROOT_TAG cannot be specified as a data // component. if (pIcd->uiDataComponent) { rc = RC_SET( NE_XFLM_ILLEGAL_DATA_COMPONENT); goto Exit; } } else { F_DataVector searchKey; F_DataVector dataPart; // Get the dictionary number - look up in the index if (RC_BAD( rc = searchKey.setUINT( 0, uiElementId == ELM_ELEMENT_COMPONENT_TAG ? ELM_ELEMENT_TAG : ELM_ATTRIBUTE_TAG))) { goto Exit; } if (RC_BAD( rc = searchKey.setUnicode( 1, puzName))) { goto Exit; } if (puzNamespace) { if (RC_BAD( rc = searchKey.setUnicode( 2, puzNamespace))) { goto Exit; } } if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &dataPart))) { if (rc == NE_XFLM_NOT_FOUND) { // This may be a built-in type which is not indexed. if ( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if ( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, (bIsAttr) ? ELM_ATTRIBUTE_TAG : ELM_ELEMENT_TAG, puzName, NULL, TRUE, puzNamespace, &uiNameDictNumber))) { if ( rc == NE_XFLM_NOT_FOUND) { rc = (RCODE)(bIsAttr ? RC_SET( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME) : RC_SET( NE_XFLM_UNDEFINED_ELEMENT_NAME)); } goto Exit; } } else { goto Exit; } } else { // Data part of the retrieved key has the dictionary number // for this collection. if (RC_BAD( rc = dataPart.getUINT( 3, &uiNameDictNumber))) { if (rc == NE_XFLM_NOT_FOUND) { uiNameDictNumber = 0; rc = NE_XFLM_OK; } else { goto Exit; } } } flmAssert( uiNameDictNumber); } if (pIcd->uiDictNum && uiNameDictNumber != pIcd->uiDictNum) { rc = (RCODE)(bIsAttr ? RC_SET( NE_XFLM_ATTRIBUTE_NAME_MISMATCH) : RC_SET( NE_XFLM_ELEMENT_NAME_MISMATCH)); goto Exit; } pIcd->uiDictNum = uiNameDictNumber; bHadDictNumber = TRUE; } // If this is a key component, make a few more checks if (pIcd->uiKeyComponent) { // If no limit was set, use a default. if (pIcd->uiFlags & ICD_SUBSTRING) { pIxd->uiFlags |= IXD_HAS_SUBSTRING; } if (!bLimitSet) { if (pIcd->uiFlags & ICD_SUBSTRING) { pIcd->uiLimit = ICD_DEFAULT_SUBSTRING_LIMIT; } else { pIcd->uiLimit = ICD_DEFAULT_LIMIT; } } } else { // There are certain things that cannot be set for data // and context components. Verify that they were not set. if (bRequiredSet) { rc = RC_SET( NE_XFLM_CANNOT_SET_REQUIRED); goto Exit; } if (bLimitSet) { rc = RC_SET( NE_XFLM_CANNOT_SET_LIMIT); goto Exit; } if (bIndexOnSet) { rc = RC_SET( NE_XFLM_CANNOT_SET_INDEX_ON); goto Exit; } if (bCompareRuleSet) { rc = RC_SET( NE_XFLM_CANNOT_SET_COMPARE_RULES); goto Exit; } if (!pIcd->uiDataComponent) { // Insert into its place in the list of context components pPrevIcd = NULL; pTmpIcd = pIxd->pFirstContext; while (pTmpIcd && pIcd->uiCdl > pTmpIcd->uiCdl) { pPrevIcd = pTmpIcd; pTmpIcd = pTmpIcd->pNextKeyComponent; } // Link after pPrevIcd if (pPrevIcd) { if ((pIcd->pNextKeyComponent = pPrevIcd->pNextKeyComponent) != NULL) { pIcd->pNextKeyComponent->pPrevKeyComponent = pIcd; } else { pIxd->pLastContext = pIcd; } pPrevIcd->pNextKeyComponent = pIcd; pIcd->pPrevKeyComponent = pPrevIcd; } else { if ((pIcd->pNextKeyComponent = pIxd->pFirstContext) != NULL) { pIxd->pFirstContext->pPrevKeyComponent = pIcd; } else { pIxd->pLastContext = pIcd; } pIxd->pFirstContext = pIcd; } pIxd->uiNumContextComponents++; } } // Make sure we had an element or attribute number specified if (!bHadDictNumber) { rc = (RCODE)(bIsAttr ? RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NUMBER) : RC_SET( NE_XFLM_MISSING_ELEMENT_NUMBER)); goto Exit; } // Get the element or attribute's data type if (bIsAttr) { F_AttrElmInfo attrInfo; if (RC_BAD( rc = pDict->getAttribute( this, pIcd->uiDictNum, &attrInfo))) { goto Exit; } uiDataType = attrInfo.m_uiDataType; } else { if (pIcd->uiDictNum == ELM_ROOT_TAG) { uiDataType = XFLM_NODATA_TYPE; } else { F_AttrElmInfo elmInfo; if (RC_BAD( rc = pDict->getElement( this, pIcd->uiDictNum, &elmInfo))) { goto Exit; } uiDataType = elmInfo.m_uiDataType; } } icdSetDataType( pIcd, uiDataType); Exit: if (pAttr) { pAttr->Release(); } if (puzName) { f_free( &puzName); } if (puzNamespace) { f_free( &puzNamespace); } if (pNameTable) { pNameTable->Release(); } return( rc); } /*************************************************************************** Desc: Determine if a NODE is an index component. ***************************************************************************/ FSTATIC RCODE isIndexComponent( F_Db * pDb, F_DOMNode * pNode, FLMBOOL * pbIsIndexComponent, FLMUINT * puiElementNum) { RCODE rc = NE_XFLM_OK; *pbIsIndexComponent = TRUE; if( pNode->getNodeType() != ELEMENT_NODE) { *pbIsIndexComponent = FALSE; goto Exit; } if( RC_BAD( rc = pNode->getNameId( pDb, puiElementNum))) { goto Exit; } switch (*puiElementNum) { case ELM_ELEMENT_COMPONENT_TAG: case ELM_ATTRIBUTE_COMPONENT_TAG: break; default: *pbIsIndexComponent = FALSE; goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Compare the old index definition with the new to see if anything changed - to determine if we really need to rebuild the index. ***************************************************************************/ FSTATIC FLMBOOL indexDefsSame( IXD * pOldIxd, IXD * pNewIxd ) { FLMBOOL bSame = FALSE; ICD * pOldIcd; ICD * pNewIcd; if (pOldIxd->uiCollectionNum != pNewIxd->uiCollectionNum || pOldIxd->uiNumIcds != pNewIxd->uiNumIcds || pOldIxd->uiNumKeyComponents != pNewIxd->uiNumKeyComponents || pOldIxd->uiNumDataComponents != pNewIxd->uiNumDataComponents || ~(pOldIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) != ~(pNewIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) || pOldIxd->uiLanguage != pNewIxd->uiLanguage) { goto Exit; } // Traverse the ICDs and make sure they are the same pOldIcd = pOldIxd->pIcdTree; pNewIcd = pNewIxd->pIcdTree; for (;;) { // Compare the ICDs if (pOldIcd->uiDictNum != pNewIcd->uiDictNum || pOldIcd->uiFlags != pNewIcd->uiFlags || pOldIcd->uiCdl != pNewIcd->uiCdl || pOldIcd->uiKeyComponent != pNewIcd->uiKeyComponent || pOldIcd->uiDataComponent != pNewIcd->uiDataComponent || pOldIcd->uiLimit != pNewIcd->uiLimit) { goto Exit; } if (pOldIcd->pFirstChild) { if (!pNewIcd->pFirstChild) { // Old ICD has a child, new one doesn't, indexes are // different. goto Exit; } pOldIcd = pOldIcd->pFirstChild; pNewIcd = pNewIcd->pFirstChild; continue; } while (pOldIcd && !pOldIcd->pNextSibling) { // Old ICD has no next sibling, new one doesn't, indexes are // different. if (!pNewIcd || pNewIcd->pNextSibling) { goto Exit; } pOldIcd = pOldIcd->pParent; pNewIcd = pNewIcd->pParent; } if (!pOldIcd) { // Traversed back to parent ICD for old ICD, but not // for new ICD, indexes are different. However, this // should never happen. if (pNewIcd) { flmAssert( 0); goto Exit; } break; } // OLD ICD has a sibling it can traverse to, new one // doesn't, indexes are different. if (!pNewIcd || !pNewIcd->pNextSibling) { goto Exit; } pOldIcd = pOldIcd->pNextSibling; pNewIcd = pNewIcd->pNextSibling; } bSame = TRUE; Exit: return( bSame); } /*************************************************************************** Desc: Update an index definition. ***************************************************************************/ RCODE F_Dict::updateIndexDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiIndexNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting ) { RCODE rc = NE_XFLM_OK; void * pvMark = m_dictPool.poolMark(); FLMUNICODE * puzIndexName = NULL; F_DOMNode * pNode = NULL; FLMUINT uiElementId; FLMBOOL bIsIndexComponent; FLMUINT uiComponentNum; IXD * pIxd; IXD * pOldIxd; ICD * pIcd; ICD * pLastIcd; ICD * pTmpIcd; FLMBOOL bLinkAsChild; FLMBOOL bHadRequired; FLMBOOL bSinglePath; FLMBOOL bHasChildren; FLMUINT uiEncId; if (bOpeningDict) { flmAssert( !bDeleting); pOldIxd = NULL; } else { if (RC_BAD( rc = getIndex( uiIndexNum, NULL, &pOldIxd, TRUE))) { if (rc == NE_XFLM_BAD_IX) { pOldIxd = NULL; rc = NE_XFLM_OK; } else { goto Exit; } } } if (bDeleting) { flmAssert( uiIndexNum); if (pOldIxd) { // Get rid of the old b-tree if (RC_BAD( rc = pDb->m_pDatabase->lFileDelete( pDb, NULL, &pOldIxd->lfInfo, (FLMBOOL)((pOldIxd->uiFlags & IXD_ABS_POS) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE), (FLMBOOL)(pOldIxd->pFirstData ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)))) { goto Exit; } // Remove from index fixup list if we are deleting an index. // It is impossible to be deleting an index in a background // thread, so if there is a fixup, it is here because the // index was added during this transaction (in the background). // If the transaction aborts the IXD will simply go away, and // there will be no need to fix it up. if (pDb->m_pIxdFixups) { IXD_FIXUP * pIxdFixup = pDb->m_pIxdFixups; IXD_FIXUP * pPrevIxdFixup = NULL; while (pIxdFixup && pIxdFixup->uiIndexNum != uiIndexNum) { pPrevIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; } if (pIxdFixup) { if (pPrevIxdFixup) { pPrevIxdFixup->pNext = pIxdFixup->pNext; } else { pDb->m_pIxdFixups = pIxdFixup->pNext; } f_free( &pIxdFixup); } } // On delete or modify index make sure something is in the stop list. if (!(pDb->m_uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = pDb->addToStopList( uiIndexNum))) { goto Exit; } } // Unlink the old ICDs. unlinkIcds( pOldIxd->pIcdTree); } // NOTE: It is possible that the index may not be in the // index table yet, because dictDocumentDone had not been // called to put it in there, but we are calling dictDocumentDone // to remove it. // Remove the tag number from the name table m_pNameTable->removeTag( ELM_INDEX_TAG, uiIndexNum); if (uiIndexNum >= m_uiLowestIxNum && uiIndexNum <= m_uiHighestIxNum) { m_ppIxdTbl [uiIndexNum - m_uiLowestIxNum] = NULL; } goto Exit; } // Allocate a new IXD if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( IXD), (void **)&pIxd))) { goto Exit; } if (RC_BAD( rc = pDb->getIndexDef( ui64DocumentID, &puzIndexName, &pIxd->uiIndexNum, &pIxd->uiCollectionNum, &pIxd->uiLanguage, &pIxd->uiFlags, &pIxd->ui64LastDocIndexed, &uiEncId, &pNode, bOpeningDict, bDeleting))) { goto Exit; } if (!uiIndexNum) { uiIndexNum = pIxd->uiIndexNum; } else { flmAssert( uiIndexNum == pIxd->uiIndexNum); } pIxd->ui64IxDefNodeId = ui64DocumentID; // Process each sub-element, setting up the path for the index // Start at the first child if (RC_BAD( rc = pNode->getFirstChild( pDb, (IF_DOMNode **)&pNode))) { // Change not-found to illegal index definition - must be at // least one subordinate node. if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_DEF); } goto Exit; } pLastIcd = NULL; bLinkAsChild = TRUE; bSinglePath = TRUE; for (;;) { if (RC_BAD( rc = isIndexComponent( pDb, pNode, &bIsIndexComponent, &uiElementId))) { goto Exit; } if (bIsIndexComponent) { // Allocate an ICD and link in if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( ICD), (void **)&pIcd))) { goto Exit; } pIcd->uiCdl = pIxd->uiNumIcds; pIxd->uiNumIcds++; pIcd->pIxd = pIxd; pIcd->uiIndexNum = pIxd->uiIndexNum; if (!pIxd->pIcdTree) { pIxd->pIcdTree = pIcd; } else if (bLinkAsChild) { // link as child pLastIcd->pFirstChild = pIcd; pIcd->pParent = pLastIcd; if (pLastIcd->pPrevSibling) { bSinglePath = FALSE; } } else { // link as sibling pLastIcd->pNextSibling = pIcd; if (pLastIcd->pFirstChild) { bSinglePath = FALSE; } pIcd->pPrevSibling = pLastIcd; pIcd->pParent = pLastIcd->pParent; } bLinkAsChild = FALSE; pLastIcd = pIcd; if (RC_BAD( rc = pDb->getIndexComponentDef( this, pNode, uiElementId, pIxd, pIcd))) { goto Exit; } // The ICD with a tag of ELM_ROOT_TAG cannot be // linked as a child to another node. It always // has to be the lone root ICD. if (pIcd->uiDictNum == ELM_ROOT_TAG && (pIcd->pParent || pIcd->pNextSibling || pIcd->pPrevSibling)) { rc = RC_SET( NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG); goto Exit; } // Make sure that this ICD does not have the same // dictionary number as any prior sibling. pTmpIcd = pIcd->pPrevSibling; while (pTmpIcd) { if (pIcd->uiDictNum == pTmpIcd->uiDictNum && ((pIcd->uiFlags & ICD_IS_ATTRIBUTE) == (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE))) { rc = RC_SET( NE_XFLM_DUP_SIBLING_IX_COMPONENTS); goto Exit; } pTmpIcd = pTmpIcd->pPrevSibling; } // If the node was an attribute component, we are not interested in // anything that might be subordinate to it - only siblings. if (uiElementId == ELM_ATTRIBUTE_COMPONENT_TAG) { if ( RC_BAD( rc = pNode->hasChildren( pDb, &bHasChildren))) { goto Exit; } // Attribute components should not have children if ( bHasChildren) { rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_DEF); goto Exit; } goto Get_Sibling; } // See if there is a child node if (RC_OK( rc = pNode->getFirstChild( pDb, (IF_DOMNode **)&pNode))) { bLinkAsChild = TRUE; continue; } if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // Fall through to see if there are any sibling nodes } Get_Sibling: if (RC_OK( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode))) { continue; } if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // No siblings, go to the parent node and see if it // has a sibling if (!pLastIcd || (pLastIcd = pLastIcd->pParent) == NULL) { break; } bLinkAsChild = FALSE; if (RC_BAD( rc = pNode->getParentNode( pDb, (IF_DOMNode **)&pNode))) { // Should not be not-found - because we came down // through a parent node. flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } goto Get_Sibling; } if (bSinglePath) { pIxd->uiFlags |= IXD_SINGLE_PATH; } // Look at all of the key components. Verify that we have all of the // needed components and that none are missing. If none are set to // ICD_REQUIRED_PIECE, set them all to ICD_REQUIRED_IN_SET pIcd = pIxd->pFirstKey; uiComponentNum = 1; bHadRequired = FALSE; while (pIcd) { if (pIcd->uiKeyComponent != uiComponentNum) { rc = RC_SET( NE_XFLM_MISSING_KEY_COMPONENT); goto Exit; } if (pIcd->uiFlags & ICD_REQUIRED_PIECE) { pIcd->uiFlags |= ICD_REQUIRED_IN_SET; bHadRequired = TRUE; } uiComponentNum++; pIcd = pIcd->pNextKeyComponent; } // If we don't have at least one key component, the index // definition is invalid if (!pIxd->uiNumKeyComponents) { rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_DEF); goto Exit; } // If none of the key components were marked as required, mark // them all as ICD_REQUIRED_IN_SET. if (!bHadRequired) { pIcd = pIxd->pFirstKey; while (pIcd) { pIcd->uiFlags |= ICD_REQUIRED_IN_SET; pIcd = pIcd->pNextKeyComponent; } } // Look at all of the data components. Verify that we have all of the // needed components and that none are missing. pIcd = pIxd->pFirstData; uiComponentNum = 1; while (pIcd) { if (pIcd->uiDataComponent != uiComponentNum) { rc = RC_SET( NE_XFLM_MISSING_DATA_COMPONENT); goto Exit; } uiComponentNum++; pIcd = pIcd->pNextDataComponent; } // Look at all of the leaf ICDs. They must be data components or // key components. pIcd = pIxd->pFirstContext; while (pIcd) { // Context components cannot be leaf components - only data // and key components can be at the leaf. if (!pIcd->pFirstChild) { rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_COMPONENT); goto Exit; } pIcd = pIcd->pNextKeyComponent; } // Add to the tag table, unless we already have our quota of // index names. Remove the tag by number first, in case // it was renamed. if (!bOpeningDict) { m_pNameTable->removeTag( ELM_INDEX_TAG, pIxd->uiIndexNum); } if (RC_BAD( rc = m_pNameTable->addTag( ELM_INDEX_TAG, puzIndexName, NULL, pIxd->uiIndexNum, 0, NULL, 0, bOpeningDict ? FALSE : TRUE))) { goto Exit; } // If we are not opening the dictionary and the indexes are // the same, no need to change the index out if (!bOpeningDict && pOldIxd && indexDefsSame( pOldIxd, pIxd)) { // Discard the new IXD, it is not needed. m_dictPool.poolReset( pvMark); } else { if (!bOpeningDict) { if (pOldIxd) { // If modifying make sure something is in the stop list. if (!(pDb->m_uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = pDb->addToStopList( uiIndexNum))) { goto Exit; } } // Delete the old b-tree LFILE if (RC_BAD( rc = pDb->m_pDatabase->lFileDelete( pDb, NULL, &pOldIxd->lfInfo, (FLMBOOL)((pOldIxd->uiFlags & IXD_ABS_POS) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE), (FLMBOOL)(pOldIxd->pFirstData ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)))) { goto Exit; } } // Create a NEW LFILE for the index. If this is a new index // definition, we have not yet created one. If this is a // modified index definition, the old LFILE would have been // deleted up above. if (RC_BAD( rc = pDb->m_pDatabase->lFileCreate( pDb, &pIxd->lfInfo, NULL, uiIndexNum, XFLM_LF_INDEX, (FLMBOOL)((pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE), (FLMBOOL)(pIxd->pFirstData ? TRUE : FALSE), uiEncId))) { goto Exit; } } // Make room in the table for the new index if necessary. if (pIxd->uiIndexNum < m_uiLowestIxNum || pIxd->uiIndexNum > m_uiHighestIxNum) { if (RC_BAD( rc = reallocTbl( pIxd->uiIndexNum, sizeof( IXD *), (void **)&m_ppIxdTbl, &m_uiLowestIxNum, &m_uiHighestIxNum, 20, XFLM_MAX_INDEX_NUM))) { goto Exit; } } // Link the new ICDs into their ICD chains and unlink the old ICDs // from their ICD chains. We don't want to do either of these until // we are sure we are going to succeed. m_ppIxdTbl [pIxd->uiIndexNum - m_uiLowestIxNum] = pIxd; if (RC_BAD( rc = linkIcds( pIxd->pIcdTree))) { goto Exit; } if (pOldIxd) { // Unlink the old ICDs. unlinkIcds( pOldIxd->pIcdTree); } // Build the index, unless we are just opening the dictionary if (!bOpeningDict) { if (RC_BAD( rc = pDb->buildIndex( uiIndexNum, pIxd->uiFlags))) { goto Exit; } } } Exit: if (pNode) { pNode->Release(); } if (puzIndexName) { f_free( &puzIndexName); } if (RC_BAD( rc)) { m_dictPool.poolReset( pvMark); } return( rc); } /*************************************************************************** Desc: Retrieve a collection definition - get name and number. ***************************************************************************/ RCODE F_Db::getCollectionDef( FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzCollectionName, FLMUINT * puiCollectionNumber, FLMUINT * puiEncId ) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_CachedNode * pCachedNode; IF_DOMNode * pAttr = NULL; FLMBOOL bHadCollectionNumber = FALSE; FLMBOOL bHadCollectionName = FALSE; FLMUINT uiNameId; // Set up defaults if (ppuzCollectionName) { *ppuzCollectionName = NULL; } if (puiCollectionNumber) { *puiCollectionNumber = 0; } if (puiEncId) { *puiEncId = 0; } // Retrieve the root element of the collection definition if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) { goto Exit; } flmAssert( pNode->getNodeType() == ELEMENT_NODE); pCachedNode = pNode->m_pCachedNode; if( !pCachedNode->hasAttributes()) { rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NUMBER); goto Exit; } if( RC_BAD( rc = pNode->getFirstAttribute( this, &pAttr))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch( uiNameId) { case ATTR_NAME_TAG: if (ppuzCollectionName) { if (RC_BAD( rc = pAttr->getUnicode( this, ppuzCollectionName))) { goto Exit; } if (RC_BAD( rc = fdictLegalCollectionName( *ppuzCollectionName))) { goto Exit; } } bHadCollectionName = TRUE; break; case ATTR_DICT_NUMBER_TAG: if (puiCollectionNumber) { if (RC_BAD( rc = pAttr->getUINT( this, puiCollectionNumber))) { goto Exit; } if (RC_BAD( rc = fdictLegalCollectionNumber( *puiCollectionNumber))) { goto Exit; } } bHadCollectionNumber = TRUE; break; case ATTR_ENCRYPTION_ID_TAG: if (puiEncId) { if (RC_BAD( rc = pAttr->getUINT( this, puiEncId))) { goto Exit; } if (RC_BAD( rc = fdictLegalEncDefNumber( *puiEncId))) { goto Exit; } } break; default: // Ignore all other attributes break; } if( RC_BAD( rc = pAttr->getNextSibling( this, &pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } // Make sure we had both a name and number specified if (!bHadCollectionName) { rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NAME); goto Exit; } if (!bHadCollectionNumber) { rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NUMBER); goto Exit; } Exit: if (pNode) { pNode->Release(); } if (pAttr) { pAttr->Release(); } return( rc); } /*************************************************************************** Desc: Parse a data dictionary collection definition. ***************************************************************************/ RCODE F_Dict::updateCollectionDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiCollectionNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting ) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; F_COLLECTION * pOldCollection; FLMUNICODE * puzCollectionName = NULL; FLMUINT uiTmp; FLMUINT uiEncId; if (bOpeningDict) { flmAssert( !bDeleting); pOldCollection = NULL; } else { if (RC_BAD( rc = getCollection( uiCollectionNum, &pOldCollection))) { if (rc == NE_XFLM_BAD_COLLECTION) { pOldCollection = NULL; rc = NE_XFLM_OK; } else { goto Exit; } } } if (bDeleting) { flmAssert( uiCollectionNum); if (pOldCollection) { if (RC_BAD( rc = pDb->m_pDatabase->lFileDelete( pDb, pOldCollection, &pOldCollection->lfInfo, FALSE, TRUE))) { goto Exit; } } pDb->removeCollectionNodes( uiCollectionNum, pDb->m_ui64CurrTransID); // NOTE: It is possible that the collection may not be in the // collection table yet, because dictDocumentDone had not been // called to put it in there, but we are calling dictDocumentDone // to remove it. // Remove the tag number from the name table m_pNameTable->removeTag( ELM_COLLECTION_TAG, uiCollectionNum); if (uiCollectionNum >= m_uiLowestCollectionNum && uiCollectionNum <= m_uiHighestCollectionNum) { m_ppCollectionTbl [uiCollectionNum - m_uiLowestCollectionNum] = NULL; } goto Exit; } // Allocate a new collection if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_COLLECTION), (void **)&pCollection))) { goto Exit; } if (RC_BAD( rc = pDb->getCollectionDef( ui64DocumentID, &puzCollectionName, &uiTmp, &uiEncId))) { goto Exit; } if (!uiCollectionNum) { uiCollectionNum = uiTmp; } else { flmAssert( uiCollectionNum == uiTmp); } if (!bOpeningDict) { // If this is not a new collection, get the LFILE info // for new collection we just allocated. if (pOldCollection) { f_memcpy( pCollection, pOldCollection, sizeof( F_COLLECTION)); } else { flmAssert( !bDeleting); // Create a NEW LFILE for the collection if (RC_BAD( rc = pDb->m_pDatabase->lFileCreate( pDb, &pCollection->lfInfo, pCollection, uiCollectionNum, XFLM_LF_COLLECTION, FALSE, TRUE, uiEncId))) { goto Exit; } } } // Add to the tag table, unless we already have our quota of // collection names. Remove the tag by number first, in case // it was renamed. if (!bOpeningDict) { m_pNameTable->removeTag( ELM_COLLECTION_TAG, uiCollectionNum); } if (RC_BAD( rc = m_pNameTable->addTag( ELM_COLLECTION_TAG, puzCollectionName, NULL, uiCollectionNum, 0, NULL, 0, bOpeningDict ? FALSE : TRUE))) { if (rc == NE_XFLM_EXISTS) { rc = NE_XFLM_OK; } else { goto Exit; } } if (uiCollectionNum < m_uiLowestCollectionNum || uiCollectionNum > m_uiHighestCollectionNum) { if (RC_BAD( rc = reallocTbl( uiCollectionNum, sizeof( F_COLLECTION *), (void **)&m_ppCollectionTbl, &m_uiLowestCollectionNum, &m_uiHighestCollectionNum, 20, XFLM_MAX_COLLECTION_NUM))) { goto Exit; } } m_ppCollectionTbl [uiCollectionNum - m_uiLowestCollectionNum] = pCollection; Exit: if (puzCollectionName) { f_free( &puzCollectionName); } return( rc); } /*************************************************************************** Desc: Retrieve a prefix definition - get name and number. ***************************************************************************/ RCODE F_Db::getPrefixDef( F_Dict * pDict, FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzPrefixName, FLMUINT * puiPrefixNumber) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_CachedNode * pCachedNode; IF_DOMNode * pAttr = NULL; FLMBOOL bHadPrefixNumber = FALSE; FLMBOOL bHadPrefixName = FALSE; FLMUINT uiNameId; // Set up defaults if (ppuzPrefixName) { *ppuzPrefixName = NULL; } if (puiPrefixNumber) { *puiPrefixNumber = 0; } // Retrieve the root element of the prefix definition if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) { goto Exit; } flmAssert( pNode->getNodeType() == ELEMENT_NODE); pCachedNode = pNode->m_pCachedNode; if( !pCachedNode->hasAttributes()) { rc = RC_SET( NE_XFLM_MISSING_PREFIX_NUMBER); goto Exit; } if( RC_BAD( rc = pNode->getFirstAttribute( this, &pAttr))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch( uiNameId) { case ATTR_NAME_TAG: { FLMUINT uiPrefixLen; FLMUINT uiBufferSize; if (ppuzPrefixName) { if( RC_BAD( rc = pAttr->getUnicodeChars( this, &uiPrefixLen))) { goto Exit; } uiBufferSize = sizeof( FLMUNICODE) * (uiPrefixLen + 1); if( RC_BAD( rc = pDict->m_dictPool.poolAlloc( uiBufferSize, (void **)ppuzPrefixName))) { goto Exit; } if (RC_BAD( rc = pAttr->getUnicode( this, *ppuzPrefixName, uiBufferSize, 0, uiPrefixLen, NULL))) { goto Exit; } if (RC_BAD( rc = fdictLegalPrefixName( *ppuzPrefixName))) { goto Exit; } } bHadPrefixName = TRUE; break; } case ATTR_DICT_NUMBER_TAG: { if (puiPrefixNumber) { if (RC_BAD( rc = pAttr->getUINT( this, puiPrefixNumber))) { goto Exit; } if (RC_BAD( rc = fdictLegalPrefixNumber( *puiPrefixNumber))) { goto Exit; } } bHadPrefixNumber = TRUE; break; } default: { // Ignore all other attributes break; } } if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } // Make sure we had both a name and number specified if (!bHadPrefixName) { rc = RC_SET( NE_XFLM_MISSING_PREFIX_NAME); goto Exit; } if (!bHadPrefixNumber) { rc = RC_SET( NE_XFLM_MISSING_PREFIX_NUMBER); goto Exit; } Exit: if (pNode) { pNode->Release(); } if (pAttr) { pAttr->Release(); } return( rc); } /*************************************************************************** Desc: Parse a data dictionary prefix definition. ***************************************************************************/ RCODE F_Dict::updatePrefixDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiPrefixNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting) { RCODE rc = NE_XFLM_OK; F_PREFIX * pPrefix = NULL; F_PREFIX * pOldPrefix = NULL; FLMUNICODE * puzPrefixName = NULL; FLMUINT uiTmp; void * pvMark = m_dictPool.poolMark(); if (bOpeningDict) { flmAssert( !bDeleting); pOldPrefix = NULL; } else { if (RC_BAD( rc = getPrefix( uiPrefixNum, &pOldPrefix))) { if (rc == NE_XFLM_BAD_PREFIX) { pOldPrefix = NULL; rc = NE_XFLM_OK; } else { goto Exit; } } } if (bDeleting) { flmAssert( uiPrefixNum); // NOTE: It is possible that the prefix may not be in the // collection table yet, because dictDocumentDone had not been // called to put it in there, but we are calling dictDocumentDone // to remove it. // Remove the tag number from the name table m_pNameTable->removeTag( ELM_PREFIX_TAG, uiPrefixNum); if (uiPrefixNum >= m_uiLowestPrefixNum && uiPrefixNum <= m_uiHighestPrefixNum) { m_ppPrefixTbl [uiPrefixNum - m_uiLowestPrefixNum] = NULL; } goto Exit; } // Allocate a new prefix if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_PREFIX), (void **)&pPrefix))) { goto Exit; } if (RC_BAD( rc = pDb->getPrefixDef( this, ui64DocumentID, &puzPrefixName, &uiTmp))) { goto Exit; } if (!uiPrefixNum) { uiPrefixNum = uiTmp; } else { flmAssert( uiPrefixNum == uiTmp); } // Add to the tag table, unless we already have our quota of // prefix names. Remove the tag by number first, in case // it was renamed. if (!bOpeningDict) { m_pNameTable->removeTag( ELM_PREFIX_TAG, uiPrefixNum); } if (RC_BAD( rc = m_pNameTable->addTag( ELM_PREFIX_TAG, puzPrefixName, NULL, uiPrefixNum, 0, NULL, 0, bOpeningDict ? FALSE : TRUE))) { goto Exit; } if (uiPrefixNum < m_uiLowestPrefixNum || uiPrefixNum > m_uiHighestPrefixNum) { if (RC_BAD( rc = reallocTbl( uiPrefixNum, sizeof( F_PREFIX *), (void **)&m_ppPrefixTbl, &m_uiLowestPrefixNum, &m_uiHighestPrefixNum, 20, XFLM_MAX_PREFIX_NUM))) { goto Exit; } } pPrefix->ui64PrefixId = uiPrefixNum; pPrefix->puzPrefixName = puzPrefixName; m_ppPrefixTbl [uiPrefixNum - m_uiLowestPrefixNum] = pPrefix; Exit: if( RC_BAD( rc)) { if( pvMark) { m_dictPool.poolReset( pvMark); } } return( rc ); } /*************************************************************************** Desc: Retrieve an encDef definition - get name and number and key ***************************************************************************/ RCODE F_Db::getEncDefDef( F_Dict * pDict, FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzEncDefName, FLMUINT * puiEncDefNumber, FLMUINT * puiEncDefKeySize, IF_CCS ** ppCcs) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_CachedNode * pCachedNode; F_DOMNode * pAttr = NULL; FLMBOOL bHadEncDefNumber = FALSE; FLMBOOL bHadEncDefName = FALSE; FLMBOOL bHadEncKey = FALSE; FLMBOOL bHadEncKeySize = FALSE; FLMBOOL bHadAlgorithm = FALSE; FLMUINT uiNameId; void * pvEncKeyBuf = NULL; FLMUINT32 ui32EncKeyLen; FLMBOOL bStartedUpdateTrans = FALSE; FLMBOOL bRestartReadTrans = FALSE; FLMBYTE * pszAlgorithm = NULL; FLMUINT uiEncType = 0; FLMUINT uiEncKeySize = 0; // Set up defaults if (ppuzEncDefName) { *ppuzEncDefName = NULL; } if (puiEncDefNumber) { *puiEncDefNumber = 0; } flmAssert( ppCcs); if (*ppCcs) { (*ppCcs)->Release(); } *ppCcs = NULL; // Retrieve the root element of the encDef definition if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) { goto Exit; } flmAssert( pNode->getNodeType() == ELEMENT_NODE); pCachedNode = pNode->m_pCachedNode; if( !pCachedNode->hasAttributes()) { rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NUMBER); goto Exit; } if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) { goto Exit; } // Cycle through the attributes for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch( uiNameId) { case ATTR_NAME_TAG: { FLMUINT uiEncDefLen; FLMUINT uiBufferSize; if (ppuzEncDefName) { if( RC_BAD( rc = pAttr->getUnicodeChars( this, &uiEncDefLen))) { goto Exit; } uiBufferSize = sizeof( FLMUNICODE) * (uiEncDefLen + 1); if( RC_BAD( rc = pDict->m_dictPool.poolAlloc( uiBufferSize, (void **)ppuzEncDefName))) { goto Exit; } if (RC_BAD( rc = pAttr->getUnicode( this, *ppuzEncDefName, uiBufferSize, 0, uiEncDefLen, NULL))) { goto Exit; } if (RC_BAD( rc = fdictLegalEncDefName( *ppuzEncDefName))) { goto Exit; } } bHadEncDefName = TRUE; break; } case ATTR_DICT_NUMBER_TAG: { if (puiEncDefNumber) { if (RC_BAD( rc = pAttr->getUINT( this, puiEncDefNumber))) { goto Exit; } if (RC_BAD( rc = fdictLegalEncDefNumber( *puiEncDefNumber))) { goto Exit; } } bHadEncDefNumber = TRUE; break; } case ATTR_ENCRYPTION_KEY_TAG: { FLMUINT uiBytesReturned; if( RC_BAD( rc = pAttr->getDataLength( this, (FLMUINT *)&ui32EncKeyLen))) { goto Exit; } if( !ui32EncKeyLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = f_alloc( ui32EncKeyLen, &pvEncKeyBuf))) { goto Exit; } if (RC_BAD( rc = pAttr->getBinary( this, pvEncKeyBuf, 0, ui32EncKeyLen, &uiBytesReturned))) { goto Exit; } bHadEncKey = TRUE; break; } case ATTR_ENCRYPTION_KEY_SIZE_TAG: { if (RC_BAD( rc = pAttr->getUINT( this, &uiEncKeySize))) { goto Exit; } // Note: Will validate the key size when we finish the loop. bHadEncKeySize = TRUE; break; } case ATTR_TYPE_TAG: { // Get the encryption Algorithm if( RC_BAD( rc = pAttr->getUTF8( this, &pszAlgorithm))) { goto Exit; } if (RC_BAD( rc = fdictLegalEncDefType( (char *)pszAlgorithm, &uiEncType))) { goto Exit; } bHadAlgorithm = TRUE; break; } default: { // Ignore all other attributes break; } } if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } // Make sure we had both a name and number specified if (!bHadEncDefName) { rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NAME); goto Exit; } if (!bHadEncDefNumber) { rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NUMBER); goto Exit; } if (bHadAlgorithm && bHadEncKeySize) { if (RC_BAD( rc = fdictLegalEncKeySize( uiEncType, uiEncKeySize))) { goto Exit; } } else if (!bHadAlgorithm) { rc = RC_SET( NE_XFLM_MISSING_ENC_ALGORITHM); goto Exit; } if( bHadEncKey) { if( RC_BAD( rc = flmAllocCCS( ppCcs))) { goto Exit; } if (RC_BAD( rc = (*ppCcs)->init( FALSE, uiEncType))) { goto Exit; } if( RC_BAD( rc = (*ppCcs)->setKeyFromStore( (FLMBYTE *)pvEncKeyBuf, NULL, m_pDatabase->m_pWrappingKey))) { goto Exit; } } else { if( !bHadEncKeySize) { // Pick a key size based on the encryption algorithm. (void)fdictGetLegalKeySize( uiEncType, &uiEncKeySize); } // This must be a new encryption definition that doesn't have a key yet. // Generate a new encryption key and save it in the DOM node document. // We will need an update transaction before we can proceed. if( getTransType() == XFLM_READ_TRANS) { // End this transaction if( RC_BAD( rc = transCommit())) { goto Exit; } bRestartReadTrans = TRUE; } if( getTransType() == XFLM_NO_TRANS) { if( RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedUpdateTrans = TRUE; } if( RC_BAD( rc = flmAllocCCS( ppCcs))) { goto Exit; } if( RC_BAD( rc = (*ppCcs)->init( FALSE, uiEncType))) { goto Exit; } TryNewKeySize: if( RC_BAD( rc = (*ppCcs)->generateEncryptionKey( uiEncKeySize))) { if( !fdictGetLegalKeySize( uiEncType, &uiEncKeySize)) { goto Exit; } rc = NE_XFLM_OK; goto TryNewKeySize; } if( RC_BAD( rc = (*ppCcs)->getKeyToStore( (FLMBYTE **)&pvEncKeyBuf, &ui32EncKeyLen, NULL, m_pDatabase->m_pWrappingKey))) { goto Exit; } // Set the key in the DOM node as a binary string. if( RC_BAD( rc = pNode->createAttribute( this, ATTR_ENCRYPTION_KEY_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setBinary( this, pvEncKeyBuf, ui32EncKeyLen))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( !bHadEncKeySize) { // Set the key size if( RC_BAD( rc = pNode->createAttribute( this, ATTR_ENCRYPTION_KEY_SIZE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( this, uiEncKeySize))) { goto Exit; } } else { if( RC_BAD( rc = pNode->getAttribute( this, ATTR_ENCRYPTION_KEY_SIZE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } } if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // End the transaction if( bStartedUpdateTrans) { if( RC_BAD( rc = transCommit())) { goto Exit; } bStartedUpdateTrans = FALSE; } } if( puiEncDefKeySize) { *puiEncDefKeySize = uiEncKeySize; } Exit: if( RC_BAD( rc)) { if( *ppCcs) { (*ppCcs)->Release(); *ppCcs = NULL; } } if( bStartedUpdateTrans) { if( RC_OK( rc)) { // Commit the update transaction if( RC_BAD( rc = transCommit())) { (void)transAbort(); } } else { (void)transAbort(); } } if( bRestartReadTrans) { rc = transBegin( XFLM_READ_TRANS); } if( pNode) { pNode->Release(); } if( pAttr) { pAttr->Release(); } if( pvEncKeyBuf) { f_free( &pvEncKeyBuf); } if( pszAlgorithm) { f_free( &pszAlgorithm); } return( rc); } /*************************************************************************** Desc: Parse a data dictionary encryption definition. ***************************************************************************/ RCODE F_Dict::updateEncDef( F_Db * pDb, FLMUINT64 ui64DocumentID, FLMUINT uiEncDefNum, FLMBOOL bOpeningDict, FLMBOOL bDeleting) { RCODE rc = NE_XFLM_OK; F_ENCDEF * pEncDef = NULL; F_ENCDEF * pOldEncDef; FLMUNICODE * puzEncDefName = NULL; FLMUINT uiTmp; void * pvMark = m_dictPool.poolMark(); FLMUINT uiEncKeySize = 0; IF_CCS * pCcs = NULL; if (bOpeningDict) { flmAssert( !bDeleting); pOldEncDef = NULL; } else { if (RC_BAD( rc = getEncDef( uiEncDefNum, &pOldEncDef))) { if (rc == NE_XFLM_BAD_ENCDEF_NUM) { pOldEncDef = NULL; rc = NE_XFLM_OK; } else { goto Exit; } } } if (bDeleting) { flmAssert( uiEncDefNum); // NOTE: It is possible that the encdef may not be in the // collection table yet, because dictDocumentDone had not been // called to put it in there, but we are calling dictDocumentDone // to remove it. // Remove the tag number from the name table // VISIT: Before we can delete the encryption definition tag, we need // to be sure it is not being used. Take a look at the collection tag // to see how they are controlled. m_pNameTable->removeTag( ELM_ENCDEF_TAG, uiEncDefNum); if (uiEncDefNum >= m_uiLowestEncDefNum && uiEncDefNum <= m_uiHighestEncDefNum) { m_ppEncDefTbl [uiEncDefNum - m_uiLowestEncDefNum] = NULL; } goto Exit; } if (!pOldEncDef) { // Allocate a new encdef if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_ENCDEF), (void **)&pEncDef))) { goto Exit; } if (RC_BAD( rc = pDb->getEncDefDef( this, ui64DocumentID, &puzEncDefName, &uiTmp, &uiEncKeySize, &pCcs))) { goto Exit; } if (!uiEncDefNum) { uiEncDefNum = uiTmp; } else { flmAssert( uiEncDefNum == uiTmp); } // Add to the tag table, unless we already have our quota of // encryption definition names. Remove the tag by number first, in case // it was renamed. if (!bOpeningDict) { m_pNameTable->removeTag( ELM_ENCDEF_TAG, uiEncDefNum); } if (RC_BAD( rc = m_pNameTable->addTag( ELM_ENCDEF_TAG, puzEncDefName, NULL, uiEncDefNum, 0, NULL, 0, bOpeningDict ? FALSE : TRUE))) { goto Exit; } if (uiEncDefNum < m_uiLowestEncDefNum || uiEncDefNum > m_uiHighestEncDefNum) { if (RC_BAD( rc = reallocTbl( uiEncDefNum, sizeof( F_ENCDEF *), (void **)&m_ppEncDefTbl, &m_uiLowestEncDefNum, &m_uiHighestEncDefNum, 20, XFLM_MAX_ENCDEF_NUM))) { goto Exit; } } flmAssert(m_ppEncDefTbl [uiEncDefNum - m_uiLowestEncDefNum] == NULL); pEncDef->ui64EncDefId = uiEncDefNum; pEncDef->ui64DocumentId = ui64DocumentID; pEncDef->puzEncDefName = puzEncDefName; pEncDef->uiEncKeySize = uiEncKeySize; pEncDef->pCcs = pCcs; pEncDef->pCcs->AddRef(); m_ppEncDefTbl [uiEncDefNum - m_uiLowestEncDefNum] = pEncDef; } Exit: if( RC_BAD( rc)) { if( pvMark) { m_dictPool.poolReset( pvMark); } } if( pCcs) { pCcs->Release(); } return( rc); } /*************************************************************************** Desc: Update a definition in the in-memory dictionary - add, modify, or delete it. ***************************************************************************/ RCODE F_Dict::updateDict( F_Db * pDb, FLMUINT uiDictType, FLMUINT64 ui64DocumentID, FLMUINT uiDictNumber, FLMBOOL bOpeningDict, FLMBOOL bDeleting) { RCODE rc = NE_XFLM_OK; #ifdef FLM_DEBUG if (!bOpeningDict) { flmAssert( pDb->m_uiFlags & FDB_UPDATED_DICTIONARY); } #endif flmAssert( !pDb->m_pDatabase->m_pRfl || !pDb->m_pDatabase->m_pRfl->isLoggingEnabled() || pDb->getTransType() == XFLM_READ_TRANS); // Commit any keys in the KREF buffers. if (RC_BAD( rc = pDb->keysCommit( FALSE))) { goto Exit; } switch (uiDictType) { case ELM_ELEMENT_TAG: if (RC_BAD( rc = updateElementDef( pDb, ui64DocumentID, uiDictNumber, bOpeningDict, bDeleting))) { goto Exit; } break; case ELM_ATTRIBUTE_TAG: if (RC_BAD( rc = updateAttributeDef( pDb, ui64DocumentID, uiDictNumber, bOpeningDict, bDeleting))) { goto Exit; } break; case ELM_INDEX_TAG: if (RC_BAD( rc = updateIndexDef( pDb, ui64DocumentID, uiDictNumber, bOpeningDict, bDeleting))) { goto Exit; } break; case ELM_COLLECTION_TAG: if (RC_BAD( rc = updateCollectionDef( pDb, ui64DocumentID, uiDictNumber, bOpeningDict, bDeleting))) { goto Exit; } break; case ELM_PREFIX_TAG: if (RC_BAD( rc = updatePrefixDef( pDb, ui64DocumentID, uiDictNumber, bOpeningDict, bDeleting))) { goto Exit; } break; case ELM_ENCDEF_TAG: if (!pDb->m_pDatabase->m_bInLimitedMode) { if (RC_BAD( rc = updateEncDef( pDb, ui64DocumentID, uiDictNumber, bOpeningDict, bDeleting))) { goto Exit; } } break; default: // May be other things in the dictionary that we don't care // about break; } // Commit any keys in the KREF buffers. if (RC_BAD( rc = pDb->keysCommit( FALSE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Setup the predefined collections and indexes. ****************************************************************************/ RCODE F_Dict::setupPredefined( FLMUINT uiDefaultLanguage) { RCODE rc = NE_XFLM_OK; IXD * pIxd; ICD * pIcd; FLMUINT uiLoop; ATTR_ELM_DEF * pElementDef; ATTR_ELM_DEF * pAttributeDef; // Set up reserved elements table if (RC_BAD( rc = f_calloc( (XFLM_LAST_RESERVED_ELEMENT_TAG - XFLM_FIRST_RESERVED_ELEMENT_TAG + 1) * sizeof( ATTR_ELM_DEF), &m_pReservedElementDefTbl))) { goto Exit; } // Set up reserved attributes table if (RC_BAD( rc = f_calloc( (XFLM_LAST_RESERVED_ATTRIBUTE_TAG - XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1) * sizeof( ATTR_ELM_DEF), &m_pReservedAttributeDefTbl))) { goto Exit; } // Set the data types and state to active for all reserved elements and // attributes. for (uiLoop = 0; FlmReservedElementTags [uiLoop].pszTagName; uiLoop++) { pElementDef = &m_pReservedElementDefTbl [ FlmReservedElementTags [uiLoop].uiTagNum - XFLM_FIRST_RESERVED_ELEMENT_TAG]; pElementDef->uiFlags = (FlmReservedElementTags [uiLoop].uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (ATTR_ELM_STATE_ACTIVE & ATTR_ELM_STATE_MASK); } for (uiLoop = 0; FlmReservedAttributeTags [uiLoop].pszTagName; uiLoop++) { FLMUINT uiTagNum = FlmReservedAttributeTags [uiLoop].uiTagNum; pAttributeDef = &m_pReservedAttributeDefTbl[ uiTagNum - XFLM_FIRST_RESERVED_ATTRIBUTE_TAG]; pAttributeDef->uiFlags = (FlmReservedAttributeTags [uiLoop].uiDataType & ATTR_ELM_DATA_TYPE_MASK) | (ATTR_ELM_STATE_ACTIVE & ATTR_ELM_STATE_MASK); // Special case for ATTR_XMLNS_XFLAIM_TAG and ATTR_XMLNS_TAG. // These attributes allow applications to use "xmlns:xflaim" or // "xmlns" in dictionary definitions without requiring the need // to explicitly create an attribute definition for the "xmlns:xflaim" // or "xmlns" attributes. if( uiTagNum == ATTR_XMLNS_XFLAIM_TAG || uiTagNum == ATTR_XMLNS_TAG) { pAttributeDef->uiFlags |= ATTR_ELM_NS_DECL; } } // Allocate memory for the predefined collections if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_COLLECTION) * 3, (void **)&m_pDictCollection))) { goto Exit; } m_pDataCollection = &m_pDictCollection[ 1]; m_pMaintCollection = &m_pDictCollection[ 2]; m_pDictCollection->lfInfo.uiLfNum = XFLM_DICT_COLLECTION; m_pDictCollection->lfInfo.eLfType = XFLM_LF_COLLECTION; m_pDataCollection->lfInfo.uiLfNum = XFLM_DATA_COLLECTION; m_pDataCollection->lfInfo.eLfType = XFLM_LF_COLLECTION; m_pMaintCollection->lfInfo.uiLfNum = XFLM_MAINT_COLLECTION; m_pMaintCollection->lfInfo.eLfType = XFLM_LF_COLLECTION; // Allocate IXDs for the predefined indexes. if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( IXD) * 2, (void **)&m_pNameIndex))) { goto Exit; } m_pNumberIndex = &m_pNameIndex [1]; // Initialize the name index IXD pIxd = m_pNameIndex; pIxd->uiIndexNum = XFLM_DICT_NAME_INDEX; pIxd->uiCollectionNum = XFLM_DICT_COLLECTION; // pIxd->pIcdTree = NULL; // Set by poolCalloc // pIxd->pFirstKey = NULL; // Set by poolCalloc // pIxd->pLastKey = NULL; // Set by poolCalloc // pIxd->pFirstContext = NULL; // Set by poolCalloc // pIxd->pLastContext = NULL; // Set by poolCalloc // pIxd->pFirstData = NULL; // Set by poolCalloc // pIxd->pLastData = NULL; // Set by poolCalloc pIxd->uiNumIcds = 4; pIxd->uiNumKeyComponents = 3; pIxd->uiNumDataComponents = 1; // pIxd->uiNumContextComponents = 0; // Set by poolCalloc pIxd->uiFlags = IXD_SINGLE_PATH; pIxd->uiLanguage = uiDefaultLanguage; pIxd->ui64LastDocIndexed = ~((FLMUINT64)0); pIxd->lfInfo.uiLfNum = XFLM_DICT_NAME_INDEX; pIxd->lfInfo.eLfType = XFLM_LF_INDEX; // Initialize the number index IXD pIxd = m_pNumberIndex; pIxd->uiIndexNum = XFLM_DICT_NUMBER_INDEX; pIxd->uiCollectionNum = XFLM_DICT_COLLECTION; // pIxd->pIcdTree = NULL; // Set by poolCalloc // pIxd->pFirstKey = NULL; // Set by poolCalloc // pIxd->pLastKey = NULL; // Set by poolCalloc // pIxd->pFirstContext = NULL; // Set by poolCalloc // pIxd->pLastContext = NULL; // Set by poolCalloc // pIxd->pFirstData = NULL; // Set by poolCalloc // pIxd->pLastData = NULL; // Set by poolCalloc pIxd->uiNumIcds = 2; pIxd->uiNumKeyComponents = 2; // pIxd->uiNumDataComponents = 0; // Set by poolCalloc // pIxd->uiNumContextComponents = 0; // Set by poolCalloc pIxd->uiFlags = IXD_SINGLE_PATH; pIxd->uiLanguage = uiDefaultLanguage; pIxd->ui64LastDocIndexed = ~((FLMUINT64)0); pIxd->lfInfo.uiLfNum = XFLM_DICT_NUMBER_INDEX; pIxd->lfInfo.eLfType = XFLM_LF_INDEX; // Set up the ICDs for the name index if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( ICD) * 4, (void **)&m_pNameIndex->pIcdTree))) { goto Exit; } pIcd = m_pNameIndex->pIcdTree; m_pNameIndex->pFirstKey = pIcd; pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; pIcd->pIxd = m_pNameIndex; pIcd->uiDictNum = ELM_ROOT_TAG; pIcd->uiFlags = ICD_PRESENCE | ICD_REQUIRED_PIECE; // pIcd->uiCompareRules = 0; // Set by poolCalloc // pIcd->pParent = NULL; // Set by poolCalloc pIcd->pFirstChild = pIcd + 1; // pIcd->pPrevSibling = NULL; // Set by poolCalloc // pIcd->pNextSibling = NULL; // Set by poolCalloc // pIcd->pPrevKeyComponent = NULL; // Set by poolCalloc pIcd->pNextKeyComponent = pIcd + 1; // pIcd->uiCdl = 0; // Set by poolCalloc pIcd->uiKeyComponent = 1; // pIcd->pPrevDataComponent = NULL; // Set by poolCalloc // pIcd->pNextDataComponent = NULL; // Set by poolCalloc // pIcd->uiDataComponent = 0; // Set by poolCalloc // pIcd->uiLimit = 0; // Set by poolCalloc icdSetDataType( pIcd, XFLM_NODATA_TYPE); pIcd++; pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; pIcd->pIxd = m_pNameIndex; pIcd->uiDictNum = ATTR_NAME_TAG; pIcd->uiFlags = ICD_VALUE | ICD_REQUIRED_PIECE | ICD_IS_ATTRIBUTE; // pIcd->uiCompareRules = 0; // Set by poolCalloc pIcd->pParent = m_pNameIndex->pIcdTree; // pIcd->pFirstChild = NULL; // Set by poolCalloc // pIcd->pPrevSibling = NULL; // Set by poolCalloc pIcd->pNextSibling = pIcd + 1; pIcd->pPrevKeyComponent = pIcd - 1; pIcd->pNextKeyComponent = pIcd + 1; pIcd->uiCdl = 1; pIcd->uiKeyComponent = 2; // pIcd->pPrevDataComponent = NULL; // Set by poolCalloc // pIcd->pNextDataComponent = NULL; // Set by poolCalloc // pIcd->uiDataComponent = 0; // Set by poolCalloc // pIcd->uiLimit = 0; // Set by poolCalloc icdSetDataType( pIcd, attrElmGetType( getReservedAttributeDef( pIcd->uiDictNum))); pIcd++; m_pNameIndex->pLastKey = pIcd; pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; pIcd->pIxd = m_pNameIndex; pIcd->uiDictNum = ATTR_TARGET_NAMESPACE_TAG; pIcd->uiFlags = ICD_VALUE | ICD_IS_ATTRIBUTE; // pIcd->uiCompareRules = 0; // Set by poolCalloc pIcd->pParent = m_pNameIndex->pIcdTree; // pIcd->pFirstChild = NULL; // Set by poolCalloc pIcd->pPrevSibling = pIcd - 1; pIcd->pNextSibling = pIcd + 1; pIcd->pPrevKeyComponent = pIcd - 1; // pIcd->pNextKeyComponent = NULL; // Set by poolCalloc pIcd->uiCdl = 2; pIcd->uiKeyComponent = 3; // pIcd->pPrevDataComponent = NULL; // Set by poolCalloc // pIcd->pNextDataComponent = NULL; // Set by poolCalloc // pIcd->uiDataComponent = 0; // Set by poolCalloc // pIcd->uiLimit = 0; // Set by poolCalloc icdSetDataType( pIcd, attrElmGetType( getReservedAttributeDef( pIcd->uiDictNum))); pIcd++; m_pNameIndex->pFirstData = pIcd; m_pNameIndex->pLastData = pIcd; pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; pIcd->pIxd = m_pNameIndex; pIcd->uiDictNum = ATTR_DICT_NUMBER_TAG; pIcd->uiFlags = ICD_VALUE | ICD_IS_ATTRIBUTE; // pIcd->uiCompareRules = 0; // Set by poolCalloc pIcd->pParent = m_pNameIndex->pIcdTree; // pIcd->pFirstChild = NULL; // Set by poolCalloc pIcd->pPrevSibling = pIcd - 1; // pIcd->pNextSibling = NULL; // Set by poolCalloc // pIcd->pPrevKeyComponent = NULL; // Set by poolCalloc // pIcd->pNextKeyComponent = NULL; // Set by poolCalloc pIcd->uiCdl = 3; // pIcd->uiKeyComponent = 0; // Set by poolCalloc // pIcd->pPrevDataComponent = NULL; // Set by poolCalloc // pIcd->pNextDataComponent = NULL; // Set by poolCalloc pIcd->uiDataComponent = 1; // pIcd->uiLimit = 0; // Set by poolCalloc icdSetDataType( pIcd, attrElmGetType( getReservedAttributeDef( pIcd->uiDictNum))); // Set up the ICDs for the number index if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( ICD) * 2, (void **)&m_pNumberIndex->pIcdTree))) { goto Exit; } pIcd = m_pNumberIndex->pIcdTree; pIcd->uiIndexNum = m_pNumberIndex->uiIndexNum; m_pNumberIndex->pFirstKey = pIcd; pIcd->pIxd = m_pNumberIndex; pIcd->uiDictNum = ELM_ROOT_TAG; pIcd->uiFlags = ICD_PRESENCE | ICD_REQUIRED_PIECE; // pIcd->uiCompareRules = 0; // Set by poolCalloc // pIcd->pParent = NULL; // Set by poolCalloc pIcd->pFirstChild = pIcd + 1; // pIcd->pPrevSibling = NULL; // Set by poolCalloc // pIcd->pNextSibling = NULL; // Set by poolCalloc // pIcd->pPrevKeyComponent = NULL; // Set by poolCalloc pIcd->pNextKeyComponent = pIcd + 1; // pIcd->uiCdl = 0; // Set by poolCalloc pIcd->uiKeyComponent = 1; // pIcd->pPrevDataComponent = NULL; // Set by poolCalloc // pIcd->pNextDataComponent = NULL; // Set by poolCalloc // pIcd->uiDataComponent = 0; // Set by poolCalloc // pIcd->uiLimit = 0; // Set by poolCalloc icdSetDataType( pIcd, XFLM_NODATA_TYPE); pIcd++; m_pNumberIndex->pLastKey = pIcd; pIcd->uiIndexNum = m_pNumberIndex->uiIndexNum; pIcd->pIxd = m_pNumberIndex; pIcd->uiDictNum = ATTR_DICT_NUMBER_TAG; pIcd->uiFlags = ICD_VALUE | ICD_REQUIRED_PIECE | ICD_IS_ATTRIBUTE; // pIcd->uiCompareRules = 0; // Set by poolCalloc pIcd->pParent = m_pNumberIndex->pIcdTree; // pIcd->pFirstChild = NULL; // Set by poolCalloc // pIcd->pPrevSibling = NULL; // Set by poolCalloc // pIcd->pNextSibling = NULL; // Set by poolCalloc pIcd->pPrevKeyComponent = pIcd - 1; // pIcd->pNextKeyComponent = NULL; // Set by poolCalloc pIcd->uiCdl = 1; pIcd->uiKeyComponent = 2; // pIcd->pPrevDataComponent = NULL; // Set by poolCalloc // pIcd->pNextDataComponent = NULL; // Set by poolCalloc // pIcd->uiDataComponent = 0; // Set by poolCalloc // pIcd->uiLimit = 0; // Set by poolCalloc icdSetDataType( pIcd, attrElmGetType( getReservedAttributeDef( pIcd->uiDictNum))); if (RC_BAD( rc = linkIcds( m_pNameIndex->pIcdTree))) { goto Exit; } if (RC_BAD( rc = linkIcds( m_pNumberIndex->pIcdTree))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Allocate an element table. ****************************************************************************/ RCODE F_Dict::allocElementTable( FLMUINT uiLowestElementNum, FLMUINT uiHighestElementNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; // There should not already be an element table or an extended element // table. flmAssert( m_pElementDefTbl == NULL && m_pExtElementDefTbl == NULL && m_hExtElementDefMutex == F_MUTEX_NULL); // No need for a fixed element table if we don't have any element // numbers in that range. if (uiHighestElementNum && uiLowestElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) { m_uiLowestElementNum = uiLowestElementNum; if (uiHighestElementNum > FLM_HIGH_FIXED_ELEMENT_NUM) { m_uiHighestElementNum = FLM_HIGH_FIXED_ELEMENT_NUM; } else { m_uiHighestElementNum = uiHighestElementNum; } uiCount = m_uiHighestElementNum - m_uiLowestElementNum + 1; if (RC_BAD( rc = f_calloc( uiCount * sizeof( ATTR_ELM_DEF), &m_pElementDefTbl))) { goto Exit; } } // See if we should allocate an extended element table if (uiHighestElementNum >= FLM_LOW_EXT_ELEMENT_NUM) { FLMUINT uiNewSize = uiHighestElementNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) { uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; } // Need to allocate a mutex too. if (RC_BAD( rc = f_mutexCreate( &m_hExtElementDefMutex))) { goto Exit; } // Allocate a new array if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, &m_pExtElementDefTbl))) { goto Exit; } m_uiExtElementDefTblSize = uiNewSize; } Exit: return( rc); } /**************************************************************************** Desc: Allocate an attribute table. ****************************************************************************/ RCODE F_Dict::allocAttributeTable( FLMUINT uiLowestAttributeNum, FLMUINT uiHighestAttributeNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; // There should not already be an element table or an extended element // table. flmAssert( m_pAttributeDefTbl == NULL && m_pExtAttributeDefTbl == NULL && m_hExtAttributeDefMutex == F_MUTEX_NULL); // No need for a fixed attribute table if we don't have any attribute // numbers in that range. if (uiHighestAttributeNum && uiLowestAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) { m_uiLowestAttributeNum = uiLowestAttributeNum; if (uiHighestAttributeNum > FLM_HIGH_FIXED_ELEMENT_NUM) { m_uiHighestAttributeNum = FLM_HIGH_FIXED_ELEMENT_NUM; } else { m_uiHighestAttributeNum = uiHighestAttributeNum; } uiCount = m_uiHighestAttributeNum - m_uiLowestAttributeNum + 1; if (RC_BAD( rc = f_calloc( uiCount * sizeof( ATTR_ELM_DEF), &m_pAttributeDefTbl))) { goto Exit; } } // See if we should allocate an extended attribute table if (uiHighestAttributeNum >= FLM_LOW_EXT_ATTRIBUTE_NUM) { FLMUINT uiNewSize = uiHighestAttributeNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) { uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; } // Need to allocate a mutex too. if (RC_BAD( rc = f_mutexCreate( &m_hExtAttributeDefMutex))) { goto Exit; } // Allocate a new array if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, &m_pExtAttributeDefTbl))) { goto Exit; } m_uiExtAttributeDefTblSize = uiNewSize; } Exit: return( rc); } /**************************************************************************** Desc: Allocate an index table. ****************************************************************************/ RCODE F_Dict::allocIndexTable( FLMUINT uiLowestIndexNum, FLMUINT uiHighestIndexNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; // There should not already be an index table. flmAssert( m_ppIxdTbl == NULL); m_uiLowestIxNum = uiLowestIndexNum; m_uiHighestIxNum = uiHighestIndexNum; if ((uiCount = getIndexCount( FALSE)) > 0) { if (RC_BAD( rc = f_calloc( uiCount * sizeof( IXD *), &m_ppIxdTbl))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Allocate a prefix table. ****************************************************************************/ RCODE F_Dict::allocPrefixTable( FLMUINT uiLowestPrefixNum, FLMUINT uiHighestPrefixNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; flmAssert( m_ppPrefixTbl == NULL); m_uiLowestPrefixNum = uiLowestPrefixNum; m_uiHighestPrefixNum = uiHighestPrefixNum; if ((uiCount = getPrefixCount()) > 0) { if (RC_BAD( rc = f_calloc( uiCount * sizeof( F_PREFIX *), &m_ppPrefixTbl))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Allocate an encdef table. ****************************************************************************/ RCODE F_Dict::allocEncDefTable( FLMUINT uiLowestEncDefNum, FLMUINT uiHighestEncDefNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; flmAssert( m_ppEncDefTbl == NULL); m_uiLowestEncDefNum = uiLowestEncDefNum; m_uiHighestEncDefNum = uiHighestEncDefNum; if ((uiCount = getEncDefCount()) > 0) { if (RC_BAD( rc = f_calloc( uiCount * sizeof( F_ENCDEF *), &m_ppEncDefTbl))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Allocate a collection table. ****************************************************************************/ RCODE F_Dict::allocCollectionTable( FLMUINT uiLowestCollectionNum, FLMUINT uiHighestCollectionNum ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; // There should not already be a collection table. flmAssert( m_ppCollectionTbl == NULL); m_uiLowestCollectionNum = uiLowestCollectionNum; m_uiHighestCollectionNum = uiHighestCollectionNum; if ((uiCount = getCollectionCount( FALSE)) > 0) { if (RC_BAD( rc = f_calloc( uiCount * sizeof( LFILE *), &m_ppCollectionTbl))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Find the node IDs where we keep the next element, next attribute, next index, and next collection numbers. ****************************************************************************/ RCODE F_Dict::createNextDictNums( F_Db * pDb) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_DOMNode * pAttr = NULL; // Create a new root element if (RC_BAD( rc = pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_NEXT_DICT_NUMS_TAG, (IF_DOMNode **)&pNode))) { goto Exit; } if( RC_BAD( rc = pNode->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // The NODE ID of this element had better be 1, because it is the // first thing we create in the dictionary. Plus, the // getNextDictNumNodeIds method is counting on it being one! if( pNode->getNodeId() != XFLM_DICTINFO_DOC_ID) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Create and populate the four attributes that hold next element, // next attribute, next index, and next collection numbers. // Freeze each of the nodes so that they can only be modified by // FLAIM. // Node for next element number if (RC_BAD( rc = pNode->createAttribute( pDb, ATTR_NEXT_ELEMENT_NUM_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Node for next attribute number if (RC_BAD( rc = pNode->createAttribute( pDb, ATTR_NEXT_ATTRIBUTE_NUM_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Node for next index number if (RC_BAD( rc = pNode->createAttribute( pDb, ATTR_NEXT_INDEX_NUM_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Node for next collection number if (RC_BAD( rc = pNode->createAttribute( pDb, ATTR_NEXT_COLLECTION_NUM_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Node for next prefix number if (RC_BAD( rc = pNode->createAttribute( pDb, ATTR_NEXT_PREFIX_NUM_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Node for next encdef number if (RC_BAD( rc = pNode->createAttribute( pDb, ATTR_NEXT_ENCDEF_NUM_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } Exit: if (pNode) { pNode->Release(); } if (pAttr) { pAttr->Release(); } return( rc); } /**************************************************************************** Desc: Allocate the next dictionary number for a specific dictionary type. ****************************************************************************/ RCODE F_Dict::allocNextDictNum( F_Db * pDb, FLMUINT uiDictType, FLMUINT * puiDictNumber) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDoc = NULL; F_DOMNode * pAttr = NULL; FLMUINT uiAttrName; FLMUINT uiMaxNum; if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, XFLM_DICTINFO_DOC_ID, &pDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } if( pDoc->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } switch (uiDictType) { case ELM_ELEMENT_TAG: uiAttrName = ATTR_NEXT_ELEMENT_NUM_TAG; uiMaxNum = XFLM_MAX_ELEMENT_NUM; break; case ELM_ATTRIBUTE_TAG: uiAttrName = ATTR_NEXT_ATTRIBUTE_NUM_TAG; uiMaxNum = XFLM_MAX_ATTRIBUTE_NUM; break; case ELM_INDEX_TAG: uiAttrName = ATTR_NEXT_INDEX_NUM_TAG; uiMaxNum = XFLM_MAX_INDEX_NUM; break; case ELM_COLLECTION_TAG: uiAttrName = ATTR_NEXT_COLLECTION_NUM_TAG; uiMaxNum = XFLM_MAX_COLLECTION_NUM; break; case ELM_PREFIX_TAG: uiAttrName = ATTR_NEXT_PREFIX_NUM_TAG; uiMaxNum = XFLM_MAX_PREFIX_NUM; break; case ELM_ENCDEF_TAG: uiAttrName = ATTR_NEXT_ENCDEF_NUM_TAG; uiMaxNum = XFLM_MAX_ENCDEF_NUM; break; default: // Nothing to allocate for other types. *puiDictNumber will // return as zero. *puiDictNumber = 0; goto Exit; } if( RC_BAD( rc = pDoc->getAttribute( pDb, uiAttrName, (IF_DOMNode **)&pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->getUINT( pDb, puiDictNumber))) { goto Exit; } // Dictionary number better be > 0 if( !(*puiDictNumber)) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // See if we have exceeded the limit for this type. if( *puiDictNumber > uiMaxNum) { *puiDictNumber = 0; switch( uiDictType) { case ELM_ELEMENT_TAG: rc = RC_SET( NE_XFLM_NO_MORE_ELEMENT_NUMS); break; case ELM_ATTRIBUTE_TAG: rc = RC_SET( NE_XFLM_NO_MORE_ATTRIBUTE_NUMS); break; case ELM_INDEX_TAG: rc = RC_SET( NE_XFLM_NO_MORE_INDEX_NUMS); break; case ELM_COLLECTION_TAG: rc = RC_SET( NE_XFLM_NO_MORE_COLLECTION_NUMS); break; case ELM_PREFIX_TAG: rc = RC_SET( NE_XFLM_NO_MORE_PREFIX_NUMS); break; case ELM_ENCDEF_TAG: rc = RC_SET( NE_XFLM_NO_MORE_ENCDEF_NUMS); } goto Exit; } // Need to increment the dictionary number for the next // caller. if( RC_BAD( rc = pAttr->removeModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( pDb, *puiDictNumber + 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } Exit: if( pAttr) { pAttr->Release(); } if( pDoc) { pDoc->Release(); } return( rc); } /**************************************************************************** Desc: Check and set the next dictionary number for a specific dictionary type. ****************************************************************************/ RCODE F_Dict::setNextDictNum( F_Db * pDb, FLMUINT uiDictType, FLMUINT uiDictNumber) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDoc = NULL; F_DOMNode * pAttr = NULL; FLMUINT uiAttrName; FLMUINT uiCurrDictNumber; if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, XFLM_DICTINFO_DOC_ID, &pDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } if( pDoc->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } switch( uiDictType) { case ELM_ELEMENT_TAG: { if( uiDictNumber > XFLM_MAX_ELEMENT_NUM) { rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); goto Exit; } uiAttrName = ATTR_NEXT_ELEMENT_NUM_TAG; break; } case ELM_ATTRIBUTE_TAG: { if (uiDictNumber > XFLM_MAX_ATTRIBUTE_NUM) { rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); goto Exit; } uiAttrName = ATTR_NEXT_ATTRIBUTE_NUM_TAG; break; } case ELM_INDEX_TAG: { if (uiDictNumber > XFLM_MAX_INDEX_NUM) { rc = RC_SET( NE_XFLM_BAD_IX); goto Exit; } uiAttrName = ATTR_NEXT_INDEX_NUM_TAG; break; } case ELM_COLLECTION_TAG: { if (uiDictNumber > XFLM_MAX_COLLECTION_NUM) { rc = RC_SET( NE_XFLM_BAD_COLLECTION); goto Exit; } uiAttrName = ATTR_NEXT_COLLECTION_NUM_TAG; break; } case ELM_PREFIX_TAG: { if (uiDictNumber > XFLM_MAX_PREFIX_NUM) { rc = RC_SET( NE_XFLM_BAD_PREFIX); goto Exit; } uiAttrName = ATTR_NEXT_PREFIX_NUM_TAG; break; } case ELM_ENCDEF_TAG: { if (uiDictNumber > XFLM_MAX_ENCDEF_NUM) { rc = RC_SET( NE_XFLM_BAD_ENCDEF_NUM); goto Exit; } uiAttrName = ATTR_NEXT_ENCDEF_NUM_TAG; break; } default: { // Doesn't really matter on other dictionary types // because dictionary number is not used for anything. goto Exit; } } if( RC_BAD( rc = pDoc->getAttribute( pDb, uiAttrName, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->getUINT( pDb, &uiCurrDictNumber))) { goto Exit; } // Dictionary number better be > 0 if( !uiCurrDictNumber) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // If the new dictionary number >= current next dictionary // number, need to set the next dictionary number to one greater // than the new dictionary number. if( uiDictNumber >= uiCurrDictNumber) { if( RC_BAD( rc = pAttr->removeModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( pDb, uiDictNumber + 1))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } Exit: if( pAttr) { pAttr->Release(); } if( pDoc) { pDoc->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ void F_AttrElmInfo::resetInfo( void) { m_uiDictNum = 0; m_uiDataType = XFLM_NODATA_TYPE; m_uiFlags = 0; m_uiState = ATTR_ELM_STATE_ACTIVE; m_pFirstIcd = NULL; if( m_pDocNode) { m_pDocNode->Release(); m_pDocNode = NULL; } if( m_pTargetNamespaceAttr) { m_pTargetNamespaceAttr->Release(); m_pTargetNamespaceAttr = NULL; } if( m_pNameAttr) { m_pNameAttr->Release(); m_pNameAttr = NULL; } } /**************************************************************************** Desc: Read in LFH headers. ****************************************************************************/ RCODE F_Db::dictReadLFH( void) { RCODE rc = NE_XFLM_OK; LFILE * pLFile; F_COLLECTION * pCollection; F_CachedBlock * pSCache = NULL; FLMBOOL bReleaseCache = FALSE; F_BLK_HDR * pBlkHdr; FLMUINT uiBlkAddress; FLMUINT uiPos; FLMUINT uiEndPos; FLMUINT uiBlkSize = m_pDatabase->m_uiBlockSize; LFILE TmpLFile; F_COLLECTION TmpCollection; f_memset( &TmpLFile, 0, sizeof( LFILE)); f_memset( &TmpCollection, 0, sizeof( F_COLLECTION)); uiBlkAddress = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr; while (uiBlkAddress) { if (RC_BAD( rc = m_pDatabase->getBlock( this, NULL, uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; pBlkHdr = pSCache->m_pBlkHdr; uiPos = SIZEOF_STD_BLK_HDR; uiEndPos = blkGetEnd( uiBlkSize, SIZEOF_STD_BLK_HDR, pBlkHdr); // Read through all of the logical file definitions in the block for( ; uiPos + sizeof( F_LF_HDR) <= uiEndPos; uiPos += sizeof( F_LF_HDR)) { F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)(pBlkHdr) + uiPos); eLFileType eLfType = (eLFileType)pLfHdr->ui32LfType; // Have to fix up the offsets later when they are read in if (eLfType == XFLM_LF_INVALID) { continue; } // Populate the LFILE in the dictionary, if one has been set up. if (eLfType == XFLM_LF_INDEX) { FSLFileIn( (FLMBYTE *)pLfHdr, &TmpLFile, NULL, uiBlkAddress, uiPos); if (RC_OK( m_pDict->getIndex( TmpLFile.uiLfNum, &pLFile, NULL, TRUE))) { f_memcpy( pLFile, &TmpLFile, sizeof( LFILE)); } // LFILE better have a non-zero root block. if (!TmpLFile.uiRootBlk) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } else { // Better be a container flmAssert( eLfType == XFLM_LF_COLLECTION); FSLFileIn( (FLMBYTE *)pLfHdr, &TmpCollection.lfInfo, &TmpCollection, uiBlkAddress, uiPos); if (RC_OK( m_pDict->getCollection( TmpCollection.lfInfo.uiLfNum, &pCollection, TRUE))) { f_memcpy( pCollection, &TmpCollection, sizeof( F_COLLECTION)); } // LFILE better have a non-zero root block. if (!TmpCollection.lfInfo.uiRootBlk) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } } // Get the next block in the chain uiBlkAddress = (FLMUINT)pBlkHdr->ui32NextBlkInChain; ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } return( rc ); } /**************************************************************************** Desc: Read in all element, attribute, index, or collection definitions - as specified in uiDictType. ****************************************************************************/ RCODE F_Db::dictReadDefs( FLMUINT uiDictType) { RCODE rc = NE_XFLM_OK; F_DataVector key; LFILE * pLFile; IXD * pIxd; F_Btree * pbTree = NULL; FLMBYTE ucKeyBuf [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT uiFoundDictType; FLMUINT uiLowest; FLMUINT uiHighest; FLMUINT uiDictNum; IXKeyCompare compareObject; if (RC_BAD( rc = m_pDict->getIndex( XFLM_DICT_NUMBER_INDEX, &pLFile, &pIxd))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } // First determine the low and high field numbers. // If the LFILE is not yet set up, the index has not yet been // created, so there will be no definitions to read. This will // be the case when we are first creating the dictionary. We have // started a transaction, and it is trying to read in the definitions // but there are none. flmAssert( pLFile->uiRootBlk); // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree))) { goto Exit; } // Open the B-Tree compareObject.setIxInfo( this, pIxd); compareObject.setCompareNodeIds( FALSE); compareObject.setCompareDocId( FALSE); compareObject.setSearchKey( &key); if (RC_BAD( rc = pbTree->btOpen( this, pLFile, FALSE, FALSE, &compareObject))) { goto Exit; } if (RC_BAD( rc = key.setUINT( 0, uiDictType))) { goto Exit; } if (RC_BAD( rc = key.outputKey( pIxd, 0, ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, SEARCH_KEY_FLAG))) { goto Exit; } // Position to the first key, if any if (RC_BAD( rc = pbTree->btLocateEntry( ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, XFLM_INCL, NULL))) { // May not have found anything. if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } key.reset(); if (RC_BAD( rc = key.inputKey( pIxd, ucKeyBuf, uiKeyLen))) { goto Exit; } // See if we went past the last key of this type. if (RC_BAD( rc = key.getUINT( 0, &uiFoundDictType))) { goto Exit; } if (uiFoundDictType != uiDictType) { goto Exit; // Will return NE_XFLM_OK } if (RC_BAD( rc = key.getUINT( 1, &uiLowest))) { goto Exit; } uiHighest = uiLowest; // Position to the end of keys of this type key.reset(); if (RC_BAD( rc = key.setUINT( 0, uiDictType))) { goto Exit; } if (RC_BAD( rc = key.setUINT( 1, 0xFFFFFFFF))) { goto Exit; } if (RC_BAD( rc = key.outputKey( pIxd, 0, ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, SEARCH_KEY_FLAG))) { goto Exit; } // Position to just past the specified key. if (RC_BAD( rc = pbTree->btLocateEntry( ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, XFLM_EXCL, NULL))) { // May not have found anything, in which case we need to // position to the last key in the index. if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { if (RC_BAD( rc = pbTree->btLastEntry( ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen))) { goto Exit; } } else { goto Exit; } } else { // Backup one key - since we will have gone just beyond // keys of this type. if (RC_BAD( rc = pbTree->btPrevEntry( ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen))) { goto Exit; } } // At this point we better be positioned on the last key of this type key.reset(); if (RC_BAD( rc = key.inputKey( pIxd, ucKeyBuf, uiKeyLen))) { goto Exit; } if (RC_BAD( rc = key.getUINT( 0, &uiFoundDictType))) { goto Exit; } // See if we went past the last key of this type - should not // be possible, unless there is a corruption. if (uiFoundDictType != uiDictType) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if (RC_BAD( rc = key.getUINT( 1, &uiHighest))) { goto Exit; } // uiHighest better be >= uiLowest or we have // b-tree corruption. if (uiHighest < uiLowest) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Pre-allocate the tables if (uiDictType == ELM_ELEMENT_TAG) { if (RC_BAD( rc = m_pDict->allocElementTable( uiLowest, uiHighest))) { goto Exit; } } else if (uiDictType == ELM_ATTRIBUTE_TAG) { if (RC_BAD( rc = m_pDict->allocAttributeTable( uiLowest, uiHighest))) { goto Exit; } } else if (uiDictType == ELM_INDEX_TAG) { if (RC_BAD( rc = m_pDict->allocIndexTable( uiLowest, uiHighest))) { goto Exit; } } else if (uiDictType == ELM_PREFIX_TAG) { if (RC_BAD( rc = m_pDict->allocPrefixTable( uiLowest, uiHighest))) { goto Exit; } } else if (uiDictType == ELM_ENCDEF_TAG) { if (RC_BAD( rc = m_pDict->allocEncDefTable( uiLowest, uiHighest))) { goto Exit; } } else // (uiDictType == ELM_COLLECTION_TAG) { flmAssert( uiDictType == ELM_COLLECTION_TAG); if (RC_BAD( rc = m_pDict->allocCollectionTable( uiLowest, uiHighest))) { goto Exit; } } // Position back to the first key for this type key.reset(); if (RC_BAD( rc = key.setUINT( 0, uiDictType))) { goto Exit; } if (RC_BAD( rc = key.outputKey( pIxd, 0, ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, SEARCH_KEY_FLAG))) { goto Exit; } if (RC_BAD( rc = pbTree->btLocateEntry( ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, XFLM_INCL, NULL))) { // May not have found anything. if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } // Loop through all of the keys of this dictionary type for (;;) { key.reset(); if (RC_BAD( rc = key.inputKey( pIxd, ucKeyBuf, uiKeyLen))) { goto Exit; } // See if we went past the last key of this type. if (RC_BAD( rc = key.getUINT( 0, &uiFoundDictType))) { goto Exit; } if (uiFoundDictType != uiDictType) { break; } // Get the dictionary number if (RC_BAD( rc = key.getUINT( 1, &uiDictNum))) { goto Exit; } // No need to process any more elements or attributes if the // dictionary number is in the extended range. if ((uiDictType == ELM_ELEMENT_TAG && uiDictNum >= FLM_LOW_EXT_ELEMENT_NUM) || (uiDictType == ELM_ATTRIBUTE_TAG && uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM)) { if (uiDictType == ELM_ELEMENT_TAG) { m_pDict->m_pNameTable->m_bLoadedAllElements = FALSE; } else { m_pDict->m_pNameTable->m_bLoadedAllAttributes = FALSE; } break; } if (RC_BAD( rc = m_pDict->updateDict( this, uiDictType, key.getDocumentID(), 0, TRUE, FALSE))) { goto Exit; } // Go to the next key if (RC_BAD( rc = pbTree->btNextEntry( ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen))) { // May not have found anything. if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; break; } goto Exit; } } Exit: if (pbTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree); } return( rc); } /**************************************************************************** Desc: Open a dictionary by reading in all of the dictionary tables from the dictionaries. ****************************************************************************/ RCODE F_Db::dictOpen( void) { RCODE rc = NE_XFLM_OK; // At this point, better not be pointing to a dictionary. flmAssert( !m_pDict); // Should never get here for a temporary database. flmAssert( !m_pDatabase->m_bTempDb); // Allocate a new F_Dict object for reading the dictionary // into memory. if ((m_pDict = f_new F_Dict) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } // Allocate the name table if (RC_BAD( rc = m_pDict->allocNameTable())) { goto Exit; } // Add in all of the reserved dictionary tags to the name table. if (RC_BAD( rc = m_pDict->getNameTable()->addReservedDictTags())) { goto Exit; } // Allocate the fixed collections and indexes and set them up if (RC_BAD( rc = m_pDict->setupPredefined( m_pDatabase->m_uiDefaultLanguage))) { goto Exit; } // Read in the LFH's for the predefined stuff. if (RC_BAD( rc = dictReadLFH())) { goto Exit; } // If dictionary collection is not yet set up, do nothing. if (m_pDict->m_pDictCollection->lfInfo.uiBlkAddress && m_pDict->m_pDictCollection->lfInfo.uiOffsetInBlk) { // Read in definitions in the following order: // 1) attribute definitions // 2) element definitions // 3) collection definitions // 4) index definitions // This guarantees that things will be defined by the // time they are referenced. if (RC_BAD( rc = dictReadDefs( ELM_ATTRIBUTE_TAG))) { goto Exit; } if (RC_BAD( rc = dictReadDefs( ELM_ELEMENT_TAG))) { goto Exit; } if (RC_BAD( rc = dictReadDefs( ELM_COLLECTION_TAG))) { goto Exit; } if (RC_BAD( rc = dictReadDefs( ELM_INDEX_TAG))) { goto Exit; } if (RC_BAD( rc = dictReadDefs( ELM_PREFIX_TAG))) { goto Exit; } if (RC_BAD( rc = dictReadDefs( ELM_ENCDEF_TAG))) { goto Exit; } // Must read LFHs to get the LFILE information for the // collections and indexes we have just added. if (RC_BAD( rc = dictReadLFH())) { goto Exit; } } m_pDict->getNameTable()->sortTags(); if (m_pDatabase) { m_pDict->m_bInLimitedMode = m_pDatabase->inLimitedMode(); } // VISIT: Should we assume limited mode if we don't have a database file ? Exit: if (RC_BAD( rc) && m_pDict) { m_pDict->Release(); m_pDict = NULL; } return( rc); } /**************************************************************************** Desc: Creates a new dictionary for a database. This occurs on database create and on a dictionary change. ****************************************************************************/ RCODE F_Db::createNewDict( void) { RCODE rc = NE_XFLM_OK; // Unlink the DB from the current F_Dict object, if any. if (m_pDict) { m_pDatabase->lockMutex(); unlinkFromDict(); m_pDatabase->unlockMutex(); } // Allocate a new F_Dict object for the new dictionary we // are going to create. if (RC_BAD( rc = dictOpen())) { goto Exit; } // Update the F_Db flags to indicate that the dictionary // was updated. m_uiFlags |= FDB_UPDATED_DICTIONARY; // Create a special document in the dictionary to hold // the next element, next attribute, next index, and next // collection numbers. if (RC_BAD( rc = m_pDict->createNextDictNums( this))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Add data dictionary records to the data dictionary. ****************************************************************************/ RCODE F_Db::dictCreate( const char * pszDictPath, // Name of dictionary file. This is only // used if dictBuf is NULL. If both // dictPath and dictBuf are NULL, the // database will be created with an empty // dictionary const char * pszDictBuf) // Buffer containing dictionary in ASCII // GEDCOM If NULL pszDictPath will be used { RCODE rc = NE_XFLM_OK; IF_FileHdl * pDictFileHdl = NULL; FLMBOOL bFileOpen = FALSE; LFILE TempLFile; F_COLLECTION TempCollection; char * pszXMLBuffer = NULL; FLMUINT64 ui64FileSize; FLMUINT uiBytesRead; IF_BufferIStream * pStream = NULL; // This should never be called for a temporary database. flmAssert( !m_pDatabase->m_bTempDb); // Create the default data collection if (RC_BAD(rc = m_pDatabase->lFileCreate( this, &TempCollection.lfInfo, &TempCollection, XFLM_DATA_COLLECTION, XFLM_LF_COLLECTION, FALSE, TRUE))) { goto Exit; } // Create the dictionary collection and indexes if (RC_BAD(rc = m_pDatabase->lFileCreate( this, &TempCollection.lfInfo, &TempCollection, XFLM_DICT_COLLECTION, XFLM_LF_COLLECTION, FALSE, TRUE))) { goto Exit; } if (RC_BAD( rc = m_pDatabase->lFileCreate( this, &TempLFile, NULL, XFLM_DICT_NUMBER_INDEX, XFLM_LF_INDEX, FALSE, FALSE))) { goto Exit; } if (RC_BAD( rc = m_pDatabase->lFileCreate( this, &TempLFile, NULL, XFLM_DICT_NAME_INDEX, XFLM_LF_INDEX, FALSE, TRUE))) { goto Exit; } // Create the maintenance collection if (RC_BAD(rc = m_pDatabase->lFileCreate( this, &TempCollection.lfInfo, &TempCollection, XFLM_MAINT_COLLECTION, XFLM_LF_COLLECTION, FALSE, TRUE))) { goto Exit; } // Create a new dictionary we can work with. if (RC_BAD( rc = createNewDict())) { goto Exit; } // If we have an XML buffer, there is no need to open the file. if (!pszDictBuf && pszDictPath) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( pszDictPath, FLM_IO_RDONLY, &pDictFileHdl))) { goto Exit; } bFileOpen = TRUE; // Get the file size and allocate a buffer to hold the entire thing. if (RC_BAD( rc = pDictFileHdl->size( &ui64FileSize))) { goto Exit; } // Add 1 to size so we can NULL terminate the string we read. if (RC_BAD( rc = f_alloc( (FLMUINT)(ui64FileSize + 1), &pszXMLBuffer))) { goto Exit; } // Read the entire file into the buffer if (RC_BAD( rc = pDictFileHdl->read( 0, (FLMUINT)ui64FileSize, pszXMLBuffer, &uiBytesRead))) { goto Exit; } pszXMLBuffer [uiBytesRead] = 0; pszDictBuf = pszXMLBuffer; } if (!pszDictBuf || !(*pszDictBuf)) { // Neither a dictionary buffer or file were specified. goto Exit; } // Parse through the buffer, extracting each XML document, // add to the dictionary and F_Dict object. The import method // reads stuff from the stream, parses it into XML documents, // and calls documentDone when the document is complete. // The documentDone method checks the dictionary syntax, // adds to the dictionary, etc. if( RC_BAD( rc = FlmAllocBufferIStream( &pStream))) { goto Exit; } if (RC_BAD( rc = pStream->openStream( pszDictBuf, 0))) { goto Exit; } if (RC_BAD( import( pStream, XFLM_DICT_COLLECTION))) { goto Exit; } m_pDict->getNameTable()->sortTags(); Exit: if( pStream) { pStream->Release(); } if( bFileOpen) { pDictFileHdl->closeFile(); } if( pDictFileHdl) { pDictFileHdl->Release(); } if( pszXMLBuffer) { f_free( pszXMLBuffer); } return( rc); } libxflaim-5.1.969/src/flog.h0000644000175000017500000000430310511001742017124 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the logging routines. They use the // IF_Logger_Client and IF_LogMessage_Client classes. // // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flog.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FLOG_H #define FLOG_H // Logging functions for use within FLAIM IF_LogMessageClient * flmBeginLogMessage( eLogMessageType eMsgType); void flmEndLogMessage( IF_LogMessageClient ** ppLogMessage); /*============================================================================ Debug Logging Functions ============================================================================*/ #ifdef FLM_DBG_LOG void scaLogWrite( F_Database * pDatabase, FLMUINT uiWriteAddress, FLMBYTE * pucBlkBuf, FLMUINT uiBufferLen, FLMUINT uiBlockSize, char * pszEvent); void flmDbgLogWrite( F_Database * pDatabase, FLMUINT uiBlkAddress, FLMUINT uiWriteAddress, FLMUINT64 ui64TransId, char * pszEvent); void flmDbgLogUpdate( F_Database * pDatabase, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId, RCODE rc, char * pszEvent); void flmDbgLogMsg( char * pszMsg); void flmDbgLogInit( void); void flmDbgLogExit( void); void flmDbgLogFlush( void); #endif // #ifdef FLM_DBG_LOG #endif // #ifndef FLOG_H libxflaim-5.1.969/src/flchktr.cpp0000644000175000017500000001513110511001742020166 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Check b-trees for physical integrity. // // Tabs: 3 // // Copyright (c) 1991-1992, 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flchktr.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE chkReadBlkFromDisk( F_Db * pDb, F_Database * pDatabase, XFLM_DB_HDR * pDbHdr, F_SuperFileHdl * pSFileHdl, FLMUINT uiFilePos, F_BLK_HDR * pBlkHdr); /******************************************************************** Desc: Read a block - try cache first, then disk if not in cache. *********************************************************************/ RCODE F_DbCheck::blkRead( FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, F_CachedBlock ** ppSCache, FLMINT32 * pi32BlkErrCodeRV) { RCODE rc = NE_XFLM_OK; if (*ppSCache) { ScaReleaseCache( *ppSCache, FALSE); *ppSCache = NULL; *ppBlkHdr = NULL; } else if (*ppBlkHdr) { f_free( ppBlkHdr); *ppBlkHdr = NULL; } if (m_pDb->m_uiKilledTime) { rc = RC_SET( NE_XFLM_OLD_VIEW); goto Exit; } // Get the block from cache. if (RC_OK( rc = m_pDb->m_pDatabase->getBlock( m_pDb, NULL, uiBlkAddress, NULL, ppSCache))) { *ppBlkHdr = (*ppSCache)->getBlockPtr(); } else { // Try to read the block directly from disk. FLMUINT uiBlkSize = m_pDb->m_pDatabase->m_uiBlockSize; FLMUINT64 ui64TransID; F_BLK_HDR * pBlkHdr; FLMUINT64 ui64LastReadTransID; FLMUINT uiPrevBlkAddr; FLMUINT uiFilePos; // If we didn't get a corruption error, jump to exit. if( !gv_pXFlmDbSystem->errorIsFileCorrupt( rc)) { goto Exit; } // Allocate memory for the block. if( RC_BAD( rc = f_calloc( uiBlkSize, ppBlkHdr))) { goto Exit; } pBlkHdr = *ppBlkHdr; uiFilePos = uiBlkAddress; ui64TransID = m_pDb->m_ui64CurrTransID; ui64LastReadTransID = ~((FLMUINT64)0); // Follow version chain until we find version we need. for (;;) { if (RC_BAD( rc = chkReadBlkFromDisk( m_pDb, m_pDb->m_pDatabase, &m_pDb->m_pDatabase->m_lastCommittedDbHdr, m_pDb->m_pSFileHdl, uiFilePos, pBlkHdr))) { goto Exit; } // See if we can use the current version of the block, or if we // must go get a previous version. if (pBlkHdr->ui64TransID <= ui64TransID) { break; } // If the transaction ID is greater than or equal to the last // one we read, we have a corruption. This test will keep us // from looping around forever. if (pBlkHdr->ui64TransID >= ui64LastReadTransID) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } ui64LastReadTransID = pBlkHdr->ui64TransID; // Block is too new, go for next older version. // If previous block address is same as current file position or // zero, we have a problem. uiPrevBlkAddr = (FLMUINT)pBlkHdr->ui32PriorBlkImgAddr; if (uiPrevBlkAddr == uiFilePos || !uiPrevBlkAddr) { rc = (m_pDb->m_uiKilledTime) ? RC_SET( NE_XFLM_OLD_VIEW) : RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } uiFilePos = uiPrevBlkAddr; } // See if we even got the block we thought we wanted. if ((FLMUINT)pBlkHdr->ui32BlkAddr != uiBlkAddress) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } } Exit: *pi32BlkErrCodeRV = 0; if (RC_BAD( rc)) { switch (rc) { case NE_XFLM_DATA_ERROR: *pi32BlkErrCodeRV = FLM_COULD_NOT_SYNC_BLK; break; case NE_XFLM_BLOCK_CRC: *pi32BlkErrCodeRV = FLM_BAD_BLK_CHECKSUM; break; } } return( rc); } /************************************************************************ Desc: Read a block from disk *************************************************************************/ FSTATIC RCODE chkReadBlkFromDisk( F_Db * pDb, F_Database * pDatabase, XFLM_DB_HDR * pDbHdr, F_SuperFileHdl * pSFileHdl, FLMUINT uiFilePos, F_BLK_HDR * pBlkHdr ) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesRead; FLMUINT uiBlkSize = (FLMUINT)pDbHdr->ui16BlockSize; F_Dict * pDict; if (RC_BAD( rc = pSFileHdl->readBlock( uiFilePos, uiBlkSize, (FLMBYTE *)pBlkHdr, &uiBytesRead))) { if (rc == NE_FLM_IO_END_OF_FILE) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } if (uiBytesRead < uiBlkSize) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if (RC_BAD( rc = flmPrepareBlockForUse( uiBlkSize, pBlkHdr))) { goto Exit; } // Decrypt the block if encrypted if (RC_BAD( rc = pDb->getDictionary( &pDict))) { goto Exit; } if (RC_BAD( rc = pDatabase->decryptBlock( pDict, (FLMBYTE *)pBlkHdr))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Report an error *********************************************************************/ RCODE F_DbCheck::chkReportError( FLMINT32 i32ErrCode, FLMUINT32 ui32ErrLocale, FLMUINT32 ui32ErrLfNumber, FLMUINT32 ui32ErrLfType, FLMUINT32 ui32ErrBTreeLevel, FLMUINT32 ui32ErrBlkAddress, FLMUINT32 ui32ErrParentBlkAddress, FLMUINT32 ui32ErrElmOffset, FLMUINT64 ui64ErrNodeId) { XFLM_CORRUPT_INFO CorruptInfo; FLMBOOL bFixErr; CorruptInfo.i32ErrCode = i32ErrCode; CorruptInfo.ui32ErrLocale = ui32ErrLocale; CorruptInfo.ui32ErrLfNumber = ui32ErrLfNumber; CorruptInfo.ui32ErrLfType = ui32ErrLfType; CorruptInfo.ui32ErrBTreeLevel = ui32ErrBTreeLevel; CorruptInfo.ui32ErrBlkAddress = ui32ErrBlkAddress; CorruptInfo.ui32ErrParentBlkAddress = ui32ErrParentBlkAddress; CorruptInfo.ui32ErrElmOffset = ui32ErrElmOffset; CorruptInfo.ui64ErrNodeId = ui64ErrNodeId; CorruptInfo.ifpErrIxKey = NULL; if (m_pDbCheckStatus && RC_OK( m_LastStatusRc)) { bFixErr = FALSE; m_LastStatusRc = m_pDbCheckStatus->reportCheckErr( &CorruptInfo, &bFixErr); } if (i32ErrCode != FLM_OLD_VIEW) { m_bPhysicalCorrupt = TRUE; m_uiFlags &= ~XFLM_DO_LOGICAL_CHECK; } return( m_LastStatusRc); } libxflaim-5.1.969/src/fnumber.cpp0000644000175000017500000004712210511001742020174 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines that do conversions between internal number and numeric // key format to platform number types. // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fnumber.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #define DEFINE_NUMBER_MAXIMUMS #include "flaimsys.h" /**************************************************************************** Desc: Converts a UINT to its storage value *****************************************************************************/ RCODE FlmUINT2Storage( FLMUINT uiNum, FLMUINT * puiBufLen, // In (buffer size) / Out (bytes used) FLMBYTE * pucBuf) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = flmNumber64ToStorage( uiNum, puiBufLen, pucBuf, FALSE, FALSE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Converts an INT to its storage value *****************************************************************************/ RCODE FlmINT2Storage( FLMINT iNum, FLMUINT * puiBufLen, // In (buffer size) / Out (bytes used) FLMBYTE * pucBuf) { FLMBOOL bNeg = FALSE; RCODE rc = NE_XFLM_OK; if( iNum < 0) { iNum = -iNum; bNeg = TRUE; } if( RC_BAD( rc = flmNumber64ToStorage( (FLMUINT64)iNum, puiBufLen, pucBuf, bNeg, FALSE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Converts a number to its storage or collation format Notes: Changes to this code must also be made to flmUINT32ToStorage *****************************************************************************/ RCODE flmNumber64ToStorage( FLMUINT64 ui64Num, FLMUINT * puiBufLen, FLMBYTE * pucBuf, FLMBOOL bNegative, FLMBOOL bCollation) { RCODE rc = NE_XFLM_OK; FLMUINT uiByteCount = 0; FLMUINT32 ui32Low = (FLMUINT32)ui64Num; FLMUINT32 ui32High = (FLMUINT32)(ui64Num >> 32); if( *puiBufLen < FLM_MAX_NUM_BUF_SIZE) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Negative flag should not be set if the number is 0 if( !ui64Num && bNegative) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Build the storage format, either for an index key (if bCollation // is TRUE) or for a data element. if( !bCollation) { // Build the data storage representation of the number // Numbers are stored in a little-endian format do { *pucBuf++ = (FLMBYTE)ui32Low; ui32Low >>= 8; uiByteCount++; } while (ui32Low); if( ui32High) { for( ; uiByteCount < 4; uiByteCount++) { *pucBuf++ = 0; } for( ; ui32High; uiByteCount++) { *pucBuf++ = (FLMBYTE)ui32High; ui32High >>= 8; } } // If a number is negative, the high bit of the right-most // byte needs to be set. If the value of the number is such // that its positive representation already has the high-bit // set, we need to store an additional sign byte. if( !bNegative) { if( *(pucBuf - 1) & 0x80) { *pucBuf++ = 0; uiByteCount++; } } else { if( (*(pucBuf - 1) & 0x80) == 0) { *(pucBuf - 1) |= 0x80; } else { *pucBuf++ = 0x80; uiByteCount++; } } } else { FLMBYTE * pucStart = pucBuf++; if( ui32High) { if( ui32High & 0xFF000000) { *pucBuf++ = (FLMBYTE)(ui32High >> 24); *pucBuf++ = (FLMBYTE)(ui32High >> 16); *pucBuf++ = (FLMBYTE)(ui32High >> 8); *pucBuf++ = (FLMBYTE)ui32High; uiByteCount += 4; } else if( ui32High & 0x00FF0000) { *pucBuf++ = (FLMBYTE)(ui32High >> 16); *pucBuf++ = (FLMBYTE)(ui32High >> 8); *pucBuf++ = (FLMBYTE)ui32High; uiByteCount += 3; } else if( ui32High & 0x0000FF00) { *pucBuf++ = (FLMBYTE)(ui32High >> 8); *pucBuf++ = (FLMBYTE)ui32High; uiByteCount += 2; } else if( ui32High) { *pucBuf++ = (FLMBYTE)ui32High; uiByteCount++; } } if( ui32Low) { if( ui32Low & 0xFF000000) { *pucBuf++ = (FLMBYTE)(ui32Low >> 24); *pucBuf++ = (FLMBYTE)(ui32Low >> 16); *pucBuf++ = (FLMBYTE)(ui32Low >> 8); *pucBuf++ = (FLMBYTE)ui32Low; uiByteCount += 4; } else if( ui32Low & 0x00FF0000) { *pucBuf++ = (FLMBYTE)(ui32Low >> 16); *pucBuf++ = (FLMBYTE)(ui32Low >> 8); *pucBuf++ = (FLMBYTE)ui32Low; uiByteCount += 3; } else if( ui32Low & 0x0000FF00) { *pucBuf++ = (FLMBYTE)(ui32Low >> 8); *pucBuf++ = (FLMBYTE)ui32Low; uiByteCount += 2; } else if( ui32Low) { *pucBuf++ = (FLMBYTE)ui32Low; uiByteCount++; } } else if( !ui32High) { *pucBuf++ = 0; uiByteCount++; } if( !bNegative) { // Positive numbers must collate after negative numbers, // so all positive numbers will start with a byte // in the range of 0xC8 - 0xCF. *pucStart = (FLMBYTE)(0xC8 + (uiByteCount - 1)); uiByteCount++; } else { FLMBYTE * pucTmp = pucStart + 1; while( pucTmp < pucBuf) { *pucTmp = ~(*pucTmp); pucTmp++; } // Negative numbers must collate before positive numbers, // so all negative numbers will start with a byte // in the range of 0xC0 - 0xC7. *pucStart = (FLMBYTE)(0xC8 - uiByteCount); uiByteCount++; } } // Set the number of bytes in the buffer before returning. *puiBufLen = uiByteCount; Exit: return( rc); } /**************************************************************************** Desc: Converts a storage value back into a number Notes: Changes to this code must also be made to flmStorage2Number64 *****************************************************************************/ RCODE flmStorage2Number( FLMUINT uiType, FLMUINT uiBufLen, const FLMBYTE * pucBuf, FLMUINT * puiNum, FLMINT * piNum) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLMUINT uiNum = 0; FLMBOOL bNeg = FALSE; if( !uiBufLen) { if( puiNum) { *puiNum = 0; } else { *piNum = 0; } goto Exit; } if( !pucBuf) { rc = RC_SET( NE_XFLM_CONV_NULL_SRC); goto Exit; } switch( uiType) { case XFLM_NUMBER_TYPE : { // Make sure the number buffer does not exceed the // max length. If there is an extra byte for the // sign (byte 9) make sure it has a value of either // 0x80 or 0 (by masking of the high bit). if( uiBufLen > FLM_MAX_NUM_BUF_SIZE || (uiBufLen == FLM_MAX_NUM_BUF_SIZE && (pucBuf[ uiBufLen - 1] & 0x7F) != 0)) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } // Look at the high bit of the most-significant byte // to determine if the number is signed if( pucBuf[ uiBufLen - 1] & 0x80) { if( puiNum) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } bNeg = TRUE; } uiNum = pucBuf[ uiBufLen - 1] & 0x7F; uiBufLen--; for( uiLoop = 1; uiLoop <= uiBufLen; uiLoop++) { if( gv_b32BitPlatform && (uiNum & 0xFF000000)) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } uiNum = (uiNum << 8) + pucBuf[ uiBufLen - uiLoop]; } break; } case XFLM_TEXT_TYPE: { FLMBYTE ucNumBuf[ 64]; FLMUINT uiNumBufLen = sizeof( ucNumBuf); FLMBYTE * pucTmp; if( RC_BAD( rc = flmStorage2UTF8( XFLM_TEXT_TYPE, uiBufLen, pucBuf, &uiNumBufLen, ucNumBuf))) { goto Exit; } pucTmp = &ucNumBuf[ 0]; if( *pucTmp == ASCII_DASH) { if( puiNum) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } bNeg = TRUE; pucTmp++; } while( *pucTmp) { if( *pucTmp < ASCII_ZERO || *pucTmp > ASCII_NINE) { break; } if( uiNum > (~(FLMUINT)0) / 10) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } uiNum *= (FLMUINT)10; if( uiNum > (~(FLMUINT)0) - (FLMUINT)(*pucTmp - ASCII_ZERO)) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } uiNum += (FLMUINT)(*pucTmp - ASCII_ZERO); pucTmp++; } break; } default : { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } } if( puiNum) { if( bNeg) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } *puiNum = uiNum; } else { flmAssert( piNum); if( bNeg) { if( uiNum > gv_uiMaxSignedIntVal + 1) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } *piNum = -(FLMINT)uiNum; } else { if( uiNum > gv_uiMaxSignedIntVal) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } *piNum = (FLMINT)uiNum; } } Exit: return( rc); } /**************************************************************************** Desc: Converts a storage value back into a number Notes: Changes to this code must also be made to flmStorage2Number *****************************************************************************/ RCODE flmStorage2Number64( FLMUINT uiType, FLMUINT uiBufLen, const FLMBYTE * pucBuf, FLMUINT64 * pui64Num, FLMINT64 * pi64Num) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLMUINT64 ui64Num = 0; FLMBOOL bNeg = FALSE; if( !uiBufLen) { if( pui64Num) { *pui64Num = 0; } else { *pi64Num = 0; } goto Exit; } if( !pucBuf) { rc = RC_SET( NE_XFLM_CONV_NULL_SRC); goto Exit; } switch( uiType) { case XFLM_NUMBER_TYPE : { // Make sure the number buffer does not exceed the // max length. If there is an extra byte for the // sign (byte 9) make sure it has a value of either // 0x80 or 0 (by masking of the high bit). if( uiBufLen > FLM_MAX_NUM_BUF_SIZE || (uiBufLen == FLM_MAX_NUM_BUF_SIZE && (pucBuf[ uiBufLen - 1] & 0x7F) != 0)) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } // Look at the high bit of the most-significant byte // to determine if the number is signed if( pucBuf[ uiBufLen - 1] & 0x80) { if( pui64Num) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } bNeg = TRUE; } ui64Num = pucBuf[ uiBufLen - 1] & 0x7F; uiBufLen--; for( uiLoop = 1; uiLoop <= uiBufLen; uiLoop++) { ui64Num = (ui64Num << 8) + pucBuf[ uiBufLen - uiLoop]; } break; } case XFLM_TEXT_TYPE : { FLMBYTE ucNumBuf[ 64]; FLMUINT uiNumBufLen = sizeof( ucNumBuf); FLMBYTE * pucTmp; if( RC_BAD( rc = flmStorage2UTF8( XFLM_TEXT_TYPE, uiBufLen, pucBuf, &uiNumBufLen, ucNumBuf))) { goto Exit; } pucTmp = &ucNumBuf[ 0]; if( *pucTmp == ASCII_DASH) { if( pui64Num) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } bNeg = TRUE; pucTmp++; } while( *pucTmp) { if( *pucTmp < ASCII_ZERO || *pucTmp > ASCII_NINE) { break; } if( ui64Num > (~(FLMUINT64)0) / 10) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num *= (FLMUINT64)10; if( ui64Num > (~(FLMUINT64)0) - (FLMUINT64)(*pucTmp - ASCII_ZERO)) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num += (FLMUINT64)(*pucTmp - ASCII_ZERO); pucTmp++; } break; } default : { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } } if( pui64Num) { *pui64Num = ui64Num; } else { flmAssert( pi64Num); if( bNeg) { if( ui64Num > gv_ui64MaxSignedIntVal + 1) { rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } *pi64Num = -(FLMINT64)ui64Num; } else { if( ui64Num > gv_ui64MaxSignedIntVal) { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } *pi64Num = (FLMINT64)ui64Num; } } Exit: return( rc); } /**************************************************************************** Desc: Converts a numeric storage value into a collation value *****************************************************************************/ RCODE flmStorageNum2CollationNum( const FLMBYTE * pucStorageBuf, FLMUINT uiStorageLen, FLMBYTE * pucCollBuf, FLMUINT * puiCollLen) { FLMUINT uiLoop; FLMUINT uiOffset; FLMUINT uiMaxLen = *puiCollLen; FLMBYTE ucVal; FLMBOOL bNegative = FALSE; RCODE rc = NE_XFLM_OK; if( !pucStorageBuf || !uiStorageLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } // Look at the high bit of the most-significant byte // to determine if the number is signed if( pucStorageBuf[ uiStorageLen - 1] & 0x80) { bNegative = TRUE; } uiOffset = 1; if( (ucVal = pucStorageBuf[ uiStorageLen - 1] & 0x7F) != 0 || uiStorageLen == 1) // Handle the special case of zero { if( bNegative) { ucVal = ~ucVal; } if( uiOffset >= uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } pucCollBuf[ uiOffset++] = ucVal; } uiStorageLen--; // Check for overflow if( uiOffset + uiStorageLen >= uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Map the little-endian storage format to the big-endian // collation format if( !bNegative) { for( uiLoop = 1; uiLoop <= uiStorageLen; uiLoop++) { pucCollBuf[ uiOffset++] = pucStorageBuf[ uiStorageLen - uiLoop]; } } else { for( uiLoop = 1; uiLoop <= uiStorageLen; uiLoop++) { pucCollBuf[ uiOffset++] = ~pucStorageBuf[ uiStorageLen - uiLoop]; } } flmAssert( uiOffset >= 2); // Store the numeric collation marker and byte count if( !bNegative) { // Positive numbers must collate after negative numbers, // so all positive numbers will start with a byte // in the range of 0xC8 - 0xCF. pucCollBuf[ 0] = (FLMBYTE)(0xC8 + (uiOffset - 2)); } else { // Negative numbers must collate before positive numbers, // so all negative numbers will start with a byte // in the range of 0xC0 - 0xC7. pucCollBuf[ 0] = (FLMBYTE)(0xC8 - (uiOffset - 1)); } // Set the key length *puiCollLen = uiOffset; Exit: return( rc); } /**************************************************************************** Desc: Converts a numeric collation value into a storage value *****************************************************************************/ RCODE flmCollationNum2StorageNum( const FLMBYTE * pucCollBuf, FLMUINT uiCollLen, FLMBYTE * pucStorageBuf, FLMUINT * puiStorageLen) { FLMUINT uiLoop; FLMUINT uiOffset; FLMUINT uiMaxOffset = *puiStorageLen; FLMUINT uiNumKeyBytes; FLMBOOL bNegative = FALSE; RCODE rc = NE_XFLM_OK; if( !pucCollBuf || !uiCollLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } // Make sure this looks like a valid numeric key piece if( (pucCollBuf[ 0] & 0xC0) != 0xC0) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Get the byte count if( (uiNumKeyBytes = (FLMUINT)(pucCollBuf[ 0] & 0x0F)) >= 8) { uiNumKeyBytes -= 7; } else { uiNumKeyBytes = 8 - uiNumKeyBytes; bNegative = TRUE; } pucCollBuf++; uiCollLen--; // Make sure the buffer has at least the number of bytes // we need if( uiCollLen != uiNumKeyBytes) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( uiNumKeyBytes >= uiMaxOffset) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Translate the collation value into a numeric value if( !bNegative) { for( uiLoop = 0; uiLoop < uiNumKeyBytes; uiLoop++) { pucStorageBuf[ uiNumKeyBytes - uiLoop - 1] = pucCollBuf[ uiLoop]; } } else { for( uiLoop = 0; uiLoop < uiNumKeyBytes; uiLoop++) { pucStorageBuf[ uiNumKeyBytes - uiLoop - 1] = ~pucCollBuf[ uiLoop]; } } uiOffset = uiNumKeyBytes; // If a number is negative, the high bit of the right-most // byte needs to be set. If the value of the number is such // that its positive representation already has the high-bit // set, we need to store an additional sign byte. if( !bNegative) { if( pucStorageBuf[ uiOffset - 1] & 0x80) { if( uiOffset >= uiMaxOffset) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } pucStorageBuf[ uiOffset++] = 0; } } else { if( (pucStorageBuf[ uiOffset - 1] & 0x80) == 0) { pucStorageBuf[ uiOffset - 1] |= 0x80; } else { if( uiOffset >= uiMaxOffset) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } pucStorageBuf[ uiOffset++] = 0x80; } } // Set the storage length *puiStorageLen = uiOffset; Exit: return( rc); } /**************************************************************************** Desc: Converts a collation value (numeric only) to a number *****************************************************************************/ RCODE flmCollation2Number( FLMUINT uiBufLen, const FLMBYTE * pucBuf, FLMUINT64 * pui64Num, FLMBOOL * pbNeg, FLMUINT * puiBytesProcessed) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLMUINT uiNumBytes; FLMUINT64 ui64Num; FLMBOOL bNeg = FALSE; *pui64Num = 0; if( !uiBufLen) { goto Exit; } if( !pucBuf) { rc = RC_SET( NE_XFLM_CONV_NULL_SRC); goto Exit; } // Make sure this looks like a valid numeric key piece if( (pucBuf[ 0] & 0xC0) != 0xC0) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Get the byte count if( (uiNumBytes = (FLMUINT)(pucBuf[ 0] & 0x0F)) >= 8) { uiNumBytes -= 7; } else { uiNumBytes = 8 - uiNumBytes; bNeg = TRUE; } pucBuf++; uiBufLen--; // Make sure the buffer has at least the number of bytes // we need if( uiBufLen < uiNumBytes) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Reconstruct the number ui64Num = 0; if( !bNeg) { for( uiLoop = 0; uiLoop < uiNumBytes; uiLoop++) { ui64Num += (((FLMUINT64)pucBuf[ uiLoop]) << (8 * ((uiNumBytes - uiLoop) - 1))); } } else { for( uiLoop = 0; uiLoop < uiNumBytes; uiLoop++) { ui64Num += (((FLMUINT64)((FLMBYTE)~pucBuf[ uiLoop])) << (8 * ((uiNumBytes - uiLoop) - 1))); } } *pui64Num = ui64Num; if( puiBytesProcessed) { *puiBytesProcessed = uiNumBytes + 1; } if( pbNeg) { *pbNeg = bNeg; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE flmStorageNumberToNumber( const FLMBYTE * pucNumBuf, FLMUINT uiNumBufLen, FLMUINT64 * pui64Number, FLMBOOL * pbNeg) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLMUINT64 ui64Num = 0; FLMBOOL bNeg = FALSE; if( !uiNumBufLen) { goto Exit; } // Make sure the number buffer does not exceed the // max length. If there is an extra byte for the // sign (byte 9) make sure it has a value of either // 0x80 or 0 (by masking of the high bit). if( uiNumBufLen > FLM_MAX_NUM_BUF_SIZE || (uiNumBufLen == FLM_MAX_NUM_BUF_SIZE && (pucNumBuf[ uiNumBufLen - 1] & 0x7F) != 0)) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } // Look at the high bit of the most-significant byte // to determine if the number is signed if( pucNumBuf[ uiNumBufLen - 1] & 0x80) { bNeg = TRUE; } ui64Num = pucNumBuf[ uiNumBufLen - 1] & 0x7F; uiNumBufLen--; for( uiLoop = 1; uiLoop <= uiNumBufLen; uiLoop++) { ui64Num = (ui64Num << 8) + pucNumBuf[ uiNumBufLen - uiLoop]; } Exit: *pui64Number = ui64Num; *pbNeg = bNeg; return( rc); } libxflaim-5.1.969/src/fxpath.h0000644000175000017500000002366510511001742017503 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the FLAIM XML import and export utility classes // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fxpath.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FXPATH_H #define FXPATH_H typedef enum { UNKNOWN_TOKEN = 0, // 0 OP_AND_TOKEN = XFLM_AND_OP, // 1 OP_OR_TOKEN = XFLM_OR_OP, // 2 OP_NOT_TOKEN = XFLM_NOT_OP, // 3 OP_EQ_TOKEN = XFLM_EQ_OP, // 4 OP_NE_TOKEN = XFLM_NE_OP, // 5 OP_APPROX_EQ_TOKEN = XFLM_APPROX_EQ_OP, // 6 OP_LT_TOKEN = XFLM_LT_OP, // 7 OP_LE_TOKEN = XFLM_LE_OP, // 8 OP_GT_TOKEN = XFLM_GT_OP, // 9 OP_GE_TOKEN = XFLM_GE_OP, // 10 OP_BITAND_TOKEN = XFLM_BITAND_OP, // 11 OP_BITOR_TOKEN = XFLM_BITOR_OP, // 12 OP_BITXOR_TOKEN = XFLM_BITXOR_OP, // 13 OP_MULT_TOKEN = XFLM_MULT_OP, // 14 OP_DIV_TOKEN = XFLM_DIV_OP, // 15 OP_MOD_TOKEN = XFLM_MOD_OP, // 16 OP_PLUS_TOKEN = XFLM_PLUS_OP, // 17 OP_MINUS_TOKEN = XFLM_MINUS_OP, // 18 OP_NEG_TOKEN = XFLM_NEG_OP, // 19 OP_LPAREN_TOKEN = XFLM_LPAREN_OP, // 20 OP_RPAREN_TOKEN = XFLM_RPAREN_OP, // 21 OP_COMMA_TOKEN = XFLM_COMMA_OP, // 22 OP_LBRACKET_TOKEN = XFLM_LBRACKET_OP, // 23 OP_RBRACKET_TOKEN = XFLM_RBRACKET_OP, // 24 OP_FSLASH_TOKEN, // 25 OP_DOUBLE_FSLASH_TOKEN, // 26 OP_UNION_TOKEN, // 27 PERIOD_TOKEN, // 28 DOUBLE_PERIOD_TOKEN, // 29 COMMA_TOKEN, // 30 DOUBLE_COLON_TOKEN, // 31 NAME_TEST_WILD_TOKEN, // 32 NAME_TEST_NCWILD_TOKEN, // 33 NAME_TEST_QNAME_TOKEN, // 34 NODE_TYPE_COMMENT_TOKEN, // 35 NODE_TYPE_TEXT_TOKEN, // 36 NODE_TYPE_PI_TOKEN, // 37 NODE_TYPE_NODE_TOKEN, // 38 AXIS_ANCESTOR_TOKEN, // 39 AXIS_ANCESTOR_OR_SELF_TOKEN, // 40 AXIS_ATTRIB_TOKEN, // 41 AXIS_CHILD_TOKEN, // 42 AXIS_DESCENDANT_TOKEN, // 43 AXIS_DESCENDANT_OR_SELF_TOKEN, // 44 AXIS_FOLLOWING_TOKEN, // 45 AXIS_FOLLOWING_SIB_TOKEN, // 46 AXIS_NAMESPACE_TOKEN, // 47 AXIS_PARENT_TOKEN, // 48 AXIS_PRECEDING_TOKEN, // 49 AXIS_PRECEDING_SIB_TOKEN, // 50 AXIS_SELF_TOKEN, // 51 AXIS_ATSIGN_TOKEN, // 52 AXIS_META_TOKEN, // 53 LITERAL_TOKEN, // 54 NUMBER_TOKEN, // 55 VAR_REF_TOKEN, // 56 LBRACE_TOKEN, // 57 RBRACE_TOKEN, // 58 FUNC_LAST_TOKEN, // 59 FUNC_POSITION_TOKEN, // 60 FUNC_COUNT_TOKEN, // 61 FUNC_ID_TOKEN, // 62 FUNC_LOCAL_NAME_TOKEN, // 63 FUNC_NAMESPACE_URI_TOKEN, // 64 FUNC_NAME_TOKEN, // 65 FUNC_STRING_TOKEN, // 66 FUNC_CONCAT_TOKEN, // 67 FUNC_STARTS_WITH_TOKEN, // 68 FUNC_CONTAINS_TOKEN, // 69 FUNC_SUBSTR_BEFORE_TOKEN, // 70 FUNC_SUBSTR_AFTER_TOKEN, // 71 FUNC_SUBSTR_TOKEN, // 72 FUNC_STR_LEN_TOKEN, // 73 FUNC_NORM_SPACE_TOKEN, // 74 FUNC_TRANSLATE_TOKEN, // 75 FUNC_NOT_TOKEN, // 76 FUNC_TRUE_TOKEN, // 77 FUNC_FALSE_TOKEN, // 78 FUNC_UNKNOWN_TOKEN, // 79 FUNC_LANG_TOKEN, // 80 FUNC_NUMBER_TOKEN, // 81 FUNC_SUM_TOKEN, // 82 FUNC_FLOOR_TOKEN, // 83 FUNC_CEILING_TOKEN, // 84 FUNC_ROUND_TOKEN, // 85 BINARY_TOKEN, // 86 FUNC_CB_TOKEN, // 87 END_TOKEN // 88 } eXPathTokenType; class F_XPathBase : public F_Object { public: FINLINE FLMBOOL isOperator( eXPathTokenType eType) { switch( eType) { case OP_AND_TOKEN: case OP_OR_TOKEN: case OP_MOD_TOKEN: case OP_DIV_TOKEN: case OP_MULT_TOKEN: case OP_FSLASH_TOKEN: case OP_DOUBLE_FSLASH_TOKEN: case OP_UNION_TOKEN: case OP_PLUS_TOKEN: case OP_MINUS_TOKEN: case OP_EQ_TOKEN: case OP_NE_TOKEN: case OP_LT_TOKEN: case OP_LE_TOKEN: case OP_GT_TOKEN: case OP_GE_TOKEN: return( TRUE); default: break; } return( FALSE); } FINLINE FLMBOOL tokenCanHaveFlags( eXPathTokenType eType) { switch( eType) { case OP_EQ_TOKEN: case OP_NE_TOKEN: case OP_LT_TOKEN: case OP_LE_TOKEN: case OP_GT_TOKEN: case OP_GE_TOKEN: return( TRUE); default: break; } return( FALSE); } FINLINE FLMBOOL isAxisSpecifier( eXPathTokenType eType) { switch( eType) { case AXIS_ANCESTOR_TOKEN: case AXIS_ANCESTOR_OR_SELF_TOKEN: case AXIS_ATTRIB_TOKEN: case AXIS_CHILD_TOKEN: case AXIS_DESCENDANT_TOKEN: case AXIS_DESCENDANT_OR_SELF_TOKEN: case AXIS_FOLLOWING_TOKEN: case AXIS_FOLLOWING_SIB_TOKEN: case AXIS_NAMESPACE_TOKEN: case AXIS_PARENT_TOKEN: case AXIS_PRECEDING_TOKEN: case AXIS_PRECEDING_SIB_TOKEN: case AXIS_SELF_TOKEN: case AXIS_ATSIGN_TOKEN: return( TRUE); default: break; } return( FALSE); } }; /***************************************************************************** Desc: ******************************************************************************/ class F_XPathToken : public F_XPathBase { public: F_XPathToken() { m_pValBuf = NULL; m_uiValBufSize = 0; reset(); } ~F_XPathToken() { if( m_pValBuf) { f_free( &m_pValBuf); } } FINLINE void reset( void) { m_eTokenType = UNKNOWN_TOKEN; m_uiTokenFlags = 0; m_ui64Val = 0; m_puzPrefix = NULL; m_puzLocal = NULL;; } FINLINE RCODE resizeBuffer( FLMUINT uiNewSize) { RCODE rc = NE_XFLM_OK; void * pOrigBuf = m_pValBuf; if( !m_pValBuf) { if( RC_BAD( rc = f_alloc( uiNewSize, &m_pValBuf))) { goto Exit; } } else { if( RC_BAD( rc = f_realloc( uiNewSize, &m_pValBuf))) { goto Exit; } if( m_puzPrefix) { m_puzPrefix = (FLMUNICODE *)(((FLMBYTE *)m_puzPrefix - (FLMBYTE *)pOrigBuf) + (FLMBYTE *)m_pValBuf); } if( m_puzLocal) { m_puzLocal = (FLMUNICODE *)(((FLMBYTE *)m_puzLocal - (FLMBYTE *)pOrigBuf) + (FLMBYTE *)m_pValBuf); } } m_uiValBufSize = uiNewSize; Exit: return( rc); } FINLINE eXPathTokenType getType( void) { return( m_eTokenType); } FINLINE FLMUNICODE * getPrefixPtr( void) { return( m_puzPrefix); } FINLINE FLMUNICODE * getLocalPtr( void) { return( m_puzLocal); } FINLINE FLMUINT64 getNumber( void) { return( m_ui64Val); } FINLINE FLMUINT getTokenFlags( void) { return( m_uiTokenFlags); } private: eXPathTokenType m_eTokenType; FLMUINT m_uiTokenFlags; void * m_pValBuf; FLMUINT m_uiValBufSize; FLMUINT m_uiValBufLen; FLMUINT64 m_ui64Val; FLMUNICODE * m_puzPrefix; FLMUNICODE * m_puzLocal; friend class F_XPathTokenizer; friend class F_XPath; }; /***************************************************************************** Desc: ******************************************************************************/ class F_XPathTokenizer : public F_XPathBase, public F_XMLNamespaceMgr { public: F_XPathTokenizer() { m_pIStream = NULL; m_uiUngetCount = 0; m_eLastTokenType = UNKNOWN_TOKEN; } ~F_XPathTokenizer() { if( m_pIStream) { m_pIStream->Release(); } } RCODE setup( IF_IStream * pIStream); RCODE getNextToken( F_XPathToken * pToken); private: RCODE skipWhitespace( void); RCODE getChar( FLMUNICODE * puChar); RCODE peekChar( FLMUNICODE * puChar); RCODE ungetChar( FLMUNICODE uChar); RCODE getNumber( F_XPathToken * pToken); RCODE getName( F_XPathToken * pToken); RCODE getBinary( F_XPathToken * pToken); RCODE getLiteral( F_XPathToken * pToken); IF_IStream * m_pIStream; eXPathTokenType m_eLastTokenType; FLMUINT m_uiUngetCount; #define XPATH_MAX_UNGET_CHARS 4 FLMUNICODE m_uUngetBuf[ XPATH_MAX_UNGET_CHARS]; }; class F_XPathExpr; class F_XPathPredicate; class F_XPathAxisProducer; class F_XPathStep; /***************************************************************************** Desc: ******************************************************************************/ class F_XPath : public F_XPathBase, public F_XMLNamespaceMgr { public: F_XPath() { } ~F_XPath() { } RCODE parseQuery( F_Db * pDb, IF_IStream * pIStream, IF_Query * pQuery); RCODE parseQuery( F_Db * pDb, char * pszQuery, IF_Query * pQuery); private: RCODE processFilterExpr( F_XPathExpr ** ppExpr); RCODE processPathExpr( F_XPathExpr ** ppExpr); RCODE processUnionExpr( F_XPathExpr ** ppExpr); RCODE processNodeTest( FLMBOOL bAttr, F_XPathExpr ** ppExpr); RCODE processStep( F_XPathExpr ** ppExpr); RCODE processRelativeLocationPath( F_XPathExpr ** ppExpr); RCODE processUnaryExpr( F_XPathExpr ** ppExpr); RCODE processOrExpr( F_XPathExpr ** ppExpr); RCODE processAndExpr( F_XPathExpr ** ppExpr); RCODE processEqualityExpr( F_XPathExpr ** ppExpr); RCODE processRelationalExpr( F_XPathExpr ** ppExpr); RCODE processAdditiveExpr( F_XPathExpr ** ppExpr); RCODE processMultiplicativeExpr( F_XPathExpr ** ppExpr); RCODE processPrimaryExpr( F_XPathExpr ** ppExpr); RCODE getNextToken( void); F_XPathTokenizer m_tokenizer; F_XPathToken m_curToken; }; #endif // FXPATH_H libxflaim-5.1.969/src/f_btpool.cpp0000644000175000017500000000522610511001742020341 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: B-Tree pool class file // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: f_btpool.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Method for initializing the Btree Pool. ****************************************************************************/ RCODE F_BtPool::btpInit( void) { RCODE rc = NE_XFLM_OK; flmAssert( !m_bInitialized); // Create a mutex to control access to the pool. if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } m_bInitialized = TRUE; Exit: return rc; } /**************************************************************************** Desc: Method to reserve a btree object from the pool. ****************************************************************************/ RCODE F_BtPool::btpReserveBtree( F_Btree ** ppBtree) { RCODE rc = NE_XFLM_OK; flmAssert( m_bInitialized); // Lock the mutex first! f_mutexLock( m_hMutex); if (m_pBtreeList) { *ppBtree = m_pBtreeList; m_pBtreeList = m_pBtreeList->m_pNext; (*ppBtree)->m_pNext = NULL; } else { if (( *ppBtree = f_new F_Btree()) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } Exit: f_mutexUnlock( m_hMutex); return rc; } /**************************************************************************** Desc: Method to return a Btree to the pool. ****************************************************************************/ void F_BtPool::btpReturnBtree( F_Btree ** ppBtree) { flmAssert( m_bInitialized); // Close the Btree (*ppBtree)->btClose(); // Lock the mutex first! f_mutexLock( m_hMutex); (*ppBtree)->m_pNext = m_pBtreeList; m_pBtreeList = *ppBtree; *ppBtree = NULL; f_mutexUnlock( m_hMutex); } libxflaim-5.1.969/src/fscursor.cpp0000644000175000017500000006442410511001742020410 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Cursor routines to get the complexity of the file system out // of the search code. // // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fscursor.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "fscursor.h" FSTATIC int nodeIdCompFn( void * pvData1, void * pvData2, void * pvUserData); /**************************************************************************** Desc: ****************************************************************************/ FSIndexCursor::FSIndexCursor() { m_pbTree = NULL; m_bTreeOpen = FALSE; m_pucCurKeyDataBuf = NULL; m_uiCurKeyDataBufSize = 0; m_uiCurKeyDataLen = 0; m_pIxd = NULL; m_pLFile = NULL; m_pDb = NULL; m_eTransType = XFLM_NO_TRANS; m_pNodeIdSet = NULL; resetCursor(); } /**************************************************************************** Desc: ****************************************************************************/ FSIndexCursor::~FSIndexCursor() { closeBTree(); if (m_pucCurKeyDataBuf) { f_free( &m_pucCurKeyDataBuf); } if (m_pbTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pbTree); } if (m_pNodeIdSet) { m_pNodeIdSet->Release(); m_pNodeIdSet = NULL; } } /**************************************************************************** Desc: Resets any allocations, keys, state, etc. ****************************************************************************/ void FSIndexCursor::resetCursor( void) { closeBTree(); m_uiIndexNum = 0; m_uiBlkChangeCnt = 0; m_ui64CurrTransId = 0; m_curKey.uiKeyLen = 0; m_uiCurKeyDataLen = 0; m_bAtBOF = TRUE; m_bAtEOF = FALSE; m_bSetup = FALSE; if (m_pNodeIdSet) { m_pNodeIdSet->Release(); m_pNodeIdSet = NULL; } m_bElimDups = FALSE; m_bMovingForward = TRUE; } /**************************************************************************** Desc: Comparison function for comparing node ids. ****************************************************************************/ FSTATIC int nodeIdCompFn( void * pvData1, void * pvData2, void * // pvUserData ) { if (*((FLMUINT64 *)pvData1) < *((FLMUINT64 *)pvData2)) { return( -1); } else if (*((FLMUINT64 *)pvData1) > *((FLMUINT64 *)pvData2)) { return( 1); } else { return( 0); } } /**************************************************************************** Desc: Allocate a result set for duplicate checking. ****************************************************************************/ RCODE FSIndexCursor::allocDupCheckSet( void) { RCODE rc = NE_XFLM_OK; char szTmpDir [F_PATH_MAX_SIZE]; // If it is not a compound index, no need for a result set. if (!m_bElimDups) { goto Exit; } if (m_pNodeIdSet) { m_pNodeIdSet->Release(); m_pNodeIdSet = NULL; } if ((m_pNodeIdSet = f_new F_DynSearchSet) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = gv_pXFlmDbSystem->getTempDir( szTmpDir))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = NE_XFLM_OK; } else { goto Exit; } } if (!szTmpDir [0] && m_pDb) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_pDb->m_pDatabase->m_pszDbPath, szTmpDir, NULL))) { goto Exit; } } if (RC_BAD( rc = m_pNodeIdSet->setup( szTmpDir, sizeof( FLMUINT64)))) { goto Exit; } m_pNodeIdSet->setCompareFunc( nodeIdCompFn, NULL); Exit: if (RC_BAD( rc)) { if (m_pNodeIdSet) { m_pNodeIdSet->Release(); m_pNodeIdSet = NULL; } } return( rc); } /**************************************************************************** Desc: Check to see if we have already returned the node. ****************************************************************************/ RCODE FSIndexCursor::checkIfDup( FLMUINT64 ui64NodeId, FLMBOOL * pbDup ) { RCODE rc = NE_XFLM_OK; *pbDup = FALSE; // Should only be called if m_bElimDups is TRUE flmAssert( m_bElimDups); // If we have not yet allocated the result set, do it now. if (!m_pNodeIdSet) { if (RC_BAD( rc = allocDupCheckSet())) { goto Exit; } } // See if we can add the node id to the result set if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId))) { if (rc == NE_FLM_EXISTS) { *pbDup = TRUE; rc = NE_XFLM_OK; } else { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Resets to a new transaction that may change the read consistency of the query. ****************************************************************************/ RCODE FSIndexCursor::resetTransaction( F_Db * pDb) { RCODE rc = NE_XFLM_OK; LFILE * pLFile; IXD * pIxd; if (RC_BAD( rc = pDb->m_pDict->getIndex( m_uiIndexNum, &pLFile, &pIxd))) { goto Exit; } if (m_pDb != pDb || pLFile != m_pLFile || pIxd != m_pIxd) { m_pLFile = pLFile; m_pIxd = pIxd; if (m_bTreeOpen) { closeBTree(); } m_pDb = pDb; m_eTransType = pDb->m_eTransType; } m_ixCompare.setIxInfo( pDb, m_pIxd); m_ui64CurrTransId = pDb->m_ui64CurrTransID; m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt; Exit: return( rc); } /**************************************************************************** Desc: Setup the from and until keys in the cursor. Return counts after positioning to the from and until key in the index. ****************************************************************************/ RCODE FSIndexCursor::setupKeys( F_Db * pDb, IXD * pIxd, PATH_PRED * pPred, FLMBOOL * pbDoNodeMatch, FLMBOOL * pbCanCompareOnKey, FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks FLMUINT * puiTotalRefs, // [out] total references FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. { RCODE rc = NE_XFLM_OK; F_Btree * pUntilBTree = NULL; FLMINT iCompare; m_uiIndexNum = pIxd->uiIndexNum; // Need to eliminate dups if we are working with // a compound index or an index that has one or more // data components along with the key. This is because // the query code only looks at the 0th key component // of keys that are returned. So when we have compound // keys or keys with data components, we could get back // multiple keys from the index with the same 0th // component (but different 1st, 2nd, etc. components). m_bElimDups = (pIxd->uiNumKeyComponents > 1 || pIxd->uiNumDataComponents) ? TRUE : FALSE; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if (RC_BAD( rc = flmBuildFromAndUntilKeys( pIxd, pPred, &m_fromExtKey, m_fromKey.ucKey, &m_fromKey.uiKeyLen, &m_untilExtKey, m_untilKey.ucKey, &m_untilKey.uiKeyLen, pbDoNodeMatch, pbCanCompareOnKey))) { goto Exit; } m_curKey.uiKeyLen = 0; m_bSetup = TRUE; // Want any of the counts back? if (puiLeafBlocksBetween || puiTotalRefs) { // Get a btree object if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree))) { goto Exit; } if (RC_OK( rc = setKeyPosition( pDb, TRUE, FALSE, &m_fromExtKey, &m_fromKey, &m_curKey, TRUE, NULL, NULL, NULL))) { // All keys between FROM and UNTIL may be gone. if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, &m_untilExtKey, NULL, NULL, FALSE, FALSE, m_curKey.ucKey, m_curKey.uiKeyLen, m_untilKey.ucKey, m_untilKey.uiKeyLen, &iCompare))) { goto Exit; } if (iCompare <= 0) { KEYPOS tmpUntilKey; if (RC_OK( rc = pUntilBTree->btOpen( pDb, m_pLFile, isAbsolutePositionable(), FALSE, &m_ixCompare))) { // Going forward so position is exclusive rc = setKeyPosition( pDb, FALSE, FALSE, &m_untilExtKey, &m_untilKey, &tmpUntilKey, FALSE, NULL, pUntilBTree, NULL); } } else { rc = RC_SET( NE_XFLM_BOF_HIT); m_bAtBOF = TRUE; m_bAtEOF = FALSE; } } else { if (rc == NE_XFLM_EOF_HIT) { m_bAtEOF = TRUE; m_bAtBOF = FALSE; } else if (rc == NE_XFLM_BOF_HIT) { m_bAtEOF = FALSE; m_bAtBOF = TRUE; } } if (RC_BAD( rc)) { // Empty tree or empty set case. if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) { if (puiLeafBlocksBetween) { *puiLeafBlocksBetween = 0; } if (puiTotalRefs) { *puiTotalRefs = 0; } if (pbTotalsEstimated) { *pbTotalsEstimated = FALSE; } rc = NE_XFLM_OK; } goto Exit; } else { if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, puiLeafBlocksBetween, puiTotalRefs, pbTotalsEstimated, (pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) { goto Exit; } } } m_bAtBOF = TRUE; Exit: if (pUntilBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree); } return( rc); } /**************************************************************************** Desc: Open the F_Btree object if not already open. ****************************************************************************/ RCODE FSIndexCursor::openBTree( F_Db * pDb ) { RCODE rc = NE_XFLM_OK; if (!m_bTreeOpen) { if ( !m_pbTree) { if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &m_pbTree))) { goto Exit; } } Open_Btree: if (RC_BAD( rc = m_pbTree->btOpen( pDb, m_pLFile, isAbsolutePositionable(), FALSE, &m_ixCompare))) { goto Exit; } m_bTreeOpen = TRUE; m_pDb = pDb; m_eTransType = pDb->m_eTransType; m_ixCompare.setIxInfo( m_pDb, m_pIxd); } else { if (pDb != m_pDb || pDb->m_eTransType != m_eTransType) { closeBTree(); goto Open_Btree; } } Exit: return( rc); } /**************************************************************************** Desc: Get a key's data part. ****************************************************************************/ RCODE FSIndexCursor::getKeyData( F_Btree * pBTree, FLMUINT uiDataLen) { RCODE rc = NE_XFLM_OK; m_uiCurKeyDataLen = 0; // See if there is a data part if (m_pIxd->pFirstData && uiDataLen) { // If the data will fit in the search key buffer, just // reuse it since we are not going to do anything with // it after this. Otherwise, allocate a new buffer. if (uiDataLen > m_uiCurKeyDataBufSize) { FLMBYTE * pucNewBuf; FLMUINT uiNewLen = uiDataLen; if (uiNewLen < 256) { uiNewLen = 256; } if (RC_BAD( rc = f_alloc( uiNewLen, &pucNewBuf))) { goto Exit; } if (m_pucCurKeyDataBuf) { f_free( &m_pucCurKeyDataBuf); } m_pucCurKeyDataBuf = pucNewBuf; m_uiCurKeyDataBufSize = uiNewLen; } // Retrieve the data if (RC_BAD( rc = pBTree->btGetEntry( m_curKey.ucKey, XFLM_MAX_KEY_SIZE, m_curKey.uiKeyLen, m_pucCurKeyDataBuf, m_uiCurKeyDataBufSize, &m_uiCurKeyDataLen))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Set the key position given some KEYPOS structure. Please note that the blocks in the stack may or may not be used. ****************************************************************************/ RCODE FSIndexCursor::setKeyPosition( F_Db * pDb, FLMBOOL bGoingForward, FLMBOOL bExcludeKey, F_DataVector * pExtSrchKey, KEYPOS * pSearchKey, // Search key KEYPOS * pFoundKey, FLMBOOL bGetKeyData, FLMUINT * puiDataLen, F_Btree * pBTree, // BTree to use. NULL means use our // internal one. FLMUINT * puiAbsolutePos) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataLen; FLMINT iCompare = 0; // if pBTree is NULL, we are to use m_pbTree. Otherwise, we // need to open the pBTree and use it. if (!pBTree) { if (RC_BAD( rc = openBTree( pDb))) { goto Exit; } pBTree = m_pbTree; } if (pFoundKey != pSearchKey) { f_memcpy( pFoundKey->ucKey, pSearchKey->ucKey, pSearchKey->uiKeyLen); pFoundKey->uiKeyLen = pSearchKey->uiKeyLen; } m_ixCompare.setSearchKey( pExtSrchKey); m_ixCompare.setCompareDocId( pExtSrchKey ? FALSE : TRUE); m_ixCompare.setCompareNodeIds( pExtSrchKey ? FALSE : TRUE); if (RC_BAD( rc = pBTree->btLocateEntry( pFoundKey->ucKey, XFLM_MAX_KEY_SIZE, &pFoundKey->uiKeyLen, (bGoingForward && bExcludeKey) ? XFLM_EXCL : XFLM_INCL, puiAbsolutePos, &uiDataLen, NULL, NULL))) { if (rc != NE_XFLM_EOF_HIT) { goto Exit; } } if (bGoingForward) { if (rc == NE_XFLM_EOF_HIT) { goto Exit; } } else { // Going backwards or to last. See if we positioned too far. if (rc == NE_XFLM_EOF_HIT) { // Position to last key in tree. if (RC_BAD( rc = pBTree->btLastEntry( pFoundKey->ucKey, XFLM_MAX_KEY_SIZE, &pFoundKey->uiKeyLen, &uiDataLen, NULL, NULL))) { goto Exit; } } else { // We want to go to the previous key if we went past the key // we were aiming for, or if we landed on that key, but we // are doing an exclusive lookup. if (!bExcludeKey) { if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, pExtSrchKey, NULL, NULL, pExtSrchKey ? FALSE : TRUE, pExtSrchKey ? FALSE : TRUE, pFoundKey->ucKey, pFoundKey->uiKeyLen, pSearchKey->ucKey, pSearchKey->uiKeyLen, &iCompare))) { goto Exit; } } if (bExcludeKey || iCompare > 0) { // Position to the previous key. if (RC_BAD( rc = pBTree->btPrevEntry( pFoundKey->ucKey, XFLM_MAX_KEY_SIZE, &pFoundKey->uiKeyLen, &uiDataLen, NULL, NULL))) { goto Exit; } } } } // See if there is any data to get if (bGetKeyData) { flmAssert( pFoundKey == &m_curKey); if (RC_BAD( rc = getKeyData( pBTree, uiDataLen))) { goto Exit; } } if (puiDataLen) { *puiDataLen = uiDataLen; } Exit: if (RC_BAD( rc)) { pFoundKey->uiKeyLen = 0; if (pBTree == m_pbTree) { closeBTree(); } } return( rc); } /**************************************************************************** Desc: Populate a key from the current key. ****************************************************************************/ RCODE FSIndexCursor::populateKey( F_DataVector * pKey ) { RCODE rc = NE_XFLM_OK; pKey->reset(); if (RC_BAD( rc = pKey->inputKey( m_pIxd, m_curKey.ucKey, m_curKey.uiKeyLen))) { goto Exit; } // See if there is a data part if (m_pIxd->pFirstData && m_uiCurKeyDataLen) { // Parse the data if (RC_BAD( rc = pKey->inputData( m_pIxd, m_pucCurKeyDataBuf, m_uiCurKeyDataLen))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Return the current record and record id. ****************************************************************************/ RCODE FSIndexCursor::currentKey( F_Db * pDb, F_DataVector * pKey) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if (m_bAtBOF) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if (m_bAtEOF) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // If we get this far, we are positioned on some key. flmAssert( m_curKey.uiKeyLen); if (RC_BAD( rc = populateKey( pKey))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Make sure the current key is positioned in a key set. If not, move to the next or previous key set until it is. ****************************************************************************/ RCODE FSIndexCursor::checkIfKeyInRange( FLMBOOL bPositionForward) { RCODE rc = NE_XFLM_OK; FLMINT iCompare; if (bPositionForward) { // See if the key is within the bounds of the current key set. // It is already guaranteed to be >= the fromKey, we just need to // make sure it is <= the until key. if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, &m_untilExtKey, NULL, NULL, FALSE, FALSE, m_curKey.ucKey, m_curKey.uiKeyLen, m_untilKey.ucKey, m_untilKey.uiKeyLen, &iCompare))) { goto Exit; } if (iCompare > 0) { m_bAtEOF = TRUE; rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { // See if the key is within the bounds of the current key set. // It is already guaranteed to be <= the untilKey, we just need to // make sure it is >= the from key. if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, &m_fromExtKey, NULL, NULL, FALSE, FALSE, m_curKey.ucKey, m_curKey.uiKeyLen, m_fromKey.ucKey, m_fromKey.uiKeyLen, &iCompare))) { goto Exit; } if (iCompare < 0) { m_bAtBOF = TRUE; rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Position to and return the first key. ****************************************************************************/ RCODE FSIndexCursor::firstKey( F_Db * pDb, F_DataVector * pKey) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); // If at BOF and we have a key, then we are positioned on the first // key already - this would have happened if we had positioned to // calculate a cost. Rather than do the positioning again, we simply // set m_bAtBOF to FALSE. if (m_bAtBOF && m_curKey.uiKeyLen) { m_bAtBOF = FALSE; } else { m_bAtBOF = m_bAtEOF = FALSE; if (RC_BAD( rc = setKeyPosition( pDb, TRUE, FALSE, &m_fromExtKey, &m_fromKey, &m_curKey, TRUE, NULL, NULL, NULL))) { if (rc == NE_XFLM_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } // Make sure the current key is within the FROM/UNTIL range. if (RC_BAD( rc = checkIfKeyInRange( TRUE))) { goto Exit; } // On firstKey, clear out the duplicate elimination result // set, if there is one. if (m_bElimDups) { if (RC_BAD( rc = allocDupCheckSet())) { goto Exit; } } if (RC_BAD( rc = populateKey( pKey))) { goto Exit; } if (m_bElimDups) { FLMUINT64 ui64NodeId = pKey->getID( 0); if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId))) { flmAssert( rc != NE_FLM_EXISTS); goto Exit; } } m_bMovingForward = TRUE; Exit: if (RC_BAD( rc)) { m_curKey.uiKeyLen = 0; } return( rc); } /**************************************************************************** Desc: Position to the next key. ****************************************************************************/ RCODE FSIndexCursor::nextKey( F_Db * pDb, F_DataVector * pKey, FLMBOOL bSkipCurrKey) { RCODE rc = NE_XFLM_OK; KEYPOS saveCurrentKey; FLMUINT uiDataLen; FLMINT iCompare; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); if (m_bAtEOF) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } if (m_bAtBOF || !m_curKey.uiKeyLen) { rc = firstKey( pDb, pKey); goto Exit; } // On change of direction, clear out the duplicate elimination result // set, if there is one. if (m_bElimDups && !m_bMovingForward) { if (RC_BAD( rc = allocDupCheckSet())) { goto Exit; } } // Save the current key, so we can tell when we have gone beyond it. if (bSkipCurrKey) { getCurrKey( &saveCurrentKey); } // See if we need to reset the b-tree object we are using if (m_bTreeOpen && (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) { closeBTree(); } for (;;) { // checkTransaction may have closed the B-tree, in which case we // need to reopen the b-tree and position exclusive of the current key. if (!m_bTreeOpen) { if (RC_BAD( rc = setKeyPosition( pDb, TRUE, TRUE, NULL, &m_curKey, &m_curKey, FALSE, &uiDataLen, NULL, NULL))) { if (rc == NE_XFLM_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } else { // set the compare object's search key to NULL because m_curKey // should always be something we obtained from the index, and // we should not need a search key for doing extended // comparisons on truncated keys. m_ixCompare.setSearchKey( NULL); m_ixCompare.setCompareNodeIds( TRUE); m_ixCompare.setCompareDocId( TRUE); // Get the next key, if any if (RC_BAD( rc = m_pbTree->btNextEntry( m_curKey.ucKey, XFLM_MAX_KEY_SIZE, &m_curKey.uiKeyLen, &uiDataLen, NULL, NULL))) { if (rc == NE_XFLM_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } if (!bSkipCurrKey) { Check_Key: if (RC_BAD( rc = getKeyData( m_pbTree, uiDataLen))) { goto Exit; } // We got to the next key, make sure it is in the key range. if (RC_BAD( rc = checkIfKeyInRange( TRUE))) { goto Exit; } if (RC_BAD( rc = populateKey( pKey))) { goto Exit; } if (m_bElimDups) { FLMBOOL bDup; if (RC_BAD( rc = checkIfDup( pKey->getID( 0), &bDup))) { goto Exit; } if (bDup) { continue; } } break; } // If the bSkipCurrKey flag is TRUE, we want to skip keys until // we come to one where the key part is different. if (RC_BAD( rc = ixKeyCompare( pDb, m_pIxd, NULL, NULL, NULL, FALSE, FALSE, m_curKey.ucKey, m_curKey.uiKeyLen, saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen, &iCompare))) { goto Exit; } // See if the key part is the same. if (iCompare != 0) { goto Check_Key; } } m_bMovingForward = TRUE; Exit: if (RC_BAD( rc)) { m_curKey.uiKeyLen = 0; } return( rc); } /**************************************************************************** Desc: Position to and return the last key in the range. ****************************************************************************/ RCODE FSIndexCursor::lastKey( F_Db * pDb, F_DataVector * pKey) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); // Position to the until key. m_bAtBOF = m_bAtEOF = FALSE; if (RC_BAD( rc = setKeyPosition( pDb, FALSE, FALSE, &m_untilExtKey, &m_untilKey, &m_curKey, TRUE, NULL, NULL, NULL))) { if (rc == NE_XFLM_BOF_HIT) { m_bAtBOF = TRUE; } goto Exit; } // Make sure the current key is within one of the FROM/UNTIL sets. if (RC_BAD( rc = checkIfKeyInRange( FALSE))) { goto Exit; } // On firstKey, clear out the duplicate elimination result // set, if there is one. if (m_bElimDups) { if (RC_BAD( rc = allocDupCheckSet())) { goto Exit; } } if (RC_BAD( rc = populateKey( pKey))) { goto Exit; } if (m_bElimDups) { FLMUINT64 ui64NodeId = pKey->getID( 0); if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId))) { flmAssert( rc != NE_FLM_EXISTS); goto Exit; } } m_bMovingForward = FALSE; Exit: if (RC_BAD( rc)) { m_curKey.uiKeyLen = 0; } return( rc); } /**************************************************************************** Desc: Position to the PREVIOUS key in the range. ****************************************************************************/ RCODE FSIndexCursor::prevKey( F_Db * pDb, F_DataVector * pKey, FLMBOOL bSkipCurrKey) { RCODE rc = NE_XFLM_OK; KEYPOS saveCurrentKey; FLMUINT uiDataLen; FLMINT iCompare; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); if (m_bAtBOF) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if (m_bAtEOF || !m_curKey.uiKeyLen) { rc = lastKey( pDb, pKey); goto Exit; } // On change of direction, clear out the duplicate elimination result // set, if there is one. if (m_bElimDups && m_bMovingForward) { if (RC_BAD( rc = allocDupCheckSet())) { goto Exit; } } // Save the current key, so we can tell when we have gone beyond it. if (bSkipCurrKey) { getCurrKey( &saveCurrentKey); } // See if we need to reset the b-tree object we are using if (m_bTreeOpen && (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) { closeBTree(); } for (;;) { // checkTransaction may have closed the B-tree, in which case we // need to reopen the b-tree and position exclusive of the current key. if (!m_bTreeOpen) { if (RC_BAD( rc = setKeyPosition( pDb, FALSE, TRUE, NULL, &m_curKey, &m_curKey, FALSE, &uiDataLen, NULL, NULL))) { if (rc == NE_XFLM_BOF_HIT) { m_bAtBOF = TRUE; } goto Exit; } } else { // set the compare object's search key to NULL because m_curKey // should always be something we obtained from the index, and // we should not need a search key for doing extended // comparisons on truncated keys. m_ixCompare.setSearchKey( NULL); m_ixCompare.setCompareNodeIds( TRUE); m_ixCompare.setCompareDocId( TRUE); // Get the previous key, if any if (RC_BAD( rc = m_pbTree->btPrevEntry( m_curKey.ucKey, XFLM_MAX_KEY_SIZE, &m_curKey.uiKeyLen, &uiDataLen, NULL, NULL))) { if (rc == NE_XFLM_BOF_HIT) { m_bAtBOF = TRUE; } goto Exit; } } if (!bSkipCurrKey) { Check_Key: if (RC_BAD( rc = getKeyData( m_pbTree, uiDataLen))) { goto Exit; } // We got to the previous key, make sure it is in the key range. if (RC_BAD( rc = checkIfKeyInRange( FALSE))) { goto Exit; } if (RC_BAD( rc = populateKey( pKey))) { goto Exit; } if (m_bElimDups) { FLMBOOL bDup; if (RC_BAD( rc = checkIfDup( pKey->getID( 0), &bDup))) { goto Exit; } if (bDup) { continue; } } break; } // If the bSkipCurrKey flag is TRUE, we want to skip keys until // we come to one where the key part is different. if (RC_BAD( rc = ixKeyCompare( pDb, m_pIxd, NULL, NULL, NULL, FALSE, FALSE, m_curKey.ucKey, m_curKey.uiKeyLen, saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen, &iCompare))) { goto Exit; } // See if the key part is the same. if (iCompare != 0) { goto Check_Key; } } m_bMovingForward = FALSE; Exit: if (RC_BAD( rc)) { m_curKey.uiKeyLen = 0; } return( rc); } libxflaim-5.1.969/src/fvector.cpp0000644000175000017500000011634410511001742020211 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the code for the F_DataVector class. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fvector.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #if defined( FLM_WATCOM_NLM) // Disable "Warning! W549: col(XX) 'sizeof' operand contains // compiler generated information" #pragma warning 549 9 #endif /**************************************************************************** Desc: ****************************************************************************/ F_DataVector::F_DataVector() { m_pVectorElements = &m_VectorArray [0]; m_uiVectorArraySize = MIN_VECTOR_ELEMENTS; m_pucDataBuf = m_ucIntDataBuf; m_uiDataBufLength = sizeof( m_ucIntDataBuf); reset(); } /**************************************************************************** Desc: ****************************************************************************/ F_DataVector::~F_DataVector() { if (m_pVectorElements != &m_VectorArray [0]) { f_free( &m_pVectorElements); } if (m_pucDataBuf && m_pucDataBuf != m_ucIntDataBuf) { f_free( &m_pucDataBuf); } reset(); } /**************************************************************************** Desc: Clear the data vector, but don't free any buffers that have been allocated. That will only happen in the destructor. The reset() method is so that we can get efficient re-use of the vector. So, if it has allocated buffers, etc. we don't want to free them. ****************************************************************************/ void FLMAPI F_DataVector::reset( void) { m_ui64DocumentID = 0; m_uiNumElements = 0; m_uiDataBufOffset = 0; } /**************************************************************************** Desc: Make sure the vector array is allocated at least up to the element number that is passed in. ****************************************************************************/ RCODE F_DataVector::allocVectorArray( FLMUINT uiElementNumber) { RCODE rc = NE_XFLM_OK; if (uiElementNumber >= m_uiNumElements) { // May need to allocate a new vector array if (uiElementNumber >= m_uiVectorArraySize) { FLMUINT uiNewArraySize = uiElementNumber + 32; F_VECTOR_ELEMENT * pNewVector; if (m_pVectorElements == &m_VectorArray [0]) { if (RC_BAD( rc = f_alloc( uiNewArraySize * sizeof( F_VECTOR_ELEMENT), &pNewVector))) { goto Exit; } if (m_uiNumElements) { f_memcpy( pNewVector, m_pVectorElements, m_uiNumElements * sizeof( F_VECTOR_ELEMENT)); } } else { pNewVector = m_pVectorElements; if (RC_BAD( rc = f_realloc( uiNewArraySize * sizeof( F_VECTOR_ELEMENT), &pNewVector))) { goto Exit; } } m_pVectorElements = pNewVector; m_uiVectorArraySize = uiNewArraySize; } // Initialized everything between the old last element and // the new element, including the new element, to zeroes. f_memset( &m_pVectorElements [m_uiNumElements], 0, sizeof( F_VECTOR_ELEMENT) * (uiElementNumber - m_uiNumElements + 1)); m_uiNumElements = uiElementNumber + 1; } Exit: return( rc); } /**************************************************************************** Desc: Store a data value into its vector element. ****************************************************************************/ RCODE F_DataVector::storeValue( FLMINT uiElementNumber, FLMUINT uiDataType, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBYTE ** ppucDataPtr) { RCODE rc = NE_XFLM_OK; F_VECTOR_ELEMENT * pVector; FLMBYTE * pucDataPtr; FLMUINT uiTemp; // Find or allocate space for the vector if (RC_BAD( rc = allocVectorArray( uiElementNumber))) { goto Exit; } pVector = &m_pVectorElements [uiElementNumber]; // Will the data fit inside uiDataOffset? if (uiDataLen <= sizeof( FLMUINT)) { pucDataPtr = (FLMBYTE *)&pVector->uiDataOffset; } else if (uiDataLen <= pVector->uiDataLength) { // New data will fit in original space. Simply reuse it. pucDataPtr = m_pucDataBuf + pVector->uiDataOffset; } else { // New data will not fit in originally allocated space. // Must allocate new space. // Always align the new allocation so that if it gets // reused later for binary data it will be properly aligned. if ((m_uiDataBufOffset & FLM_ALLOC_ALIGN) != 0) { uiTemp = (FLM_ALLOC_ALIGN + 1) - (m_uiDataBufOffset & FLM_ALLOC_ALIGN); m_uiDataBufOffset += uiTemp; } if (uiDataLen + m_uiDataBufOffset > m_uiDataBufLength) { // Re-allocate the data buffer. if( m_pucDataBuf == m_ucIntDataBuf) { if (RC_BAD( rc = f_alloc( m_uiDataBufOffset + uiDataLen + 512, &m_pucDataBuf))) { goto Exit; } f_memcpy( m_pucDataBuf, m_ucIntDataBuf, m_uiDataBufOffset); } else { if (RC_BAD( rc = f_realloc( m_uiDataBufOffset + uiDataLen + 512, &m_pucDataBuf))) { goto Exit; } } m_uiDataBufLength = m_uiDataBufOffset + uiDataLen + 512; } pucDataPtr = m_pucDataBuf + m_uiDataBufOffset; pVector->uiDataOffset = m_uiDataBufOffset; m_uiDataBufOffset += uiDataLen; } // Store the data - may be zero length. if( pucData) { if( uiDataLen > 1) { f_memcpy( pucDataPtr, pucData, uiDataLen); } else if( uiDataLen) { *pucDataPtr = *pucData; } } pVector->uiFlags |= VECT_SLOT_HAS_DATA; pVector->uiDataLength = uiDataLen; pVector->uiDataType = uiDataType; if( ppucDataPtr) { *ppucDataPtr = pucDataPtr; } Exit: return( rc); } /**************************************************************************** Desc: Set the id for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setID( FLMUINT uiElementNumber, FLMUINT64 ui64ID) { RCODE rc = NE_XFLM_OK; F_VECTOR_ELEMENT * pVector; // Find or allocate space for the vector element if (RC_BAD( rc = allocVectorArray( uiElementNumber))) { goto Exit; } pVector = &m_pVectorElements [uiElementNumber]; pVector->uiFlags |= VECT_SLOT_HAS_ID; pVector->ui64ID = ui64ID; Exit: return( rc); } /**************************************************************************** Desc: Set the name id for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setNameId( FLMUINT uiElementNumber, FLMUINT uiNameId, FLMBOOL bIsAttr, FLMBOOL bIsData) { RCODE rc = NE_XFLM_OK; F_VECTOR_ELEMENT * pVector; // Find or allocate space for the vector element if (RC_BAD( rc = allocVectorArray( uiElementNumber))) { goto Exit; } pVector = &m_pVectorElements [uiElementNumber]; pVector->uiFlags |= VECT_SLOT_HAS_NAME_ID; if (bIsAttr) { pVector->uiFlags |= VECT_SLOT_IS_ATTR; } else { pVector->uiFlags &= (~(VECT_SLOT_IS_ATTR)); } if (bIsData) { pVector->uiFlags |= VECT_SLOT_IS_DATA; } else { pVector->uiFlags &= (~(VECT_SLOT_IS_DATA)); } pVector->uiNameId = uiNameId; Exit: return( rc); } /**************************************************************************** Desc: Set a FLMINT value for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setINT( FLMUINT uiElementNumber, FLMINT iNum) { RCODE rc = NE_XFLM_OK; FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiStorageLen; FLMBOOL bNeg = FALSE; if (iNum < 0) { bNeg = TRUE; iNum = -iNum; } uiStorageLen = sizeof( ucStorageBuf); if( ((FLMUINT)iNum) <= gv_uiMaxUInt32Val) { if( RC_BAD( rc = flmNumber64ToStorage( (FLMUINT64)iNum, &uiStorageLen, ucStorageBuf, bNeg, FALSE))) { goto Exit; } } else { if( RC_BAD( rc = flmNumber64ToStorage( (FLMUINT64)iNum, &uiStorageLen, ucStorageBuf, bNeg, FALSE))) { goto Exit; } } if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Set a FLMINT64 value for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setINT64( FLMUINT uiElementNumber, FLMINT64 i64Num) { RCODE rc = NE_XFLM_OK; FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiStorageLen; FLMBOOL bNeg = FALSE; if (i64Num < 0) { bNeg = TRUE; i64Num = -i64Num; } uiStorageLen = sizeof( ucStorageBuf); if (RC_BAD( rc = flmNumber64ToStorage( i64Num, &uiStorageLen, ucStorageBuf, bNeg, FALSE))) { goto Exit; } if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Set a FLMUINT value for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setUINT( FLMUINT uiElementNumber, FLMUINT uiNum) { RCODE rc = NE_XFLM_OK; FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiStorageLen; uiStorageLen = sizeof( ucStorageBuf); if (uiNum <= gv_uiMaxUInt32Val) { if (RC_BAD( rc = flmNumber64ToStorage( uiNum, &uiStorageLen, ucStorageBuf, FALSE, FALSE))) { goto Exit; } } else { if (RC_BAD( rc = flmNumber64ToStorage( uiNum, &uiStorageLen, ucStorageBuf, FALSE, FALSE))) { goto Exit; } } if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Set a FLMUINT64 value for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setUINT64( FLMUINT uiElementNumber, FLMUINT64 ui64Num) { RCODE rc = NE_XFLM_OK; FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiStorageLen; uiStorageLen = sizeof( ucStorageBuf); if (RC_BAD( rc = flmNumber64ToStorage( ui64Num, &uiStorageLen, ucStorageBuf, FALSE, FALSE))) { goto Exit; } if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Set a FLMUNICODE value for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setUnicode( FLMUINT uiElementNumber, const FLMUNICODE * puzUnicode) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucDataPtr; FLMUINT uiLen; FLMUINT uiCharCount; FLMBYTE ucTmpBuf [64]; // A NULL or empty puzUnicode string is allowed - on those cases // just set the data type. if (puzUnicode == NULL || *puzUnicode == 0) { rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, NULL, 0); goto Exit; } // See if it will fit in our temporary buffer on the stack. uiLen = sizeof( ucTmpBuf); if (RC_OK( rc = flmUnicode2Storage( puzUnicode, 0, ucTmpBuf, &uiLen, &uiCharCount))) { if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, ucTmpBuf, uiLen))) { goto Exit; } } else if (rc != NE_XFLM_CONV_DEST_OVERFLOW) { goto Exit; } else { // Determine the length needed. if (RC_BAD( rc = flmUnicode2Storage( puzUnicode, 0, NULL, &uiLen, &uiCharCount))) { goto Exit; } // Allocate space for it in the vector and get a pointer // back so we can then store it. if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, NULL, uiLen, &pucDataPtr))) { goto Exit; } // Store it out to the space we just allocated. if (RC_BAD( rc = flmUnicode2Storage( puzUnicode, uiCharCount, pucDataPtr, &uiLen, NULL))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Set a UTF8 value for a vector element. ****************************************************************************/ RCODE FLMAPI F_DataVector::setUTF8( FLMUINT uiElementNumber, const FLMBYTE * pszUTF8, FLMUINT uiBytesInBuffer) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucDataPtr; FLMUINT uiLen; FLMBYTE ucTmpBuf [64]; // A NULL or empty pszNative string is allowed - on those cases // just set the data type. if (pszUTF8 == NULL || *pszUTF8 == 0) { rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, NULL, 0); goto Exit; } // See if it will fit in our temporary buffer on the stack. uiLen = sizeof( ucTmpBuf); if (RC_OK( rc = flmUTF8ToStorage( pszUTF8, uiBytesInBuffer, ucTmpBuf, &uiLen))) { if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, ucTmpBuf, uiLen))) { goto Exit; } } else if (rc != NE_XFLM_CONV_DEST_OVERFLOW) { goto Exit; } else { // Determine the length needed. if (RC_BAD( rc = flmUTF8ToStorage( pszUTF8, uiBytesInBuffer, NULL, &uiLen))) { goto Exit; } // Allocate space for it in the vector and get a pointer // back so we can then store it. if (RC_BAD( rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, NULL, uiLen, &pucDataPtr))) { goto Exit; } // Store it out to the space we just allocated. if (RC_BAD( rc = flmUTF8ToStorage( pszUTF8, uiBytesInBuffer, pucDataPtr, &uiLen))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Get a pointer to the UTF8 - no conversions are done. ****************************************************************************/ RCODE FLMAPI F_DataVector::getUTF8Ptr( FLMUINT uiElementNumber, const FLMBYTE ** ppszUTF8, FLMUINT * puiBufLen) { RCODE rc = NE_XFLM_OK; F_VECTOR_ELEMENT * pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA); void * pvValue; FLMUINT uiStorageLen; FLMUINT uiSenLen; if (!pVector) { *ppszUTF8 = NULL; if (puiBufLen) { *puiBufLen = 0; } goto Exit; } if (pVector->uiDataType != XFLM_TEXT_TYPE) { rc = RC_SET( NE_XFLM_BAD_DATA_TYPE); goto Exit; } if ((pvValue = getDataPtr( pVector)) != NULL) { *ppszUTF8 = (FLMBYTE *)pvValue; uiStorageLen = pVector->uiDataLength; if( RC_BAD( rc = flmGetCharCountFromStorageBuf( ppszUTF8, uiStorageLen, NULL, &uiSenLen))) { goto Exit; } flmAssert( uiStorageLen > uiSenLen); uiStorageLen -= uiSenLen; } else { *ppszUTF8 = NULL; uiStorageLen = 0; } if (puiBufLen) { *puiBufLen = uiStorageLen; } Exit: return( rc); } /**************************************************************************** Desc: Allocate data for a unicode element and retrieve it. ****************************************************************************/ RCODE FLMAPI F_DataVector::getUnicode( FLMUINT uiElementNumber, FLMUNICODE ** ppuzUnicode) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen; // Get the unicode length (does not include NULL terminator) if (RC_BAD( rc = getUnicode( uiElementNumber, NULL, &uiLen))) { goto Exit; } if (uiLen) { // Account for NULL character. uiLen += sizeof( FLMUNICODE); if( RC_BAD( rc = f_alloc( uiLen, ppuzUnicode))) { goto Exit; } if (RC_BAD( rc = getUnicode( uiElementNumber, *ppuzUnicode, &uiLen))) { goto Exit; } } else { *ppuzUnicode = NULL; } Exit: return( rc); } /**************************************************************************** Desc: Compose a key buffer from the vector's components. ****************************************************************************/ RCODE FLMAPI F_DataVector::outputKey( IF_Db * ifpDb, FLMUINT uiIndexNum, FLMUINT, // uiMatchFlags, //VISIT: Need to remove this from the interface. FLMBYTE * pucKeyBuf, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen) { RCODE rc = NE_XFLM_OK; IXD * pIxd; if (RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if (RC_BAD( rc = outputKey( pIxd, XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID, pucKeyBuf, uiKeyBufSize, puiKeyLen, 0))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Compose a key buffer from the vector's components. ****************************************************************************/ RCODE F_DataVector::outputKey( IXD * pIxd, FLMUINT uiMatchFlags, FLMBYTE * pucKeyBuf, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT uiSearchKeyFlag) { RCODE rc = NE_XFLM_OK; ICD * pIcd; FLMBYTE * pucToKey; FLMBYTE * pucKeyLenPos; FLMUINT uiToKeyLen; FLMUINT uiKeyLen; FLMUINT uiKeyComponent; FLMUINT uiDataComponent; FLMUINT uiContextComponent; FLMBOOL bDataTruncated; FLMUINT uiDataType; FLMUINT uiLanguage; F_VECTOR_ELEMENT * pVector = NULL; FLMBYTE ucIDBuf [256]; FLMUINT uiIDLen = 0; FLMUINT64 ui64Id; FLMUINT uiMaxKeySize; FLMUINT uiDataLen; const FLMBYTE * pucDataPtr; FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE]; FLMBYTE * pucTmpSen; FLMUINT uiSenLen; FLMUINT uiIDMatchFlags = uiMatchFlags & (XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID); IF_BufferIStream * pBufferStream = NULL; if (uiIDMatchFlags) { pucToKey = &ucIDBuf [0]; uiIDLen = 0; // Put document ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (sizeof( ucIDBuf) - uiIDLen >= 9) { uiIDLen += f_encodeSEN( m_ui64DocumentID, &pucToKey); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( m_ui64DocumentID, &pucTmpSen); if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucToKey, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucToKey += uiSenLen; } if (uiIDMatchFlags & XFLM_MATCH_IDS) { // Append the Key component NODE IDs to the key for (uiKeyComponent = 0; uiKeyComponent < pIxd->uiNumKeyComponents; uiKeyComponent++) { ui64Id = getID( uiKeyComponent); // Put node ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (sizeof( ucIDBuf) - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64Id, &pucToKey); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen); if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucToKey, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucToKey += uiSenLen; } } // Append the Data NODE IDs to the key for (uiDataComponent = 0; uiDataComponent < pIxd->uiNumDataComponents; uiDataComponent++) { ui64Id = getID( uiDataComponent + pIxd->uiNumKeyComponents); // Put node ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (sizeof( ucIDBuf) - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64Id, &pucToKey); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen); if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucToKey, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucToKey += uiSenLen; } } // Append the Context NODE IDs to the key for (uiContextComponent = 0; uiContextComponent < pIxd->uiNumContextComponents; uiContextComponent++) { ui64Id = getID( uiContextComponent + pIxd->uiNumKeyComponents + pIxd->uiNumDataComponents); // Put node ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (sizeof( ucIDBuf) - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64Id, &pucToKey); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen); if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucToKey, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucToKey += uiSenLen; } } } if (uiIDLen >= uiKeyBufSize) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } } // Output the key components uiMaxKeySize = uiKeyBufSize - uiIDLen; uiLanguage = pIxd->uiLanguage; uiKeyLen = 0; pucToKey = pucKeyBuf; pIcd = pIxd->pFirstKey; for (uiKeyComponent = 0;;pIcd = pIcd->pNextKeyComponent, uiKeyComponent++) { pucKeyLenPos = pucToKey; pucToKey += 2; uiKeyLen += 2; uiDataType = icdGetDataType( pIcd); // Find matching node in the tree - if not found skip and continue. if ((pVector = getVector( uiKeyComponent, VECT_SLOT_HAS_DATA)) == NULL) { UW2FBA( 0, pucKeyLenPos); } else { uiToKeyLen = 0; bDataTruncated = FALSE; // Take the dictionary number and make it the key if (pIcd->uiFlags & ICD_PRESENCE) { FLMUINT uiNum; // Component better be a number - and better match the // tag number of the ICD if (RC_BAD( rc = getUINT( uiKeyComponent, &uiNum))) { goto Exit; } flmAssert( uiNum == pIcd->uiDictNum || pIcd->uiDictNum == ELM_ROOT_TAG); // Output the tag number if (uiKeyLen + 4 > uiMaxKeySize) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_UINT32ToBigEndian( (FLMUINT32)uiNum, pucToKey); uiToKeyLen = 4; } else if (pIcd->uiFlags & ICD_METAPHONE) { FLMUINT uiMeta; FLMBYTE ucStorageBuf[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiStorageLen; if (uiDataType != XFLM_TEXT_TYPE) { rc = RC_SET_AND_ASSERT( NE_XFLM_SYNTAX); goto Exit; } if (pVector->uiDataType == XFLM_TEXT_TYPE) { if (RC_BAD( rc = getUTF8Ptr( uiKeyComponent, &pucDataPtr, &uiDataLen))) { goto Exit; } if( !pBufferStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferStream))) { goto Exit; } } if (RC_BAD( rc = pBufferStream->openStream( (const char *)pucDataPtr, uiDataLen))) { goto Exit; } if (RC_BAD( rc = f_getNextMetaphone( pBufferStream, &uiMeta))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_SYNTAX); } goto Exit; } pBufferStream->closeStream(); } else if (pVector->uiDataType == XFLM_NUMBER_TYPE) { if( RC_BAD( rc = getUINT( uiKeyComponent, &uiMeta))) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_SYNTAX); goto Exit; } if ( uiMeta) { uiStorageLen = FLM_MAX_NUM_BUF_SIZE; if( RC_BAD( rc = flmNumber64ToStorage( uiMeta, &uiStorageLen, ucStorageBuf, FALSE, FALSE))) { goto Exit; } if( !pBufferStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferStream))) { goto Exit; } } if (RC_BAD( rc = pBufferStream->openStream( (const char *)ucStorageBuf, uiStorageLen))) { goto Exit; } // Output the metaphone key piece uiToKeyLen = uiMaxKeySize - uiKeyLen; if( RC_BAD( rc = KYCollateValue( pucToKey, &uiToKeyLen, pBufferStream, XFLM_NUMBER_TYPE, pIcd->uiFlags, pIcd->uiCompareRules, pIcd->uiLimit, NULL, NULL, uiLanguage, FALSE, FALSE, &bDataTruncated, NULL))) { goto Exit; } pBufferStream->closeStream(); } } else { if (uiDataType == XFLM_TEXT_TYPE) { if (RC_BAD( rc = getUTF8Ptr( uiKeyComponent, &pucDataPtr, &uiDataLen))) { goto Exit; } } else { pucDataPtr = (FLMBYTE *)getDataPtr( pVector); uiDataLen = pVector->uiDataLength; } if (uiDataLen) { if( !pBufferStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferStream))) { goto Exit; } } if (RC_BAD( rc = pBufferStream->openStream( (const char *)pucDataPtr, uiDataLen))) { goto Exit; } uiToKeyLen = uiMaxKeySize - uiKeyLen; if( RC_BAD( rc = KYCollateValue( pucToKey, &uiToKeyLen, pBufferStream, uiDataType, pIcd->uiFlags, pIcd->uiCompareRules, pIcd->uiLimit, NULL, NULL, uiLanguage, (FLMBOOL) ((pIcd->uiFlags & ICD_SUBSTRING) ? (isLeftTruncated( pVector) ? FALSE : TRUE) : FALSE), isRightTruncated( pVector), &bDataTruncated, NULL))) { goto Exit; } pBufferStream->closeStream(); } } if (uiToKeyLen) { // Increment total key length pucToKey += uiToKeyLen; uiKeyLen += uiToKeyLen; } if (!bDataTruncated) { UW2FBA( (FLMUINT16)(uiToKeyLen | uiSearchKeyFlag), pucKeyLenPos); } else { UW2FBA( (FLMUINT16)(uiToKeyLen | TRUNCATED_FLAG | uiSearchKeyFlag), pucKeyLenPos); } } // Check if done. if (!pIcd->pNextKeyComponent) { break; } } // Output the node IDs, if requested. if (uiIDMatchFlags) { // There will always be room at this point for the // IDs - because it was subtracted out above. f_memcpy( pucToKey, ucIDBuf, uiIDLen); } *puiKeyLen = uiKeyLen + uiIDLen; Exit: if( pBufferStream) { pBufferStream->Release(); pBufferStream = NULL; } return( rc); } /**************************************************************************** Desc: Populate a vector's components from the key part of an index key. ****************************************************************************/ RCODE FLMAPI F_DataVector::inputKey( IF_Db * ifpDb, FLMUINT uiIndexNum, const FLMBYTE * pucKey, FLMUINT uiKeyLen) { RCODE rc = NE_XFLM_OK; IXD * pIxd; if (RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if (RC_BAD( rc = inputKey( pIxd, pucKey, uiKeyLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Populate a vector's components from the key part of an index key. ****************************************************************************/ RCODE F_DataVector::inputKey( IXD * pIxd, const FLMBYTE * pucKey, FLMUINT uiKeyLen) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucKeyEnd = pucKey + uiKeyLen; FLMBYTE ucDataBuf [XFLM_MAX_KEY_SIZE]; FLMUINT uiDataLen; ICD * pIcd; FLMUINT uiLanguage = pIxd->uiLanguage; FLMUINT uiComponentLen; FLMUINT uiDataType; FLMBOOL bDataRightTruncated; FLMBOOL bFirstSubstring; FLMBOOL bIsText; FLMUINT uiComponent; FLMUINT64 ui64Id; FLMUINT uiDictNumber; flmAssert( uiKeyLen); // Loop for each compound piece of key uiComponent = 0; pIcd = pIxd->pFirstKey; while (pIcd) { if (uiKeyLen < 2) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } uiComponentLen = getKeyComponentLength( pucKey); bDataRightTruncated = isKeyComponentTruncated( pucKey); uiKeyLen -= 2; pucKey += 2; if (uiComponentLen > uiKeyLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } bFirstSubstring = FALSE; bIsText = (icdGetDataType( pIcd) == XFLM_TEXT_TYPE && !(pIcd->uiFlags & (ICD_PRESENCE | ICD_METAPHONE))) ? TRUE : FALSE; uiDataType = icdGetDataType( pIcd); uiDictNumber = pIcd->uiDictNum; if (uiComponentLen) { if (pIcd->uiFlags & ICD_PRESENCE) { FLMUINT uiNum; if (uiComponentLen != 4 || bDataRightTruncated) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } uiNum = (FLMUINT)f_bigEndianToUINT32( pucKey); // What is stored in the key better match the dictionary // number of the ICD. if (pIcd->uiDictNum != ELM_ROOT_TAG) { if (uiNum != uiDictNumber) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } else { uiDictNumber = uiNum; } if (RC_BAD( rc = setUINT( uiComponent, uiNum))) { goto Exit; } } else if (pIcd->uiFlags & ICD_METAPHONE) { uiDataLen = sizeof( ucDataBuf); if ( uiComponentLen) { if( RC_BAD( rc = flmCollationNum2StorageNum( pucKey, uiComponentLen, ucDataBuf, &uiDataLen))) { goto Exit; } } else { uiDataLen = 0; } if (bDataRightTruncated) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Allocate and copy value into the component. NOTE: // storeValue handles zero length data. if (RC_BAD( rc = storeValue( uiComponent, XFLM_NUMBER_TYPE, ucDataBuf, uiDataLen))) { goto Exit; } } else { // Grab only the Nth section of key if compound key switch (uiDataType) { case XFLM_TEXT_TYPE: { FLMBOOL bTmpTruncated = FALSE; if (uiComponentLen) { uiDataLen = sizeof( ucDataBuf); if (RC_BAD( rc = flmColText2StorageText( pucKey, uiComponentLen, ucDataBuf, &uiDataLen, uiLanguage, &bTmpTruncated, &bFirstSubstring))) { goto Exit; } if (bTmpTruncated != bDataRightTruncated) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } else { uiDataLen = 0; } break; } case XFLM_NUMBER_TYPE: { if (uiComponentLen) { uiDataLen = sizeof( ucDataBuf); if( RC_BAD( rc = flmCollationNum2StorageNum( pucKey, uiComponentLen, ucDataBuf, &uiDataLen))) { goto Exit; } } else { uiDataLen = 0; } if (bDataRightTruncated) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } break; } case XFLM_BINARY_TYPE: { uiDataLen = uiComponentLen; if (uiComponentLen > sizeof( ucDataBuf)) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if (uiComponentLen) { f_memcpy( ucDataBuf, pucKey, uiComponentLen); } break; } default: rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Allocate and copy value into the component. NOTE: // storeValue handles zero length data. if (RC_BAD( rc = storeValue( uiComponent, uiDataType, ucDataBuf, uiDataLen))) { goto Exit; } // Set first sub-string and truncated flags. if ((pIcd->uiFlags & ICD_SUBSTRING) && !bFirstSubstring) { setLeftTruncated( uiComponent); } if (bDataRightTruncated) { setRightTruncated( uiComponent); } } } else { if ((pIcd->uiFlags & ICD_PRESENCE) && uiDictNumber == ELM_ROOT_TAG) { uiDictNumber = 0; } } // Store the name ID if (RC_BAD( rc = setNameId( uiComponent, uiDictNumber, (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? TRUE : FALSE), FALSE))) { goto Exit; } // Position to the end of this component flmAssert( uiKeyLen >= uiComponentLen); pucKey += uiComponentLen; uiKeyLen -= uiComponentLen; uiComponent++; pIcd = pIcd->pNextKeyComponent; } // See if we have a document ID. if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, &m_ui64DocumentID))) { goto Exit; } // Get the node IDs for the key components, if any pIcd = pIxd->pFirstKey; uiComponent = 0; while (pIcd) { // Extract the component node ID if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, &ui64Id))) { goto Exit; } // No need to store if it is zero if (ui64Id) { if (RC_BAD( rc = setID( uiComponent, ui64Id))) { goto Exit; } } uiComponent++; pIcd = pIcd->pNextKeyComponent; } // Get the node IDs for the data components, if any pIcd = pIxd->pFirstData; while (pIcd) { // Extract the component node ID if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, &ui64Id))) { goto Exit; } // No need to store if it is zero if (ui64Id) { if (RC_BAD( rc = setID( uiComponent, ui64Id))) { goto Exit; } } // Store the name ID if (RC_BAD( rc = setNameId( uiComponent, pIcd->uiDictNum, (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? TRUE : FALSE), TRUE))) { goto Exit; } uiComponent++; pIcd = pIcd->pNextDataComponent; } // Get the node IDs for the context components, if any pIcd = pIxd->pFirstContext; while (pIcd) { // Extract the component node ID if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, &ui64Id))) { goto Exit; } // No need to store if it is zero if (ui64Id) { if (RC_BAD( rc = setID( uiComponent, ui64Id))) { goto Exit; } } // Store the name ID if (RC_BAD( rc = setNameId( uiComponent, pIcd->uiDictNum, (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? TRUE : FALSE), TRUE))) { goto Exit; } uiComponent++; pIcd = pIcd->pNextKeyComponent; } Exit: return( rc); } /**************************************************************************** Desc: Compose a data buffer from the vector's components. ****************************************************************************/ RCODE FLMAPI F_DataVector::outputData( IF_Db * ifpDb, FLMUINT uiIndexNum, FLMBYTE * pucDataBuf, FLMUINT uiDataBufSize, FLMUINT * puiDataLen) { RCODE rc = NE_XFLM_OK; IXD * pIxd; if (RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if (RC_BAD( rc = outputData( pIxd, pucDataBuf, uiDataBufSize, puiDataLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Compose a data buffer from the vector's components. ****************************************************************************/ RCODE F_DataVector::outputData( IXD * pIxd, FLMBYTE * pucDataBuf, FLMUINT uiDataBufSize, FLMUINT * puiDataLen) { RCODE rc = NE_XFLM_OK; ICD * pIcd = pIxd->pFirstData; FLMUINT uiDataComponent = 0; F_VECTOR_ELEMENT * pVector; FLMBYTE * pucData; FLMUINT uiDataLength; FLMUINT uiTotalLength = 0; FLMBYTE ucTmpSen [32]; FLMBYTE * pucTmpSen = &ucTmpSen [0]; FLMUINT uiSENLen; FLMUINT uiLastDataLen = 0; while (pIcd) { if ((pVector = getVector( uiDataComponent + pIxd->uiNumKeyComponents, VECT_SLOT_HAS_DATA)) != NULL) { // Cannot do data conversions right now. flmAssert( pVector->uiDataType == icdGetDataType( pIcd)); uiDataLength = pVector->uiDataLength; pucData = (FLMBYTE *)getDataPtr( pVector); } else { uiDataLength = 0; pucData = NULL; } // Output the length of the data as a SEN value uiSENLen = f_encodeSEN( uiDataLength, &pucTmpSen); if (uiTotalLength + uiSENLen > uiDataBufSize) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucDataBuf, ucTmpSen, uiSENLen); pucDataBuf += uiSENLen; uiTotalLength += uiSENLen; // Output the data if (uiDataLength) { if (uiTotalLength + uiDataLength > uiDataBufSize) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucDataBuf, pucData, uiDataLength); pucDataBuf += uiDataLength; uiTotalLength += uiDataLength; uiLastDataLen = uiTotalLength; } pIcd = pIcd->pNextDataComponent; uiDataComponent++; } Exit: // Even if rc == NE_XFLM_CONV_DEST_OVERFLOW, return a length *puiDataLen = uiLastDataLen; return( rc); } /**************************************************************************** Desc: Populate a vector's data components from the data part of a key. ****************************************************************************/ RCODE FLMAPI F_DataVector::inputData( IF_Db * ifpDb, FLMUINT uiIndexNum, const FLMBYTE * pucData, FLMUINT uiInputLen) { RCODE rc = NE_XFLM_OK; IXD * pIxd; if( RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if (RC_BAD( rc = inputData( pIxd, pucData, uiInputLen))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Populate a vector's data components from the data part of a key. ****************************************************************************/ RCODE F_DataVector::inputData( IXD * pIxd, const FLMBYTE * pucData, FLMUINT uiInputLen) { RCODE rc = NE_XFLM_OK; ICD * pIcd = pIxd->pFirstData; FLMUINT uiDataComponent = 0; FLMUINT uiDataLength; FLMUINT uiSENLen; while (pIcd) { if (!uiInputLen) { break; } // Get the data length - it is stored as a SEN uiSENLen = f_getSENLength( *pucData); if (uiSENLen > uiInputLen) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucData, &pucData[ uiSENLen], &uiDataLength))) { goto Exit; } uiInputLen -= uiSENLen; if (uiDataLength > uiInputLen) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Store the name ID if (RC_BAD( rc = setNameId( uiDataComponent + pIxd->uiNumKeyComponents, pIcd->uiDictNum, (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? TRUE : FALSE), TRUE))) { goto Exit; } // Store the data into the vector. if (RC_BAD( rc = storeValue( uiDataComponent + pIxd->uiNumKeyComponents, icdGetDataType( pIcd), pucData, uiDataLength, NULL))) { goto Exit; } pucData += uiDataLength; uiInputLen -= uiDataLength; pIcd = pIcd->pNextDataComponent; uiDataComponent++; } // Output the remaining name IDs, even if the data is missing. while (pIcd) { // Store the name ID if (RC_BAD( rc = setNameId( uiDataComponent + pIxd->uiNumKeyComponents, pIcd->uiDictNum, (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) ? TRUE : FALSE), TRUE))) { goto Exit; } pIcd = pIcd->pNextDataComponent; uiDataComponent++; } Exit: return( rc); } /**************************************************************************** Desc: Create and empty data vector and return it's interface... ****************************************************************************/ RCODE FLMAPI F_DbSystem::createIFDataVector( IF_DataVector ** ifppDV) { RCODE rc = NE_XFLM_OK; IF_DataVector * ifpDV; if( (ifpDV = f_new F_DataVector) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } *ifppDV = ifpDV; Exit: return( rc); } libxflaim-5.1.969/src/flmstat.cpp0000644000175000017500000010556610511001742020217 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains routines for updating FLAIM statistics. // // Tabs: 3 // // Copyright (c) 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flmstat.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE flmStatGetDbByName( XFLM_STATS * pFlmStats, char * pszDbName, FLMUINT uiLowStart, XFLM_DB_STATS ** ppDbStatsRV, FLMUINT * puiDBAllocSeqRV, FLMUINT * puiDbTblPosRV); FSTATIC void flmUpdateLFileStats( XFLM_LFILE_STATS * pDest, XFLM_LFILE_STATS * pSrc); FSTATIC RCODE flmUpdateDbStats( XFLM_DB_STATS * pDest, XFLM_DB_STATS * pSrc); FSTATIC FLMUINT flmDaysInMonth( FLMUINT uiYear, FLMUINT uiMonth); FSTATIC void flmAdjustTime( F_TMSTAMP * pTime, FLMINT iStartPoint); /**************************************************************************** Desc: This routine returns a pointer to a particular database's statistics block. ****************************************************************************/ FSTATIC RCODE flmStatGetDbByName( XFLM_STATS * pFlmStats, char * pszDbName, FLMUINT uiLowStart, XFLM_DB_STATS ** ppDbStatsRV, FLMUINT * puiDBAllocSeqRV, FLMUINT * puiDbTblPosRV) { RCODE rc = NE_XFLM_OK; FLMUINT uiTblSize; XFLM_DB_STATS * pDbStatTbl; FLMUINT uiLow; FLMUINT uiMid = 0; FLMUINT uiHigh; FLMINT iCmp = 0; FLMUINT uiNewSize; FLMUINT uiElement; char * pszTmpDbName = NULL; // If there is a database array, search it first. if (((pDbStatTbl = pFlmStats->pDbStats) != NULL) && ((uiTblSize = pFlmStats->uiNumDbStats) != 0)) { for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize { uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 // See if we found a match. #ifdef FLM_UNIX if ((iCmp = f_strcmp( pszDbName, pDbStatTbl [uiMid].pszDbName)) == 0) #else if ((iCmp = f_stricmp( pszDbName, pDbStatTbl [uiMid].pszDbName)) == 0) #endif { // Found match. *ppDbStatsRV = &pDbStatTbl [uiMid]; if (puiDBAllocSeqRV) { *puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq; } if (puiDbTblPosRV) { *puiDbTblPosRV = uiMid; } goto Exit; } // Check if we are done - where uiLow equals uiHigh. if (uiLow >= uiHigh) { // Item not found. break; } if (iCmp < 0) { if (uiMid == uiLowStart) { break; // Way too high? } uiHigh = uiMid - 1; // Too high } else { if (uiMid == uiTblSize) { break; // Done - Hit the top } uiLow = uiMid + 1; // Too low } } } // If the array is full, or was never allocated, allocate a new one. if (pFlmStats->uiDbStatArraySize <= pFlmStats->uiNumDbStats) { if (!pFlmStats->pDbStats) { uiNewSize = INIT_DB_STAT_ARRAY_SIZE; } else { uiNewSize = pFlmStats->uiDbStatArraySize + DB_STAT_ARRAY_INCR_SIZE; } if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( XFLM_DB_STATS) * uiNewSize), &pDbStatTbl))) { goto Exit; } // Save whatever was in the old table, if any. if (pFlmStats->pDbStats && pFlmStats->uiNumDbStats) { f_memcpy( pDbStatTbl, pFlmStats->pDbStats, (FLMINT)(sizeof( XFLM_DB_STATS) * pFlmStats->uiNumDbStats)); } if (pFlmStats->pDbStats) { f_free( &pFlmStats->pDbStats); } pFlmStats->uiDBAllocSeq++; pFlmStats->pDbStats = pDbStatTbl; pFlmStats->uiDbStatArraySize = uiNewSize; } // Allocate space for the database name if (RC_BAD( rc = f_alloc( f_strlen( pszDbName) + 1, &pszTmpDbName))) { goto Exit; } // Insert the item into the array. if (iCmp != 0) { uiElement = pFlmStats->uiNumDbStats; // If our new database number is greater than database number of the // element pointed to by uiMid, increment uiMid so that the new // database number will be inserted after it instead of before it. if (iCmp > 0) { uiMid++; } // Move everything up in the array, including the slot pointed to // by uiMid. while (uiElement > uiMid) { f_memcpy( &pDbStatTbl [uiElement], &pDbStatTbl [uiElement - 1], sizeof( XFLM_DB_STATS)); uiElement--; } f_memset( &pDbStatTbl [uiMid], 0, sizeof( XFLM_DB_STATS)); } pDbStatTbl [uiMid].pszDbName = pszTmpDbName; pszTmpDbName = NULL; f_strcpy( pDbStatTbl [uiMid].pszDbName, pszDbName); pFlmStats->uiNumDbStats++; *ppDbStatsRV = &pDbStatTbl [uiMid]; if (puiDBAllocSeqRV) { *puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq; } if (puiDbTblPosRV) { *puiDbTblPosRV = uiMid; } Exit: if (pszTmpDbName) { f_free( &pszTmpDbName); } return( rc); } /**************************************************************************** Desc: This routine returns a pointer to a particular database's statistics block. ****************************************************************************/ RCODE flmStatGetDb( XFLM_STATS * pFlmStats, F_Database * pDatabase, FLMUINT uiLowStart, XFLM_DB_STATS ** ppDbStatsRV, FLMUINT * puiDBAllocSeqRV, FLMUINT * puiDbTblPosRV) { if (!pFlmStats) { *ppDbStatsRV = NULL; if (puiDBAllocSeqRV) { *puiDBAllocSeqRV = 0; } if (puiDbTblPosRV) { *puiDbTblPosRV = 0; } return( NE_XFLM_OK); } return( flmStatGetDbByName( pFlmStats, pDatabase->getDbNamePtr(), uiLowStart, ppDbStatsRV, puiDBAllocSeqRV, puiDbTblPosRV)); } /**************************************************************************** Desc: This routine returns a pointer to a particular logical file in a particular database's statistics block. ****************************************************************************/ RCODE flmStatGetLFile( XFLM_DB_STATS * pDbStats, FLMUINT uiLFileNum, eLFileType eLfType, FLMUINT uiLowStart, XFLM_LFILE_STATS ** ppLFileStatsRV, FLMUINT * puiLFileAllocSeqRV, FLMUINT * puiLFileTblPosRV) { RCODE rc = NE_XFLM_OK; FLMUINT uiTblSize; XFLM_LFILE_STATS * pLFileStatTbl; XFLM_LFILE_STATS * pLFileCurrStat; FLMUINT uiLow; FLMUINT uiMid = 0; FLMUINT uiHigh; FLMINT iCmp = 0; FLMUINT uiNewSize; FLMUINT uiElement; if (!pDbStats) { *ppLFileStatsRV = NULL; if (puiLFileAllocSeqRV) { *puiLFileAllocSeqRV = 0; } if (puiLFileTblPosRV) { *puiLFileTblPosRV = 0; } goto Exit; } // If there is a database array, search it first. if (((pLFileStatTbl = pDbStats->pLFileStats) != NULL) && ((uiTblSize = pDbStats->uiNumLFileStats) != 0)) { for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize { uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 // See if we found a match. pLFileCurrStat = &pLFileStatTbl [uiMid]; if (uiLFileNum < pLFileCurrStat->uiLFileNum) { iCmp = -1; } else if (uiLFileNum > pLFileCurrStat->uiLFileNum) { iCmp = 1; } else if (eLfType < pLFileCurrStat->eLfType) { iCmp = -1; } else if (eLfType > pLFileCurrStat->eLfType) { iCmp = 1; } else { // Found match. *ppLFileStatsRV = pLFileCurrStat; if (puiLFileAllocSeqRV) { *puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq; } if (puiLFileTblPosRV) { *puiLFileTblPosRV = uiMid; } goto Exit; } // Check if we are done - where uiLow equals uiHigh. if (uiLow >= uiHigh) { // Item not found. break; } if (iCmp < 0) { if (uiMid == uiLowStart) { break; // Way too high? } uiHigh = uiMid - 1; // Too high } else { if (uiMid == uiTblSize) { break; // Done - Hit the top } uiLow = uiMid + 1; // Too low } } } // If the array is full, or was never allocated, allocate a new one. if (pDbStats->uiLFileStatArraySize <= pDbStats->uiNumLFileStats) { if (!pDbStats->pLFileStats) { uiNewSize = INIT_LFILE_STAT_ARRAY_SIZE; } else { uiNewSize = pDbStats->uiLFileStatArraySize + LFILE_STAT_ARRAY_INCR_SIZE; } if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( XFLM_LFILE_STATS) * uiNewSize), &pLFileStatTbl))) { goto Exit; } // Save whatever was in the old table, if any. if ((pDbStats->pLFileStats) && (pDbStats->uiNumLFileStats)) { f_memcpy( pLFileStatTbl, pDbStats->pLFileStats, (FLMUINT)(sizeof( XFLM_LFILE_STATS) * pDbStats->uiNumLFileStats)); } if (pDbStats->pLFileStats) { f_free( &pDbStats->pLFileStats); } pDbStats->uiLFileAllocSeq++; pDbStats->pLFileStats = pLFileStatTbl; pDbStats->uiLFileStatArraySize = uiNewSize; } // Insert the item into the array. if (iCmp != 0) { uiElement = pDbStats->uiNumLFileStats; // If our new database number is greater than database number of the // element pointed to by uiMid, increment uiMid so that the new // database number will be inserted after it instead of before it. if (iCmp > 0) { uiMid++; } // Move everything up in the array, including the slot pointed to // by uiMid. while (uiElement > uiMid) { f_memcpy( &pLFileStatTbl [uiElement], &pLFileStatTbl [uiElement - 1], sizeof( XFLM_LFILE_STATS)); uiElement--; } f_memset( &pLFileStatTbl [uiMid], 0, sizeof( XFLM_LFILE_STATS)); } pLFileStatTbl [uiMid].uiLFileNum = uiLFileNum; pLFileStatTbl [uiMid].eLfType = eLfType; pDbStats->uiNumLFileStats++; *ppLFileStatsRV = &pLFileStatTbl [uiMid]; if (puiLFileAllocSeqRV) { *puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq; } if (puiLFileTblPosRV) { *puiLFileTblPosRV = uiMid; } Exit: return( rc); } /**************************************************************************** Desc: This routine returns a pointer to a particular LFILE's statistics block. ****************************************************************************/ XFLM_LFILE_STATS * F_Db::getLFileStatPtr( LFILE * pLFile) { if (!pLFile) { return( (XFLM_LFILE_STATS *)NULL); } if ((!m_pLFileStats) || (m_uiLFileAllocSeq != m_pDbStats->uiLFileAllocSeq) || (m_pLFileStats->uiLFileNum != pLFile->uiLfNum)) { if (RC_BAD( flmStatGetLFile( m_pDbStats, pLFile->uiLfNum, pLFile->eLfType, 0, &m_pLFileStats, &m_uiLFileAllocSeq, NULL))) { m_pLFileStats = NULL; m_uiLFileAllocSeq = 0; } } return( m_pLFileStats); } /**************************************************************************** Desc: This routine resets the statistics in a FLM_STAT structure. ****************************************************************************/ void flmStatReset( XFLM_STATS * pStats, FLMBOOL bFree) { FLMUINT uiDb; XFLM_DB_STATS * pDbStats; FLMUINT uiLFile; XFLM_LFILE_STATS * pLFile; if ((pDbStats = pStats->pDbStats) != NULL) { for (uiDb = 0; uiDb < pStats->uiNumDbStats; uiDb++, pDbStats++) { if ((pLFile = pDbStats->pLFileStats) != NULL) { if (bFree) { f_free( &pDbStats->pLFileStats); } else { for (uiLFile = 0; uiLFile < pDbStats->uiNumLFileStats; uiLFile++, pLFile++) { FLMUINT uiSaveLFileNum = pLFile->uiLFileNum; eLFileType eSaveLfType = pLFile->eLfType; f_memset( pLFile, 0, sizeof( XFLM_LFILE_STATS)); pLFile->uiLFileNum = uiSaveLFileNum; pLFile->eLfType = eSaveLfType; } } } if (!bFree) { char * pszSaveDbName; FLMUINT uiSaveLFileAllocSeq = pDbStats->uiLFileAllocSeq; XFLM_LFILE_STATS * pSaveLFileStats = pDbStats->pLFileStats; FLMUINT uiSaveLFileStatArraySize = pDbStats->uiLFileStatArraySize; FLMUINT uiSaveNumLFileStats = pDbStats->uiNumLFileStats; pszSaveDbName = pDbStats->pszDbName; f_memset( pDbStats, 0, sizeof( XFLM_DB_STATS)); pDbStats->pszDbName = pszSaveDbName; pDbStats->uiLFileAllocSeq = uiSaveLFileAllocSeq; pDbStats->pLFileStats = pSaveLFileStats; pDbStats->uiLFileStatArraySize = uiSaveLFileStatArraySize; pDbStats->uiNumLFileStats = uiSaveNumLFileStats; } else { f_free( &pDbStats->pszDbName); } } if (bFree) { f_free( &pStats->pDbStats); } } if ((bFree) || (!pDbStats)) { pStats->pDbStats = NULL; pStats->uiDbStatArraySize = 0; pStats->uiNumDbStats = 0; } pStats->uiStartTime = 0; pStats->uiStopTime = 0; if (pStats->bCollectingStats) { f_timeGetSeconds( &pStats->uiStartTime); } } /**************************************************************************** Desc: This routine updates statistics from one XFLM_RTRANS_STATS structure into another. ****************************************************************************/ FINLINE void flmUpdateRTransStats( XFLM_RTRANS_STATS * pDest, XFLM_RTRANS_STATS * pSrc) { flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans); flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans); } /**************************************************************************** Desc: This routine updates statistics from one XFLM_UTRANS_STATS structure into another. ****************************************************************************/ FINLINE void flmUpdateUTransStats( XFLM_UTRANS_STATS * pDest, XFLM_UTRANS_STATS * pSrc) { flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans); flmUpdateCountTimeStats( &pDest->GroupCompletes, &pSrc->GroupCompletes); pDest->ui64GroupFinished += pSrc->ui64GroupFinished; flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans); } /**************************************************************************** Desc: This routine updates statistics from one XFLM_BLOCKIO_STATS structure into another. ****************************************************************************/ void flmUpdateBlockIOStats( XFLM_BLOCKIO_STATS * pDest, XFLM_BLOCKIO_STATS * pSrc) { flmUpdateDiskIOStats( &pDest->BlockReads, &pSrc->BlockReads); flmUpdateDiskIOStats( &pDest->OldViewBlockReads, &pSrc->OldViewBlockReads); pDest->ui32BlockChkErrs += pSrc->ui32BlockChkErrs; pDest->ui32OldViewBlockChkErrs += pSrc->ui32OldViewBlockChkErrs; pDest->ui32OldViewErrors += pSrc->ui32OldViewErrors; flmUpdateDiskIOStats( &pDest->BlockWrites, &pSrc->BlockWrites); } /**************************************************************************** Desc: This routine updates statistics from one XFLM_LFILE_STATS structure into another. ****************************************************************************/ FSTATIC void flmUpdateLFileStats( XFLM_LFILE_STATS * pDest, XFLM_LFILE_STATS * pSrc) { pDest->bHaveStats = TRUE; flmUpdateBlockIOStats( &pDest->RootBlockStats, &pSrc->RootBlockStats); flmUpdateBlockIOStats( &pDest->MiddleBlockStats, &pSrc->MiddleBlockStats); flmUpdateBlockIOStats( &pDest->LeafBlockStats, &pSrc->LeafBlockStats); pDest->ui64BlockSplits += pSrc->ui64BlockSplits; pDest->ui64BlockCombines += pSrc->ui64BlockCombines; } /**************************************************************************** Desc: This routine updates statistics from one XFLM_DB_STATS structure into another. ****************************************************************************/ FSTATIC RCODE flmUpdateDbStats( XFLM_DB_STATS * pDestDb, XFLM_DB_STATS * pSrcDb) { RCODE rc = NE_XFLM_OK; FLMUINT uiSrcLFile; FLMUINT uiDestLFile; XFLM_LFILE_STATS * pDestLFile; XFLM_LFILE_STATS * pSrcLFile; FLMUINT uiLowLFileStart; FLMUINT uiSaveNumLFiles; flmUpdateRTransStats( &pDestDb->ReadTransStats, &pSrcDb->ReadTransStats); flmUpdateUTransStats( &pDestDb->UpdateTransStats, &pSrcDb->UpdateTransStats); pDestDb->bHaveStats = TRUE; flmUpdateBlockIOStats( &pDestDb->LFHBlockStats, &pSrcDb->LFHBlockStats); flmUpdateBlockIOStats( &pDestDb->AvailBlockStats, &pSrcDb->AvailBlockStats); flmUpdateDiskIOStats( &pDestDb->DbHdrWrites, &pSrcDb->DbHdrWrites); flmUpdateDiskIOStats( &pDestDb->LogBlockWrites, &pSrcDb->LogBlockWrites); flmUpdateDiskIOStats( &pDestDb->LogBlockRestores, &pSrcDb->LogBlockRestores); flmUpdateDiskIOStats( &pDestDb->LogBlockReads, &pSrcDb->LogBlockReads); pDestDb->uiLogBlockChkErrs += pSrcDb->uiLogBlockChkErrs; pDestDb->uiReadErrors += pSrcDb->uiReadErrors; pDestDb->uiWriteErrors += pSrcDb->uiWriteErrors; flmUpdateCountTimeStats( &pDestDb->LockStats.NoLocks, &pSrcDb->LockStats.NoLocks); flmUpdateCountTimeStats( &pDestDb->LockStats.WaitingForLock, &pSrcDb->LockStats.WaitingForLock); flmUpdateCountTimeStats( &pDestDb->LockStats.HeldLock, &pSrcDb->LockStats.HeldLock); // Go through the LFILE statistics. for (uiDestLFile = 0, uiSrcLFile = 0, uiLowLFileStart = 0, pSrcLFile = pSrcDb->pLFileStats; uiSrcLFile < pSrcDb->uiNumLFileStats; uiSrcLFile++, pSrcLFile++) { if (!pSrcLFile->bHaveStats) { continue; } // Find or add the store in the destination store array. uiSaveNumLFiles = pDestDb->uiNumLFileStats; if (RC_BAD( rc = flmStatGetLFile( pDestDb, pSrcLFile->uiLFileNum, pSrcLFile->eLfType, uiLowLFileStart, &pDestLFile, NULL, &uiLowLFileStart))) { goto Exit; } if (uiLowLFileStart < pDestDb->uiNumLFileStats - 1) { uiLowLFileStart++; } // If we created the LFILE, all we have to do is copy the // LFILE statistics. It will be quicker. if (uiSaveNumLFiles != pDestDb->uiNumLFileStats) { f_memcpy( pDestLFile, pSrcLFile, sizeof( XFLM_LFILE_STATS)); } else { // LFILE was already present, need to go through and // update the statistics. flmUpdateLFileStats( pDestLFile, pSrcLFile); } } Exit: return( rc); } /**************************************************************************** Desc: This routine updates statistics from one FLM_STAT structure into the global statistics. ****************************************************************************/ RCODE flmStatUpdate( XFLM_STATS * pSrcStats) { RCODE rc = NE_XFLM_OK; FLMUINT uiSrcDb; FLMUINT uiDestDb; XFLM_DB_STATS * pDestDb; XFLM_DB_STATS * pSrcDb; FLMUINT uiLowDbStart; // Do not update the statistics if the source statistics were started // at an earlier time that the destination statistics start time. if (!gv_XFlmSysData.Stats.bCollectingStats || pSrcStats->uiStartTime < gv_XFlmSysData.Stats.uiStartTime) { return( NE_XFLM_OK); } f_mutexLock( gv_XFlmSysData.hStatsMutex); // Go through each of the source's databases for ( uiDestDb = 0, uiSrcDb = 0, uiLowDbStart = 0, pSrcDb = pSrcStats->pDbStats; uiSrcDb < pSrcStats->uiNumDbStats; uiSrcDb++, pSrcDb++) { if (!pSrcDb->bHaveStats) { continue; } // Find or add the store in the destination store array. if (RC_BAD( rc = flmStatGetDbByName( &gv_XFlmSysData.Stats, pSrcDb->pszDbName, uiLowDbStart, &pDestDb, NULL, &uiLowDbStart))) { goto Exit; } if (uiLowDbStart < gv_XFlmSysData.Stats.uiNumDbStats - 1) { uiLowDbStart++; } if (RC_BAD( rc = flmUpdateDbStats( pDestDb, pSrcDb))) { goto Exit; } } Exit: f_mutexUnlock( gv_XFlmSysData.hStatsMutex); if (RC_OK( rc)) { flmStatReset( pSrcStats, FALSE); } return( rc); } /**************************************************************************** Desc: This routine frees however many queries that are over the limit of the number we can save. Note: This routine will ALWAYS unlock the query mutex on leaving. It may be locked when entering. ****************************************************************************/ void flmFreeSavedQueries( FLMBOOL bMutexAlreadyLocked) { QUERY_HDR * pQueriesToFree = NULL; // Must determine the queries to free inside the mutex lock and then free // them outside the mutex lock, because freeing a query may cause an // embedded query to be put into the list again, which will cause the // mutex to be locked again. if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hQueryMutex); } while (gv_XFlmSysData.uiQueryCnt > gv_XFlmSysData.uiMaxQueries) { gv_XFlmSysData.pOldestQuery = gv_XFlmSysData.pOldestQuery->pPrev; gv_XFlmSysData.uiQueryCnt--; } // Whatever is found after pOldestQuery should be freed. Unlink // those from the list and point to them with pQueriesToFree. if (!gv_XFlmSysData.pOldestQuery) { pQueriesToFree = gv_XFlmSysData.pNewestQuery; gv_XFlmSysData.pNewestQuery = NULL; } else if (gv_XFlmSysData.pOldestQuery->pNext) { pQueriesToFree = gv_XFlmSysData.pOldestQuery->pNext; pQueriesToFree->pPrev = NULL; gv_XFlmSysData.pOldestQuery->pNext = NULL; } f_mutexUnlock( gv_XFlmSysData.hQueryMutex); // Now clean up each of the queries in the pQueriesToFree list. // This can be done outside the mutex lock because we are now // dealing with a completely local list that no other thread can // see. Also, the mutex must NOT be locked at this point because // FlmCursorCleanup may free an embedded query, which will want // to lock the mutex again. while (pQueriesToFree) { QUERY_HDR * pQueryHdrToFree = pQueriesToFree; pQueriesToFree = pQueriesToFree->pNext; // VISIT flmAssert( 0); // pQueryHdrToFree->pCursor->cleanup(); f_free( &pQueryHdrToFree); } } /**************************************************************************** Desc: This routine saves a query so it can be analyzed later. ****************************************************************************/ void flmSaveQuery( F_Query * pQuery) { QUERY_HDR * pQueryHdr = NULL; FLMBOOL bNeedToCleanup = TRUE; FLMBOOL bMutexLocked = FALSE; // Allocate memory for the new query if (RC_BAD( f_calloc( sizeof( QUERY_HDR), &pQueryHdr))) { goto Exit; } pQueryHdr->pQuery = pQuery; f_mutexLock( gv_XFlmSysData.hQueryMutex); bMutexLocked = TRUE; // uiMaxQueries was originally checked outside of the mutex lock. // Make sure it is still non-zero. if (gv_XFlmSysData.uiMaxQueries) { // Link query to head of list. bNeedToCleanup = FALSE; if ((pQueryHdr->pNext = gv_XFlmSysData.pNewestQuery) != NULL) { pQueryHdr->pNext->pPrev = pQueryHdr; } else { gv_XFlmSysData.pOldestQuery = pQueryHdr; } gv_XFlmSysData.pNewestQuery = pQueryHdr; if (++gv_XFlmSysData.uiQueryCnt > gv_XFlmSysData.uiMaxQueries) { flmFreeSavedQueries( TRUE); // flmFreeSavedQueries will always unlock the mutex. bMutexLocked = FALSE; } } Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hQueryMutex); } // Must clean up the query if we didn't get it into the list for // some reason. if (bNeedToCleanup) { if (pQueryHdr) { f_free( &pQueryHdr); } // VISIT flmAssert( 0); // pCursor->cleanup(); } } /**************************************************************************** Desc: This routine copies statistics from one FLM_STAT structure into another. This is used to retrieve statistics. ****************************************************************************/ RCODE flmStatCopy( XFLM_STATS * pDestStats, XFLM_STATS * pSrcStats) { RCODE rc = NE_XFLM_OK; FLMUINT uiDb; XFLM_DB_STATS * pDestDb; XFLM_DB_STATS * pSrcDb; FLMUINT uiCount; FLMUINT uiLoop; XFLM_DB_STATS * pDbStats; XFLM_LFILE_STATS * pLFile; f_memcpy( pDestStats, pSrcStats, sizeof( XFLM_STATS)); // Zero out the database array. We need to do this // so that if we get an error, we can correctly release memory for // the destination structure. pDestStats->uiNumDbStats = 0; pDestStats->uiDbStatArraySize = 0; pDestStats->pDbStats = NULL; uiCount = 0; if (pSrcStats->uiNumDbStats) { for (uiLoop = 0, pDbStats = pSrcStats->pDbStats; uiLoop < pSrcStats->uiNumDbStats; uiLoop++, pDbStats++) { if (pDbStats->bHaveStats) { uiCount++; } } } if (uiCount) { if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( XFLM_DB_STATS) * uiCount, &pDestStats->pDbStats))) { goto Exit; } for (uiLoop = 0, uiCount = 0, pDbStats = pSrcStats->pDbStats; uiLoop < pSrcStats->uiNumDbStats; uiLoop++, pDbStats++) { if (pDbStats->bHaveStats) { pDestDb = &pDestStats->pDbStats [uiCount]; f_memcpy( pDestDb, pDbStats, sizeof( XFLM_DB_STATS)); // Allocate space for the database name if (RC_BAD( rc = f_alloc( f_strlen( pDbStats->pszDbName) + 1, &pDestDb->pszDbName))) { goto Exit; } f_strcpy( pDestDb->pszDbName, pDbStats->pszDbName); // Zero out each store's LFILE statistics. We need to do this // so that if we get an error, we can correctly release memory for // the destination structure. pDestDb->uiNumLFileStats = 0; pDestDb->uiLFileStatArraySize = 0; pDestDb->pLFileStats = NULL; uiCount++; } } pDestStats->uiDbStatArraySize = pDestStats->uiNumDbStats = uiCount; } for (uiDb = pSrcStats->uiNumDbStats, pDestDb = pDestStats->pDbStats, pSrcDb = pSrcStats->pDbStats; uiDb; uiDb--, pSrcDb++) { if (!pSrcDb->bHaveStats) { continue; } pDestDb->uiNumLFileStats = 0; pDestDb->uiLFileStatArraySize = 0; pDestDb->pLFileStats = NULL; uiCount = 0; for (uiLoop = 0, pLFile = pSrcDb->pLFileStats; uiLoop < pSrcDb->uiNumLFileStats; uiLoop++, pLFile++) { if (pLFile->bHaveStats) { uiCount++; } } if (uiCount) { if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( XFLM_LFILE_STATS) * uiCount, &pDestDb->pLFileStats))) { goto Exit; } uiCount = 0; for (uiLoop = 0, pLFile = pSrcDb->pLFileStats; uiLoop < pSrcDb->uiNumLFileStats; uiLoop++, pLFile++) { if (pLFile->bHaveStats) { f_memcpy( &pDestDb->pLFileStats [uiCount], pLFile, sizeof( XFLM_LFILE_STATS)); uiCount++; } } pDestDb->uiLFileStatArraySize = pDestDb->uiNumLFileStats = uiCount; } pDestDb++; } Exit: if (RC_BAD( rc)) { flmStatFree( pDestStats); } return( rc); } /**************************************************************************** Desc: This routine locates the appropriate XFLM_BLOCKIO_STATS structure for a given block of data. NULL is returned if an appropriate one cannot be found. VISIT: uiBlkType passed in is a guess. Remove this parm and start using pBlk. ****************************************************************************/ XFLM_BLOCKIO_STATS * flmGetBlockIOStatPtr( XFLM_DB_STATS * pDbStats, XFLM_LFILE_STATS * pLFileStats, FLMBYTE * pucBlk) { F_BLK_HDR * pBlkHdr = (F_BLK_HDR *)pucBlk; if (pBlkHdr->ui8BlkType == BT_FREE) { pDbStats->bHaveStats = TRUE; return( &pDbStats->AvailBlockStats); } else if (pBlkHdr->ui8BlkType == BT_LFH_BLK) { pDbStats->bHaveStats = TRUE; return( &pDbStats->LFHBlockStats); } else if (pLFileStats) { pLFileStats->bHaveStats = pDbStats->bHaveStats = TRUE; // VISIT: Consider a one level tree. // Is it more important to count root stats over leaf stats? // What about the Data Only Blocks? // Consider invalid type. if (pBlkHdr->ui8BlkType != BT_LEAF && pBlkHdr->ui8BlkType != BT_NON_LEAF && pBlkHdr->ui8BlkType != BT_NON_LEAF_COUNTS && pBlkHdr->ui8BlkType != BT_LEAF_DATA) { return( &pLFileStats->LeafBlockStats); } if (pBlkHdr->ui32NextBlkInChain == 0 && pBlkHdr->ui32PrevBlkInChain == 0) { return( &pLFileStats->RootBlockStats); } else if ((pBlkHdr->ui8BlkType != BT_LEAF) && (pBlkHdr->ui8BlkType != BT_LEAF_DATA)) { return( &pLFileStats->MiddleBlockStats); } else { return( &pLFileStats->LeafBlockStats); } } else { return( (XFLM_BLOCKIO_STATS *)NULL); } } /******************************************************************** Desc: Determine if a given year is a leap year. *********************************************************************/ FINLINE FLMUINT flmLeapYear( FLMUINT uiYear ) { if (uiYear % 4 != 0) { return( 0); } if (uiYear % 100 != 0) { return( 1); } if (uiYear % 400 != 0) { return( 0); } return( 1); } /******************************************************************** Desc: Calculate days in a given month of a given year. *********************************************************************/ FSTATIC FLMUINT flmDaysInMonth( FLMUINT uiYear, FLMUINT uiMonth ) { switch (uiMonth + 1) { case 4: case 6: case 9: case 11: return( 30); case 2: return( 28 + flmLeapYear( uiYear)); default: return( 31); } } /******************************************************************** Desc: Adjust the time. *********************************************************************/ FSTATIC void flmAdjustTime( F_TMSTAMP * pTime, FLMINT iStartPoint ) { switch (iStartPoint) { case 1: goto Adj_1; case 2: goto Adj_2; case 3: goto Adj_3; case 4: goto Adj_4; case 5: goto Adj_5; case 6: goto Adj_6; } Adj_1: if (pTime->hundredth >= 100) { pTime->second++; pTime->hundredth = 0; } Adj_2: if (pTime->second == 60) { pTime->minute++; pTime->second = 0; } Adj_3: if (pTime->minute == 60) { pTime->hour++; pTime->minute = 0; } Adj_4: if (pTime->hour == 24) { pTime->day++; pTime->hour = 0; } Adj_5: if ((FLMUINT)pTime->day > flmDaysInMonth( pTime->year, pTime->month)) { pTime->month++; pTime->day = 1; } Adj_6: if (pTime->month > 11) { pTime->year++; pTime->month = 1; } } /******************************************************************** Desc: Calculate the elapsed time, including milliseconds. *********************************************************************/ void flmAddElapTime( F_TMSTAMP * pStartTime, FLMUINT64 * pui64ElapMilli ) { F_TMSTAMP StartTime; F_TMSTAMP EndTime; FLMUINT uiSec = 0; FLMUINT uiHundredth = 0; f_timeGetTimeStamp( &EndTime); f_memcpy( &StartTime, pStartTime, sizeof( F_TMSTAMP)); if (StartTime.year < EndTime.year) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.hour) { uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } if (StartTime.day > 1) { uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) - StartTime.day + 1) * (FLMUINT)86400; StartTime.day = 1; StartTime.month++; flmAdjustTime( &StartTime, 6); } if (StartTime.month > 1) { while (StartTime.month <= 11) { uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year, StartTime.month) * (FLMUINT)86400); StartTime.month++; } StartTime.year++; } while (StartTime.year < EndTime.year) { uiSec += (FLMUINT)((FLMUINT)(365 + flmLeapYear( StartTime.year)) * (FLMUINT)86400); StartTime.year++; } } if (StartTime.month < EndTime.month) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.hour) { uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } if (StartTime.day > 1) { uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) - StartTime.day + 1) * (FLMUINT)86400; StartTime.day = 1; StartTime.month++; flmAdjustTime( &StartTime, 6); } while (StartTime.month < EndTime.month) { uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year, StartTime.month) * (FLMUINT)86400); StartTime.month++; } } if (StartTime.day < EndTime.day) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.hour) { uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } uiSec += (FLMUINT)(EndTime.day - StartTime.day) * (FLMUINT)86400; StartTime.day = 1; StartTime.month++; flmAdjustTime( &StartTime, 6); } if (StartTime.hour < EndTime.hour) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.minute) { uiSec += (FLMUINT)((60 - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } uiSec += (FLMUINT)((EndTime.hour - StartTime.hour) * 3600); StartTime.hour = 0; StartTime.day++; flmAdjustTime( &StartTime, 5); } if (StartTime.minute < EndTime.minute) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } if (StartTime.second) { uiSec += (FLMUINT)(60 - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } uiSec += (FLMUINT)((EndTime.minute - StartTime.minute) * 60); StartTime.minute = 0; StartTime.hour++; flmAdjustTime( &StartTime, 4); } if (StartTime.second < EndTime.second) { if (StartTime.hundredth) { uiHundredth += (FLMUINT)(100 - StartTime.hundredth); StartTime.hundredth = 0; StartTime.second++; flmAdjustTime( &StartTime, 2); } uiSec += (FLMUINT)(EndTime.second - StartTime.second); StartTime.second = 0; StartTime.minute++; flmAdjustTime( &StartTime, 3); } if (StartTime.hundredth < EndTime.hundredth) { uiHundredth += (FLMUINT)(EndTime.hundredth - StartTime.hundredth); } if (uiSec) { (*pui64ElapMilli) += (FLMUINT64)((uiHundredth * 10 + uiSec * 1000)); } else { (*pui64ElapMilli) += (FLMUINT64)(uiHundredth * 10); } } libxflaim-5.1.969/src/fquery.cpp0000644000175000017500000117171110511001742020054 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the methods for F_Query class. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fquery.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "fquery.h" #include "fscursor.h" #define MIN_OPT_COST 8 static FLMUINT uiPrecedenceTable[ XFLM_RPAREN_OP - XFLM_AND_OP + 1] = { 2, // XFLM_AND_OP 1, // XFLM_OR_OP 10, // XFLM_NOT_OP 6, // XFLM_EQ_OP 6, // XFLM_NE_OP 6, // XFLM_APPROX_EQ_OP 7, // XFLM_LT_OP 7, // XFLM_LE_OP 7, // XFLM_GT_OP 7, // XFLM_GE_OP 5, // XFLM_BITAND_OP 3, // XFLM_BITOR_OP 4, // XFLM_BITXOR_OP 9, // XFLM_MULT_OP 9, // XFLM_DIV_OP 9, // XFLM_MOD_OP 8, // XFLM_PLUS_OP 8, // XFLM_MINUS_OP 10, // XFLM_NEG_OP 0, // XFLM_LPAREN_OP 0 // XFLM_RPAREN_OP }; FINLINE FLMUINT getPrecedence( eQueryOperators eOperator) { return( uiPrecedenceTable [eOperator - XFLM_AND_OP]); } FSTATIC void fqUnlinkFromParent( FQNODE * pQNode); FSTATIC void fqLinkFirstChild( FQNODE * pParent, FQNODE * pChild); FSTATIC void fqLinkLastChild( FQNODE * pParent, FQNODE * pChild); FSTATIC void fqReplaceNode( FQNODE * pNodeToReplace, FQNODE * pReplacementNode); FSTATIC RCODE fqGetPosition( FQVALUE * pQValue, FLMUINT * puiPos); FSTATIC RCODE fqCompareValues( FQVALUE * pValue1, FLMBOOL bInclusive1, FLMBOOL bNullIsLow1, FQVALUE * pValue2, FLMBOOL bInclusive2, FLMBOOL bNullIsLow2, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piCmp); FSTATIC RCODE fqCheckUnionPredicates( CONTEXT_PATH * pContextPath, FLMUINT uiLanguage, PATH_PRED * pPred); FSTATIC void fqClipContext( OP_CONTEXT * pContext); FSTATIC void fqImportChildContexts( OP_CONTEXT * pDestContext, OP_CONTEXT * pSrcContext); FSTATIC void fqImportContextPaths( OP_CONTEXT * pDestContext, OP_CONTEXT * pSrcContext); FSTATIC void fqImportContext( OP_CONTEXT * pDestContext, OP_CONTEXT * pSrcContext); FSTATIC void fqMergeContexts( FQNODE * pQNode, OP_CONTEXT * pDestContext); FSTATIC void fqCheckPathMatch( XPATH_COMPONENT * pXPathContextComponent, XPATH_COMPONENT * pXPathComponent); FSTATIC FQNODE * fqEvalLogicalOperands( FQNODE * pQNode); FSTATIC FQNODE * fqClipNotNode( FQNODE * pQNode, FQNODE ** ppExpr); FSTATIC RCODE fqGetNodeIdValue( FQVALUE * pQValue); FSTATIC FLMBOOL haveChildKeyComponents( ICD * pParentIcd); FSTATIC RCODE fqEvalOperator( FLMUINT uiLanguage, FQNODE * pQNode); FSTATIC void fqResetIterator( FQNODE * pQNode, FLMBOOL bFullRelease, FLMBOOL bUseKeyNodes); FSTATIC RCODE fqGetValueFromNode( F_Db * pDb, IF_DOMNode * pNode, FQVALUE * pQValue, FLMUINT uiMetaDataType); FSTATIC void fqResetQueryTree( FQNODE * pQueryTree, FLMBOOL bUseKeyNodes, FLMBOOL bResetAllXPaths); FSTATIC RCODE fqTryEvalOperator( FLMUINT uiLanguage, FQNODE ** ppCurrNode); FSTATIC FQNODE * fqBackupTree( FQNODE * pCurrNode, FLMBOOL * pbGetNodeValue); FSTATIC void fqReleaseQueryExpr( FQNODE * pQNode); FSTATIC FLMBOOL fqTestValue( FQNODE * pQueryExpr); FSTATIC RCODE fqGetValueFromKey( FLMUINT uiDataType, F_DataVector * pKey, FQVALUE * pQValue, FLMBYTE ** ppucValue, FLMUINT uiValueBufSize); FSTATIC RCODE fqPredCompare( FLMUINT uiLanguage, PATH_PRED * pPred, FQVALUE * pQValue, FLMBOOL * pbPasses); FSTATIC void fqMarkXPathNodeListPassed( PATH_PRED * pPred); FSTATIC int nodeIdCompareFunc( void * pvData1, void * pvData2, void * pvUserData); /*************************************************************************** Desc: Constructor ***************************************************************************/ F_Query::F_Query() { m_pool.poolInit( 1024); m_uiLanguage = FLM_US_LANG; m_uiCollection = XFLM_DATA_COLLECTION; initVars(); } /*************************************************************************** Desc: Destructor ***************************************************************************/ F_Query::~F_Query() { clearQuery(); m_pool.poolFree(); } /*************************************************************************** Desc: Reset function ***************************************************************************/ void F_Query::clearQuery( void) { stopBuildingResultSet(); resetQuery(); if (m_pDatabase) { m_pDatabase->lockMutex(); // Unlink the query from the list off of the F_Database object. if (m_pPrev) { m_pPrev->m_pNext = m_pNext; } else { m_pDatabase->m_pFirstQuery = m_pNext; } if (m_pNext) { m_pNext->m_pPrev = m_pPrev; } else { m_pDatabase->m_pLastQuery = m_pPrev; } m_pDatabase->unlockMutex(); } if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } if (m_ppObjectList) { while (m_uiObjectCount) { m_uiObjectCount--; m_ppObjectList [m_uiObjectCount]->Release(); m_ppObjectList [m_uiObjectCount] = NULL; } f_free( &m_ppObjectList); } if (m_pDocIdSet) { m_pDocIdSet->Release(); m_pDocIdSet = NULL; } if (m_pFSIndexCursor) { m_pFSIndexCursor->Release(); m_pFSIndexCursor = NULL; } // Clear all of the predicate cursors that may still be // laying around. if (m_pQuery && m_pQuery->pContext) { OP_CONTEXT * pContext = m_pQuery->pContext; CONTEXT_PATH * pContextPath; PATH_PRED * pPred; for (;;) { // Clear predicates of the context we are in. pContextPath = pContext->pFirstPath; while (pContextPath) { pPred = pContextPath->pFirstPred; while (pPred) { // Predicate should only have either an index cursor // or a collection cursor or an app. predicate. if (pPred->pFSIndexCursor) { flmAssert( !pPred->pFSCollectionCursor); flmAssert( !pPred->pNodeSource); pPred->pFSIndexCursor->Release(); } else if (pPred->pFSCollectionCursor) { flmAssert( !pPred->pFSIndexCursor); flmAssert( !pPred->pNodeSource); pPred->pFSCollectionCursor->Release(); } else if (pPred->pNodeSource) { flmAssert( !pPred->pFSIndexCursor); flmAssert( !pPred->pFSCollectionCursor); pPred->pNodeSource->releaseResources(); } pPred = pPred->pNext; } pContextPath = pContextPath->pNext; } if (pContext->pFirstChild) { pContext = pContext->pFirstChild; continue; } // Go to sibling context, if any while (!pContext->pNextSib) { if ((pContext = pContext->pParent) == NULL) { break; } } // If pContext is NULL at this point, there are no // more contexts. if (!pContext) { break; } pContext = pContext->pNextSib; // There has to have been a sibling context at this point. flmAssert( pContext); } } if (m_pSortResultSet) { m_pSortResultSet->Release(); m_pSortResultSet = NULL; } if (m_pQueryStatus) { m_pQueryStatus->Release(); m_pQueryStatus = NULL; } if (m_pQueryValidator) { m_pQueryValidator->Release(); m_pQueryValidator = NULL; } initVars(); } /*************************************************************************** Desc: Initialize all the member variables. ***************************************************************************/ void F_Query::initVars( void) { m_rc = NE_XFLM_OK; m_bScan = FALSE; m_bScanIndex = FALSE; m_bResetAllXPaths = FALSE; m_pFSIndexCursor = NULL; m_bEmpty = FALSE; m_pSortIxd = NULL; m_pSortResultSet = NULL; m_pFirstWaiter = NULL; m_bStopBuildingResultSet = FALSE; m_uiBuildThreadId = 0; m_bPositioningEnabled = FALSE; m_bResultSetPopulated = FALSE; m_bEntriesAlreadyInOrder = FALSE; m_bEncryptResultSet = FALSE; m_eState = XFLM_QUERY_NOT_POSITIONED; m_pQueryStatus = NULL; m_pQueryValidator = NULL; m_pDatabase = NULL; m_pPrev = NULL; m_pNext = NULL; m_bOptimized = FALSE; m_pCurrContext = NULL; m_pCurrContextPath = NULL; m_pCurrPred = NULL; m_pExprXPathSource = NULL; m_pQuery = NULL; m_pCurrOpt = NULL; m_pDb = NULL; m_pCurExprState = NULL; m_pCurrDoc = NULL; m_pCurrNode = NULL; m_ppObjectList = NULL; m_uiObjectListSize = 0; m_uiObjectCount = 0; m_bRemoveDups = FALSE; m_pDocIdSet = NULL; m_uiIndex = 0; m_bIndexSet = FALSE; m_uiTimeLimit = 0; m_uiStartTime = 0; m_pool.poolReset( NULL); } /*************************************************************************** Desc: Allocate a new expression evaluation state structure ***************************************************************************/ RCODE F_Query::allocExprState( void) { RCODE rc = NE_XFLM_OK; EXPR_STATE * pExprState; if (!m_pCurExprState || !m_pCurExprState->pNext) { if (RC_BAD( rc = m_pool.poolCalloc( sizeof( EXPR_STATE), (void **)&pExprState))) { goto Exit; } if ((pExprState->pPrev = m_pCurExprState) != NULL) { m_pCurExprState->pNext = pExprState; } m_pCurExprState = pExprState; } else { EXPR_STATE * pSaveNext; EXPR_STATE * pSavePrev; m_pCurExprState = m_pCurExprState->pNext; // Zero out everything except for the prev and next pointers pSaveNext = m_pCurExprState->pNext; pSavePrev = m_pCurExprState->pPrev; f_memset( m_pCurExprState, 0, sizeof( EXPR_STATE)); m_pCurExprState->pNext = pSaveNext; m_pCurExprState->pPrev = pSavePrev; } m_pCurExprState->uiNumExprNeeded = 1; Exit: return( rc); } /*************************************************************************** Desc: Unlinks a node from its parent and siblings. This routine assumes that the test has already been made that the node has a parent. ***************************************************************************/ FSTATIC void fqUnlinkFromParent( FQNODE * pQNode) { flmAssert( pQNode->pParent); if (pQNode->pPrevSib) { pQNode->pPrevSib->pNextSib = pQNode->pNextSib; } else { pQNode->pParent->pFirstChild = pQNode->pNextSib; } if (pQNode->pNextSib) { pQNode->pNextSib->pPrevSib = pQNode->pPrevSib; } else { pQNode->pParent->pLastChild = pQNode->pPrevSib; } pQNode->pParent = NULL; pQNode->pPrevSib = NULL; pQNode->pNextSib = NULL; } /*************************************************************************** Desc: Links one FQNODE as the first child of another. Will unlink the child node from any parent it may be linked to. ***************************************************************************/ FSTATIC void fqLinkFirstChild( FQNODE * pParent, FQNODE * pChild ) { // If necessary, unlink the child from parent and siblings if (pChild->pParent) { fqUnlinkFromParent( pChild); } // Link child as the first child to parent pChild->pParent = pParent; pChild->pPrevSib = NULL; if ((pChild->pNextSib = pParent->pFirstChild) != NULL) { pChild->pNextSib->pPrevSib = pChild; } else { pParent->pLastChild = pChild; } pParent->pFirstChild = pChild; } /*************************************************************************** Desc: Links one FQNODE as the last child of another. Will unlink the child node from any parent it may be linked to. ***************************************************************************/ FSTATIC void fqLinkLastChild( FQNODE * pParent, FQNODE * pChild ) { // If necessary, unlink the child from parent and siblings if (pChild->pParent) { fqUnlinkFromParent( pChild); } // Link child as the last child to parent pChild->pParent = pParent; pChild->pNextSib = NULL; if ((pChild->pPrevSib = pParent->pLastChild) != NULL) { pChild->pPrevSib->pNextSib = pChild; } else { pParent->pFirstChild = pChild; } pParent->pLastChild = pChild; } /**************************************************************************** Desc: Replace one node with another node in the tree. ****************************************************************************/ FSTATIC void fqReplaceNode( FQNODE * pNodeToReplace, FQNODE * pReplacementNode ) { FQNODE_p pParentNode; FLMBOOL bLinkAsFirst = (pNodeToReplace->pNextSib) ? TRUE : FALSE; if (pReplacementNode->pParent) { fqUnlinkFromParent( pReplacementNode); } if ((pParentNode = pNodeToReplace->pParent) != NULL) { fqUnlinkFromParent( pNodeToReplace); if (bLinkAsFirst) { fqLinkFirstChild( pParentNode, pReplacementNode); } else { fqLinkLastChild( pParentNode, pReplacementNode); } } } /*************************************************************************** Desc: Allocate a value node ***************************************************************************/ RCODE F_Query::allocValueNode( FLMUINT uiValLen, eValTypes eValType, FQNODE ** ppQNode ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (!m_pCurExprState) { if (RC_BAD( rc = allocExprState())) { goto Exit; } } if (m_pCurExprState->bExpectingLParen) { rc = RC_SET( NE_XFLM_Q_EXPECTING_LPAREN); goto Exit; } if (!expectingOperand()) { rc = RC_SET( NE_XFLM_Q_UNEXPECTED_VALUE); goto Exit; } if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQNODE), (void **)ppQNode))) { goto Exit; } pQNode = *ppQNode; pQNode->eNodeType = FLM_VALUE_NODE; pQNode->currVal.eValType = eValType; pQNode->currVal.uiDataLen = uiValLen; pQNode->currVal.uiFlags = VAL_IS_CONSTANT; // For string and binary data, allocate a buffer. if (uiValLen && (eValType == XFLM_UTF8_VAL || eValType == XFLM_BINARY_VAL)) { if (RC_BAD( rc = m_pool.poolAlloc( uiValLen, (void **)&pQNode->currVal.val.pucBuf))) { goto Exit; } } if (m_pCurExprState->pCurOperatorNode) { fqLinkLastChild( m_pCurExprState->pCurOperatorNode, pQNode); } else { flmAssert( !m_pCurExprState->pExpr); m_pCurExprState->pExpr = pQNode; } m_pCurExprState->bExpectingOperator = TRUE; m_pCurExprState->pLastNode = pQNode; Exit: return( rc); } /*************************************************************************** Desc: Adds a unicode value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addUnicodeValue( const FLMUNICODE * puzVal) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; FLMUINT uiValLen; FLMUINT uiCharCount; FLMUINT uiSenLen; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (RC_BAD( rc = flmUnicode2Storage( puzVal, 0, NULL, &uiValLen, &uiCharCount))) { goto Exit; } if (RC_BAD( rc = allocValueNode( uiValLen, XFLM_UTF8_VAL, &pQNode))) { goto Exit; } if (uiValLen) { FLMBOOL bHaveWildCards; const FLMUNICODE * puzTmp; // See if there are wildcards puzTmp = puzVal; bHaveWildCards = FALSE; while (*puzTmp) { if (*puzTmp == ASCII_BACKSLASH) { // Skip over the next character no matter what // because it is escaped. puzTmp++; if (*puzTmp == 0) { break; } } else if (*puzTmp == ASCII_WILDCARD) { bHaveWildCards = TRUE; break; } puzTmp++; } if (RC_BAD( rc = flmUnicode2Storage( puzVal, uiCharCount, pQNode->currVal.val.pucBuf, &pQNode->currVal.uiDataLen, &uiCharCount))) { goto Exit; } // Skip past the SEN if (RC_BAD( rc = flmGetCharCountFromStorageBuf( (const FLMBYTE **)&pQNode->currVal.val.pucBuf, pQNode->currVal.uiDataLen, NULL, &uiSenLen))) { goto Exit; } pQNode->currVal.uiDataLen -= uiSenLen; if (bHaveWildCards) { pQNode->currVal.uiFlags |= VAL_HAS_WILDCARDS; } } Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds a UTF8 value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addUTF8Value( const char * pszVal, FLMUINT uiUTF8Len) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; FLMUINT uiValLen; FLMUINT uiSenLen; FLMBYTE * pucEnd = NULL; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (RC_BAD( rc = flmUTF8ToStorage( (FLMBYTE *)pszVal, uiUTF8Len, NULL, &uiValLen))) { goto Exit; } if (RC_BAD( rc = allocValueNode( uiValLen, XFLM_UTF8_VAL, &pQNode))) { goto Exit; } if (uiValLen) { FLMBOOL bHaveWildCards; const FLMBYTE * pszTmp; FLMUNICODE uzChar; // See if there are wildcards pszTmp = (FLMBYTE *)pszVal; if (uiUTF8Len) { pucEnd = (FLMBYTE *)pszVal + uiUTF8Len; } bHaveWildCards = FALSE; for (;;) { if (RC_BAD( rc = f_getCharFromUTF8Buf( &pszTmp, pucEnd, &uzChar))) { goto Exit; } if (uzChar == ASCII_BACKSLASH) { // Skip over the next character no matter what // because it is escaped. if (RC_BAD( rc = f_getCharFromUTF8Buf( &pszTmp, pucEnd, &uzChar))) { goto Exit; } if (!uzChar) { break; } } else if (uzChar == ASCII_WILDCARD) { bHaveWildCards = TRUE; break; } if (!uzChar) { break; } } if (RC_BAD( rc = flmUTF8ToStorage( (FLMBYTE *)pszVal, uiUTF8Len, (FLMBYTE *)pQNode->currVal.val.pucBuf, &pQNode->currVal.uiDataLen))) { goto Exit; } // Skip past the SEN if (RC_BAD( rc = flmGetCharCountFromStorageBuf( (const FLMBYTE **)&pQNode->currVal.val.pucBuf, pQNode->currVal.uiDataLen, NULL, &uiSenLen))) { goto Exit; } pQNode->currVal.uiDataLen -= uiSenLen; if (bHaveWildCards) { pQNode->currVal.uiFlags |= VAL_HAS_WILDCARDS; } } Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds a binary value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addBinaryValue( const void * pvVal, FLMUINT uiValLen) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; if (RC_BAD( rc = allocValueNode( uiValLen, XFLM_BINARY_VAL, &pQNode))) { goto Exit; } if (uiValLen) { f_memcpy( pQNode->currVal.val.pucBuf, pvVal, uiValLen); } Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds a UINT value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addUINTValue( FLMUINT uiVal ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; if (RC_BAD( rc = allocValueNode( 0, XFLM_UINT_VAL, &pQNode))) { goto Exit; } pQNode->currVal.val.uiVal = uiVal; Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds an INT value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addINTValue( FLMINT iVal ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; if (RC_BAD( rc = allocValueNode( 0, XFLM_INT_VAL, &pQNode))) { goto Exit; } pQNode->currVal.val.iVal = iVal; Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds a UINT64 value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addUINT64Value( FLMUINT64 ui64Val ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; if (RC_BAD( rc = allocValueNode( 0, XFLM_UINT64_VAL, &pQNode))) { goto Exit; } pQNode->currVal.val.ui64Val = ui64Val; Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds an INT64 value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addINT64Value( FLMINT64 i64Val ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; if (RC_BAD( rc = allocValueNode( 0, XFLM_INT64_VAL, &pQNode))) { goto Exit; } pQNode->currVal.val.i64Val = i64Val; Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Adds a BOOL value to the query criteria. ***************************************************************************/ RCODE FLMAPI F_Query::addBoolean( FLMBOOL bVal, FLMBOOL bUnknown ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; if (RC_BAD( rc = allocValueNode( 0, XFLM_BOOL_VAL, &pQNode))) { goto Exit; } pQNode->currVal.val.eBool = (XFlmBoolType)(bUnknown ? XFLM_UNKNOWN : (XFlmBoolType)(bVal ? XFLM_TRUE : XFLM_FALSE)); Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Add an XPATH component ***************************************************************************/ RCODE FLMAPI F_Query::addXPathComponent( eXPathAxisTypes eXPathAxis, eDomNodeType eNodeType, FLMUINT uiDictNum, IF_QueryNodeSource * pNodeSource ) { RCODE rc = NE_XFLM_OK; XPATH_COMPONENT * pXPathComponent; FXPATH * pXPath; FQNODE * pQNode; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (!m_pCurExprState) { if (RC_BAD( rc = allocExprState())) { goto Exit; } } // Must be expecting an operand, or the last node // must be an XPATH if (!expectingOperand() && m_pCurExprState->pLastNode->eNodeType != FLM_XPATH_NODE) { rc = RC_SET( NE_XFLM_Q_UNEXPECTED_XPATH_COMPONENT); goto Exit; } // If axis is META_AXIS, verify that the specific type of meta data // requested is valid. if (eXPathAxis == META_AXIS) { switch (uiDictNum) { case XFLM_META_NODE_ID: case XFLM_META_DOCUMENT_ID: case XFLM_META_PARENT_ID: case XFLM_META_FIRST_CHILD_ID: case XFLM_META_LAST_CHILD_ID: case XFLM_META_NEXT_SIBLING_ID: case XFLM_META_PREV_SIBLING_ID: case XFLM_META_VALUE: break; default: rc = RC_SET( NE_XFLM_Q_INVALID_META_DATA_TYPE); goto Exit; } } // Allocate an XPATH component if (RC_BAD( rc = m_pool.poolCalloc( sizeof( XPATH_COMPONENT), (void **)&pXPathComponent))) { goto Exit; } pXPathComponent->eNodeType = eNodeType; pXPathComponent->eXPathAxis = eXPathAxis; pXPathComponent->uiDictNum = uiDictNum; pXPathComponent->pNodeSource = pNodeSource; if (m_pCurExprState->pPrev && m_pCurExprState->pXPathComponent) { // pXPathContext is the XPATH component context for this // XPATH component. This may be used in optimization. pXPathComponent->pXPathContext = m_pCurExprState->pXPathComponent; } // If we are not expecting an operand, then the last component // has to be an XPATH node. if (!expectingOperand()) { pXPath = m_pCurExprState->pLastNode->nd.pXPath; } else { // Need to allocate a node and an XPATH if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQNODE), (void **)&pQNode))) { goto Exit; } if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FXPATH), (void **)&pXPath))) { goto Exit; } pQNode->eNodeType = FLM_XPATH_NODE; pQNode->nd.pXPath = pXPath; // Link this node into the expression if (m_pCurExprState->pCurOperatorNode) { fqLinkLastChild( m_pCurExprState->pCurOperatorNode, pQNode); } else { flmAssert( !m_pCurExprState->pExpr); m_pCurExprState->pExpr = pQNode; } m_pCurExprState->bExpectingOperator = TRUE; m_pCurExprState->pLastNode = pQNode; } pXPathComponent->pXPathNode = m_pCurExprState->pLastNode; // Link the new XPATH component into the XPATH as the last // component. if ((pXPathComponent->pPrev = pXPath->pLastComponent) != NULL) { pXPath->pLastComponent->pNext = pXPathComponent; } else { pXPath->pFirstComponent = pXPathComponent; } pXPath->pLastComponent = pXPathComponent; if (pNodeSource) { if (RC_BAD( rc = objectAddRef( pNodeSource))) { goto Exit; } } Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Keep track of objects supplied by the application that we use for callbacks, etc. ***************************************************************************/ RCODE F_Query::objectAddRef( F_Object * pObject) { RCODE rc = NE_XFLM_OK; // If object list is full, make room for 20 more if (m_uiObjectCount == m_uiObjectListSize) { if (RC_BAD( rc = f_realloc( sizeof( F_Object *) * (m_uiObjectListSize + 20), &m_ppObjectList))) { goto Exit; } m_uiObjectListSize += 20; } m_ppObjectList [m_uiObjectCount++] = pObject; pObject->AddRef(); Exit: return( rc); } /*************************************************************************** Desc: Get a context position from a value. It must be a positive integer. Anything else will cause an error to be returned. ***************************************************************************/ FSTATIC RCODE fqGetPosition( FQVALUE * pQValue, FLMUINT * puiPos ) { RCODE rc = NE_XFLM_OK; switch (pQValue->eValType) { case XFLM_UINT_VAL: if (!pQValue->val.uiVal) { rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); goto Exit; } *puiPos = pQValue->val.uiVal; break; case XFLM_UINT64_VAL: if (!pQValue->val.ui64Val || pQValue->val.ui64Val > (FLMUINT64)(~((FLMUINT)0))) { rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); goto Exit; } *puiPos = (FLMUINT)pQValue->val.ui64Val; break; case XFLM_INT_VAL: if (pQValue->val.iVal <= 0) { rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); goto Exit; } *puiPos = (FLMUINT)pQValue->val.iVal; break; case XFLM_INT64_VAL: if (pQValue->val.i64Val <= 0 || pQValue->val.i64Val > (FLMINT64)(~((FLMUINT)0))) { rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); goto Exit; } *puiPos = (FLMUINT)pQValue->val.i64Val; break; default: rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Determine if an XPATH component has a test for context position. ***************************************************************************/ FINLINE FLMBOOL hasContextPosTest( XPATH_COMPONENT * pXPathComponent ) { return( pXPathComponent->pContextPosExpr || pXPathComponent->uiContextPosNeeded ? TRUE : FALSE); } /*************************************************************************** Desc: Add an operator to the query expression ***************************************************************************/ RCODE FLMAPI F_Query::addOperator( eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; FQEXPR * pQExpr; FQNODE * pParentNode; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (!m_pCurExprState) { if (RC_BAD( rc = allocExprState())) { goto Exit; } } // If we are expecting a left paren (for a function), that is // the only thing that is acceptable at this point. if (m_pCurExprState->bExpectingLParen && eOperator != XFLM_LPAREN_OP) { rc = RC_SET( NE_XFLM_Q_EXPECTING_LPAREN); goto Exit; } switch (eOperator) { case XFLM_LPAREN_OP: // If the operator is a left paren, increment the nesting level if (expectingOperator()) { rc = RC_SET( NE_XFLM_Q_UNEXPECTED_LPAREN); goto Exit; } m_pCurExprState->uiNestLevel++; m_pCurExprState->bExpectingLParen = FALSE; goto Exit; case XFLM_RPAREN_OP: if (expectingOperand()) { rc = RC_SET( NE_XFLM_Q_UNEXPECTED_RPAREN); goto Exit; } if (!m_pCurExprState->uiNestLevel) { rc = RC_SET( NE_XFLM_Q_UNMATCHED_RPAREN); goto Exit; } m_pCurExprState->uiNestLevel--; // See if this is the right paren to a function call if (!m_pCurExprState->uiNestLevel && parsingFunction()) { // If we have a valid expression, link it to the // function node as its last parameter. if (m_pCurExprState->pExpr) { m_pCurExprState->uiNumExpressions++; // uiNumExprNeeded might be zero. if (m_pCurExprState->uiNumExpressions > m_pCurExprState->uiNumExprNeeded) { rc = RC_SET( NE_XFLM_Q_INVALID_NUM_FUNC_ARGS); goto Exit; } // Allocate an expression node and link it to the // function. if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQEXPR), (void **)&pQExpr))) { goto Exit; } if ((pQExpr->pPrev = m_pCurExprState->pQFunction->pLastArg) != NULL) { pQExpr->pPrev->pNext = pQExpr; } else { m_pCurExprState->pQFunction->pFirstArg = pQExpr; } m_pCurExprState->pQFunction->pLastArg = pQExpr; pQExpr->pExpr = m_pCurExprState->pExpr; if (RC_BAD( rc = getPredicates( &pQExpr->pExpr, NULL, m_pCurExprState->pXPathComponent))) { goto Exit; } // If this is a user-defined function, make sure the // expression parameter (there should only be one) is // an XPATH expression, and that the getPredicates call // didn't eliminate the expression. if (m_pCurExprState->pQFunction->pFuncObj) { if (!pQExpr->pExpr || pQExpr->pExpr->eNodeType != FLM_XPATH_NODE) { rc = RC_SET( NE_XFLM_Q_INVALID_FUNC_ARG); goto Exit; } } } // See if we got the required number of arguments for // the function if (m_pCurExprState->uiNumExpressions < m_pCurExprState->uiNumExprNeeded) { rc = RC_SET( NE_XFLM_Q_INVALID_NUM_FUNC_ARGS); goto Exit; } // Return to the former context. m_pCurExprState = m_pCurExprState->pPrev; } goto Exit; case XFLM_NEG_OP: case XFLM_NOT_OP: if (expectingOperator()) { rc = RC_SET( NE_XFLM_Q_EXPECTING_OPERATOR); goto Exit; } break; case XFLM_COMMA_OP: // In order for a comma to be legal, the following conditions // must be met: // 1) Must be inside a function // 2) Must need at least two arguments for the function // 3) Must not already have enough arguments // 4) Must be at nesting level 1 // 5) Must be expecting an operator // 6) Must have a non-empty expression we can link to // function node. if (!parsingFunction() || m_pCurExprState->uiNumExprNeeded < 2 || m_pCurExprState->uiNumExpressions < m_pCurExprState->uiNumExprNeeded - 1 || m_pCurExprState->uiNestLevel > 1 || expectingOperand() || !m_pCurExprState->pExpr) { rc = RC_SET( NE_XFLM_Q_UNEXPECTED_COMMA); goto Exit; } m_pCurExprState->uiNumExpressions++; // Allocate an expression node and link it to the if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQEXPR), (void **)&pQExpr))) { goto Exit; } if ((pQExpr->pPrev = m_pCurExprState->pQFunction->pLastArg) != NULL) { pQExpr->pPrev->pNext = pQExpr; } else { m_pCurExprState->pQFunction->pFirstArg = pQExpr; } m_pCurExprState->pQFunction->pLastArg = pQExpr; if (RC_BAD( rc = getPredicates( &pQExpr->pExpr, NULL, m_pCurExprState->pXPathComponent))) { goto Exit; } // Reset the expression m_pCurExprState->pExpr = NULL; m_pCurExprState->pCurOperatorNode = NULL; // The following conditions better already be set flmAssert( expectingOperand()); flmAssert( !m_pCurExprState->bExpectingLParen); flmAssert( m_pCurExprState->uiNestLevel == 1); goto Exit; case XFLM_LBRACKET_OP: // Last node has to be an XPATH node if (m_pCurExprState->pLastNode->eNodeType != FLM_XPATH_NODE) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_LBRACKET); goto Exit; } // Save the last node into pQNode, because when we call allocExprState // we will no longer be able to get at it. pQNode = m_pCurExprState->pLastNode; // Cannot add expressions if the last component already has // an expression to test context position. if (hasContextPosTest( pQNode->nd.pXPath->pLastComponent)) { rc = RC_SET( NE_XFLM_Q_NEW_EXPR_NOT_ALLOWED); goto Exit; } // Always allocate a new expression state for an xpath expression if (RC_BAD( rc = allocExprState())) { goto Exit; } m_pCurExprState->pXPathComponent = pQNode->nd.pXPath->pLastComponent; goto Exit; case XFLM_RBRACKET_OP: // Right bracket is only allowed if we are parsing an // xpath expression and we are at nesting level zero and // we are not expecting an operand if (!parsingXPathExpr() || m_pCurExprState->uiNestLevel || (expectingOperand() && m_pCurExprState->pExpr)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_RBRACKET); goto Exit; } // If we have a non-empty expression, link it to the // list of xpath component expressions off of the xpath // component. if (m_pCurExprState->pExpr) { // If the XPATH component does not have any expression yet, // make this expression its expression. Otherwise, AND // this expression to the existing expression. if (m_pCurExprState->pExpr->eNodeType == FLM_VALUE_NODE) { if (RC_BAD( rc = fqGetPosition( &m_pCurExprState->pExpr->currVal, &m_pCurExprState->pXPathComponent->uiContextPosNeeded))) { goto Exit; } } else if (m_pCurExprState->pExpr->eNodeType == FLM_OPERATOR_NODE && isArithOp( m_pCurExprState->pExpr->nd.op.eOperator)) { m_pCurExprState->pXPathComponent->pContextPosExpr = m_pCurExprState->pExpr; if (RC_BAD( rc = getPredicates( &m_pCurExprState->pXPathComponent->pContextPosExpr, NULL, m_pCurExprState->pXPathComponent))) { goto Exit; } // If, after the optimization, we are left with a constant, // NULL out pContextPosExpr and put it into uiContextPosNeeded. if (m_pCurExprState->pXPathComponent->pContextPosExpr->eNodeType == FLM_VALUE_NODE) { if (RC_BAD( rc = fqGetPosition( &m_pCurExprState->pXPathComponent->pContextPosExpr->currVal, &m_pCurExprState->pXPathComponent->uiContextPosNeeded))) { goto Exit; } m_pCurExprState->pXPathComponent->pContextPosExpr = NULL; } } else if (!m_pCurExprState->pXPathComponent->pExpr) { m_pCurExprState->pXPathComponent->pExpr = m_pCurExprState->pExpr; if (RC_BAD( rc = getPredicates( &m_pCurExprState->pXPathComponent->pExpr, NULL, m_pCurExprState->pXPathComponent))) { goto Exit; } } else { // Create an AND node and link the existing expression with // this new expression as children of this new AND node. if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQNODE), (void **)&pQNode))) { goto Exit; } pQNode->eNodeType = FLM_OPERATOR_NODE; pQNode->nd.op.eOperator = XFLM_AND_OP; fqLinkFirstChild( pQNode, m_pCurExprState->pXPathComponent->pExpr); fqLinkLastChild( pQNode, m_pCurExprState->pExpr); m_pCurExprState->pXPathComponent->pExpr = pQNode; // Set up a context node for the new AND node. // If the left operand's context (which would have been // set up previously by a call to getPredicates) is // an intersect context, we can point this node // right at it, and make the context's root node this // new node. Otherwise, we have to create a new context // and link the left operand's context to it. if (pQNode->pFirstChild->pContext->bIntersect) { pQNode->pContext = pQNode->pFirstChild->pContext; pQNode->pContext->pQRootNode = pQNode; } else { if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) { goto Exit; } // Put first child's context as child of this node's // context. pQNode->pContext->pFirstChild = pQNode->pFirstChild->pContext; pQNode->pContext->pLastChild = pQNode->pFirstChild->pContext; pQNode->pFirstChild->pContext->pParent = pQNode->pContext; } // Get the predicates of ONLY the right-hand side of the // tree - because we haven't gotten its predicates yet, but // the left hand side has already been done. if (RC_BAD( rc = getPredicates( &m_pCurExprState->pXPathComponent->pExpr, pQNode->pLastChild, m_pCurExprState->pXPathComponent))) { goto Exit; } } } // Return to the former context. m_pCurExprState = m_pCurExprState->pPrev; goto Exit; default: if (expectingOperand()) { rc = RC_SET( NE_XFLM_Q_EXPECTING_OPERAND); goto Exit; } if (!isLegalOperator( eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERATOR); goto Exit; } break; } // Cannot set both XFLM_COMP_COMPRESS_WHITESPACE and XFLM_COMP_NO_WHITESPACE // in comparison rules. Also, cannot set XFLM_COMP_IGNORE_LEADING_SPACE or // XFLM_COMP_IGNORE_TRAILING_SPACE with XFLM_COMP_NO_WHITESPACE. if ((uiCompareRules & XFLM_COMP_NO_WHITESPACE) && (uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | XFLM_COMP_IGNORE_LEADING_SPACE | XFLM_COMP_IGNORE_TRAILING_SPACE))) { rc = RC_SET_AND_ASSERT( NE_XFLM_Q_ILLEGAL_COMPARE_RULES); goto Exit; } // Make a QNODE and find a place for it in the query tree if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQNODE), (void **)&pQNode))) { goto Exit; } pQNode->eNodeType = FLM_OPERATOR_NODE; pQNode->nd.op.eOperator = eOperator; pQNode->nd.op.uiCompareRules = uiCompareRules; pQNode->nd.op.pOpComparer = pOpComparer; pQNode->uiNestLevel = m_pCurExprState->uiNestLevel; // If this is the first operator in the query, set the current operator // to it and graft in the current operand as its child. if (!m_pCurExprState->pExpr) { m_pCurExprState->pExpr = pQNode; m_pCurExprState->pCurOperatorNode = pQNode; goto Exit; } // Go up the stack until an operator whose nest level or precedence is < // this one's is encountered, then link this one in as the last child pParentNode = m_pCurExprState->pCurOperatorNode; while (pParentNode && (pParentNode->uiNestLevel > pQNode->uiNestLevel || (pParentNode->uiNestLevel == pQNode->uiNestLevel && getPrecedence( pParentNode->nd.op.eOperator) >= getPrecedence( eOperator)))) { pParentNode = pParentNode->pParent; } if (!pParentNode) { if (m_pCurExprState->pExpr) { fqLinkLastChild( pQNode, m_pCurExprState->pExpr); } m_pCurExprState->pExpr = pQNode; } else if (eOperator == XFLM_NOT_OP || eOperator == XFLM_NEG_OP) { // Need to treat NOT and NEG as if they were operands. // Parent better be an operator. flmAssert( pParentNode->eNodeType == FLM_OPERATOR_NODE); #ifdef FLM_DEBUG if (pParentNode->nd.op.eOperator == XFLM_NEG_OP || pParentNode->nd.op.eOperator == XFLM_NOT_OP) { // Must have no children. flmAssert( pParentNode->pFirstChild == NULL); } else { // Must only have one or zero children. flmAssert( pParentNode->pFirstChild == pParentNode->pLastChild); } #endif fqLinkLastChild( pParentNode, pQNode); flmAssert( !m_pCurExprState->bExpectingOperator); } else { // Parent better be an operator. flmAssert( pParentNode->eNodeType == FLM_OPERATOR_NODE); // Unlink last child of parent node and replace with this // new node. The parent node better already have the correct // number of children, or we are not parsing correctly. flmAssert( pParentNode->pFirstChild); if (pParentNode->nd.op.eOperator == XFLM_NEG_OP || pParentNode->nd.op.eOperator == XFLM_NOT_OP) { // Better only be one child. flmAssert( !pParentNode->pFirstChild->pNextSib); fqLinkLastChild( pQNode, pParentNode->pFirstChild); } else { // Better only be two child nodes flmAssert( pParentNode->pFirstChild->pNextSib == pParentNode->pLastChild); fqLinkLastChild( pQNode, pParentNode->pLastChild); } fqLinkLastChild( pParentNode, pQNode); } m_pCurExprState->pCurOperatorNode = pQNode; m_pCurExprState->bExpectingOperator = FALSE; m_pCurExprState->pLastNode = pQNode; if (pOpComparer) { if (RC_BAD( rc = objectAddRef( pOpComparer))) { goto Exit; } } Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Add a function to the query expression ***************************************************************************/ RCODE FLMAPI F_Query::addFunction( eQueryFunctions eFunction, IF_QueryValFunc * pFuncObj, FLMBOOL bHaveXPathExpr) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode; FQFUNCTION * pQFunction; FQNODE * pParentNode; XPATH_COMPONENT * pSaveXPathComponent; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (!m_pCurExprState) { if (RC_BAD( rc = allocExprState())) { goto Exit; } } // Must be expecting an operand if (expectingOperator()) { rc = RC_SET( NE_XFLM_Q_EXPECTING_OPERATOR); goto Exit; } // Allocate a function node if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQNODE), (void **)&pQNode))) { goto Exit; } if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQFUNCTION), (void **)&pQFunction))) { goto Exit; } pQNode->nd.pQFunction = pQFunction; pQNode->eNodeType = FLM_FUNCTION_NODE; pQFunction->eFunction = eFunction; pQFunction->pFuncObj = pFuncObj; // See if this is the first node in the expression. if (!m_pCurExprState->pExpr) { m_pCurExprState->pExpr = pQNode; } else { pParentNode = m_pCurExprState->pCurOperatorNode; flmAssert( pParentNode); // Parent better be an operator, and better have room for // function to be linked as one of its operands. flmAssert( pParentNode->eNodeType == FLM_OPERATOR_NODE); if (pParentNode->nd.op.eOperator == XFLM_NEG_OP || pParentNode->nd.op.eOperator == XFLM_NOT_OP) { // Better not have any children yet. flmAssert( !pParentNode->pFirstChild); } else { // Better only have one child node. flmAssert( pParentNode->pFirstChild == pParentNode->pLastChild); } fqLinkLastChild( pParentNode, pQNode); } m_pCurExprState->pLastNode = pQNode; pSaveXPathComponent = m_pCurExprState->pXPathComponent; // Always allocate a new expression state for a function if (RC_BAD( rc = allocExprState())) { goto Exit; } // First thing we expect in this state is a left paren m_pCurExprState->bExpectingLParen = TRUE; m_pCurExprState->pQFunction = pQFunction; m_pCurExprState->pXPathComponent = pSaveXPathComponent; if (pFuncObj) { if (RC_BAD( rc = objectAddRef( pFuncObj))) { goto Exit; } // In this case, the expression must return a node. m_pCurExprState->uiNumExprNeeded = bHaveXPathExpr ? (FLMUINT)1 : (FLMUINT)0; } else { //visit // m_pCurExprState->uiNumExprNeeded = ??? - number specified by the function. } // Parent state needs to be expecting an operator when we come out from // parsing the function. m_pCurExprState->pPrev->bExpectingOperator = TRUE; Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Compare two values ***************************************************************************/ FSTATIC RCODE fqCompareValues( FQVALUE * pValue1, FLMBOOL bInclusive1, FLMBOOL bNullIsLow1, FQVALUE * pValue2, FLMBOOL bInclusive2, FLMBOOL bNullIsLow2, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piCmp) { RCODE rc = NE_XFLM_OK; // We have already called fqCanCompare, so no need to do it here if (!pValue1) { if (!pValue2) { if (bNullIsLow2) { *piCmp = (FLMINT)(bNullIsLow1 ? 0 : 1); } else { *piCmp = (FLMINT)(bNullIsLow1 ? -1 : 0); } } else { *piCmp = (FLMINT)(bNullIsLow1 ? -1 : 1); } goto Exit; } else if (!pValue2) { *piCmp = (FLMINT)(bNullIsLow2 ? 1 : -1); goto Exit; } if (RC_BAD( rc = fqCompare( pValue1, pValue2, uiCompareRules, NULL, uiLanguage, piCmp))) { goto Exit; } // If everything else is equal, the last distinguisher // is the inclusive flags and which side of the // value we are on if we are exclusive which is indicated // by the bNullIsLow flags if (*piCmp == 0) { if (bInclusive1 != bInclusive2) { if (bNullIsLow1) { if (bNullIsLow2) { // *--> v1 // o--> v2 v1 < v2 // o--> v1 // *--> v2 v1 > v2 *piCmp = bInclusive1 ? -1 : 1; } else { // *--> v1 // v2 <--o v1 > v2 // o--> v1 // v2 <--* v1 > v2 *piCmp = 1; } } else { if (bNullIsLow2) { // v1 <--* // o--> v2 v1 < v2 // v1 <--o // *--> v2 v1 < v2 *piCmp = -1; } else { // v1 <--* // v2 <--o v1 > v2 // v1 <--o // v2 <--* v1 < v2 *piCmp = bInclusive1 ? 1 : -1; } } } else if (!bInclusive1) { // bInclusive2 is also FALSE if (bNullIsLow1) { if (!bNullIsLow2) { // o--> v1 // v2 <--o v1 > v2 *piCmp = 1; } // else // { // o--> v1 // o--> v2 v1 == v2 // *piCmp = 0; // } } else { if (bNullIsLow2) { // v1 <--o // o--> v2 v1 < v2 *piCmp = -1; } // else // { // v1 <--o // v2 <--o v1 == v2 // *piCmp = 0; // } } } // else // { // bInclusive1 == TRUE && bInclusive2 == TRUE // else case covers the cases where // both are inclusive, in which case it // doesn't matter which is low and which // is high // v1 <--* // *--> v2 v1 == v2 // v1 <--* // v2 <--* v1 == v2 // *--> v1 // v2 <--* v1 == v2 // *--> v1 // *--> v2 v1 == v2 // } } Exit: return( rc); } /*************************************************************************** Desc: Intersect a predicate into a context path. ***************************************************************************/ RCODE F_Query::intersectPredicates( CONTEXT_PATH * pContextPath, FQNODE * pXPathNode, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FQNODE * pContextNode, FLMBOOL bNotted, FQVALUE * pQValue, FLMBOOL * pbClipContext ) { RCODE rc = NE_XFLM_OK; PATH_PRED * pPred; FLMINT iCmp; FLMBOOL bDoMatch; if (!pQValue || pQValue->eValType != XFLM_UTF8_VAL) { bDoMatch = FALSE; // Comparison rules don't matter for anything that is // not text, so we normalize them to zero, so the test // below to see if the comparison rule is the same as // the comparison rule of the operator will work. uiCompareRules = 0; } else { bDoMatch = (eOperator == XFLM_EQ_OP && (pQValue->uiFlags & VAL_IS_CONSTANT) && (pQValue->uiFlags & VAL_HAS_WILDCARDS)) ? TRUE : FALSE; } if ((pPred = pContextPath->pFirstPred) != NULL) { if (eOperator == XFLM_EXISTS_OP) { // An exists operator will either // merge with an existing predicate or // cancel the whole thing out as an // empty result. // If this predicate is not-exists, another // predicate ANDed with this one can never // return a result that will match, unless // that predicate is also a not-exists, in // which case, we simply combine this one // with that one. if (bNotted) { if (pPred->eOperator != XFLM_EXISTS_OP || !pPred->bNotted) { *pbClipContext = TRUE; } } goto Exit; } else if (pPred->eOperator == XFLM_EXISTS_OP) { // If the first predicate is an exists operator // it will be the only one, because otherwise // it will have been merged with another operator // in the code just above. flmAssert( !pPred->pNext); // If the predicate is notted, another predicate // ANDed with this one can never return a result. if (pPred->bNotted) { *pbClipContext = TRUE; } else { // Change the predicate to the current // operator. pPred->eOperator = eOperator; pPred->pFromValue = pQValue; pPred->bNotted = bNotted; } goto Exit; } else if ((eOperator == XFLM_EQ_OP && !bDoMatch) || eOperator == XFLM_LE_OP || eOperator == XFLM_LT_OP || eOperator == XFLM_GE_OP || eOperator == XFLM_GT_OP) { // If there is range operator, there // should only be one of them with the // same compare rules, because they // will all always be merged with these operators // or they will cancel to yield an empty result // when doing intersections. while (pPred) { if (pPred->eOperator == XFLM_RANGE_OP && pPred->uiCompareRules == uiCompareRules) { FQVALUE * pFromValue; FQVALUE * pUntilValue; FLMBOOL bInclFrom; FLMBOOL bInclUntil; pFromValue = (eOperator == XFLM_EQ_OP || eOperator == XFLM_GE_OP || eOperator == XFLM_GT_OP) ? pQValue : NULL; pUntilValue = (eOperator == XFLM_EQ_OP || eOperator == XFLM_LE_OP || eOperator == XFLM_LT_OP) ? pQValue : NULL; bInclFrom = (FLMBOOL)(eOperator == XFLM_EQ_OP || eOperator == XFLM_GE_OP ? TRUE : FALSE); bInclUntil = (FLMBOOL)(eOperator == XFLM_EQ_OP || eOperator == XFLM_LE_OP ? TRUE : FALSE); // If the value type is not compatible with the predicate's // value type, we cannot do the comparison, and there is // no intersection. if (!fqCanCompare( pQValue, pPred->pFromValue) || !fqCanCompare( pQValue, pPred->pUntilValue)) { *pbClipContext = TRUE; } else if (RC_BAD( rc = fqCompareValues( pFromValue, bInclFrom, TRUE, pPred->pFromValue, pPred->bInclFrom, TRUE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } else if (iCmp > 0) { // From value is greater than predicate's from value. // If the from value is also greater than the predicate's // until value, we have no intersection. if (RC_BAD( rc = fqCompareValues( pFromValue, bInclFrom, TRUE, pPred->pUntilValue, pPred->bInclUntil, FALSE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } if (iCmp > 0) { *pbClipContext = TRUE; } else { pPred->pFromValue = pFromValue; pPred->bInclFrom = bInclFrom; } } else if (RC_BAD( rc = fqCompareValues( pUntilValue, bInclUntil, FALSE, pPred->pUntilValue, pPred->bInclUntil, FALSE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } else if (iCmp < 0) { // Until value is less than predicate's until value. If the // until value is also less than predicate's from value, we // have no intersection. if (RC_BAD( rc = fqCompareValues( pUntilValue, bInclUntil, FALSE, pPred->pFromValue, pPred->bInclFrom, TRUE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } if (iCmp < 0) { *pbClipContext = TRUE; } else { pPred->pUntilValue = pUntilValue; pPred->bInclUntil = bInclUntil; } } goto Exit; } pPred = pPred->pNext; } } } // Add a new predicate to the context path if (RC_BAD( rc = m_pool.poolCalloc( sizeof( PATH_PRED), (void **)&pPred))) { goto Exit; } pPred->uiCompareRules = uiCompareRules; pPred->pOpComparer = pOpComparer; // Link the predicate as the last predicate for the path if ((pPred->pPrev = pContextPath->pLastPred) != NULL) { pPred->pPrev->pNext = pPred; } else { pContextPath->pFirstPred = pPred; } pContextPath->pLastPred = pPred; // Set other items in the predicate. pPred->pContextNode = pContextNode; pPred->bNotted = bNotted; switch (eOperator) { case XFLM_EXISTS_OP: case XFLM_NE_OP: pPred->eOperator = eOperator; pPred->pFromValue = pQValue; break; case XFLM_APPROX_EQ_OP: pPred->eOperator = eOperator; pPred->pFromValue = pQValue; pPred->bInclFrom = TRUE; pPred->bInclUntil = TRUE; break; case XFLM_EQ_OP: if (bDoMatch) { pPred->eOperator = XFLM_MATCH_OP; pPred->pFromValue = pQValue; } else { pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = pQValue; pPred->pUntilValue = pQValue; pPred->bInclFrom = TRUE; pPred->bInclUntil = TRUE; } break; case XFLM_LE_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = NULL; pPred->pUntilValue = pQValue; pPred->bInclUntil = TRUE; break; case XFLM_LT_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = NULL; pPred->pUntilValue = pQValue; pPred->bInclUntil = FALSE; break; case XFLM_GE_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = pQValue; pPred->pUntilValue = NULL; pPred->bInclFrom = TRUE; break; case XFLM_GT_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = pQValue; pPred->pUntilValue = NULL; pPred->bInclFrom = FALSE; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } Exit: if (RC_OK( rc) && !(*pbClipContext)) { PATH_PRED_NODE * pPathPredNode; if (RC_OK( rc = m_pool.poolCalloc( sizeof( PATH_PRED_NODE), (void **)&pPathPredNode))) { pPathPredNode->pXPathNode = pXPathNode; pPathPredNode->pNext = pPred->pXPathNodeList; pPred->pXPathNodeList = pPathPredNode; } } return( rc); } /*************************************************************************** Desc: Check to see if any predicates need to be unioned with the passed in predicate. If so, perform the union. ***************************************************************************/ FSTATIC RCODE fqCheckUnionPredicates( CONTEXT_PATH * pContextPath, FLMUINT uiLanguage, PATH_PRED * pPred ) { RCODE rc = NE_XFLM_OK; PATH_PRED * pMergePred; FLMINT iCmp; FLMBOOL bDidOverlap; pMergePred = pContextPath->pFirstPred; // This should only be done on predicates that have a range // operator. flmAssert( pPred->eOperator == XFLM_RANGE_OP); while (pMergePred) { bDidOverlap = FALSE; if (pMergePred != pPred && pMergePred->eOperator == XFLM_RANGE_OP && pMergePred->uiCompareRules == pPred->uiCompareRules) { // If the value type is not compatible with the predicate's // value type, we cannot do the comparison, and there is // no overlap. if (!fqCanCompare( pMergePred->pFromValue, pPred->pFromValue) || !fqCanCompare( pMergePred->pFromValue, pPred->pUntilValue) || !fqCanCompare( pMergePred->pUntilValue, pPred->pFromValue) || !fqCanCompare( pMergePred->pUntilValue, pPred->pUntilValue)) { // Nothing to do here } else if (RC_BAD( rc = fqCompareValues( pMergePred->pFromValue, pMergePred->bInclFrom, TRUE, pPred->pFromValue, pPred->bInclFrom, TRUE, pPred->uiCompareRules, uiLanguage, &iCmp))) { goto Exit; } else if (iCmp >= 0) { // From value is greater than or equal to the predicate's // from value. // If the from value is also less than or equal to the // predicate's until value, we have an overlap. if (RC_BAD( rc = fqCompareValues( pMergePred->pFromValue, pMergePred->bInclFrom, TRUE, pPred->pUntilValue, pPred->bInclUntil, FALSE, pPred->uiCompareRules, uiLanguage, &iCmp))) { goto Exit; } if (iCmp <= 0) { // If the until value is greater than the predicate's // until value, change the predicate's until value. if (RC_BAD( rc = fqCompareValues( pMergePred->pUntilValue, pMergePred->bInclUntil, FALSE, pPred->pUntilValue, pPred->bInclUntil, FALSE, pPred->uiCompareRules, uiLanguage, &iCmp))) { goto Exit; } if (iCmp > 0) { pPred->pUntilValue = pMergePred->pUntilValue; pPred->bInclUntil = pMergePred->bInclUntil; } bDidOverlap = TRUE; } } // At this point we already know that the from value is // less than the predicate's from value. // See if the until value is greater than or equal // to the from value. If it is we have an overlap. else if (RC_BAD( rc = fqCompareValues( pMergePred->pUntilValue, pMergePred->bInclUntil, FALSE, pPred->pFromValue, pPred->bInclFrom, TRUE, pPred->uiCompareRules, uiLanguage, &iCmp))) { goto Exit; } else if (iCmp >= 0) { // Until value is greater than or equal to the predicate's // from value, so we definitely have an overlap. We // already know that the from value is less than the // predicate's from value, so we will change that for sure. pPred->pFromValue = pMergePred->pFromValue; pPred->bInclFrom = pMergePred->bInclFrom; // See if the until value is greater than the // predicate's until value, in which case we need to // change the predicate's until value. if (RC_BAD( rc = fqCompareValues( pMergePred->pUntilValue, pMergePred->bInclUntil, FALSE, pPred->pUntilValue, pPred->bInclUntil, FALSE, pPred->uiCompareRules, uiLanguage, &iCmp))) { goto Exit; } if (iCmp > 0) { pPred->pUntilValue = pMergePred->pUntilValue; pPred->bInclUntil = pMergePred->bInclUntil; } bDidOverlap = TRUE; } } // If the predicates overlapped, remove pMergePred from the list // of predicates. But move its list of predicate nodes into the // list off of pPred. if (bDidOverlap) { PATH_PRED_NODE * pPathPredNode; // Merge the predicate node lists, if any - into pPred's list. if ((pPathPredNode = pPred->pXPathNodeList) != NULL) { while (pPathPredNode->pNext) { pPathPredNode = pPathPredNode->pNext; } pPathPredNode->pNext = pMergePred->pXPathNodeList; } else { pPred->pXPathNodeList = pMergePred->pXPathNodeList; } // Remove pMergePred from the list if (pMergePred->pPrev) { pMergePred->pPrev->pNext = pMergePred->pNext; } else { pContextPath->pFirstPred = pMergePred->pNext; } if (pMergePred->pNext) { pMergePred->pNext->pPrev = pMergePred->pPrev; // Set up so we are on the next node pMergePred = pMergePred->pNext; } else { pContextPath->pLastPred = pMergePred->pPrev; } } else { pMergePred = pMergePred->pNext; } } Exit: return( rc); } /*************************************************************************** Desc: Union a predicate into a context path. ***************************************************************************/ RCODE F_Query::unionPredicates( CONTEXT_PATH * pContextPath, FQNODE * pXPathNode, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FQNODE * pContextNode, FLMBOOL bNotted, FQVALUE * pQValue ) { RCODE rc = NE_XFLM_OK; PATH_PRED * pPred; FLMINT iCmp; FLMBOOL bDoMatch; FLMBOOL bDidOverlap = FALSE; if (!pQValue || pQValue->eValType != XFLM_UTF8_VAL) { bDoMatch = FALSE; // Comparison rules don't matter for anything that is // not text, so we normalize them to zero, so the test // below to see if the comparison rule is the same as // the comparison rule of the operator will work. uiCompareRules = 0; } else { bDoMatch = (eOperator == XFLM_EQ_OP && (pQValue->uiFlags & VAL_IS_CONSTANT) && (pQValue->uiFlags & VAL_HAS_WILDCARDS)) ? TRUE : FALSE; } if ((pPred = pContextPath->pFirstPred) != NULL) { if (eOperator == XFLM_EXISTS_OP || eOperator == XFLM_NE_OP) { // See if there is another operator that is an exact // match of this one. while (pPred) { if (pPred->eOperator == eOperator) { if ((bNotted && pPred->bNotted) || (!bNotted && !pPred->bNotted)) { // Perfect match - no need to do any more. goto Exit; } } pPred = pPred->pNext; } } else if ((eOperator == XFLM_EQ_OP && !bDoMatch) || eOperator == XFLM_LE_OP || eOperator == XFLM_LT_OP || eOperator == XFLM_GE_OP || eOperator == XFLM_GT_OP) { // See if the operator overlaps with another range operator while (pPred) { if (pPred->eOperator == XFLM_RANGE_OP && pPred->uiCompareRules == uiCompareRules) { FQVALUE * pFromValue; FQVALUE * pUntilValue; FLMBOOL bInclFrom; FLMBOOL bInclUntil; pFromValue = (eOperator == XFLM_EQ_OP || eOperator == XFLM_GE_OP || eOperator == XFLM_GT_OP) ? pQValue : NULL; pUntilValue = (eOperator == XFLM_EQ_OP || eOperator == XFLM_LE_OP || eOperator == XFLM_LT_OP) ? pQValue : NULL; bInclFrom = (FLMBOOL)(eOperator == XFLM_EQ_OP || eOperator == XFLM_GE_OP ? TRUE : FALSE); bInclUntil = (FLMBOOL)(eOperator == XFLM_EQ_OP || eOperator == XFLM_LE_OP ? TRUE : FALSE); // If the value type is not compatible with the predicate's // value type, we cannot do the comparison, and there is // no overlap. if (!fqCanCompare( pQValue, pPred->pFromValue) || !fqCanCompare( pQValue, pPred->pUntilValue)) { // Nothing to do here } else if (RC_BAD( rc = fqCompareValues( pFromValue, bInclFrom, TRUE, pPred->pFromValue, pPred->bInclFrom, TRUE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } else if (iCmp >= 0) { // From value is greater than or equal to the predicate's // from value. // If the from value is also less than or equal to the // predicate's until value, we have an overlap. if (RC_BAD( rc = fqCompareValues( pFromValue, bInclFrom, TRUE, pPred->pUntilValue, pPred->bInclUntil, FALSE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } if (iCmp <= 0) { // If the until value is greater than the predicate's // until value, change the predicate's until value. if (RC_BAD( rc = fqCompareValues( pUntilValue, bInclUntil, FALSE, pPred->pUntilValue, pPred->bInclUntil, FALSE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } if (iCmp > 0) { pPred->pUntilValue = pUntilValue; pPred->bInclUntil = bInclUntil; } bDidOverlap = TRUE; goto Exit; } } // At this point we already know that the from value is // less than the predicate's from value. // See if the until value is greater than or equal // to the from value. If it is we have an overlap. else if (RC_BAD( rc = fqCompareValues( pUntilValue, bInclUntil, FALSE, pPred->pFromValue, pPred->bInclFrom, TRUE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } else if (iCmp >= 0) { // Until value is greater than or equal to the predicate's // from value, so we definitely have an overlap. We // already know that the from value is less than the // predicate's from value, so we will change that for sure. pPred->pFromValue = pFromValue; pPred->bInclFrom = bInclFrom; // See if the until value is greater than the // predicate's until value, in which case we need to // change the predicate's until value. if (RC_BAD( rc = fqCompareValues( pUntilValue, bInclUntil, FALSE, pPred->pUntilValue, pPred->bInclUntil, FALSE, uiCompareRules, m_uiLanguage, &iCmp))) { goto Exit; } if (iCmp > 0) { pPred->pUntilValue = pUntilValue; pPred->bInclUntil = bInclUntil; } bDidOverlap = TRUE; goto Exit; } } pPred = pPred->pNext; } } } // Add a new predicate to the context path if (RC_BAD( rc = m_pool.poolCalloc( sizeof( PATH_PRED), (void **)&pPred))) { goto Exit; } pPred->uiCompareRules = uiCompareRules; pPred->pOpComparer = pOpComparer; // Link the predicate as the last predicate for the path if ((pPred->pPrev = pContextPath->pLastPred) != NULL) { pPred->pPrev->pNext = pPred; } else { pContextPath->pFirstPred = pPred; } pContextPath->pLastPred = pPred; // Set other items in the predicate. pPred->pContextNode = pContextNode; pPred->bNotted = bNotted; switch (eOperator) { case XFLM_EXISTS_OP: case XFLM_NE_OP: pPred->eOperator = eOperator; pPred->pFromValue = pQValue; break; case XFLM_APPROX_EQ_OP: pPred->eOperator = eOperator; pPred->pFromValue = pQValue; pPred->bInclFrom = TRUE; pPred->bInclUntil = TRUE; break; case XFLM_EQ_OP: if (bDoMatch) { pPred->eOperator = XFLM_MATCH_OP; pPred->pFromValue = pQValue; } else { pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = pQValue; pPred->pUntilValue = pQValue; pPred->bInclFrom = TRUE; pPred->bInclUntil = TRUE; } break; case XFLM_LE_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = NULL; pPred->pUntilValue = pQValue; pPred->bInclUntil = TRUE; break; case XFLM_LT_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = NULL; pPred->pUntilValue = pQValue; pPred->bInclUntil = FALSE; break; case XFLM_GE_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = pQValue; pPred->pUntilValue = NULL; pPred->bInclFrom = TRUE; break; case XFLM_GT_OP: pPred->eOperator = XFLM_RANGE_OP; pPred->pFromValue = pQValue; pPred->pUntilValue = NULL; pPred->bInclFrom = FALSE; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } Exit: if (RC_OK( rc)) { PATH_PRED_NODE * pPathPredNode; if (RC_OK( rc = m_pool.poolCalloc( sizeof( PATH_PRED_NODE), (void **)&pPathPredNode))) { pPathPredNode->pXPathNode = pXPathNode; pPathPredNode->pNext = pPred->pXPathNodeList; pPred->pXPathNodeList = pPathPredNode; if (bDidOverlap) { rc = fqCheckUnionPredicates( pContextPath, m_uiLanguage, pPred); } } } return( rc); } /*************************************************************************** Desc: Prune a context out of the context tree. ***************************************************************************/ FSTATIC void fqClipContext( OP_CONTEXT * pContext ) { // If this context had a parent, we can unlink it from // its parent. if (pContext->pParent) { if (pContext->pPrevSib) { pContext->pPrevSib->pNextSib = pContext->pNextSib; } else { pContext->pParent->pFirstChild = pContext->pNextSib; } if (pContext->pNextSib) { pContext->pNextSib->pPrevSib = pContext->pPrevSib; } else { pContext->pParent->pLastChild = pContext->pPrevSib; } } } /*************************************************************************** Desc: Add a predicate to a context. ***************************************************************************/ RCODE F_Query::addPredicateToContext( OP_CONTEXT * pContext, XPATH_COMPONENT * pXPathContext, XPATH_COMPONENT * pXPathComp, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FQNODE * pContextNode, FLMBOOL bNotted, FQVALUE * pQValue, FLMBOOL * pbClipContext, FQNODE ** ppQNode ) { RCODE rc = NE_XFLM_OK; CONTEXT_PATH * pContextPath = NULL; *pbClipContext = FALSE; // Better be the leaf component of the XPATH flmAssert( !pXPathComp->pNext); // Convert the constant value in a node id predicate to // a 64 bit unsigned value. if (eOperator != XFLM_EXISTS_OP && pXPathComp->eXPathAxis == META_AXIS) { if (RC_BAD( rc = fqGetNodeIdValue( pQValue))) { goto Exit; } } // See if we can find a matching XPATH component. If not, // create a new one. NOTE: We can only match if the axis // is the SELF_AXIS, or the XPATH component is an attribute. // If the XPATH component is an attribute, it is guaranteed // there there will only be one instance of the attribute // Note also that the matching is only done if we are in the // context of another XPATH component. if (pXPathContext && !pXPathComp->pPrev && !pXPathComp->pNodeSource) { if (pXPathComp->eXPathAxis == META_AXIS) { pContextPath = pContext->pFirstPath; while (pContextPath) { if (!pContextPath->pXPathComponent->pPrev && pContextPath->pXPathComponent->uiDictNum == pXPathComp->uiDictNum && pContextPath->pXPathComponent->eXPathAxis == META_AXIS) { break; } pContextPath = pContextPath->pNext; } } else if (pXPathComp->eNodeType == ELEMENT_NODE || pXPathComp->eNodeType == DATA_NODE) { if (pXPathComp->eXPathAxis == SELF_AXIS) { pContextPath = pContext->pFirstPath; while (pContextPath) { if (!pContextPath->pXPathComponent->pPrev && pContextPath->pXPathComponent->eXPathAxis == SELF_AXIS && pContextPath->pXPathComponent->uiDictNum == pXPathComp->uiDictNum && pContextPath->pXPathComponent->eNodeType == pXPathComp->eNodeType) { break; } pContextPath = pContextPath->pNext; } } } else if (pXPathComp->eNodeType == ATTRIBUTE_NODE) { pContextPath = pContext->pFirstPath; while (pContextPath) { if (!pContextPath->pXPathComponent->pPrev && pContextPath->pXPathComponent->uiDictNum == pXPathComp->uiDictNum && pContextPath->pXPathComponent->eNodeType == ATTRIBUTE_NODE) { break; } pContextPath = pContextPath->pNext; } } } // If we did not find one, allocate it. if (!pContextPath) { if (RC_BAD( rc = m_pool.poolCalloc( sizeof( CONTEXT_PATH), (void **)&pContextPath))) { goto Exit; } // Link the new context path into the context as its last path if ((pContextPath->pPrev = pContext->pLastPath) != NULL) { pContextPath->pPrev->pNext = pContextPath; } else { pContext->pFirstPath = pContextPath; } pContext->pLastPath = pContextPath; pContextPath->pXPathComponent = pXPathComp; } // See if this operator can be merged with another one. if (pContext->bIntersect) { if (RC_BAD( rc = intersectPredicates( pContextPath, *ppQNode, eOperator, uiCompareRules, pOpComparer, pContextNode, bNotted, pQValue, pbClipContext))) { goto Exit; } // If we get a false result, then we know that the // intersection of predicates is creating a situation where // it can never be true, so we will turn the root node of // the context into a XFLM_BOOL_VAL node with a FALSE value. // The branch of the query tree represented underneath this // node will have been cut off. The caller must account for this. if (*pbClipContext) { FQNODE * pRootNode = pContext->pQRootNode; pRootNode->eNodeType = FLM_VALUE_NODE; pRootNode->pFirstChild = NULL; pRootNode->pLastChild = NULL; pRootNode->pContext = pContext->pParent; pRootNode->currVal.eValType = XFLM_BOOL_VAL; pRootNode->currVal.uiFlags = VAL_IS_CONSTANT; pRootNode->currVal.val.eBool = XFLM_FALSE; *ppQNode = pRootNode; // Clip this context out of the context tree. // Don't want to travel down this path when optimizing. fqClipContext( pContext); } } else { if (RC_BAD( rc = unionPredicates( pContextPath, *ppQNode, eOperator, uiCompareRules, pOpComparer, pContextNode, bNotted, pQValue))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Import child context from pSrcContext into pDestContext. ***************************************************************************/ FSTATIC void fqImportChildContexts( OP_CONTEXT * pDestContext, OP_CONTEXT * pSrcContext) { OP_CONTEXT * pTmpContext; if ((pTmpContext = pSrcContext->pFirstChild) != NULL) { // Change all of the parent pointers of the child contexts to // point to pDestContext; while (pTmpContext) { flmAssert( (!pTmpContext->bIntersect && pDestContext->bIntersect) || (pTmpContext->bIntersect && !pDestContext->bIntersect)); pTmpContext->pParent = pDestContext; pTmpContext = pTmpContext->pNextSib; } // Link all of pSrcContext's children as children of pDestContext. if ((pSrcContext->pFirstChild->pPrevSib = pDestContext->pLastChild) != NULL) { pDestContext->pLastChild->pNextSib = pSrcContext->pFirstChild; } else { pDestContext->pFirstChild = pSrcContext->pFirstChild; } pDestContext->pLastChild = pSrcContext->pLastChild; pSrcContext->pFirstChild = NULL; pSrcContext->pLastChild = NULL; } } /*************************************************************************** Desc: Import context paths from pSrcContext into pDestContext. ***************************************************************************/ FSTATIC void fqImportContextPaths( OP_CONTEXT * pDestContext, OP_CONTEXT * pSrcContext) { if (pSrcContext->pFirstPath) { if ((pSrcContext->pFirstPath->pPrev = pDestContext->pLastPath) != NULL) { pDestContext->pLastPath->pNext = pSrcContext->pFirstPath; } else { pDestContext->pFirstPath = pSrcContext->pFirstPath; } pDestContext->pLastPath = pSrcContext->pLastPath; pSrcContext->pFirstPath = NULL; pSrcContext->pLastPath = NULL; } } /*************************************************************************** Desc: Import pSrcContext into pDestContext. ***************************************************************************/ FSTATIC void fqImportContext( OP_CONTEXT * pDestContext, OP_CONTEXT * pSrcContext) { // Merge all of the child contexts from pSrcContext into pDestContext fqImportChildContexts( pDestContext, pSrcContext); // Merge all of the paths from pSrcContext into pDestContext fqImportContextPaths( pDestContext, pSrcContext); } /*************************************************************************** Desc: Merge the context of pQNode, if any, into pDestContext ***************************************************************************/ FSTATIC void fqMergeContexts( FQNODE * pQNode, OP_CONTEXT * pDestContext ) { OP_CONTEXT * pSrcContext = pQNode->pContext; // At this point, pQNode MUST have a context to merge in, and // pDestContext MUST be an intersect context. flmAssert( pSrcContext && pDestContext->bIntersect); // The context we are merging should not have any parent context. // It should be a root context. flmAssert( !pSrcContext->pNextSib && !pSrcContext->pPrevSib && !pSrcContext->pParent); // Root node of context should be same as pQNode. flmAssert( pSrcContext->pQRootNode == pQNode); if (pSrcContext->bIntersect) { // If the context to be merged is an intersect context (AND), // we take its child OP_CONTEXTs (which should all be non-intersect) // and make them children of the destination context. We also // move its predicates into the predicate list of the destination // context. fqImportContext( pDestContext, pSrcContext); } else { // If the context to be merged is a non-intersect context (OR) // we just put the whole thing in as a child of the destination // context. if ((pSrcContext->pPrevSib = pDestContext->pLastChild) != NULL) { pSrcContext->pPrevSib->pNextSib = pSrcContext; } else { pDestContext->pFirstChild = pSrcContext; } pDestContext->pLastChild = pSrcContext; pSrcContext->pParent = pDestContext; } pQNode->pContext = pDestContext; } /*************************************************************************** Desc: Create a new context for predicates ***************************************************************************/ RCODE F_Query::createOpContext( OP_CONTEXT * pParentContext, FLMBOOL bIntersect, FQNODE * pQRootNode ) { RCODE rc = NE_XFLM_OK; OP_CONTEXT * pContext; // Allocate a new context and link it in as a child // to the current context. if (RC_BAD( rc = m_pool.poolCalloc( sizeof( OP_CONTEXT), (void **)&pContext))) { goto Exit; } pQRootNode->pContext = pContext; pContext->pQRootNode = pQRootNode; pContext->bIntersect = bIntersect; pContext->bMustScan = FALSE; pContext->uiCost = (FLMUINT)(bIntersect ? ~((FLMUINT)0) : 0); if ((pContext->pParent = pParentContext) != NULL) { if ((pContext->pPrevSib = pParentContext->pLastChild) != NULL) { pParentContext->pLastChild->pNextSib = pContext; } else { pParentContext->pFirstChild = pContext; } pParentContext->pLastChild = pContext; } Exit: return( rc); } /*************************************************************************** Desc: Check to see if an XPATH component matches the XPATH context component we are inside of. ***************************************************************************/ FSTATIC void fqCheckPathMatch( XPATH_COMPONENT * pXPathContextComponent, XPATH_COMPONENT * pXPathComponent ) { // If this is a self:: axis, and we are in the context of another // xpath component, see if we match the context xpath component if (pXPathContextComponent && pXPathContextComponent->uiDictNum && (pXPathContextComponent->eNodeType == ELEMENT_NODE || pXPathContextComponent->eNodeType == DATA_NODE || pXPathContextComponent->eNodeType == ATTRIBUTE_NODE || pXPathContextComponent->eXPathAxis == META_AXIS) && pXPathComponent->eXPathAxis == SELF_AXIS && !pXPathComponent->pNext && ((pXPathComponent->eNodeType == pXPathContextComponent->eNodeType && !pXPathComponent->uiDictNum) || pXPathComponent->eNodeType == ANY_NODE_TYPE)) { pXPathComponent->uiDictNum = pXPathContextComponent->uiDictNum; pXPathComponent->eNodeType = pXPathContextComponent->eNodeType; if( pXPathContextComponent->eXPathAxis == META_AXIS) { pXPathComponent->eXPathAxis = META_AXIS; } } } /*************************************************************************** Desc: Get predicates for the XPATH node (pQNode) that was passed in. ***************************************************************************/ RCODE F_Query::getPathPredicates( FQNODE * pParentNode, FQNODE ** ppQNode, XPATH_COMPONENT * pXPathContext ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode = *ppQNode; FXPATH * pXPath = pQNode->nd.pXPath; XPATH_COMPONENT * pXPathComp = pXPath->pFirstComponent; OP_CONTEXT * pContext; FQNODE * pContextNode; FLMBOOL bHadComponentExpressions = FALSE; FLMBOOL bClippedContext; // pXPathContext is the XPATH component context for this // XPATH component. This may be used in optimization. // It should have already been set up. flmAssert( pXPathComp->pXPathContext == pXPathContext); fqCheckPathMatch( pXPathContext, pXPathComp); // See if any of the XPATH components have expressions ([]). If they // do, we want to AND this XPATH expression with them into // an intersect context. If we are not currently in an intersect // context, we need to create one. pContext = NULL; pContextNode = pXPathContext ? pXPathContext->pXPathNode : NULL; while (pXPathComp) { if (pXPathComp->pExpr && !pContext) { bHadComponentExpressions = TRUE; // If we have not yet determined the context for the // XPATH components to be merged into, do it now. if (pParentNode) { pContext = pQNode->pContext = pParentNode->pContext; // If the context is not an intersect context, we need to // create a new one that is, and link it as the last child // of the current context. if (!pContext->bIntersect) { if (RC_BAD( rc = createOpContext( pParentNode->pContext, TRUE, pQNode))) { goto Exit; } pContext = pQNode->pContext; } } else { // We set the bIntersect flag to TRUE for this context, // even though it is not an AND operator. This is done // because we know there will only ever be one predicate // in this context, and therefore, if it is ever merged // into another "intersect" context, we want the predicate // merged in by itself instead of the entire context. // See fqMergeContexts. if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) { goto Exit; } pContext = pQNode->pContext; } break; } pXPathComp = pXPathComp->pNext; } // Create a context if one was not created above. if (!pContext) { if (pParentNode) { pContext = pQNode->pContext = pParentNode->pContext; } else { // We set the bIntersect flag to TRUE for this context, // even though it is not an AND operator. This is done // because we know there will only ever be one predicate // in this context, and therefore, if it is ever merged // into another "intersect" context, we want the predicate // merged in by itself instead of the entire context. // See fqMergeContexts. if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) { goto Exit; } pContext = pQNode->pContext; } } else { flmAssert( pQNode->pContext == pContext); } // Create predicate, if possible // Find the terminating XPATH component. pXPathComp = pXPath->pLastComponent; // See if we can create a predicate from this component. if (pXPathComp->pNodeSource) { // Create an exists predicate if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, pXPathComp, XFLM_EXISTS_OP, 0, NULL, pContextNode, pQNode->bNotted, NULL, &bClippedContext, &pQNode))) { goto Exit; } // Context should never be clipped because it shouldn't ever // merge with another predicate. flmAssert( !bClippedContext); } else if (pXPathComp->uiDictNum && (pXPathComp->eXPathAxis == META_AXIS || pXPathComp->eNodeType == ELEMENT_NODE || pXPathComp->eNodeType == DATA_NODE || pXPathComp->eNodeType == ATTRIBUTE_NODE)) { if (pParentNode) { if (isLogicalOp( pParentNode->nd.op.eOperator)) { // Create an exists predicate if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, pXPathComp, XFLM_EXISTS_OP, 0, NULL, pContextNode, pQNode->bNotted, NULL, &bClippedContext, &pQNode))) { goto Exit; } // If adding this predicate would cause a false result, // the root node of the context will have been turned into // a boolean value with a FALSE value. There is no need to // process any more of this branch, because this branch will // have been cut off. pQNode will have been altered. if (bClippedContext) { goto Exit; } } else if (isCompareOp( pParentNode->nd.op.eOperator)) { // Sibling must be a value node if (pQNode->pNextSib) { flmAssert( !pQNode->pPrevSib); if (pQNode->pNextSib->eNodeType == FLM_VALUE_NODE) { // Create a compare predicate. if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, pXPathComp, pParentNode->nd.op.eOperator, pParentNode->nd.op.uiCompareRules, pParentNode->nd.op.pOpComparer, pContextNode, pQNode->bNotted, &pQNode->pNextSib->currVal, &bClippedContext, &pQNode))) { goto Exit; } // If adding this predicate would cause a false // result, the root node of the context will // have been turned into a boolean value with a // FALSE value. There is no need to process any // more of this branch, because this branch will // have been cut off. pQNode will have been altered. if (bClippedContext) { goto Exit; } } else { // We have a comparison operation that cannot be // optimized. If this is a non-intersect context, // there is no point in doing optimizations. // If it is an intersect context, other predicates // may come along that can be used to optimize. // We won't know until we have processed all of // them. if (!pContext->bIntersect) { pContext->bForceOptToScan = TRUE; } } } else { flmAssert( pQNode->pPrevSib); if (pQNode->pPrevSib->eNodeType == FLM_VALUE_NODE) { FQNODE * pSaveSib; // Switch the two operators around switch (pParentNode->nd.op.eOperator) { case XFLM_EQ_OP: case XFLM_NE_OP: pSaveSib = pQNode->pPrevSib; break; case XFLM_LT_OP: pSaveSib = pQNode->pPrevSib; pParentNode->nd.op.eOperator = XFLM_GE_OP; break; case XFLM_LE_OP: pSaveSib = pQNode->pPrevSib; pParentNode->nd.op.eOperator = XFLM_GT_OP; break; case XFLM_GT_OP: pSaveSib = pQNode->pPrevSib; pParentNode->nd.op.eOperator = XFLM_LE_OP; break; case XFLM_GE_OP: pSaveSib = pQNode->pPrevSib; pParentNode->nd.op.eOperator = XFLM_LT_OP; break; default: pSaveSib = NULL; break; } // If we can switch the operator, we can create a // predicate for optimization. Otherwise, we must // leave it alone - or assert? if (pSaveSib) { pQNode->pNextSib = pSaveSib; pQNode->pPrevSib = NULL; pSaveSib->pPrevSib = pQNode; pSaveSib->pNextSib = NULL; pParentNode->pFirstChild = pQNode; pParentNode->pLastChild = pSaveSib; // Create a compare predicate, but need to switch // the operators around if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, pXPathComp, pParentNode->nd.op.eOperator, pParentNode->nd.op.uiCompareRules, pParentNode->nd.op.pOpComparer, pContextNode, pQNode->bNotted, &pQNode->pNextSib->currVal, &bClippedContext, &pQNode))) { goto Exit; } // If adding this predicate would cause a false // result, the root node of the context will // have been turned into a boolean value with a // FALSE value. There is no need to process any // more of this branch, because this branch will // have been cut off. pQNode will have been // altered. if (bClippedContext) { goto Exit; } } else { // We have a comparison operation that cannot be // optimized. if (!pContext->bIntersect) { pContext->bForceOptToScan = TRUE; } } } else { // We have a comparison operation that cannot be // optimized. if (!pContext->bIntersect) { pContext->bForceOptToScan = TRUE; } } } } } else { // Create an exists predicate if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, pXPathComp, XFLM_EXISTS_OP, 0, NULL, pContextNode, pQNode->bNotted, NULL, &bClippedContext, &pQNode))) { goto Exit; } // If adding this predicate would cause a false // result, the root node of the context will // have been turned into a boolean value with a // FALSE value. There is no need to process any // more of this branch, because this branch will // have been cut off. pQNode will have been // altered. if (bClippedContext) { goto Exit; } } } else { // We have something in the expression that cannot be // optimized. if (!pContext->bIntersect) { pContext->bForceOptToScan = TRUE; } } if (bHadComponentExpressions) { // Now, merge in the expressions for each component of the // XPATH. We wait to do this until AFTER the predicates // have been added above, because this "merge" does not // actually merge RANGE operators, etc., but just adds // the predicates to the list - which is all we want to // have happen at this point. pXPathComp = pXPath->pFirstComponent; while (pXPathComp) { if (pXPathComp->pExpr) { // Merge each expression's context into pContext, which // should be an intersection context - should have // been set up above. flmAssert( pContext && pContext->bIntersect); fqMergeContexts( pXPathComp->pExpr, pContext); } pXPathComp = pXPathComp->pNext; } } Exit: *ppQNode = pQNode; return( rc); } /*************************************************************************** Desc: Evaluate operands of an AND or OR operator to see if we can replace one. TRUE && P1 will be replaced with P1 FALSE && P1 will be replaced with FALSE TRUE || P1 will be replaced with TRUE FALSE || P1 will be replaced with P1 ***************************************************************************/ FSTATIC FQNODE * fqEvalLogicalOperands( FQNODE * pQNode ) { FLMBOOL bLeftIsBool = FALSE; FLMBOOL bRightIsBool = FALSE; XFlmBoolType eLeftBoolVal = XFLM_UNKNOWN; XFlmBoolType eRightBoolVal = XFLM_UNKNOWN; FQNODE * pLeftNode = pQNode->pFirstChild; FQNODE * pRightNode = pLeftNode->pNextSib; FQNODE * pReplacementNode = NULL; OP_CONTEXT * pContext; OP_CONTEXT * pParentContext; if (isBoolNode( pLeftNode)) { bLeftIsBool = TRUE; eLeftBoolVal = pLeftNode->currVal.val.eBool; } if (isBoolNode( pRightNode)) { bRightIsBool = TRUE; eRightBoolVal = pRightNode->currVal.val.eBool; } // If neither operand is a boolean value, there is no replacement // that can be done, but we still need to go up one level. if (!bLeftIsBool && !bRightIsBool) { goto Exit; } // Handle the case where both operands are boolean values. if (bLeftIsBool && bRightIsBool) { XFlmBoolType eNewBoolVal; if (pQNode->nd.op.eOperator == XFLM_AND_OP) { if (eLeftBoolVal == XFLM_FALSE || eRightBoolVal == XFLM_FALSE) { eNewBoolVal = XFLM_FALSE; } else if (eLeftBoolVal == XFLM_TRUE && eRightBoolVal == XFLM_TRUE) { eNewBoolVal = XFLM_TRUE; } else { eNewBoolVal = XFLM_UNKNOWN; } } else // XFLM_OR_OP { if (eLeftBoolVal == XFLM_TRUE || eRightBoolVal == XFLM_TRUE) { eNewBoolVal = XFLM_TRUE; } else if (eLeftBoolVal == XFLM_FALSE && eRightBoolVal == XFLM_FALSE) { eNewBoolVal = XFLM_FALSE; } else { eNewBoolVal = XFLM_UNKNOWN; } } // Doesn't really matter which one we use to // replace the AND or OR node - we will use // the left one. pLeftNode->currVal.val.eBool = eNewBoolVal; pReplacementNode = pLeftNode; } else if (pQNode->nd.op.eOperator == XFLM_OR_OP) { // Operator is an OR, and only one of the operands // is a boolean value. if (bLeftIsBool) { pReplacementNode = (eLeftBoolVal == XFLM_TRUE) ? pLeftNode : pRightNode; } else { pReplacementNode = (eRightBoolVal == XFLM_TRUE) ? pRightNode : pLeftNode; } } else { // Operator is an AND, and only one of the operands is // a boolean value. if (bLeftIsBool) { pReplacementNode = (eLeftBoolVal != XFLM_TRUE) ? pLeftNode : pRightNode; } else { pReplacementNode = (eRightBoolVal != XFLM_TRUE) ? pRightNode : pLeftNode; } } // If the node we are going to replace (pQNode) is the root // of the context, change the context to point to the replacement // node as the new root of the context. Also, if the replacement // node is a boolean, or a non-logical operator, set the context // to be an intersect context, so that if it is merged into another // context, it will merge correctly. pContext = pQNode->pContext; if (pContext->pQRootNode == pQNode) { pParentContext = pContext->pParent; if (pReplacementNode->eNodeType == FLM_VALUE_NODE) { // If this context node is a child to another // context, we can excise it completely // At this point, if pQNode was an OR, the // replacement node is guaranteed to be TRUE. // If the replacement node was an AND, the // replacement node is guaranteed to be FALSE. // We are at the top of a context, so the entire // context can be removed. // Strip off any child contexts and predicates of this context - // Don't want to travel down this path when optimizing. pReplacementNode->pContext = pParentContext; fqClipContext( pContext); } else if (pReplacementNode->eNodeType == FLM_FUNCTION_NODE) { // Better not be anything in the context that needs to // be imported into a parent context flmAssert( !pContext->pFirstPath); flmAssert( !pContext->pFirstChild); if (pParentContext) { pReplacementNode->pContext = pParentContext; fqClipContext( pContext); } else { pReplacementNode->pContext = pContext; pContext->bIntersect = TRUE; pContext->pQRootNode = pReplacementNode; } } else if (pReplacementNode->eNodeType != FLM_OPERATOR_NODE || !isLogicalOp( pReplacementNode->nd.op.eOperator)) { if (pParentContext) { // Context has a parent context. // If there is only one path and no child contexts, // import that path into the parent context. // Otherwise, change the root node of the context // to point to the replacement node. if( pContext->pFirstPath == pContext->pLastPath && !pContext->pFirstChild) { fqImportContextPaths( pParentContext, pContext); pReplacementNode->pContext = pParentContext; fqClipContext( pContext); } else { pContext->pQRootNode = pReplacementNode; } } else { // No parent context, so set the bIntersect flag to // TRUE so that if it ever is merged to a higher // context, it will merge properly - because we // know that there is only one predicate in this // context. pContext->bIntersect = TRUE; pReplacementNode->pContext = pContext; pContext->pQRootNode = pReplacementNode; } } else { // pReplacement node is either an AND or OR operator. // At this point, we know that we are moving an AND or OR up the tree // to replace an AND or an OR. If they are the same operator, they // would have already been in the same context. If not, they would have // been in different contexts, and the replacement node will be the // same operator as the parent of the node being replaced. Hence, // the replacement node's context needs to be merged with the parent // node's context. if (pReplacementNode->pContext != pContext) { fqClipContext( pContext); // Replace node's operator is different than the node it is // replacing. AND --> OR, or OR --> AND. This means that // the parent context should be the same as the replacement // node's context, and hence, they should be merged. if (pParentContext) { flmAssert( (pParentContext->bIntersect && pReplacementNode->pContext->bIntersect) || (!pParentContext->bIntersect && !pReplacementNode->pContext->bIntersect)); fqImportContext( pParentContext, pReplacementNode->pContext); fqClipContext( pReplacementNode->pContext); pReplacementNode->pContext = pParentContext; } } } } fqReplaceNode( pQNode, pReplacementNode); pQNode = pReplacementNode; Exit: return( pQNode); } /*************************************************************************** Desc: Clip a NOT node out of the tree. ***************************************************************************/ FSTATIC FQNODE * fqClipNotNode( FQNODE * pQNode, FQNODE ** ppExpr ) { FQNODE * pKeepNode; // If this NOT node has no parent, the root // of the tree needs to be set to its child. pKeepNode = pQNode->pFirstChild; // Child better not have any siblings - NOT nodes only have // one operand. flmAssert( !pKeepNode->pNextSib && !pKeepNode->pPrevSib); // Set child to point to the NOT node's parent. if ((pKeepNode->pParent = pQNode->pParent) == NULL) { *ppExpr = pKeepNode; } else { // Link child in where the NOT node used to be. if ((pKeepNode->pPrevSib = pQNode->pPrevSib) != NULL) { pKeepNode->pPrevSib->pNextSib = pKeepNode; } else { pKeepNode->pParent->pFirstChild = pKeepNode; } if ((pKeepNode->pNextSib = pQNode->pNextSib) != NULL) { pKeepNode->pNextSib->pPrevSib = pKeepNode; } else { pKeepNode->pParent->pLastChild = pKeepNode; } } return( pKeepNode); } /*************************************************************************** Desc: Get predicates for a query expression. Also strips out NOT nodes. ***************************************************************************/ RCODE F_Query::getPredicates( FQNODE ** ppExpr, FQNODE * pStartNode, XPATH_COMPONENT * pXPathContextComponent ) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode = pStartNode ? pStartNode : *ppExpr; FQNODE * pParentNode = NULL; eNodeTypes eNodeType; eQueryOperators eOperator; FLMBOOL bNotted = FALSE; FLMBOOL bGetPredicates; // Don't get predicates if this is a constant or arithmetic expression. // But if it is an arithmetic expression, still need to reduce it to // a single constant if possible. if (pQNode->eNodeType == FLM_VALUE_NODE || pQNode->eNodeType == FLM_OPERATOR_NODE && isArithOp( pQNode->nd.op.eOperator)) { bGetPredicates = FALSE; } else { bGetPredicates = TRUE; } for (;;) { eNodeType = pQNode->eNodeType; // Need to save bNotted on each node so that when we traverse // back up the tree it can be reset properly. If bNotted is // TRUE and pQNode is an operator, we may change the operator in // some cases. Even if we change the operator, we still want to // set the bNotted flag because it also implies "for every" when set // to TRUE, and we need to remember that as well. pQNode->bNotted = bNotted; if (eNodeType == FLM_OPERATOR_NODE) { eOperator = pQNode->nd.op.eOperator; if (eOperator == XFLM_AND_OP || eOperator == XFLM_OR_OP) { // Logical sub-expressions can only be operands of // AND, OR, or NOT operators. if (pParentNode) { if (!isLogicalOp( pParentNode->nd.op.eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); goto Exit; } } if (bNotted) { eOperator = (eOperator == XFLM_AND_OP ? XFLM_OR_OP : XFLM_AND_OP); pQNode->nd.op.eOperator = eOperator; } if (pParentNode) { flmAssert( pParentNode->pContext); if (pParentNode->nd.op.eOperator == eOperator) { pQNode->pContext = pParentNode->pContext; } else { if (RC_BAD( rc = createOpContext( pParentNode->pContext, (FLMBOOL)(eOperator == XFLM_AND_OP ? TRUE : FALSE), pQNode))) { goto Exit; } } } else { if (RC_BAD( rc = createOpContext( NULL, (FLMBOOL)(eOperator == XFLM_AND_OP ? TRUE : FALSE), pQNode))) { goto Exit; } } } else if (eOperator == XFLM_NOT_OP) { // Logical sub-expressions can only be operands of // AND, OR, or NOT operators. if (pParentNode) { if (!isLogicalOp( pParentNode->nd.op.eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); goto Exit; } } bNotted = !bNotted; // Clip NOT nodes out of the tree. pQNode = fqClipNotNode( pQNode, ppExpr); pParentNode = pQNode->pParent; continue; } else if (isCompareOp( eOperator)) { // Comparison sub-expressions can only be operands of // AND, OR, or NOT operators. if (pParentNode) { if (!isLogicalOp( pParentNode->nd.op.eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); goto Exit; } // Associate context with parent node pQNode->pContext = pParentNode->pContext; } else { // We set the bIntersect flag to TRUE for this context, // even though it is not an AND operator. This is done // because we know there will only ever be one predicate // in this context, and therefore, if it is ever merged // into another "intersect" context, we want the predicate // merged in by itself instead of the entire context. // See fqMergeContexts. if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) { goto Exit; } } if (bNotted) { switch (eOperator) { case XFLM_EQ_OP: eOperator = XFLM_NE_OP; break; case XFLM_NE_OP: eOperator = XFLM_EQ_OP; break; case XFLM_LT_OP: eOperator = XFLM_GE_OP; break; case XFLM_LE_OP: eOperator = XFLM_GT_OP; break; case XFLM_GT_OP: eOperator = XFLM_LE_OP; break; case XFLM_GE_OP: eOperator = XFLM_LT_OP; break; default: // Don't change the other operators. // Will just use the bNotted flag when // evaluating. break; } pQNode->nd.op.eOperator = eOperator; } } else { // Better be an arithmetic operator we are dealing with // at this point. flmAssert( isArithOp( eOperator)); // Arithmetic sub-expressions can only be operands // of arithmetic or comparison operators if (pParentNode) { if (!isCompareOp( pParentNode->nd.op.eOperator) && !isArithOp( pParentNode->nd.op.eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); goto Exit; } } } } else if (eNodeType == FLM_XPATH_NODE) { if (bGetPredicates) { if (RC_BAD( rc = getPathPredicates( pParentNode, &pQNode, pXPathContextComponent))) { goto Exit; } } // NOTE: Upon return pQNode may no longer be // an XPATH node. This branch of the tree may // have been clipped as we evaluated predicates // that were ANDed together, in which case // pQNode will be pointing at a value node // that has a value of FALSE. Either way, // there should be no children at this point. flmAssert( !pQNode->pFirstChild); } else if (eNodeType == FLM_FUNCTION_NODE) { // a function node should not have any children flmAssert( !pQNode->pFirstChild); // If this function is ORed into the context, set the // bForceOptToScan flag on the context. This will // force the context to scan when we optimize. Note that // we cannot just set the bMustScan flag, as that flag is // initialized by the optimization code to FALSE in the case // of a non-intersect context. if (bGetPredicates) { if (pParentNode) { if (pParentNode->nd.op.eOperator == XFLM_OR_OP) { pParentNode->pContext->bForceOptToScan = TRUE; } } else { // Need to have a context for later merging. if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) { goto Exit; } pQNode->pContext->bForceOptToScan = TRUE; } } } else { flmAssert( eNodeType == FLM_VALUE_NODE); // If bNotted is TRUE and we have a boolean value, change // the value: FALSE ==> TRUE, TRUE ==> FALSE. if (bNotted && pQNode->currVal.eValType == XFLM_BOOL_VAL) { if (pQNode->currVal.val.eBool == XFLM_TRUE) { pQNode->currVal.val.eBool = XFLM_FALSE; } else if (pQNode->currVal.val.eBool == XFLM_FALSE) { pQNode->currVal.val.eBool = XFLM_TRUE; } } // Values can only be operands of arithmetic or comparison operators, // unless they are boolean values, in which case they can only be // operands of logical operators. if (pParentNode) { if (pQNode->currVal.eValType == XFLM_BOOL_VAL) { if (!isLogicalOp( pParentNode->nd.op.eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); goto Exit; } } else { if (!isCompareOp( pParentNode->nd.op.eOperator) && !isArithOp( pParentNode->nd.op.eOperator)) { rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); goto Exit; } } } // A value node should not have any children flmAssert( !pQNode->pFirstChild); } // Do traversal to child node, if any if (pQNode->pFirstChild) { pParentNode = pQNode; pQNode = pQNode->pFirstChild; continue; } // Go back up the tree until we hit something that has // a sibling. while (!pQNode->pNextSib) { // If there are no more parents, we are done. if ((pQNode = pQNode->pParent) == NULL) { goto Exit; } flmAssert( pQNode->eNodeType == FLM_OPERATOR_NODE); // Evaluate arithmetic expressions if both operands are // constants. if (isArithOp( pQNode->nd.op.eOperator) && pQNode->pFirstChild->eNodeType == FLM_VALUE_NODE && pQNode->pLastChild->eNodeType == FLM_VALUE_NODE) { if (RC_BAD( rc = fqArithmeticOperator( &pQNode->pFirstChild->currVal, &pQNode->pLastChild->currVal, pQNode->nd.op.eOperator, &pQNode->currVal))) { goto Exit; } pQNode->eNodeType = FLM_VALUE_NODE; pQNode->currVal.uiFlags = VAL_IS_CONSTANT; pQNode->pFirstChild = NULL; pQNode->pLastChild = NULL; } else { // For the AND and OR operators, check the operands to // see if they are boolean values. Boolean values can // be weeded out of the criteria as we go back up the // tree. if (pQNode->nd.op.eOperator == XFLM_OR_OP || pQNode->nd.op.eOperator == XFLM_AND_OP) { pQNode = fqEvalLogicalOperands( pQNode); if (!pQNode->pParent) { *ppExpr = pQNode; } } } pParentNode = pQNode->pParent; } // pQNode will NEVER be NULL if we get here, because we // will jump to Exit in those cases. pQNode = pQNode->pNextSib; // Need to reset the bNotted flag to what it would have // been as we traverse back up the tree. bNotted = pParentNode->bNotted; } Exit: return( rc); } /*************************************************************************** Desc: Determine if an XPATH component is getting the node's node id or document id. ***************************************************************************/ FINLINE FLMBOOL isNodeOrDocIdComponent( XPATH_COMPONENT * pXPathComponent ) { return( pXPathComponent->eXPathAxis == META_AXIS && (pXPathComponent->uiDictNum == XFLM_META_NODE_ID || pXPathComponent->uiDictNum == XFLM_META_DOCUMENT_ID) ? TRUE : FALSE); } /*************************************************************************** Desc: Get the meta data type for an xpath component, if any. ***************************************************************************/ FINLINE FLMUINT getMetaDataType( XPATH_COMPONENT * pXPathComponent ) { return( pXPathComponent->eXPathAxis != META_AXIS ? 0 : pXPathComponent->uiDictNum); } /*************************************************************************** Desc: Get the node ID constant from an FQVALUE node. ***************************************************************************/ FSTATIC RCODE fqGetNodeIdValue( FQVALUE * pQValue ) { RCODE rc = NE_XFLM_OK; switch (pQValue->eValType) { case XFLM_UINT_VAL: pQValue->eValType = XFLM_UINT64_VAL; pQValue->val.ui64Val = (FLMUINT64)pQValue->val.uiVal; break; case XFLM_MISSING_VAL: case XFLM_UINT64_VAL: break; case XFLM_INT_VAL: pQValue->eValType = XFLM_UINT64_VAL; pQValue->val.ui64Val = (FLMUINT64)((FLMINT64)(pQValue->val.iVal)); break; case XFLM_INT64_VAL: pQValue->eValType = XFLM_UINT64_VAL; pQValue->val.ui64Val = (FLMUINT64)(pQValue->val.i64Val); break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_Q_INVALID_NODE_ID_VALUE); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Optimize a predicate. ***************************************************************************/ RCODE F_Query::optimizePredicate( XPATH_COMPONENT * pXPathComponent, PATH_PRED * pPred ) { RCODE rc = NE_XFLM_OK; ICD * pIcd; ICD * pTmpIcd; XPATH_COMPONENT * pTmpXPathComponent; FLMBOOL bCanCompareOnKey; FLMBOOL bDoNodeMatch; FLMBOOL bTmpCanCompareOnKey; FLMBOOL bMustVerifyPath; FSIndexCursor * pFSIndexCursor = NULL; FLMUINT uiLeafBlocksBetween; FLMUINT uiTotalRefs; FLMBOOL bTotalsEstimated; FLMUINT uiCost; IF_BufferIStream * pBufferIStream = NULL; F_AttrElmInfo defInfo; // Special handling for app. defined source nodes if (pXPathComponent->pNodeSource) { FLMBOOL bMustScan; if (RC_OK( rc = pXPathComponent->pNodeSource->searchCost( (IF_Db *)m_pDb, pPred->bNotted, &pPred->OptInfo.uiCost, &bMustScan))) { if (bMustScan) { pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; } pPred->pNodeSource = pXPathComponent->pNodeSource; pPred->pNodeSource->AddRef(); } goto Exit; } // A predicate of the form "A operator Value" will always // return FALSE if A is missing, regardless of the operator. // So will a predicate of "exists(A)" // This is because we do not use default data for missing // values. The only time it will return TRUE // when A is missing is if the predicate is notted. // Hence, a predicate that is notted cannot be optimized. if (pPred->bNotted || (pPred->pContextNode && pPred->pContextNode->bNotted)) { pPred->OptInfo.uiCost = ~((FLMUINT)0); pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; goto Exit; } // Special handling for node id and document id attributes. // These will never be indexed, but we use a collection // cursor for them, if necessary. if (pXPathComponent->eXPathAxis == META_AXIS) { // Default is to do a full collection scan - may be changed below. pPred->OptInfo.uiCost = ~((FLMUINT)0); pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; if (pXPathComponent->uiDictNum == XFLM_META_NODE_ID || pXPathComponent->uiDictNum == XFLM_META_DOCUMENT_ID) { FLMBOOL bDocumentIds = pXPathComponent->uiDictNum == XFLM_META_DOCUMENT_ID ? TRUE : FALSE; // If the user has specified an index, we must set it up to // do an index scan. if (m_bIndexSet) { pPred->OptInfo.uiCost = ~((FLMUINT)0); pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; } else if (pPred->eOperator == XFLM_APPROX_EQ_OP) { pPred->OptInfo.uiCost = 1; // Value should have already been converted to a 64 bit // unsigned value. flmAssert( pPred->pFromValue->eValType == XFLM_UINT64_VAL); pPred->OptInfo.ui64NodeId = pPred->pFromValue->val.ui64Val; pPred->OptInfo.eOptType = XFLM_QOPT_SINGLE_NODE_ID; pPred->OptInfo.bMustVerifyPath = TRUE; } else if (pPred->eOperator == XFLM_RANGE_OP) { // Value should have already been converted to a 64 bit // unsigned value. flmAssert( pPred->pFromValue->eValType == XFLM_UINT64_VAL); pPred->OptInfo.ui64NodeId = pPred->pFromValue->val.ui64Val; if (!pPred->bInclFrom) { pPred->OptInfo.ui64NodeId++; } // Value should have already been converted to a 64 bit // unsigned value. flmAssert( pPred->pUntilValue->eValType == XFLM_UINT64_VAL); pPred->OptInfo.ui64EndNodeId = pPred->pUntilValue->val.ui64Val; if (!pPred->bInclUntil) { pPred->OptInfo.ui64EndNodeId--; } if (pPred->OptInfo.ui64NodeId == pPred->OptInfo.ui64EndNodeId) { pPred->OptInfo.uiCost = 1; pPred->OptInfo.eOptType = XFLM_QOPT_SINGLE_NODE_ID; } else { pPred->OptInfo.eOptType = XFLM_QOPT_NODE_ID_RANGE; if ((pPred->pFSCollectionCursor = f_new FSCollectionCursor) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = pPred->pFSCollectionCursor->setupRange( m_pDb, m_uiCollection, bDocumentIds, pPred->OptInfo.ui64NodeId, pPred->OptInfo.ui64EndNodeId, &uiLeafBlocksBetween, &uiTotalRefs, &bTotalsEstimated))) { pPred->pFSCollectionCursor->Release(); pPred->pFSCollectionCursor = NULL; goto Exit; } pPred->OptInfo.uiCost = uiLeafBlocksBetween; if (!pPred->OptInfo.uiCost) { pPred->OptInfo.uiCost = 1; } } pPred->OptInfo.bMustVerifyPath = TRUE; } else { // Only other operators allowed would be the NE // operator. EXISTS would have been converted to // a TRUE constant. flmAssert( pPred->eOperator == XFLM_NE_OP); } } goto Exit; } // Get the ICD chain if (pXPathComponent->eNodeType == ELEMENT_NODE) { if (RC_BAD( rc = m_pDb->m_pDict->getElement( m_pDb, pXPathComponent->uiDictNum, &defInfo))) { goto Exit; } } else if (pXPathComponent->eNodeType == ATTRIBUTE_NODE) { if (RC_BAD( rc = m_pDb->m_pDict->getAttribute( m_pDb, pXPathComponent->uiDictNum, &defInfo))) { goto Exit; } } pIcd = defInfo.m_pFirstIcd; // Get the indexes in the ICD chain that are suitable for this // predicate. for (; pIcd; pIcd = pIcd->pNextInChain) { // Stop at the first non-required ICD. if (!(pIcd->uiFlags & (ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET))) { break; } // If this ICD is not a key component, we cannot use it. // Also, it must be the FIRST key component, and none of the // other components can be required. if (pIcd->uiKeyComponent != 1) { continue; } pTmpIcd = pIcd->pNextKeyComponent; while (pTmpIcd && !(pTmpIcd->uiFlags & ICD_REQUIRED_PIECE)) { pTmpIcd = pTmpIcd->pNextKeyComponent; } if (pTmpIcd) { continue; } // Check the following conditions of suitability: // 1) Index must be on the collection we are searching. // 2) Index must be on-line if (pIcd->pIxd->uiCollectionNum != m_uiCollection || (pIcd->pIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED))) { continue; } // If the user has specified an index, and this is not // that index, we will ignore it. if (m_bIndexSet && pIcd->uiIndexNum != m_uiIndex) { continue; } // Make sure the ICD's path is of less or equal specificity // to the path for this predicate. pTmpIcd = pIcd->pParent; pTmpXPathComponent = pXPathComponent; bMustVerifyPath = FALSE; while (pTmpIcd) { // If this is the self axis, we stay on the ICD we are currently on. // The XPATH component previous to/above this one is the same as // this one. if (pTmpXPathComponent->eXPathAxis == SELF_AXIS) { FLMUINT uiTmpDictNum = pTmpXPathComponent->uiDictNum; eDomNodeType eTmpNodeType = pTmpXPathComponent->eNodeType; if (pTmpXPathComponent->pPrev) { pTmpXPathComponent = pTmpXPathComponent->pPrev; if (pTmpXPathComponent->pExpr) { bMustVerifyPath = TRUE; } } else if ((pTmpXPathComponent = pTmpXPathComponent->pXPathContext) == NULL) { break; } if (eTmpNodeType == ANY_NODE_TYPE || (eTmpNodeType == pTmpXPathComponent->eNodeType && (uiTmpDictNum == pTmpXPathComponent->uiDictNum || !uiTmpDictNum))) { continue; } else { break; } } // If the ICD is the root tag, then the path needs to be // the root axis in order for it to be a match. if (pTmpIcd->uiDictNum == ELM_ROOT_TAG) { // Root tag should not have a parent. flmAssert( pTmpIcd->pParent == NULL); // If the path is not off of the root, then the // index's path is too specific to have indexed if (pTmpXPathComponent->eXPathAxis == ROOT_AXIS) { // Should not be anything previous to a root axis. flmAssert( pTmpXPathComponent->pPrev == NULL); pTmpXPathComponent = NULL; pTmpIcd = NULL; } break; } // Cannot match on anything except parent/child relationships if (pTmpXPathComponent->eXPathAxis != CHILD_AXIS && pTmpXPathComponent->eXPathAxis != ATTRIBUTE_AXIS) { bMustVerifyPath = TRUE; break; } if (pTmpXPathComponent->pPrev) { pTmpXPathComponent = pTmpXPathComponent->pPrev; if (pTmpXPathComponent->pExpr) { bMustVerifyPath = TRUE; } } else if ((pTmpXPathComponent = pTmpXPathComponent->pXPathContext) == NULL) { break; } // See if the element is the same as the ICD. // NOTE: At this point, the ICD MUST be an element, because attributes // are not allowed as parent ICDs of another ICD. if (pTmpXPathComponent->eNodeType != ELEMENT_NODE || pTmpXPathComponent->uiDictNum != pTmpIcd->uiDictNum) { break; } // Go to ICD's parent pTmpIcd = pTmpIcd->pParent; } // If we get here and we did not get all the way up to the // parent ICD, the index path is more specific than the XPATH. if (pTmpIcd) { continue; } // If there are more components in the XPATH, it is more specific // than the ICD path, so we must verify the path. if (!bMustVerifyPath && pTmpXPathComponent && (pTmpXPathComponent->eXPathAxis == ROOT_AXIS || pTmpXPathComponent->pPrev || pTmpXPathComponent->pXPathContext)) { bMustVerifyPath = TRUE; } bCanCompareOnKey = TRUE; // If the ICD is on element or attributes and the operator is not // the exists operator, we must also fetch the node to evaluate // the predicate. if ((pIcd->uiFlags & ICD_PRESENCE) && pPred->eOperator != XFLM_EXISTS_OP) { bCanCompareOnKey = FALSE; } // If the comparison rules aren't the same as those specified // on the ICD, we will need to read the node to do the comparison. if (bCanCompareOnKey && defInfo.m_uiDataType == XFLM_TEXT_TYPE) { if (!(pPred->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE)) { if (pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) { bCanCompareOnKey = FALSE; } } // Check comparison flags that must match exactly if ((pPred->uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | XFLM_COMP_NO_WHITESPACE | XFLM_COMP_NO_UNDERSCORES | XFLM_COMP_NO_DASHES | XFLM_COMP_WHITESPACE_AS_SPACE | XFLM_COMP_IGNORE_LEADING_SPACE | XFLM_COMP_IGNORE_TRAILING_SPACE)) != (pIcd->uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | XFLM_COMP_NO_WHITESPACE | XFLM_COMP_NO_UNDERSCORES | XFLM_COMP_NO_DASHES | XFLM_COMP_WHITESPACE_AS_SPACE | XFLM_COMP_IGNORE_LEADING_SPACE | XFLM_COMP_IGNORE_TRAILING_SPACE))) { bCanCompareOnKey = FALSE; } } // Need to select best metaphone for approximate equals // on text value. if (pPred->eOperator == XFLM_APPROX_EQ_OP && pIcd->uiFlags & ICD_METAPHONE && pPred->pFromValue->eValType == XFLM_UTF8_VAL) { FLMUINT uiMeta; FLMUINT uiAltMeta; FQVALUE metaValue; FQVALUE * pSaveValue = pPred->pFromValue; FLMUINT uiMetaCost; if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } uiCost = 0; if (RC_BAD( rc = pBufferIStream->openStream( (const char *)pPred->pFromValue->val.pucBuf, pPred->pFromValue->uiDataLen))) { goto Exit; } // This is a little bit trickiness here. We need to set up the // metaphone value as a XFLM_UTF8_VAL, but then set // metaValue.val.uiVal. That is because the code that // generates the key is expecting this - see kybldkey.cpp, // flmAddNonTextKeyPiece. metaValue.eValType = XFLM_UTF8_VAL; for (;;) { if( RC_BAD( rc = f_getNextMetaphone( pBufferIStream, &uiMeta, &uiAltMeta))) { if (rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } if (!uiMeta) { if (!uiAltMeta) { continue; } uiMeta = uiAltMeta; } // Generate the from and until keys for this index. If the estimated // cost is low enough, we will look no further for a better index. if (pFSIndexCursor) { pFSIndexCursor->resetCursor(); } else { if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } // Temporarily change the from value in the predicate // to point to the metaphone value. That is what the // key will be generated on. metaValue.val.uiVal = uiMeta; pPred->pFromValue = &metaValue; rc = pFSIndexCursor->setupKeys( m_pDb, pIcd->pIxd, pPred, &bDoNodeMatch, &bTmpCanCompareOnKey, &uiLeafBlocksBetween, &uiTotalRefs, &bTotalsEstimated); // Restore the from value in the predicate before // going any further. pPred->pFromValue = pSaveValue; if (RC_BAD( rc)) { goto Exit; } // Will always have to fetch the node, so cost should // always include uiTotalRefs in this case. uiMetaCost = uiLeafBlocksBetween + uiTotalRefs; if (!uiMetaCost) { uiMetaCost = 1; } if (!uiCost || uiMetaCost < uiCost) { uiCost = uiMetaCost; if (uiCost < MIN_OPT_COST) { break; } } } if( pBufferIStream) { pBufferIStream->Release(); pBufferIStream = NULL; } bTmpCanCompareOnKey = FALSE; bDoNodeMatch = TRUE; } else { // Generate the from and until keys for this index. If the estimated // cost is low enough, we will look no further for a better index. if (pFSIndexCursor) { pFSIndexCursor->resetCursor(); } else { if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } if (RC_BAD( rc = pFSIndexCursor->setupKeys( m_pDb, pIcd->pIxd, pPred, &bDoNodeMatch, &bTmpCanCompareOnKey, &uiLeafBlocksBetween, &uiTotalRefs, &bTotalsEstimated))) { goto Exit; } if (!bTmpCanCompareOnKey) { bDoNodeMatch = TRUE; } uiCost = uiLeafBlocksBetween + uiTotalRefs; } // Could be that there are zero leaf blocks between and // bDoRecMatch is FALSE. But we do not want a cost of // zero. if (!uiCost) { uiCost = 1; } if (!pPred->OptInfo.uiCost || uiCost < pPred->OptInfo.uiCost) { FSIndexCursor * pTmpFSIndexCursor; F_NameTable * pNameTable = NULL; // Exchange the temporary file system cursor and the // file system cursor inside the predicate. Want to // keep the temporary one and reuse the one that was // inside the sub-query. pTmpFSIndexCursor = pPred->pFSIndexCursor; pPred->pFSIndexCursor = pFSIndexCursor; pFSIndexCursor = pTmpFSIndexCursor; pPred->OptInfo.eOptType = XFLM_QOPT_USING_INDEX; pPred->OptInfo.uiIxNum = pIcd->pIxd->uiIndexNum; pPred->OptInfo.szIxName [0] = 0; if (RC_OK( m_pDb->getNameTable( &pNameTable))) { FLMUINT uiIxNameLen = sizeof( pPred->OptInfo.szIxName); if (RC_BAD( pNameTable->getFromTagTypeAndNum( m_pDb, ELM_INDEX_TAG, pPred->OptInfo.uiIxNum, NULL, (char *)pPred->OptInfo.szIxName, &uiIxNameLen, NULL, NULL, NULL, NULL, TRUE))) { pPred->OptInfo.szIxName [0] = 0; } } if (pNameTable) { pNameTable->Release(); } pPred->OptInfo.uiCost = uiCost; pPred->OptInfo.bDoNodeMatch = bDoNodeMatch; pPred->OptInfo.bMustVerifyPath = bMustVerifyPath; pPred->OptInfo.bCanCompareOnKey = (bTmpCanCompareOnKey && bCanCompareOnKey) ? TRUE : FALSE; // If the cost is now low enough, we will quit looking if (uiCost < MIN_OPT_COST) { break; } } } // If no index was found, the predicate will force a full // collection scan. if (!pPred->OptInfo.uiCost) { pPred->OptInfo.uiCost = ~((FLMUINT)0); pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; } Exit: if( pBufferIStream) { pBufferIStream->Release(); pBufferIStream = NULL; } if (pFSIndexCursor) { pFSIndexCursor->Release(); } return( rc); } /*************************************************************************** Desc: Optimize a context path. ***************************************************************************/ RCODE F_Query::optimizePath( CONTEXT_PATH * pContextPath, PATH_PRED * pSingleNodeIdPred, FLMBOOL bIntersect ) { RCODE rc = NE_XFLM_OK; PATH_PRED * pPred; XPATH_COMPONENT * pXPathComponent = pContextPath->pXPathComponent; pPred = pContextPath->pFirstPred; if (bIntersect) { pContextPath->uiCost = ~((FLMUINT)0); pContextPath->pSelectedPred = NULL; pContextPath->bMustScan = TRUE; // If we already know we have a single node id predicate, there // is no need to optimize any of the other predicates, because // this one is guaranteed to have the lowest cost: 1. if (pSingleNodeIdPred) { if (RC_BAD( rc = optimizePredicate( pXPathComponent, pSingleNodeIdPred))) { goto Exit; } // Cost better have returned as 1! and the optimization // better have been XFLM_QOPT_SINGLE_NODE_ID. flmAssert( pSingleNodeIdPred->OptInfo.uiCost == 1); flmAssert( pSingleNodeIdPred->OptInfo.eOptType == XFLM_QOPT_SINGLE_NODE_ID); pContextPath->uiCost = pSingleNodeIdPred->OptInfo.uiCost; pContextPath->pSelectedPred = pSingleNodeIdPred; pContextPath->bMustScan = FALSE; } else { while (pPred) { if (RC_BAD( rc = optimizePredicate( pXPathComponent, pPred))) { goto Exit; } if (pPred->OptInfo.eOptType != XFLM_QOPT_FULL_COLLECTION_SCAN && (pPred->OptInfo.uiCost < pContextPath->uiCost || pContextPath->bMustScan)) { pContextPath->uiCost = pPred->OptInfo.uiCost; if (pContextPath->pSelectedPred) { if (pContextPath->pSelectedPred->pFSIndexCursor) { pContextPath->pSelectedPred->pFSIndexCursor->Release(); pContextPath->pSelectedPred->pFSIndexCursor = NULL; } else if (pContextPath->pSelectedPred->pFSCollectionCursor) { pContextPath->pSelectedPred->pFSCollectionCursor->Release(); pContextPath->pSelectedPred->pFSCollectionCursor = NULL; } else if (pContextPath->pSelectedPred->pNodeSource) { pContextPath->pSelectedPred->pNodeSource->Release(); pContextPath->pSelectedPred->pNodeSource = NULL; } } pContextPath->pSelectedPred = pPred; pContextPath->bMustScan = FALSE; // No need to evaluate more predicates if the cost is // MIN_OPT_COST or below. if (pPred->OptInfo.uiCost < MIN_OPT_COST) { break; } } else { if (pPred->pFSIndexCursor) { pPred->pFSIndexCursor->Release(); pPred->pFSIndexCursor = NULL; } else if (pPred->pFSCollectionCursor) { pPred->pFSCollectionCursor->Release(); pPred->pFSCollectionCursor = NULL; } else if (pPred->pNodeSource) { pContextPath->pSelectedPred->pNodeSource->Release(); pContextPath->pSelectedPred->pNodeSource = NULL; } } pPred = pPred->pNext; } } } else { pContextPath->uiCost = 0; while (pPred) { if (RC_BAD( rc = optimizePredicate( pXPathComponent, pPred))) { goto Exit; } if (~((FLMUINT)0) - pContextPath->uiCost > pPred->OptInfo.uiCost && pPred->OptInfo.eOptType != XFLM_QOPT_FULL_COLLECTION_SCAN) { pContextPath->uiCost += pPred->OptInfo.uiCost; } else { pContextPath->uiCost = ~((FLMUINT)0); pContextPath->bMustScan = TRUE; break; } pPred = pPred->pNext; } } Exit: return( rc); } /*************************************************************************** Desc: Optimize a context. It's child contexts have already been optimized. ***************************************************************************/ RCODE F_Query::optimizeContext( OP_CONTEXT * pContext, CONTEXT_PATH * pSingleNodeIdPath, PATH_PRED * pSingleNodeIdPred ) { RCODE rc = NE_XFLM_OK; OP_CONTEXT * pChildContext; CONTEXT_PATH * pContextPath; // If this context has no CONTEXT_PATHs and no child contexts, // it had to have had only non-optimizable predicates, or // no context would have been created. In that case, this context // must be forced to scan. if (pContext->bForceOptToScan || (!pContext->pFirstPath && !pContext->pFirstChild)) { pContext->uiCost = ~((FLMUINT)0); pContext->bMustScan = TRUE; goto Exit; } // Optimize each of the context paths, if any. // If this is an intersect context, select the context path // one a cost that is lower than the context's current cost. // The context's current cost may have already been set when // its child contexts were optimized. if (pContext->bIntersect) { pContext->bMustScan = TRUE; pContext->uiCost = ~((FLMUINT)0); // Determine what the lowest cost child context is. // If we have a single node id path, we already know // it is guaranteed to have the lowest cost: 1. if (pSingleNodeIdPath) { if (RC_BAD( rc = optimizePath( pSingleNodeIdPath, pSingleNodeIdPred, TRUE))) { goto Exit; } // Cost better have returned as one! // Its bMustScan flag had also better be set to FALSE! flmAssert( pSingleNodeIdPath->uiCost == 1); flmAssert( !pSingleNodeIdPath->bMustScan); pContext->uiCost = pSingleNodeIdPath->uiCost; pContext->pSelectedPath = pSingleNodeIdPath; pContext->pSelectedChild = NULL; pContext->bMustScan = FALSE; } else { pChildContext = pContext->pFirstChild; while (pChildContext) { if (!pChildContext->bMustScan && (pChildContext->uiCost < pContext->uiCost || pContext->bMustScan)) { pContext->uiCost = pChildContext->uiCost; pContext->pSelectedChild = pChildContext; pContext->pSelectedPath = NULL; pContext->bMustScan = FALSE; } pChildContext = pChildContext->pNextSib; } // Find the most optimal context path pContextPath = pContext->pFirstPath; while (pContextPath && pContext->uiCost >= MIN_OPT_COST) { if (RC_BAD( rc = optimizePath( pContextPath, NULL, TRUE))) { goto Exit; } if (!pContextPath->bMustScan && (pContextPath->uiCost < pContext->uiCost || pContext->bMustScan)) { pContext->uiCost = pContextPath->uiCost; pContext->pSelectedPath = pContextPath; pContext->pSelectedChild = NULL; pContext->bMustScan = FALSE; // If the cost is below MIN_OPT_COST, no need to do any more // cost analysis. if (pContextPath->uiCost < MIN_OPT_COST) { break; } } pContextPath = pContextPath->pNext; } } } // Have a UNION (OR) context else { // In the case of union, there is no point in optimizing any // of the context paths paths once we discover that a context // must scan the database. pContext->bMustScan = FALSE; pContext->uiCost = 0; // Add in the cost of each child context. pChildContext = pContext->pFirstChild; while (pChildContext) { if (pChildContext->bMustScan) { pContext->bMustScan = TRUE; pContext->uiCost = ~((FLMUINT)0); // Once we set the bMustScan flag, there is no point // in going any further. break; } else if (~((FLMUINT)0) - pContext->uiCost > pChildContext->uiCost) { pContext->uiCost += pChildContext->uiCost; } else { pContext->uiCost = ~((FLMUINT)0); pContext->bMustScan = TRUE; // Once we set the bMustScan flag, there is no point // in going any further. break; } pChildContext = pChildContext->pNextSib; } // In the case of a non-intersecting context, sum the costs of // each context path into the cost for the context until we // hit ~0, at which time we will mark the context as // "must scan" and quit attempting to optimize it. pContextPath = pContext->pFirstPath; while (pContextPath && !pContext->bMustScan) { if (RC_BAD( rc = optimizePath( pContextPath, NULL, FALSE))) { goto Exit; } if (pContextPath->bMustScan) { pContext->uiCost = ~((FLMUINT)0); pContext->bMustScan = TRUE; // No need to do any more optimization once we have // determined to do a collection scan. break; } else if (~((FLMUINT)0) - pContext->uiCost > pContextPath->uiCost) { pContext->uiCost += pContextPath->uiCost; } else { pContext->uiCost = ~((FLMUINT)0); pContext->bMustScan = TRUE; // No need to do any more optimization once we have // determined to do a collection scan. break; } pContextPath = pContextPath->pNext; } } Exit: return( rc); } /*************************************************************************** Desc: Setup to scan an index - the index was specified by the user. ***************************************************************************/ RCODE F_Query::setupIndexScan( void) { RCODE rc = NE_XFLM_OK; IXD * pIxd; FLMBOOL bDoNodeMatch; FLMBOOL bCanCompareOnKey; flmAssert( m_uiIndex); // If the index is not on-line or it is not associated // with the collection, we have a problem. if (RC_BAD( rc = m_pDb->m_pDict->getIndex( m_uiIndex, NULL, &pIxd, FALSE))) { goto Exit; } if (pIxd->uiCollectionNum != m_uiCollection) { rc = RC_SET( NE_XFLM_BAD_IX); goto Exit; } if ((m_pFSIndexCursor = f_new FSIndexCursor) == NULL) { rc = RC_SET( NE_XFLM_MEM); } // Setup to scan from beginning of key to end of key. if (RC_BAD( rc = m_pFSIndexCursor->setupKeys( m_pDb, pIxd, NULL, &bDoNodeMatch, &bCanCompareOnKey, NULL, NULL, NULL))) { goto Exit; } m_bScanIndex = TRUE; m_bScan = FALSE; Exit: return( rc); } /*************************************************************************** Desc: This routine determines if an ICD has an descendents that are key components. ***************************************************************************/ FSTATIC FLMBOOL haveChildKeyComponents( ICD * pParentIcd) { ICD * pTmpIcd; if ((pTmpIcd = pParentIcd->pFirstChild) == NULL) { return( FALSE); } for (;;) { if (pTmpIcd->uiKeyComponent) { return( TRUE); } if (pTmpIcd->pFirstChild) { pTmpIcd = pTmpIcd->pFirstChild; } else { while (!pTmpIcd->pNextSibling) { if ((pTmpIcd = pTmpIcd->pParent) == pParentIcd) { return( FALSE); } } if (!pTmpIcd) { break; } pTmpIcd = pTmpIcd->pNextSibling; } } return( FALSE); } /*************************************************************************** Desc: Determines if the optimization index already has the order we need for sorting. ***************************************************************************/ RCODE F_Query::checkSortIndex( FLMUINT uiOptIndex) { RCODE rc = NE_XFLM_OK; IXD * pOptIxd; ICD * pSortIcd; ICD * pOptIcd; // Should not be called unless we have a sort key specified. flmAssert( m_pSortIxd); // Get the index definition. if (RC_BAD( rc = m_pDb->m_pDict->getIndex( uiOptIndex, NULL, &pOptIxd, TRUE))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } // Verify that the language is the same. if (m_pSortIxd->uiLanguage != pOptIxd->uiLanguage) { goto Exit; } // Make sure the sort IXD is completely represented in the optimization IXD // If we find all of the keys in the same context, the optimization // index will work as the sort index. Even if the optimization index // has more key components it will work, because it will provide the // keys in the same order. pSortIcd = m_pSortIxd->pIcdTree; pOptIcd = pOptIxd->pIcdTree; for (;;) { // See if there is a matching opt ICD in the list of siblings to // opt ICD. pOptIcd = (pOptIcd->pParent) ? pOptIcd->pParent->pFirstChild : pOptIxd->pIcdTree; while (pOptIcd) { if (pOptIcd->uiDictNum == pSortIcd->uiDictNum && (pOptIcd->uiFlags & ICD_IS_ATTRIBUTE) == (pSortIcd->uiFlags & ICD_IS_ATTRIBUTE)) { // If the match ICD is a key component, we want a // search ICD that is the same key component and compare rules. if (pSortIcd->uiKeyComponent) { if (pOptIcd->uiKeyComponent == pSortIcd->uiKeyComponent && pOptIcd->uiFlags == pSortIcd->uiFlags) { break; } } else { break; } } pOptIcd = pOptIcd->pNextSibling; } // Did we find a matching opt ICD? if (!pOptIcd) { // If the sort ICD is a key component, or there are key ICDs // subordinate to the sort ICD, then the indexes cannot match, // because there is no matching context for the child key ICDs. if (pSortIcd->uiKeyComponent || haveChildKeyComponents( pSortIcd)) { goto Exit; } Check_Siblings: while (!pSortIcd->pNextSibling) { if ((pSortIcd = pSortIcd->pParent) == NULL) { break; } pOptIcd = pOptIcd->pParent; // If pSortIcd != NULL, pOptIcd better be also! flmAssert( pOptIcd); } if (!pSortIcd) { // Done - index key components match. break; } // pNextSibling better be non-NULL at this point. // NOTE: Do not set pOptIcd to its sibling - it may not have one. // That doesn't matter, because we will search for a matching // sibling up above. pSortIcd = pSortIcd->pNextSibling; // No need to check this sort ICD if it is not a key // component and it has no child ICDs. if (!pSortIcd->uiKeyComponent && !pSortIcd->pFirstChild) { goto Check_Siblings; } } else { // Go to the child nodes, if any if (pSortIcd->pFirstChild) { if (!pOptIcd->pFirstChild) { // Sort ICD has a child, opt ICD doesn't. See if there are // any index components subordinate to sort ICD. If so, the indexes // are different. Otherwise, it really doesn't matter - we can // simply proceed with sort ICD's siblings. if (haveChildKeyComponents( pSortIcd)) { goto Exit; } goto Check_Siblings; } else { pSortIcd = pSortIcd->pFirstChild; pOptIcd = pOptIcd->pFirstChild; } } else { goto Check_Siblings; } } } m_bEntriesAlreadyInOrder = TRUE; Exit: return( rc); } /*************************************************************************** Desc: Optimize a query expression. ***************************************************************************/ RCODE F_Query::optimize( void) { RCODE rc = NE_XFLM_OK; FQNODE * pQNode = m_pQuery; OP_CONTEXT * pContext; if (m_bOptimized) { rc = RC_SET( NE_XFLM_Q_ALREADY_OPTIMIZED); goto Exit; } // We save the F_Database object so that we can always check and make // sure we are associated with this database on any query operations // that occur after optimization. -- Link it into the list of queries // off of the F_Database object. NOTE: We may not always use the // same F_Db object, but it must always be the same F_Database object. m_pDatabase = m_pDb->m_pDatabase; m_pNext = NULL; m_pDatabase->lockMutex(); if ((m_pPrev = m_pDatabase->m_pLastQuery) != NULL) { m_pPrev->m_pNext = this; } else { m_pDatabase->m_pFirstQuery = this; } m_pDatabase->m_pLastQuery = this; m_pDatabase->unlockMutex(); // Verify sort keys, if any if (m_pSortIxd) { if (RC_BAD( rc = verifySortKeys())) { goto Exit; } } // Make sure we have a completed expression if( m_pCurExprState) { if ( m_pCurExprState->pPrev || m_pCurExprState->uiNestLevel || (m_pCurExprState->pLastNode && m_pCurExprState->pLastNode->eNodeType == FLM_OPERATOR_NODE)) { rc = RC_SET( NE_XFLM_Q_INCOMPLETE_QUERY_EXPR); goto Exit; } if (RC_BAD( rc = getPredicates( &m_pCurExprState->pExpr, NULL, NULL))) { goto Exit; } m_pQuery = m_pCurExprState->pExpr; } m_uiLanguage = m_pDb->getDefaultLanguage(); // An empty expression should scan the database and return everything. if ((pQNode = m_pQuery) == NULL) { if (m_bIndexSet && m_uiIndex) { rc = setupIndexScan(); } else { m_bScan = TRUE; } goto Exit; } // Handle the case of a value node or arithmetic expression at the root // These types of expressions do not return results from the database. if (pQNode->eNodeType == FLM_VALUE_NODE) { if (pQNode->currVal.eValType == XFLM_BOOL_VAL && pQNode->currVal.val.eBool == XFLM_TRUE) { m_bScan = TRUE; } else { m_bEmpty = TRUE; } } else if (pQNode->eNodeType == FLM_OPERATOR_NODE && isArithOp( pQNode->nd.op.eOperator)) { m_bEmpty = TRUE; goto Exit; } // If the user explicitly said to NOT use an index, we will not if (m_bIndexSet && !m_uiIndex) { m_bScan = TRUE; goto Exit; } // Start with the context in the root node. if ((pContext = pQNode->pContext) == NULL) { m_bScan = TRUE; goto Exit; } for (;;) { CONTEXT_PATH * pSingleNodeIdPath = NULL; PATH_PRED * pSingleNodeIdPred = NULL; // See if any of the context paths in this context have a // single node ID predicate. If so, we can optimize it right // away and ignore all of the other predicates. if (pContext->bIntersect) { pSingleNodeIdPath = pContext->pFirstPath; while (pSingleNodeIdPath) { if (isNodeOrDocIdComponent( pSingleNodeIdPath->pXPathComponent)) { // See if any of the predicates are single-node id. pSingleNodeIdPred = pSingleNodeIdPath->pFirstPred; while (pSingleNodeIdPred) { if (pSingleNodeIdPred->pFromValue && pSingleNodeIdPred->pUntilValue) { // From and until values should have already been // converted to appropriate node ids - 64 bit values. flmAssert( pSingleNodeIdPred->pFromValue->eValType == XFLM_UINT64_VAL); flmAssert( pSingleNodeIdPred->pUntilValue->eValType == XFLM_UINT64_VAL); if (!pSingleNodeIdPred->bInclFrom) { pSingleNodeIdPred->pFromValue->val.ui64Val++; pSingleNodeIdPred->bInclFrom = TRUE; } if (!pSingleNodeIdPred->bInclUntil) { pSingleNodeIdPred->pUntilValue->val.ui64Val--; pSingleNodeIdPred->bInclUntil = TRUE; } if (pSingleNodeIdPred->pFromValue->val.ui64Val == pSingleNodeIdPred->pUntilValue->val.ui64Val) { break; } } pSingleNodeIdPred = pSingleNodeIdPred->pNext; } } if (pSingleNodeIdPred) { break; } pSingleNodeIdPath = pSingleNodeIdPath->pNext; } } if (pContext->pFirstChild && !pSingleNodeIdPath) { pContext = pContext->pFirstChild; continue; } // Optimize the context paths of the context we are on if (RC_BAD( rc = optimizeContext( pContext, pSingleNodeIdPath, pSingleNodeIdPred))) { goto Exit; } // Go to sibling context, if any while (!pContext->pNextSib) { if ((pContext = pContext->pParent) == NULL) { break; } // Child contexts have all been optimized and the cost // reflected up to this context. But there may be // CONTEXT_PATHs on this context that will change that // cost. if (RC_BAD( rc = optimizeContext( pContext, NULL, NULL))) { goto Exit; } } // If pContext is NULL at this point, there are no // other contexts to optimize. if (!pContext) { break; } pContext = pContext->pNextSib; // There has to have been a sibling context at this point. flmAssert( pContext); } if (m_pQuery->pContext->bMustScan) { // If we were told to use an index, we will use that index // and scan the entire index. if (m_bIndexSet) { if (RC_BAD( rc = setupIndexScan())) { goto Exit; } } else { m_bScan = TRUE; } } Exit: if (RC_OK( rc) && !m_bEmpty) { FLMUINT uiOptIndex = 0; if (m_bScan || m_bScanIndex) { if (m_bScan) { m_scanOptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; } else { F_NameTable * pNameTable = NULL; m_scanOptInfo.eOptType = XFLM_QOPT_USING_INDEX; m_scanOptInfo.uiCost = ~((FLMUINT)0); m_scanOptInfo.uiIxNum = m_uiIndex; uiOptIndex = m_uiIndex; m_scanOptInfo.bMustVerifyPath = TRUE; m_scanOptInfo.bDoNodeMatch = TRUE; m_scanOptInfo.bCanCompareOnKey = FALSE; m_scanOptInfo.szIxName [0] = 0; if (RC_OK( m_pDb->getNameTable( &pNameTable))) { FLMUINT uiIxNameLen = sizeof( m_scanOptInfo.szIxName); if (RC_BAD( pNameTable->getFromTagTypeAndNum( m_pDb, ELM_INDEX_TAG, m_uiIndex, NULL, (char *)m_scanOptInfo.szIxName, &uiIxNameLen, NULL, NULL, NULL, NULL, TRUE))) { m_scanOptInfo.szIxName [0] = 0; } } if (pNameTable) { pNameTable->Release(); } } m_scanOptInfo.ui64KeysRead = 0; m_scanOptInfo.ui64KeyHadDupDoc = 0; m_scanOptInfo.ui64KeysPassed = 0; m_scanOptInfo.ui64NodesRead = 0; m_scanOptInfo.ui64NodesTested = 0; m_scanOptInfo.ui64NodesPassed = 0; m_scanOptInfo.ui64DocsRead = 0; m_scanOptInfo.ui64DupDocsEliminated = 0; m_scanOptInfo.ui64NodesFailedValidation = 0; m_scanOptInfo.ui64DocsFailedValidation = 0; m_scanOptInfo.ui64DocsPassed = 0; m_pCurrOpt = &m_scanOptInfo; rc = newSource(); } else { FLMBOOL bHaveMultipleIndexes = FALSE; // Need to initialize all of the optimization information. m_pCurrContext = m_pQuery->pContext; useLeafContext( TRUE); do { m_pCurrOpt->ui64KeysRead = 0; m_pCurrOpt->ui64KeyHadDupDoc = 0; m_pCurrOpt->ui64KeysPassed = 0; m_pCurrOpt->ui64NodesRead = 0; m_pCurrOpt->ui64NodesTested = 0; m_pCurrOpt->ui64NodesPassed = 0; m_pCurrOpt->ui64DocsRead = 0; m_pCurrOpt->ui64DupDocsEliminated = 0; m_pCurrOpt->ui64NodesFailedValidation = 0; m_pCurrOpt->ui64DocsFailedValidation = 0; m_pCurrOpt->ui64DocsPassed = 0; // See if we are using an index. Later, if it turns out // we are only using one index, we will see if it matches // the sort index. if (m_pSortIxd && !bHaveMultipleIndexes) { if (m_pCurrPred->pNodeSource) { FLMUINT uiIndex; if (RC_BAD( rc = m_pCurrPred->pNodeSource->getIndex( (IF_Db *)m_pDb, &uiIndex, &bHaveMultipleIndexes))) { break; } if (uiIndex) { if (uiOptIndex == 0) { uiOptIndex = uiIndex; } else if (uiIndex != uiOptIndex) { bHaveMultipleIndexes = TRUE; } } } else if (m_pCurrOpt->uiIxNum) { if (uiOptIndex == 0) { uiOptIndex = m_pCurrOpt->uiIxNum; } else if (m_pCurrOpt->uiIxNum != uiOptIndex) { bHaveMultipleIndexes = TRUE; } } } } while (useNextPredicate()); if (bHaveMultipleIndexes || RC_BAD( rc)) { uiOptIndex = 0; } } // If we are sorting the results, see if the optimization // index already has the keys in the order we need. if (RC_OK( rc) && m_pSortIxd && uiOptIndex) { rc = checkSortIndex( uiOptIndex); } } if (m_pSortIxd || m_bPositioningEnabled) { // Need to eliminate dups in case there are multiple sort key // values in a document. // NOTE: When a sort key is specified, if a document has multiple // keys, we want them to appear sorted according to whatever is the // "lowest" sort key in the document. If we create a result set, // this will always work. However, if the entries are already "in order" // we have a small problem, because the order in which the document // is retrieved will then depend on the direction of traversal used // by the application. By eliminating duplicate documents, the application // can get the expected behavior of having the document sort according // to its "lowest" key if it reads forward. However, if the application // reads "backward" the document will be retrieved as if it were sorted // by its "higest" key. setDupHandling( TRUE); // If we have sort keys and the optimization index is not in // the same order as the sort index, we must create a result set. // The same is true if the application asked to enable positioning. if (m_bPositioningEnabled || !m_bEntriesAlreadyInOrder) { if (RC_OK( rc)) { rc = createResultSet(); } } } if ( RC_OK( rc)) { m_bOptimized = TRUE; } return( rc); } /*************************************************************************** Desc: Set node's current value to missing. Also releases the stream associated with the value, if any. ***************************************************************************/ FINLINE void fqReleaseNodeValue( FQNODE * pQNode ) { if ((pQNode->currVal.eValType == XFLM_BINARY_VAL || pQNode->currVal.eValType == XFLM_UTF8_VAL) && (pQNode->currVal.uiFlags & VAL_IS_STREAM) && pQNode->currVal.val.pIStream) { pQNode->currVal.uiFlags &= (~(VAL_IS_STREAM)); pQNode->currVal.val.pIStream->Release(); pQNode->currVal.val.pIStream = NULL; } if (pQNode->eNodeType != FLM_VALUE_NODE) { pQNode->currVal.eValType = XFLM_MISSING_VAL; } } /*************************************************************************** Desc: Evaluate a simple operator. ***************************************************************************/ FSTATIC RCODE fqEvalOperator( FLMUINT uiLanguage, FQNODE * pQNode ) { RCODE rc = NE_XFLM_OK; FQNODE * pLeftOperand; FQNODE * pRightOperand; XFlmBoolType eLeftBool; XFlmBoolType eRightBool; // Right now we are only able to do operator nodes. flmAssert( pQNode->eNodeType == FLM_OPERATOR_NODE); pLeftOperand = pQNode->pFirstChild; pRightOperand = pQNode->pLastChild; // See if either of this operator's operands have already // passed. If so, we can skip the evaluation. if (pLeftOperand->currVal.eValType == XFLM_PASSING_VAL || pRightOperand->currVal.eValType == XFLM_PASSING_VAL) { pQNode->currVal.eValType = XFLM_BOOL_VAL; pQNode->currVal.val.eBool = XFLM_TRUE; goto Exit; } if (pQNode->nd.op.eOperator != XFLM_AND_OP && pQNode->nd.op.eOperator != XFLM_OR_OP) { // If the left operand is an XPATH node that is the node id or // document id, be sure to convert the right operand value to // a node id value. if (pLeftOperand->eNodeType == FLM_XPATH_NODE && pLeftOperand->nd.pXPath->pLastComponent->eXPathAxis == META_AXIS) { if (RC_BAD( rc = fqGetNodeIdValue( &pRightOperand->currVal))) { goto Exit; } } // If the right operand is an XPATH node that is the node id or // document id, be sure to convert the left operand value to // a node id value. if (pRightOperand->eNodeType == FLM_XPATH_NODE && pRightOperand->nd.pXPath->pLastComponent->eXPathAxis == META_AXIS) { if (RC_BAD( rc = fqGetNodeIdValue( &pLeftOperand->currVal))) { goto Exit; } } } pQNode->currVal.eValType = XFLM_MISSING_VAL; switch (pQNode->nd.op.eOperator) { case XFLM_AND_OP: case XFLM_OR_OP: eLeftBool = XFLM_UNKNOWN; eRightBool = XFLM_UNKNOWN; // Get the left operand if (pLeftOperand->eNodeType == FLM_OPERATOR_NODE) { // This operator may not have been evaluated because of missing // XPATH values in one or both operands, in which case // its state will be XFLM_MISSING_VALUE. If it was evaluated, // its state should show a boolean value. if (pLeftOperand->currVal.eValType == XFLM_MISSING_VAL) { eLeftBool = (pLeftOperand->bNotted ? XFLM_TRUE : XFLM_FALSE); } else { flmAssert( pLeftOperand->currVal.eValType == XFLM_BOOL_VAL); eLeftBool = pLeftOperand->currVal.val.eBool; } } else if (pLeftOperand->eNodeType == FLM_XPATH_NODE) { if (!pLeftOperand->bNotted) { eLeftBool = (pLeftOperand->currVal.eValType != XFLM_MISSING_VAL) ? XFLM_TRUE : XFLM_FALSE; } else { eLeftBool = (pLeftOperand->currVal.eValType != XFLM_MISSING_VAL) ? XFLM_FALSE : XFLM_TRUE; } } else if (pLeftOperand->eNodeType == FLM_VALUE_NODE) { flmAssert( pLeftOperand->currVal.eValType == XFLM_BOOL_VAL); eLeftBool = pLeftOperand->currVal.val.eBool; } else { flmAssert( pLeftOperand->eNodeType == FLM_FUNCTION_NODE); if (!pLeftOperand->bNotted) { eLeftBool = fqTestValue( pLeftOperand) ? XFLM_TRUE : XFLM_FALSE; } else { eLeftBool = fqTestValue( pLeftOperand) ? XFLM_FALSE : XFLM_TRUE; } } // Get the right operand if ( pRightOperand->eNodeType == FLM_OPERATOR_NODE) { // This operator may not have been evaluated because of missing // XPATH values in one or both operands, in which case // its state will be XFLM_MISSING_VALUE. If it was evaluated, // its state should show a boolean value. if (pRightOperand->currVal.eValType == XFLM_MISSING_VAL) { eRightBool = (pRightOperand->bNotted ? XFLM_TRUE : XFLM_FALSE); } else { flmAssert( pRightOperand->currVal.eValType == XFLM_BOOL_VAL); eRightBool = pRightOperand->currVal.val.eBool; } } else if (pRightOperand->eNodeType == FLM_XPATH_NODE) { if (!pRightOperand->bNotted) { eRightBool = (pRightOperand->currVal.eValType != XFLM_MISSING_VAL) ? XFLM_TRUE : XFLM_FALSE; } else { eRightBool = (pRightOperand->currVal.eValType != XFLM_MISSING_VAL) ? XFLM_FALSE : XFLM_TRUE; } } else if (pRightOperand->eNodeType == FLM_VALUE_NODE) { flmAssert( pRightOperand->currVal.eValType == XFLM_BOOL_VAL); eRightBool = pRightOperand->currVal.val.eBool; } else { flmAssert( pRightOperand->eNodeType == FLM_FUNCTION_NODE); if (!pRightOperand->bNotted) { eRightBool = fqTestValue( pRightOperand) ? XFLM_TRUE : XFLM_FALSE; } else { eRightBool = fqTestValue( pRightOperand) ? XFLM_FALSE : XFLM_TRUE; } } // Calculate the answer pQNode->currVal.eValType = XFLM_BOOL_VAL; if (pQNode->nd.op.eOperator == XFLM_AND_OP) { if (eLeftBool == XFLM_FALSE || eRightBool == XFLM_FALSE) { pQNode->currVal.val.eBool = XFLM_FALSE; } else if (eLeftBool == XFLM_UNKNOWN || eRightBool == XFLM_UNKNOWN) { pQNode->currVal.val.eBool = XFLM_UNKNOWN; } else { // Both have to be XFLM_TRUE at this point pQNode->currVal.val.eBool = XFLM_TRUE; } } else // pQNode->nd.op.eOperator == XFLM_OR_OP { if (eLeftBool == XFLM_TRUE || eRightBool == XFLM_TRUE) { pQNode->currVal.val.eBool = XFLM_TRUE; } else if (eLeftBool == XFLM_UNKNOWN || eRightBool == XFLM_UNKNOWN) { pQNode->currVal.val.eBool = XFLM_UNKNOWN; } else { // Both have to be XFLM_FALSE at this point pQNode->currVal.val.eBool = XFLM_FALSE; } } break; case XFLM_EQ_OP: case XFLM_APPROX_EQ_OP: case XFLM_NE_OP: case XFLM_LT_OP: case XFLM_LE_OP: case XFLM_GT_OP: case XFLM_GE_OP: pQNode->currVal.eValType = XFLM_BOOL_VAL; if (RC_BAD( rc = fqCompareOperands( uiLanguage, &pLeftOperand->currVal, &pRightOperand->currVal, pQNode->nd.op.eOperator, pQNode->nd.op.uiCompareRules, pQNode->nd.op.pOpComparer, pQNode->bNotted, &pQNode->currVal.val.eBool))) { goto Exit; } break; case XFLM_BITAND_OP: case XFLM_BITOR_OP: case XFLM_BITXOR_OP: case XFLM_MULT_OP: case XFLM_DIV_OP: case XFLM_MOD_OP: case XFLM_PLUS_OP: case XFLM_MINUS_OP: case XFLM_NEG_OP: if (RC_BAD( rc = fqArithmeticOperator( &pLeftOperand->currVal, &pRightOperand->currVal, pQNode->nd.op.eOperator, &pQNode->currVal))) { goto Exit; } break; default: break; } Exit: // Need to release node values in case we are holding on // to an IStream. if (pLeftOperand) { fqReleaseNodeValue( pLeftOperand); } if (pRightOperand) { fqReleaseNodeValue( pRightOperand); } return( rc); } /*************************************************************************** Desc: Get the next value from an XPATH node. ***************************************************************************/ FSTATIC void fqResetIterator( FQNODE * pQNode, FLMBOOL bFullRelease, FLMBOOL bUseKeyNodes ) { FXPATH * pXPath; XPATH_COMPONENT * pXPathComponent; // Node better be an XPATH node. flmAssert( pQNode->eNodeType == FLM_XPATH_NODE); pXPath = pQNode->nd.pXPath; if (bFullRelease) { pXPath->bIsSource = FALSE; pXPath->pSourceComponent = NULL; pXPath->bHavePassingNode = FALSE; } pXPathComponent = pXPath->pLastComponent; while (pXPathComponent) { if (bFullRelease) { pXPathComponent->bIsSource = FALSE; pXPathComponent->pExprXPathSource = NULL; pXPathComponent->pOptPred = NULL; if (pXPathComponent->pKeyNode) { pXPathComponent->pKeyNode->Release(); pXPathComponent->pKeyNode = NULL; } } else if (pXPathComponent->bIsSource && bUseKeyNodes) { break; } if (pXPathComponent->pCurrNode) { pXPathComponent->pCurrNode->Release(); pXPathComponent->pCurrNode = NULL; } if (bFullRelease && pXPathComponent->pExpr) { fqReleaseQueryExpr( pXPathComponent->pExpr); } pXPathComponent = pXPathComponent->pPrev; } pXPath->bGettingNodes = FALSE; } /*************************************************************************** Desc: Get the first, last, next or previous node from a node source object. ***************************************************************************/ RCODE F_Query::getNodeSourceNode( FLMBOOL bForward, IF_QueryNodeSource * pNodeSource, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode ) { RCODE rc = NE_XFLM_OK; FLMUINT uiTimeLimit = m_uiTimeLimit; FLMUINT uiCurrTime; FLMUINT uiElapsedTime; // Determine the timeout limit that is left. if (uiTimeLimit) { uiCurrTime = FLM_GET_TIMER(); uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiStartTime); if (uiElapsedTime >= m_uiTimeLimit) { rc = RC_SET( NE_XFLM_TIMEOUT); goto Exit; } else { uiTimeLimit = FLM_TIMER_UNITS_TO_MILLI( (m_uiTimeLimit - uiElapsedTime)); // Always give at least one milli-second. if (!uiTimeLimit) { uiTimeLimit = 1; } } } if (*ppCurrNode) { // Get next or previous node. rc = (RCODE)(bForward ? pNodeSource->getNext( (IF_Db *)m_pDb, pContextNode, ppCurrNode, uiTimeLimit, m_pQueryStatus) : pNodeSource->getPrev( (IF_Db *)m_pDb, pContextNode, ppCurrNode, uiTimeLimit, m_pQueryStatus)); } else { // Get first or last node. rc = (RCODE)(bForward ? pNodeSource->getFirst( (IF_Db *)m_pDb, pContextNode, ppCurrNode, uiTimeLimit, m_pQueryStatus) : pNodeSource->getLast( (IF_Db *)m_pDb, pContextNode, ppCurrNode, uiTimeLimit, m_pQueryStatus)); } if (RC_BAD( rc)) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) { rc = NE_XFLM_OK; if (*ppCurrNode) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; } } goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from a ROOT_AXIS. ***************************************************************************/ RCODE F_Query::getRootAxisNode( IF_DOMNode ** ppCurrNode ) { RCODE rc = NE_XFLM_OK; // Better be an element we are searching for if (m_pCurrDoc->getNodeType() == DOCUMENT_NODE) { if (RC_BAD( rc = m_pCurrDoc->getFirstChild( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } // Search for an element node - it will be the root // of the document while ((*ppCurrNode)->getNodeType() != ELEMENT_NODE) { if (RC_BAD( rc = (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; rc = NE_XFLM_OK; } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } else { *ppCurrNode = m_pCurrDoc; (*ppCurrNode)->AddRef(); if ((*ppCurrNode)->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Get the next/previous node in a document ***************************************************************************/ RCODE F_Query::walkDocument( FLMBOOL bForward, FLMBOOL bWalkAttributes, FLMUINT uiAttrNameId, IF_DOMNode ** ppCurrNode ) { RCODE rc = NE_XFLM_OK; if (!(*ppCurrNode)) { *ppCurrNode = m_pCurrDoc; (*ppCurrNode)->AddRef(); goto Exit; } if ((*ppCurrNode)->getNodeType() == ATTRIBUTE_NODE) { if (uiAttrNameId) { // This attribute node should be the node that we already processed. // We simply want to go back to its parent node. No need to // walk through all of the sibling attributes. rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } else { rc = bForward ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode); } if (RC_OK( rc)) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } // Need to get the node's encompassing element node and // process it now. else if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } // See if the node has a child else if (RC_OK( rc = bForward ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode))) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } Walk_To_Attr_Nodes: if (bWalkAttributes && (*ppCurrNode)->getNodeType() == ELEMENT_NODE) { if( uiAttrNameId) { rc = (*ppCurrNode)->getAttribute( m_pDb, uiAttrNameId, ppCurrNode); } else { rc = bForward ? (*ppCurrNode)->getFirstAttribute( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastAttribute( m_pDb, ppCurrNode); } if (RC_OK( rc)) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { rc = NE_XFLM_OK; } } } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { // Go up tree until we find a sibling. for (;;) { if (RC_OK( rc = bForward ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode))) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } goto Walk_To_Attr_Nodes; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; break; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } Exit: return( rc); } /*************************************************************************** Desc: Get the next or previous child node. ***************************************************************************/ RCODE F_Query::getChildAxisNode( FLMBOOL bForward, IF_DOMNode * pContextNode, FLMUINT uiChildNameId, IF_DOMNode ** ppCurrNode ) { RCODE rc = NE_XFLM_OK; if (!pContextNode) { if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) { goto Exit; } } else { if (*ppCurrNode) { // If name id is non-zero, the caller should have prevented us // from coming back in here. flmAssert( !uiChildNameId); // Get next or previous sibling - should still be a child // of our context node, whatever it was. rc = (RCODE)(bForward ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); } else if (uiChildNameId) { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); rc = (*ppCurrNode)->getChildElement( m_pDb, uiChildNameId, ppCurrNode); } else { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); rc = (RCODE)(bForward ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); } if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from a PARENT_AXIS. ***************************************************************************/ RCODE F_Query::getParentAxisNode( FLMBOOL bForward, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NodeId; if (!pContextNode) { for (;;) { if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) { goto Exit; } if (!(*ppCurrNode)) { break; } // This node must be a parent of another node, // which means it must have at least one child node. if (RC_BAD( rc = (*ppCurrNode)->getFirstChildId( m_pDb, &ui64NodeId))) { goto Exit; } if (ui64NodeId) { break; } } } else { // PARENT_AXIS always starts from the context node - it // really doesn't matter what the last node was. if (RC_BAD( rc = pContextNode->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { if (*ppCurrNode) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; } rc = NE_XFLM_OK; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } Exit: return( rc); } /*************************************************************************** Desc: Get the next ancestor node. ***************************************************************************/ RCODE F_Query::getAncestorAxisNode( FLMBOOL bForward, FLMBOOL bIncludeSelf, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64ContextNodeId; FLMUINT64 ui64NodeId; FLMUINT64 ui64ParentId; FLMUINT64 ui64Tmp; FLMUINT uiContextAttrNameId; FLMUINT uiNameId; FLMUINT uiTmp; if (!pContextNode) { for (;;) { if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) { goto Exit; } if (!(*ppCurrNode)) { break; } // If we are including self, whatever node we get to matches // the axis. if (bIncludeSelf) { break; } // This node must be an ancestor of another node, // which means it must have at least one child node. if (RC_BAD( rc = (*ppCurrNode)->getFirstChildId( m_pDb, &ui64NodeId))) { goto Exit; } if (ui64NodeId) { break; } } } else { if( RC_BAD( rc = ((F_DOMNode *)pContextNode)->getNodeId( m_pDb, &ui64ContextNodeId, &uiContextAttrNameId))) { goto Exit; } if (*ppCurrNode) { if (bForward) { if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } else { if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, &ui64NodeId, &uiNameId))) { goto Exit; } // If the current node is the context node, we // have no further to go. if( ui64NodeId == ui64ContextNodeId && uiContextAttrNameId == uiNameId) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; goto Exit; } // Start from the context node, and go to just before // the node we are on. If we don't hit it, this node // is not an ancestor of our context node. (*ppCurrNode)->Release(); *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); for (;;) { if (RC_BAD( rc = (*ppCurrNode)->getParentId( m_pDb, &ui64ParentId))) { goto Exit; } if (ui64ParentId == ui64NodeId) { if (*ppCurrNode == pContextNode && !bIncludeSelf) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; } break; } if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } } else { if (bForward) { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); // If including context node, we have what we want. // Otherwise, we have to go to the parent. if (bIncludeSelf) { goto Exit; } if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } else { // Start at document root. Return it if it is not the // context node, or if we are including self. if( RC_BAD( rc = ((F_DOMNode *)m_pCurrDoc)->getNodeId( m_pDb, &ui64Tmp, &uiTmp))) { goto Exit; } if( ui64Tmp != ui64ContextNodeId || uiTmp != uiContextAttrNameId || bIncludeSelf) { *ppCurrNode = m_pCurrDoc; (*ppCurrNode)->AddRef(); } } } } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from a DESCENDANT_AXIS or a DESCENDANT_OR_SELF_AXIS. ***************************************************************************/ RCODE F_Query::getDescendantAxisNode( FLMBOOL bForward, FLMBOOL bIncludeSelf, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NodeId; FLMUINT64 ui64ContextNodeId; FLMUINT64 ui64Tmp; FLMUINT uiContextAttrNameId; FLMUINT uiTmp; if (!pContextNode) { for (;;) { if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) { goto Exit; } if (!(*ppCurrNode)) { break; } // If we are including self, whatever node we get to matches // the axis. if (bIncludeSelf) { break; } // This node must be a descendant of some // other node, so it must have a parent. if (RC_BAD( rc = (*ppCurrNode)->getParentId( m_pDb, &ui64NodeId))) { goto Exit; } if (ui64NodeId) { break; } } } else { if( RC_BAD( rc = ((F_DOMNode *)pContextNode)->getNodeId( m_pDb, &ui64ContextNodeId, &uiContextAttrNameId))) { goto Exit; } if (bForward) { if (!(*ppCurrNode)) { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); if (bIncludeSelf) { goto Exit; } } // See if the node has a child if (RC_OK( rc = (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode))) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { rc = NE_XFLM_OK; // Go up tree until we find a sibling. - Don't // go up past the context node. for (;;) { // If we are on the context node, we are done. if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, &ui64Tmp, &uiTmp))) { goto Exit; } if( ui64Tmp == ui64ContextNodeId && uiTmp == uiContextAttrNameId) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; goto Exit; } if (RC_OK( rc = (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode))) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } break; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } } else { if (*ppCurrNode) { // If we are going backwards and we are on the context node // there are no more nodes to get. if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, &ui64Tmp, &uiTmp))) { goto Exit; } if( ui64Tmp == ui64ContextNodeId && uiTmp == uiContextAttrNameId) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; goto Exit; } // See if the node has a previous sibling if (RC_BAD( rc = (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } // Go down to the last child of the previous sibling // that was found. Get_Last_Child: for (;;) { if (RC_BAD( rc = (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } // We are positioned on the rightmost child now. rc = NE_XFLM_OK; break; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } } else { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); // Go down to the last child of the context node. goto Get_Last_Child; } // If we arrive at the context node, we are done, unless // we are including self. if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, &ui64Tmp, &uiTmp))) { goto Exit; } if( ui64Tmp == ui64ContextNodeId && uiTmp == uiContextAttrNameId && !bIncludeSelf) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; } } } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from a PRECEDING_SIBLING_AXIS or FOLLOWING_SIBLING_AXIS. ***************************************************************************/ RCODE F_Query::getSibAxisNode( FLMBOOL bForward, FLMBOOL bPrevSibAxis, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NodeId; FLMUINT64 ui64CurrNodeId; FLMUINT64 ui64ContextNodeId; FLMUINT uiCurrNameId; FLMUINT uiContextAttrNameId; if (!pContextNode) { for (;;) { if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) { goto Exit; } if (!(*ppCurrNode)) { break; } // This node must be the next or previous sibling to some // node - which means it must have a previous or next sibling. rc = (RCODE)(bPrevSibAxis ? (*ppCurrNode)->getNextSibId( m_pDb, &ui64NodeId) : (*ppCurrNode)->getPrevSibId( m_pDb, &ui64NodeId)); if (RC_BAD( rc)) { goto Exit; } if (ui64NodeId) { break; } } } else { if (bForward) { if (!(*ppCurrNode)) { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); } rc = (RCODE)(bPrevSibAxis ? (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } else { if (*ppCurrNode) { rc = (RCODE)(bPrevSibAxis ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } else { FLMBOOL bAttr; *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); bAttr = (*ppCurrNode)->getNodeType() == ATTRIBUTE_NODE ? TRUE : FALSE; // Go to the parent and get either the first or // last child, depending on the axis. That is // where we need to start from. if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // A node without a parent should not have any siblings! rc = NE_XFLM_OK; (*ppCurrNode)->Release(); *ppCurrNode = NULL; } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } // Get the node's first or last child. if (!bAttr) { rc = (RCODE)(bPrevSibAxis ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); } else { rc = (RCODE)(bPrevSibAxis ? (*ppCurrNode)->getFirstAttribute( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastAttribute( m_pDb, ppCurrNode)); } if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } // If we landed on the context node, we are done - there will be // no more siblings to get. if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, &ui64CurrNodeId, &uiCurrNameId))) { goto Exit; } if( RC_BAD( rc = ((F_DOMNode *)pContextNode)->getNodeId( m_pDb, &ui64ContextNodeId, &uiContextAttrNameId))) { goto Exit; } if( ui64CurrNodeId == ui64ContextNodeId && uiCurrNameId == uiContextAttrNameId) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; } } } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from a PRECEDING_AXIS or FOLLOWING_AXIS. ***************************************************************************/ RCODE F_Query::getPrevOrAfterAxisNode( FLMBOOL bForward, FLMBOOL bPrevAxis, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode) { RCODE rc = NE_XFLM_OK; FLMUINT uiChildCnt; IF_DOMNode * pParentNode = NULL; IF_DOMNode * pSibNode = NULL; IF_DOMNode * pHighestAncestorWithSib = NULL; FLMUINT64 ui64NodeId; FLMUINT64 ui64ContextId; FLMUINT64 ui64Tmp; if (!pContextNode) { for (;;) { if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) { goto Exit; } if (!(*ppCurrNode)) { break; } if (pParentNode) { pParentNode->Release(); } pParentNode = *ppCurrNode; pParentNode->AddRef(); // This node must be previous to some other node if the // axis is PRECEDING_AXIS (bPrevAxis == TRUE) or after some // other node if the axis is FOLLOWING_AXIS // (bPrevAxis == FALSE). This does not count descendants // or ancestors or attribute nodes. for (;;) { rc = (RCODE)(bPrevAxis ? pParentNode->getNextSibId( m_pDb, &ui64NodeId) : pParentNode->getPrevSibId( m_pDb, &ui64NodeId)); if (RC_BAD( rc)) { goto Exit; } if (ui64NodeId) { goto Exit; } if (RC_BAD( rc = pParentNode->getParentNode( m_pDb, &pParentNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; break; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } } else { if( RC_BAD( rc = pContextNode->getNodeId( m_pDb, &ui64ContextId))) { goto Exit; } // Context node better not be an attribute node. That is illegal. // To be nice here, we will change the context node to be the // attribute's encompassing element node. if (pContextNode->getNodeType() == ATTRIBUTE_NODE) { if (RC_BAD( rc = pContextNode->getParentNode( m_pDb, &pContextNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } if (bForward) { if (*ppCurrNode) { // Go to child of current node rc = (RCODE)(bPrevAxis ? (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode) : (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode)); if (RC_OK( rc)) { rc = incrNodesRead(); goto Exit; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } } else { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); } for (;;) { rc = (RCODE)(bPrevAxis ? (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode)); if (RC_OK( rc)) { rc = incrNodesRead(); goto Exit; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } // Go to the parent node if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; rc = NE_XFLM_OK; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } else { // Searching backwards if (!(*ppCurrNode)) { *ppCurrNode = pContextNode; (*ppCurrNode)->AddRef(); pHighestAncestorWithSib = NULL; // Find highest parent that has a sibling for (;;) { rc = (RCODE)(bPrevAxis ? (*ppCurrNode)->getPreviousSibling( m_pDb, &pSibNode) : (*ppCurrNode)->getNextSibling( m_pDb, &pSibNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } else { goto Exit; } } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } if (pHighestAncestorWithSib) { pHighestAncestorWithSib->Release(); } pHighestAncestorWithSib = *ppCurrNode; pHighestAncestorWithSib->AddRef(); } // Go to node's parent if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { goto Exit; } } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } (*ppCurrNode)->Release(); *ppCurrNode = NULL; // If none of the ancestry had a prev/next sibling // we are done because there will be no nodes to // process. if (!pHighestAncestorWithSib) { goto Exit; } // Find the leftmost/rightmost sibling of the ancestor node, // and go down to its leftmost/rightmost child. // That is equivalent to going up to the parent node (which // there must be one!) and then going down to the // leftmost/rightmost child of that parent. *ppCurrNode = pHighestAncestorWithSib; (*ppCurrNode)->AddRef(); if (RC_OK( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { // Should not be possible, because we already know that // pHighestAncestorWithSib has a sibling! - which // necessitates it having a parent! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Now go down to the leftmost/rightmost child uiChildCnt = 0; for (;;) { rc = (RCODE)(bPrevAxis ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // If we didn't go down to any child, this is // a problem, because we got to the parent node // from a child node! if (!uiChildCnt) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else { rc = NE_XFLM_OK; } } goto Exit; } else { uiChildCnt++; if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } } // The following code is only executed if *ppCurrNode is non-NULL // when we enter this method. rc = (RCODE)(bPrevAxis ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); if (RC_OK( rc)) { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } // If we have hit the context node, we are done if( RC_BAD( rc = (*ppCurrNode)->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } if( ui64Tmp == ui64ContextId) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; goto Exit; } // Go to rightmost/leftmost child - If we hit the // context node anywhere in here, we are done. for (;;) { rc = (RCODE)(bPrevAxis ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { goto Exit; } } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } // See if we hit the context node. if( RC_BAD( rc = (*ppCurrNode)->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } if( ui64Tmp == ui64ContextId) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; goto Exit; } } } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; rc = NE_XFLM_OK; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } // Should never hit the context node here #ifdef FLM_DEBUG if( RC_BAD( rc = (*ppCurrNode)->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } flmAssert( ui64Tmp != ui64ContextId); #endif } } } } Exit: if (pParentNode) { pParentNode->Release(); } if (pSibNode) { pSibNode->Release(); } if (pHighestAncestorWithSib) { pHighestAncestorWithSib->Release(); } return( rc); } /*************************************************************************** Desc: Get the next node from a ATTRIBUTE_AXIS or NAMESPACE_AXIS. ***************************************************************************/ RCODE F_Query::getAttrAxisNode( FLMBOOL bForward, FLMBOOL bAttrAxis, FLMUINT uiAttrNameId, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode ) { RCODE rc = NE_XFLM_OK; FLMBOOL bIsNamespaceDecl; if (!pContextNode) { for (;;) { if (RC_BAD( rc = walkDocument( bForward, TRUE, uiAttrNameId, ppCurrNode))) { goto Exit; } if (!(*ppCurrNode)) { break; } // Must be an attribute node. if ((*ppCurrNode)->getNodeType() != ATTRIBUTE_NODE) { continue; } if (bAttrAxis) { break; } // Must be a namespace attribute. if (RC_BAD( rc = (*ppCurrNode)->isNamespaceDecl( m_pDb, &bIsNamespaceDecl))) { goto Exit; } if (bIsNamespaceDecl) { break; } } } else { for (;;) { if( *ppCurrNode) { flmAssert( (*ppCurrNode)->getNodeType() == ATTRIBUTE_NODE); // Better not be testing for a specific name. This should // not have been called if we already processed that // attribute. flmAssert( !uiAttrNameId); // Get the next/previous sibling, if any rc = (RCODE)(bForward ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; rc = NE_XFLM_OK; } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } else { (*ppCurrNode) = pContextNode; (*ppCurrNode)->AddRef(); // Context node better be an element node flmAssert( (*ppCurrNode)->getNodeType() == ELEMENT_NODE); // Get the first/last attribute of the node. If a specific // attribute ID is specified, get it - no need to cycle through // all of the attributes. if (uiAttrNameId) { rc = (*ppCurrNode)->getAttribute( m_pDb, uiAttrNameId, ppCurrNode); } else { rc = (RCODE)(bForward ? (*ppCurrNode)->getFirstAttribute( m_pDb, ppCurrNode) : (*ppCurrNode)->getLastAttribute( m_pDb, ppCurrNode)); } if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { (*ppCurrNode)->Release(); *ppCurrNode = NULL; rc = NE_XFLM_OK; } } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } } if (bAttrAxis) { break; } // Must be a namespace attribute. if (RC_BAD( rc = (*ppCurrNode)->isNamespaceDecl( m_pDb, &bIsNamespaceDecl))) { goto Exit; } if (bIsNamespaceDecl) { break; } } } Exit: return( rc); } /*************************************************************************** Desc: Determine the inverted axis for a given axis type. ***************************************************************************/ FINLINE eXPathAxisTypes invertedAxis( eXPathAxisTypes eAxis) { eXPathAxisTypes eInvertedAxis = ROOT_AXIS; switch (eAxis) { case ROOT_AXIS: // A root axis cannot be inverted. flmAssert( 0); eInvertedAxis = ROOT_AXIS; break; case CHILD_AXIS: case ATTRIBUTE_AXIS: case NAMESPACE_AXIS: eInvertedAxis = PARENT_AXIS; break; case PARENT_AXIS: eInvertedAxis = CHILD_AXIS; break; case ANCESTOR_AXIS: eInvertedAxis = DESCENDANT_AXIS; break; case DESCENDANT_AXIS: eInvertedAxis = ANCESTOR_AXIS; break; case FOLLOWING_SIBLING_AXIS: eInvertedAxis = PRECEDING_SIBLING_AXIS; break; case PRECEDING_SIBLING_AXIS: eInvertedAxis = FOLLOWING_SIBLING_AXIS; break; case FOLLOWING_AXIS: eInvertedAxis = PRECEDING_AXIS; break; case PRECEDING_AXIS: eInvertedAxis = FOLLOWING_AXIS; break; case SELF_AXIS: eInvertedAxis = SELF_AXIS; break; case DESCENDANT_OR_SELF_AXIS: eInvertedAxis = ANCESTOR_OR_SELF_AXIS; break; case ANCESTOR_OR_SELF_AXIS: eInvertedAxis = DESCENDANT_OR_SELF_AXIS; break; case META_AXIS: eInvertedAxis = SELF_AXIS; break; } return( eInvertedAxis); } /*************************************************************************** Desc: See if the passed in node ID satisfies the occurrence criteria for the xpath component. ***************************************************************************/ RCODE F_Query::verifyOccurrence( FLMBOOL bUseKeyNodes, XPATH_COMPONENT * pXPathComponent, IF_DOMNode * pCurrNode, FLMBOOL * pbPassed) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pContextNode = NULL; IF_DOMNode * pTmpCurrNode = NULL; FLMUINT64 ui64NodeId; FLMUINT64 ui64Tmp; FLMBOOL bGetContextNodes = FALSE; XPATH_COMPONENT * pXPathContextComponent = pXPathComponent->pPrev ? pXPathComponent->pPrev : pXPathComponent->pXPathContext; if( RC_BAD( rc = pCurrNode->getNodeId( m_pDb, &ui64NodeId))) { goto Exit; } if (!pXPathContextComponent) { pContextNode = NULL; } else if (bUseKeyNodes && pXPathContextComponent->pKeyNode) { pContextNode = pXPathContextComponent->pKeyNode; pContextNode->AddRef(); } else if (pXPathContextComponent->pCurrNode) { pContextNode = pXPathContextComponent->pCurrNode; pContextNode->AddRef(); } else { // Need to get context nodes. May have to try multiple ones. bGetContextNodes = TRUE; if (RC_BAD( rc = getXPathComponentFromAxis( pCurrNode, TRUE, FALSE, pXPathContextComponent, &pContextNode, invertedAxis( pXPathComponent->eXPathAxis), TRUE, FALSE))) { goto Exit; } if (!pContextNode) { *pbPassed = FALSE; goto Exit; } } // *pbPassed better have been passed in as TRUE // It will get set to FALSE if we don't find the node // we are looking for. flmAssert( *pbPassed); for (;;) { if (RC_BAD( rc = getXPathComponentFromAxis( pContextNode, TRUE, FALSE, pXPathComponent, &pTmpCurrNode, pXPathComponent->eXPathAxis, FALSE, FALSE))) { goto Exit; } if (!pTmpCurrNode) { No_More_Nodes: if (!bGetContextNodes) { *pbPassed = FALSE; goto Exit; } // If we didn't have a context node to begin with, // attempt another one. if (RC_BAD( rc = getXPathComponentFromAxis( pCurrNode, TRUE, FALSE, pXPathContextComponent, &pContextNode, invertedAxis( pXPathComponent->eXPathAxis), TRUE, FALSE))) { goto Exit; } // No more context nodes to try. We are done. if (!pContextNode) { *pbPassed = FALSE; goto Exit; } continue; } if( RC_BAD( rc = pTmpCurrNode->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } if( ui64Tmp == ui64NodeId) { break; } // If we are dealing with a constant position, and we didn't // find the node we were looking for, we are done. The // above call would have found it. If we are dealing // with an expression, there is no guarantee that the above // code would have found just one occurrence of a matching // node, and it may not have been the one we were looking // for. if (pXPathComponent->uiContextPosNeeded) { goto No_More_Nodes; } } Exit: if (pTmpCurrNode) { pTmpCurrNode->Release(); } if (pContextNode) { pContextNode->Release(); } return( rc); } /*************************************************************************** Desc: Get the node for an xpath component that is related to the given xpath context component by the specified axis. ***************************************************************************/ RCODE F_Query::getXPathComponentFromAxis( IF_DOMNode * pContextNode, FLMBOOL bForward, FLMBOOL bUseKeyNodes, XPATH_COMPONENT * pXPathComponent, IF_DOMNode ** ppCurrNode, eXPathAxisTypes eAxis, FLMBOOL bAxisInverted, FLMBOOL bCountNodes) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pCurrNode = NULL; FLMUINT uiOccurrence = 0; FLMBOOL bCanCountOccurrence; FLMBOOL bMatches; FLMBOOL bHasContextPosTest = hasContextPosTest( pXPathComponent); FLMUINT uiNameId; // Remember the current node, and then set to NULL. if (*ppCurrNode) { pCurrNode = *ppCurrNode; pCurrNode->AddRef(); (*ppCurrNode)->Release(); *ppCurrNode = NULL; } // Determine if we can count occurrences. if (bForward && !bAxisInverted) { uiOccurrence = 0; bCanCountOccurrence = TRUE; } else { // Cannot keep a running count if we are going backwards or if // we are operating on an inverted axis. bCanCountOccurrence = FALSE; } // Go until we get a node that passes, or until we have no more // nodes to check. for (;;) { if (pXPathComponent->pNodeSource) { // Axis is ignored for xpath components that have a node source. if (RC_BAD( rc = getNodeSourceNode( bForward, pXPathComponent->pNodeSource, pContextNode, &pCurrNode))) { goto Exit; } goto Test_Node; } // Type of search we do will depend on axis switch (eAxis) { case ROOT_AXIS: // There is only one root node to get, so if we already // got it, we are done. if (pCurrNode) { goto Exit; } flmAssert( pXPathComponent->eNodeType == ELEMENT_NODE); if (RC_BAD( rc = getRootAxisNode( &pCurrNode))) { goto Exit; } // Will only ever be one occurrence of a root node. if (!bAxisInverted) { uiOccurrence = 0; bCanCountOccurrence = TRUE; } break; case CHILD_AXIS: // For a context that requires all child elements to be unique, // there will only be one child with that name ID. // If we already got it, we are done. if (pContextNode && (((F_DOMNode *)pContextNode)->getModeFlags() & FDOM_HAVE_CELM_LIST) && pXPathComponent->uiDictNum) { if( pCurrNode) { if( RC_BAD( rc = pCurrNode->getNameId( m_pDb, &uiNameId))) { goto Exit; } if( uiNameId == pXPathComponent->uiDictNum) { goto Exit; } } if (RC_BAD( rc = getChildAxisNode( bForward, pContextNode, pXPathComponent->uiDictNum, &pCurrNode))) { goto Exit; } } else { if (RC_BAD( rc = getChildAxisNode( bForward, pContextNode, 0, &pCurrNode))) { goto Exit; } } break; case PARENT_AXIS: // There is only one parent node to get if we are in a // specific context, so if we already got it, we are // done. if (pCurrNode && pContextNode) { goto Exit; } if (RC_BAD( rc = getParentAxisNode( bForward, pContextNode, &pCurrNode))) { goto Exit; } // Will only ever be one occurrence of a parent node. if (!bAxisInverted) { uiOccurrence = 0; bCanCountOccurrence = TRUE; } break; case ANCESTOR_AXIS: if (RC_BAD( rc = getAncestorAxisNode( bForward, FALSE, pContextNode, &pCurrNode))) { goto Exit; } break; case DESCENDANT_AXIS: if (RC_BAD( rc = getDescendantAxisNode( bForward, FALSE, pContextNode, &pCurrNode))) { goto Exit; } break; case FOLLOWING_SIBLING_AXIS: if (RC_BAD( rc = getSibAxisNode( bForward, FALSE, pContextNode, &pCurrNode))) { goto Exit; } break; case PRECEDING_SIBLING_AXIS: if (RC_BAD( rc = getSibAxisNode( bForward, TRUE, pContextNode, &pCurrNode))) { goto Exit; } break; case FOLLOWING_AXIS: if (RC_BAD( rc = getPrevOrAfterAxisNode( bForward, FALSE, pContextNode, &pCurrNode))) { goto Exit; } break; case PRECEDING_AXIS: if (RC_BAD( rc = getPrevOrAfterAxisNode( bForward, TRUE, pContextNode, &pCurrNode))) { goto Exit; } break; case ATTRIBUTE_AXIS: // For a specific context, there is only one attribute with // a given name ID. If we already got it, we are done. if( pCurrNode && pContextNode && pXPathComponent->uiDictNum) { if( RC_BAD( rc = pCurrNode->getNameId( m_pDb, &uiNameId))) { goto Exit; } if( uiNameId == pXPathComponent->uiDictNum) { goto Exit; } } if (RC_BAD( rc = getAttrAxisNode( bForward, TRUE, pXPathComponent->uiDictNum, pContextNode, &pCurrNode))) { goto Exit; } break; case NAMESPACE_AXIS: if (RC_BAD( rc = getAttrAxisNode( bForward, FALSE, 0, pContextNode, &pCurrNode))) { goto Exit; } break; case SELF_AXIS: case META_AXIS: // Within a given context, there is only one occurrence of the // META or SELF axis. If we already got it, we are done. if (pCurrNode && pContextNode) { goto Exit; } if (!pContextNode) { // If the node is the SELF_AXIS, there better be // a context node! flmAssert( eAxis != SELF_AXIS); if (RC_BAD( rc = walkDocument( bForward, TRUE, 0, &pCurrNode))) { goto Exit; } } else { pCurrNode = pContextNode; pCurrNode->AddRef(); } // Will only ever be one occurrence of a meta-axis node or // a self-axis node. if (!bAxisInverted) { uiOccurrence = 0; bCanCountOccurrence = TRUE; } break; case DESCENDANT_OR_SELF_AXIS: if (RC_BAD( rc = getDescendantAxisNode( bForward, TRUE, pContextNode, &pCurrNode))) { goto Exit; } break; case ANCESTOR_OR_SELF_AXIS: if (RC_BAD( rc = getAncestorAxisNode( bForward, TRUE, pContextNode, &pCurrNode))) { goto Exit; } break; } Test_Node: if (!pCurrNode) { break; } if (bCountNodes) { m_pCurrOpt->ui64NodesTested++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } bMatches = TRUE; if (pXPathComponent->eNodeType != ANY_NODE_TYPE && eAxis != META_AXIS) { if (pCurrNode->getNodeType() != pXPathComponent->eNodeType) { bMatches = FALSE; } // Verify that the dictionary number matches. else if (pXPathComponent->eNodeType == ELEMENT_NODE || pXPathComponent->eNodeType == ATTRIBUTE_NODE || pXPathComponent->eNodeType == DATA_NODE) { if (pXPathComponent->uiDictNum) { if( RC_BAD( rc = pCurrNode->getNameId( m_pDb, &uiNameId))) { goto Exit; } if( uiNameId != pXPathComponent->uiDictNum) { bMatches = FALSE; } } } } // If there is an expression evaluate it. // The expression must pass in order to // keep this particular node. if (bMatches && pXPathComponent->pExpr) { if (RC_BAD( rc = evalExpr( pCurrNode, TRUE, bUseKeyNodes, pXPathComponent->pExpr, &bMatches, NULL))) { goto Exit; } } // See if there is an occurrence test. if (bMatches && bHasContextPosTest) { if (bCanCountOccurrence) { uiOccurrence++; if (pXPathComponent->uiContextPosNeeded) { if (uiOccurrence != pXPathComponent->uiContextPosNeeded) { bMatches = FALSE; } } else { FLMUINT uiPosNeeded; if (RC_BAD( rc = evalExpr( pCurrNode, TRUE, bUseKeyNodes, pXPathComponent->pContextPosExpr, NULL, NULL))) { goto Exit; } if (RC_BAD( fqGetPosition( &pXPathComponent->pContextPosExpr->currVal, &uiPosNeeded)) || uiOccurrence != uiPosNeeded) { bMatches = FALSE; } } } else { // Need to verify the context position because we couldn't // calculate it as we went. if (RC_BAD( rc = verifyOccurrence( bUseKeyNodes, pXPathComponent, pCurrNode, &bMatches))) { goto Exit; } } } if (bMatches) { *ppCurrNode = pCurrNode; // No need to AddRef on *ppCurrNode, just steal it from // pCurrNode. Setting pCurrNode to NULL keeps it from // being Release()'d below. pCurrNode = NULL; break; } } Exit: if (pCurrNode) { pCurrNode->Release(); } return( rc); } /*************************************************************************** Desc: Get an FQVALUE from a DOM node. ***************************************************************************/ FSTATIC RCODE fqGetValueFromNode( F_Db * pDb, IF_DOMNode * pNode, FQVALUE * pQValue, FLMUINT uiMetaDataType ) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataType; FLMUINT uiNumChars; pQValue->uiFlags = 0; if (uiMetaDataType) { switch (uiMetaDataType) { case XFLM_META_NODE_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getNodeId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_DOCUMENT_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getDocumentId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_PARENT_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getParentId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_FIRST_CHILD_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getFirstChildId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_LAST_CHILD_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getLastChildId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_NEXT_SIBLING_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getNextSibId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_PREV_SIBLING_ID: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getPrevSibId( pDb, &pQValue->val.ui64Val); goto Exit; case XFLM_META_VALUE: pQValue->eValType = XFLM_UINT64_VAL; rc = pNode->getMetaValue( pDb, &pQValue->val.ui64Val); goto Exit; default: break; } } if (RC_BAD( rc = pNode->getDataType( pDb, &uiDataType))) { goto Exit; } switch (uiDataType) { case XFLM_NODATA_TYPE: // Should have been set to missing on the outside flmAssert( pQValue->eValType == XFLM_MISSING_VAL); pQValue->eValType = XFLM_BOOL_VAL; pQValue->val.eBool = XFLM_TRUE; break; case XFLM_TEXT_TYPE: if (RC_BAD( rc = pNode->getTextIStream( pDb, &pQValue->val.pIStream, &uiNumChars))) { goto Exit; } pQValue->eValType = XFLM_UTF8_VAL; pQValue->uiFlags |= VAL_IS_STREAM; break; case XFLM_NUMBER_TYPE: // First, see if we can get it as a UINT - the most common // type. if (RC_OK( rc = pNode->getUINT( pDb, &pQValue->val.uiVal))) { pQValue->eValType = XFLM_UINT_VAL; } else if (rc == NE_XFLM_CONV_NUM_OVERFLOW) { if (RC_BAD( rc = pNode->getUINT64( pDb, &pQValue->val.ui64Val))) { goto Exit; } pQValue->eValType = XFLM_UINT64_VAL; } else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) { if (RC_OK( rc = pNode->getINT( pDb, &pQValue->val.iVal))) { pQValue->eValType = XFLM_INT_VAL; } else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) { if (RC_BAD( rc = pNode->getINT64( pDb, &pQValue->val.i64Val))) { goto Exit; } pQValue->eValType = XFLM_INT64_VAL; } } else { goto Exit; } break; case XFLM_BINARY_TYPE: if (RC_BAD( rc = pNode->getIStream( pDb, &pQValue->val.pIStream, &uiDataType))) { goto Exit; } flmAssert( uiDataType == XFLM_BINARY_TYPE); pQValue->eValType = XFLM_BINARY_VAL; pQValue->uiFlags |= VAL_IS_STREAM; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the next value from an XPATH node. ***************************************************************************/ RCODE F_Query::getNextXPathValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FLMBOOL bUseKeyNodes, FLMBOOL bXPathIsEntireExpr, FQNODE * pQNode ) { RCODE rc = NE_XFLM_OK; FXPATH * pXPath; XPATH_COMPONENT * pXPathComponent; // Node better be an XPATH node. flmAssert( pQNode->eNodeType == FLM_XPATH_NODE); pXPath = pQNode->nd.pXPath; // If the old value type was a stream, get rid of it. fqReleaseNodeValue( pQNode); if (pXPath->bGettingNodes) { pXPathComponent = pXPath->pLastComponent; } else if (pXPath->bIsSource && bUseKeyNodes) { pXPathComponent = pXPath->pSourceComponent->pNext; // This routine should never be called if the source component // is the rightmost component. That is taken care of on // the outside. flmAssert( pXPathComponent); } else { pXPathComponent = pXPath->pFirstComponent; } // This loop will go until we get to the last xpath component and get // a node. If it cannot get a node at one context, it will retreat // to the prior context, until it gets back to the first xpath // component, and there are no more to get for (;;) { IF_DOMNode * pTmpContextNode; if (!pXPathComponent->pPrev) { pTmpContextNode = pContextNode; } else if (bUseKeyNodes && pXPathComponent->pPrev->pKeyNode) { pTmpContextNode = pXPathComponent->pPrev->pKeyNode; } else { pTmpContextNode = pXPathComponent->pPrev->pCurrNode; } if (RC_BAD( rc = getXPathComponentFromAxis( pTmpContextNode, bForward, bUseKeyNodes, pXPathComponent, &pXPathComponent->pCurrNode, pXPathComponent->eXPathAxis, FALSE, bXPathIsEntireExpr && !pXPathComponent->pNext ? TRUE : FALSE))) { goto Exit; } // If we didn't get any nodes that passed here, we have nothing // to give back. if (pXPathComponent->pCurrNode) { if ((pXPathComponent = pXPathComponent->pNext) == NULL) { break; } } else if (pXPathComponent->pPrev && (!pXPathComponent->pPrev->bIsSource || !bUseKeyNodes)) { // Was no node, go to prior context to get its next node. pXPathComponent = pXPathComponent->pPrev; } else { // There was no prior context, so we do not have a value // to return for this xpath. Note that // pQNode->currVal.eValType should already have been set to // XFLM_MISSING_VAL up above, so no need to set it here. flmAssert( pQNode->currVal.eValType == XFLM_MISSING_VAL); fqResetIterator( pQNode, FALSE, bUseKeyNodes); goto Exit; } } // Extract the value from the DOM node pointed to by the // last component in the xpath. pXPath->bGettingNodes = TRUE; if (pQNode->pParent && isLogicalOp( pQNode->pParent->nd.op.eOperator)) { pQNode->currVal.eValType = XFLM_BOOL_VAL; pQNode->currVal.val.eBool = (pQNode->bNotted ? XFLM_FALSE : XFLM_TRUE); } else { if (RC_BAD( rc = fqGetValueFromNode( m_pDb, pXPath->pLastComponent->pCurrNode, &pQNode->currVal, getMetaDataType( pXPath->pLastComponent)))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Test a value and return a TRUE/FALSE boolean. ***************************************************************************/ FSTATIC FLMBOOL fqTestValue( FQNODE * pQueryExpr ) { FLMBOOL bPassed; switch (pQueryExpr->currVal.eValType) { case XFLM_BOOL_VAL: bPassed = (pQueryExpr->currVal.val.eBool == XFLM_TRUE) ? TRUE : FALSE; break; case XFLM_UINT_VAL: bPassed = (pQueryExpr->currVal.val.uiVal) ? TRUE : FALSE; break; case XFLM_UINT64_VAL: bPassed = (pQueryExpr->currVal.val.ui64Val) ? TRUE : FALSE; break; case XFLM_INT_VAL: bPassed = (pQueryExpr->currVal.val.iVal) ? TRUE : FALSE; break; case XFLM_INT64_VAL: bPassed = (pQueryExpr->currVal.val.i64Val) ? TRUE : FALSE; break; case XFLM_BINARY_VAL: case XFLM_UTF8_VAL: bPassed = (pQueryExpr->currVal.uiDataLen) ? TRUE : FALSE; break; default: bPassed = FALSE; break; } return( bPassed); } /*************************************************************************** Desc: Get the next value for a function. ***************************************************************************/ RCODE F_Query::getNextFunctionValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FQNODE * pCurrNode, F_DynaBuf * pDynaBuf) { RCODE rc = NE_XFLM_OK; ValIterator eIterator; FLMBYTE ucValBuf [sizeof( FLMUINT64)]; FLMBYTE * pucVal; IF_DOMNode * pNode = NULL; FLMBOOL bPassed; // Evaluate the parameter expression, if any. if (pCurrNode->nd.pQFunction->pFirstArg) { if (RC_BAD( rc = evalExpr( pContextNode, TRUE, FALSE, pCurrNode->nd.pQFunction->pFirstArg->pExpr, &bPassed, &pNode))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) { flmAssert( pNode == NULL); rc = NE_XFLM_OK; } else { goto Exit; } } } if (bForward) { eIterator = (!pCurrNode->bUsedValue) ? GET_FIRST_VAL : GET_NEXT_VAL; } else { eIterator = (!pCurrNode->bUsedValue) ? GET_LAST_VAL : GET_PREV_VAL; } fqReleaseNodeValue( pCurrNode); pCurrNode->currVal.uiDataLen = 0; pCurrNode->currVal.uiFlags = 0; pDynaBuf->truncateData( 0); if (RC_BAD( rc = pCurrNode->nd.pQFunction->pFuncObj->getValue( (IF_Db *)m_pDb, pNode, eIterator, &pCurrNode->currVal.eValType, &pCurrNode->bLastValue, ucValBuf, pDynaBuf))) { goto Exit; } pucVal = &ucValBuf [0]; switch (pCurrNode->currVal.eValType) { case XFLM_BOOL_VAL: pCurrNode->currVal.val.eBool = *((XFlmBoolType *)pucVal); break; case XFLM_UINT_VAL: pCurrNode->currVal.val.uiVal = *((FLMUINT *)pucVal); break; case XFLM_UINT64_VAL: pCurrNode->currVal.val.ui64Val = *((FLMUINT64 *)pucVal); break; case XFLM_INT_VAL: pCurrNode->currVal.val.iVal = *((FLMINT *)pucVal); break; case XFLM_INT64_VAL: pCurrNode->currVal.val.i64Val = *((FLMINT64 *)pucVal); break; case XFLM_BINARY_VAL: case XFLM_UTF8_VAL: pCurrNode->currVal.uiDataLen = pDynaBuf->getDataLength(); pCurrNode->currVal.val.pucBuf = (FLMBYTE *)pDynaBuf->getBufferPtr(); break; case XFLM_MISSING_VAL: default: break; } Exit: if (pNode) { pNode->Release(); } return( rc); } /*************************************************************************** Desc: Reset the entire query tree. It could have been left in a partial evaluation state from the last document evaluated. ***************************************************************************/ FSTATIC void fqResetQueryTree( FQNODE * pQueryTree, FLMBOOL bUseKeyNodes, FLMBOOL bResetAllXPaths) { FQNODE * pCurrNode = pQueryTree; for (;;) { fqReleaseNodeValue( pCurrNode); pCurrNode->bUsedValue = FALSE; pCurrNode->bLastValue = FALSE; if (pCurrNode->pFirstChild) { pCurrNode = pCurrNode->pFirstChild; } else { if (pCurrNode->eNodeType == FLM_XPATH_NODE) { // Don't reset the iterator if this xpath node is // the root node of the outermost query. if (bResetAllXPaths || pCurrNode->pParent || pCurrNode->nd.pXPath->pFirstComponent->pXPathContext) { fqResetIterator( pCurrNode, FALSE, bUseKeyNodes); } } while (!pCurrNode->pNextSib) { if ((pCurrNode = pCurrNode->pParent) == NULL) { break; } } if (!pCurrNode) { break; } pCurrNode = pCurrNode->pNextSib; } } } /*************************************************************************** Desc: Evaluate an operand in a query expression. If it is an operand of a boolean operator (AND/OR), we may be able to short-circuit the evaluation of the other operand. This will be propagated up the query tree as far as possible. If it is an operand of some other operator, we check to see if we have both operands. If not, we move *ppCurrNode to get the next sibling so we can evalute the other operand. VISIT: We could short-circuit arithmetic operations if an operand is missing. ***************************************************************************/ FSTATIC RCODE fqTryEvalOperator( FLMUINT uiLanguage, FQNODE ** ppCurrNode) { RCODE rc = NE_XFLM_OK; FQNODE * pCurrNode = *ppCurrNode; XFlmBoolType eBoolVal; XFlmBoolType eBoolPartialEval; for (;;) { if (!pCurrNode->pParent) { pCurrNode = NULL; break; } // If the current node's parent is an AND or OR // operator, see if we even need to go to the next // sibling. flmAssert( pCurrNode->pParent->eNodeType == FLM_OPERATOR_NODE); if (pCurrNode->pParent->nd.op.eOperator == XFLM_AND_OP || pCurrNode->pParent->nd.op.eOperator == XFLM_OR_OP) { eBoolVal = XFLM_UNKNOWN; eBoolPartialEval = pCurrNode->pParent->nd.op.eOperator == XFLM_AND_OP ? XFLM_FALSE : XFLM_TRUE; if (pCurrNode->eNodeType == FLM_OPERATOR_NODE) { // It may not have been evaluated because of missing // XPATH values in one or both operands, in which case // its state will be XFLM_MISSING_VALUE. If it was // evaluated, its state should show a boolean value. if (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) { eBoolVal = (pCurrNode->bNotted ? XFLM_TRUE : XFLM_FALSE); } else { flmAssert( pCurrNode->currVal.eValType == XFLM_BOOL_VAL); eBoolVal = pCurrNode->currVal.val.eBool; } } else if (pCurrNode->eNodeType == FLM_XPATH_NODE) { if (!pCurrNode->bNotted) { eBoolVal = (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) ? XFLM_FALSE : XFLM_TRUE; } else { eBoolVal = (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) ? XFLM_TRUE : XFLM_FALSE; } } else if (pCurrNode->eNodeType == FLM_VALUE_NODE) { // Only allowed value node underneath a logical operator is // a boolean value that has a value of XFLM_UNKNOWN. // XFLM_FALSE and XFLM_TRUE will already have been weeded out. flmAssert( pCurrNode->currVal.eValType == XFLM_BOOL_VAL && pCurrNode->currVal.val.eBool == XFLM_UNKNOWN); // No need to set eBoolVal to XFLM_UNKNOWN, because it will never // match eBoolPartialEval in the test below. eBoolPartialEval // is always either XFLM_FALSE or XFLM_TRUE. // eBoolVal = XFLM_UNKNOWN; } else { flmAssert( pCurrNode->eNodeType == FLM_FUNCTION_NODE); if (!pCurrNode->bNotted) { eBoolVal = fqTestValue( pCurrNode) ? XFLM_TRUE : XFLM_FALSE; } else { eBoolVal = fqTestValue( pCurrNode) ? XFLM_FALSE : XFLM_TRUE; } } if (eBoolVal == eBoolPartialEval) { pCurrNode = pCurrNode->pParent; pCurrNode->currVal.eValType = XFLM_BOOL_VAL; pCurrNode->currVal.val.eBool = eBoolVal; } else if (pCurrNode->pNextSib) { pCurrNode = pCurrNode->pNextSib; break; } else { pCurrNode = pCurrNode->pParent; if (RC_BAD( rc = fqEvalOperator( uiLanguage, pCurrNode))) { goto Exit; } } } else { // Visit: can shortcircuit comparison operators if the // value is missing. if (pCurrNode->pNextSib) { pCurrNode = pCurrNode->pNextSib; break; } pCurrNode = pCurrNode->pParent; // All operands are now present - do evaluation if (RC_BAD( rc = fqEvalOperator( uiLanguage, pCurrNode))) { goto Exit; } // If this is a comparison operator, see if we need to // do existential or universal comparison. if (isCompareOp( pCurrNode->nd.op.eOperator)) { // Evaluation should have forced current value to be a // boolean TRUE or FALSE value. flmAssert( pCurrNode->currVal.eValType == XFLM_BOOL_VAL); if ((isUniversal( pCurrNode) && pCurrNode->currVal.val.eBool == XFLM_TRUE) || (isExistential( pCurrNode) && pCurrNode->currVal.val.eBool == XFLM_FALSE)) { // Go back down right hand side to get next rightmost // operand while (pCurrNode->pLastChild) { pCurrNode = pCurrNode->pLastChild; } break; } } } } Exit: *ppCurrNode = pCurrNode; return( rc); } /*************************************************************************** Desc: Backup the query tree ***************************************************************************/ FSTATIC FQNODE * fqBackupTree( FQNODE * pCurrNode, FLMBOOL * pbGetNodeValue ) { flmAssert( !(*pbGetNodeValue)); // This was the last value, backup pCurrNode->bUsedValue = FALSE; pCurrNode->bLastValue = FALSE; // If parent node is a logical operator, there is // nothing more we can do on this branch, so we fall // through and process the thing. if (pCurrNode->pParent && !isLogicalOp( pCurrNode->pParent->nd.op.eOperator)) { while (!pCurrNode->pPrevSib) { if ((pCurrNode = pCurrNode->pParent) == NULL) { goto Exit; } // Don't backup any higher than comparison operators // We are only backing up so we can do existential // and universal on them with multiple left and right // operands. flmAssert( pCurrNode->eNodeType == FLM_OPERATOR_NODE); if (isCompareOp( pCurrNode->nd.op.eOperator)) { goto Exit; } } // If we came up to a parent that is a comparison operator // we are done - there is noplace else to backup to, so // we will fall through. if (!isCompareOp( pCurrNode->nd.op.eOperator)) { // has to be a prev sib at this point. pCurrNode = pCurrNode->pPrevSib; // Go down to rightmost child while (pCurrNode->pLastChild) { pCurrNode = pCurrNode->pLastChild; } *pbGetNodeValue = TRUE; } } Exit: return( pCurrNode); } /*************************************************************************** Desc: Get the value of a function node, or move to another node. ***************************************************************************/ RCODE F_Query::getFuncValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FQNODE ** ppCurrNode, FLMBOOL * pbGetNodeValue, F_DynaBuf * pDynaBuf) { RCODE rc = NE_XFLM_OK; FQNODE * pCurrNode = *ppCurrNode; // We currently only support user-defined functions. flmAssert( pCurrNode->nd.pQFunction->pFuncObj); flmAssert( !(*pbGetNodeValue)); if (pCurrNode->bLastValue) { pCurrNode = fqBackupTree( pCurrNode, pbGetNodeValue); } else { if (RC_BAD( rc = getNextFunctionValue( pContextNode, bForward, pCurrNode, pDynaBuf))) { goto Exit; } // Handle case where the function call is the only thing in the // query. if (!pCurrNode->pParent) { FLMBOOL bTmpPassed; FLMBOOL bFinal; if (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) { bFinal = TRUE; // No more values. If it is universal, and we got to this // point, it means that all prior values passed, so we set // bTmpPassed to TRUE in that case. If it is existential and // we got to this point, it means that all prior values // failed, so we set bTmpPassed to FALSE in that case. bTmpPassed = isUniversal( pCurrNode); } else { bTmpPassed = fqTestValue( pCurrNode); bFinal = (pCurrNode->bLastValue || (isUniversal( pCurrNode) && !bTmpPassed) || (isExistential( pCurrNode) && bTmpPassed)) ? TRUE : FALSE; } fqReleaseNodeValue( pCurrNode); if (!bFinal) { *pbGetNodeValue = TRUE; goto Exit; } pCurrNode->currVal.eValType = XFLM_BOOL_VAL; pCurrNode->currVal.val.eBool = bTmpPassed ? XFLM_TRUE : XFLM_FALSE; // Must set to NULL so that evalExpr will break out of loop. pCurrNode = NULL; goto Exit; } // Handle case where it is not the top node of the query. if (pCurrNode->currVal.eValType != XFLM_MISSING_VAL) { pCurrNode->bUsedValue = TRUE; } else { if (!pCurrNode->bUsedValue) { // Need to handle missing value if we have not done so yet. pCurrNode->bUsedValue = TRUE; pCurrNode->bLastValue = TRUE; } else { // This was the last value, backup pCurrNode = fqBackupTree( pCurrNode, pbGetNodeValue); } } } Exit: *ppCurrNode = pCurrNode; return( rc); } /*************************************************************************** Desc: Get the value of an XPATH node or move to another node. ***************************************************************************/ RCODE F_Query::getXPathValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FQNODE ** ppCurrNode, FLMBOOL * pbGetNodeValue, FLMBOOL bUseKeyNodes, FLMBOOL bXPathIsEntireExpr ) { RCODE rc = NE_XFLM_OK; FQNODE * pCurrNode = *ppCurrNode; flmAssert( !(*pbGetNodeValue)); flmAssert( !(*pbGetNodeValue)); // See if value is already set from an index. if (pCurrNode->nd.pXPath->bHavePassingNode && bUseKeyNodes) { if (pCurrNode->bUsedValue) { pCurrNode->currVal.eValType = XFLM_MISSING_VAL; } else { pCurrNode->currVal.eValType = XFLM_PASSING_VAL; if (bXPathIsEntireExpr) { m_pCurrOpt->ui64NodesTested++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } } } else if (bUseKeyNodes && pCurrNode->nd.pXPath->bIsSource && pCurrNode->nd.pXPath->pLastComponent->bIsSource) { fqReleaseNodeValue( pCurrNode); if (!pCurrNode->bUsedValue) { XPATH_COMPONENT * pLastComponent = pCurrNode->nd.pXPath->pLastComponent; if (RC_BAD( rc = fqGetValueFromNode( m_pDb, pLastComponent->pKeyNode, &pCurrNode->currVal, getMetaDataType( pLastComponent)))) { goto Exit; } pCurrNode->bUsedValue = TRUE; if (bXPathIsEntireExpr) { m_pCurrOpt->ui64NodesTested++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } } } else { if (RC_BAD( rc = getNextXPathValue( pContextNode, bForward, bUseKeyNodes, bXPathIsEntireExpr, pCurrNode))) { goto Exit; } } // If this expression is an XPATH, set pCurrNode to NULL so that // it will cause evalExpr to exit from its loop and return // the node we have found. No need to attempt to evaluate // an operators, there are none. if (!pCurrNode->pParent) { pCurrNode = NULL; goto Exit; } if (pCurrNode->currVal.eValType != XFLM_MISSING_VAL) { pCurrNode->bUsedValue = TRUE; } else { fqResetIterator( pCurrNode, FALSE, bUseKeyNodes); // See if we used the "missing" value in evaluating // the operator. Need to if we have not yet. // Otherwise, we backup in the tree because we will // have used at least one value. if (!pCurrNode->bUsedValue) { pCurrNode->bUsedValue = TRUE; } else { pCurrNode = fqBackupTree( pCurrNode, pbGetNodeValue); } } Exit: *ppCurrNode = pCurrNode; return( rc); } /*************************************************************************** Desc: Set the return value for an expression. ***************************************************************************/ RCODE F_Query::setExprReturnValue( FLMBOOL bUseKeyNodes, FQNODE * pQueryExpr, FLMBOOL * pbPassed, IF_DOMNode ** ppNode ) { RCODE rc = NE_XFLM_OK; if (pQueryExpr->eNodeType == FLM_XPATH_NODE) { if (pQueryExpr->currVal.eValType != XFLM_MISSING_VAL) { // Need to clear the current node's value. fqReleaseNodeValue( pQueryExpr); if (ppNode) { // *ppNode should have been initialized to NULL at the // beginning of the evalExpr routine. flmAssert( *ppNode == NULL); if (bUseKeyNodes && pQueryExpr->nd.pXPath->pLastComponent->pKeyNode) { *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pKeyNode; } else { *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pCurrNode; } (*ppNode)->AddRef(); m_pCurrOpt->ui64NodesPassed++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } if (pbPassed) { *pbPassed = TRUE; } } else { // *pbPassed should have been initialized to FALSE in evalExpr, // if pbPassed is non-NULL. flmAssert( !pbPassed || !(*pbPassed)); // *ppNode will already have been set to NULL - at beginning // of evalExpr - so no need to do it here. Just assert // that either ppNode is NULL or *ppNode is NULL. flmAssert( !ppNode || *ppNode == NULL); // If pCurrNode or pKeyNode is non-NULL it means we found one, but // it doesn't have any data. That's ok, because this is // only an exists test, and it should pass. if (bUseKeyNodes && pQueryExpr->nd.pXPath->pLastComponent->pKeyNode) { if (pbPassed) { *pbPassed = TRUE; } if (ppNode) { *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pKeyNode; (*ppNode)->AddRef(); } m_pCurrOpt->ui64NodesPassed++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } else if (pQueryExpr->nd.pXPath->pLastComponent->pCurrNode) { if (pbPassed) { *pbPassed = TRUE; } if (ppNode) { *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pCurrNode; (*ppNode)->AddRef(); } m_pCurrOpt->ui64NodesPassed++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } } // This routine is only called for expressions that are just an // XPATH. If it is the outermost XPATH, we don't want to // reset the iterator, as that is what we are using to // iterate with. if (pQueryExpr->nd.pXPath->pFirstComponent->pXPathContext) { fqResetIterator( pQueryExpr, FALSE, bUseKeyNodes); } } else if (pbPassed) { *pbPassed = fqTestValue( pQueryExpr); if (ppNode) { if (*pbPassed) { *ppNode = m_pCurrDoc; (*ppNode)->AddRef(); } } } Exit: return( rc); } /*************************************************************************** Desc: Evaluate a query expression. ***************************************************************************/ RCODE F_Query::evalExpr( IF_DOMNode * pContextNode, FLMBOOL bForward, FLMBOOL bUseKeyNodes, FQNODE * pQueryExpr, FLMBOOL * pbPassed, IF_DOMNode ** ppNode ) { RCODE rc = NE_XFLM_OK; FQNODE * pCurrNode = pQueryExpr; FLMBOOL bXPathIsEntireExpr = FALSE; FLMBYTE ucDynaBuf[ 64]; F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); FLMBOOL bGetNodeValue; // IMPORTANT NOTE: A non-NULL ppNode should only be passed in for the // very outermost evalExpr, not for nested ones. We use this to determine // whether to count documents read and documents passed. if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } if (pbPassed) { *pbPassed = FALSE; } // If the query is empty, it passes if (!pQueryExpr) { if (pbPassed) { *pbPassed = TRUE; } if (ppNode) { *ppNode = m_pCurrDoc; (*ppNode)->AddRef(); } goto Exit; } if (pQueryExpr->eNodeType == FLM_XPATH_NODE && !pQueryExpr->nd.pXPath->pFirstComponent->pXPathContext) { bXPathIsEntireExpr = TRUE; } fqResetQueryTree( pQueryExpr, bUseKeyNodes, m_bResetAllXPaths); m_bResetAllXPaths = FALSE; // Perform the evaluation pCurrNode = pQueryExpr; for (;;) { while (pCurrNode->pFirstChild) { pCurrNode = pCurrNode->pFirstChild; } // We should be positioned on a leaf node that is either a // value, a function, or an xpath. bGetNodeValue = FALSE; if (pCurrNode->eNodeType == FLM_VALUE_NODE) { if (!pCurrNode->pParent) { pCurrNode = NULL; break; } if (pCurrNode->bUsedValue) { pCurrNode = fqBackupTree( pCurrNode, &bGetNodeValue); } else { pCurrNode->bUsedValue = TRUE; } } else if (pCurrNode->eNodeType == FLM_FUNCTION_NODE) { if (RC_BAD( rc = getFuncValue( pContextNode, bForward, &pCurrNode, &bGetNodeValue, &dynaBuf))) { goto Exit; } } else { // Better be an xpath at this point flmAssert( pCurrNode->eNodeType == FLM_XPATH_NODE); if (RC_BAD( rc = getXPathValue( pContextNode, bForward, &pCurrNode, &bGetNodeValue, bUseKeyNodes, bXPathIsEntireExpr))) { goto Exit; } } if (!pCurrNode) { break; } if (bGetNodeValue) { continue; } // When we get to this point, we have at least one leaf // level operand in hand - pCurrNode. // See if we can evaluate the operator of pCurrNode. // This will take care of any short-circuiting evaluation // that can be done. if (RC_BAD( rc = fqTryEvalOperator( m_uiLanguage, &pCurrNode))) { goto Exit; } if (!pCurrNode) { break; } } if (RC_BAD( rc = setExprReturnValue( bUseKeyNodes, pQueryExpr, pbPassed, ppNode))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Release the resources of a query expression ***************************************************************************/ FSTATIC void fqReleaseQueryExpr( FQNODE * pQNode ) { // Reset the entire query tree. It could have been left in a partial // evaluation state from the last document evaluated. for (;;) { fqReleaseNodeValue( pQNode); pQNode->bUsedValue = FALSE; pQNode->bLastValue = FALSE; if (pQNode->pFirstChild) { pQNode = pQNode->pFirstChild; } else { if (pQNode->eNodeType == FLM_XPATH_NODE) { fqResetIterator( pQNode, TRUE, FALSE); } while (!pQNode->pNextSib) { if ((pQNode = pQNode->pParent) == NULL) { break; } } if (!pQNode) { break; } pQNode = pQNode->pNextSib; } } } /*************************************************************************** Desc: Release the resources of a query ***************************************************************************/ void FLMAPI F_Query::resetQuery( void) { if (m_pQuery) { fqReleaseQueryExpr( m_pQuery); } m_eState = XFLM_QUERY_NOT_POSITIONED; if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } } /*************************************************************************** Desc: Get leaf predicate for the current context, changing current context, context path, and predicate as needed. ***************************************************************************/ void F_Query::useLeafContext( FLMBOOL bGetFirst) { for (;;) { if (m_pCurrContext->bIntersect) { if (m_pCurrContext->pSelectedChild) { flmAssert( !m_pCurrContext->pSelectedPath); m_pCurrContext = m_pCurrContext->pSelectedChild; } else { flmAssert( m_pCurrContext->pSelectedPath); m_pCurrContextPath = m_pCurrContext->pSelectedPath; m_pCurrPred = m_pCurrContextPath->pSelectedPred; flmAssert( m_pCurrContextPath && m_pCurrPred); m_pCurrOpt = &m_pCurrPred->OptInfo; break; } } else { // In a non-intersect context, pSelectedChild should NOT have // been set. Nor should pSelectedPath. flmAssert( !m_pCurrContext->pSelectedChild); flmAssert( !m_pCurrContext->pSelectedPath); if (bGetFirst) { if (m_pCurrContext->pFirstChild) { m_pCurrContext = m_pCurrContext->pFirstChild; } else { m_pCurrContextPath = m_pCurrContext->pFirstPath; flmAssert( m_pCurrContextPath); // In a non-intersect context, pSelectedPred should NOT have // been set. flmAssert( !m_pCurrContextPath->pSelectedPred); m_pCurrPred = m_pCurrContextPath->pFirstPred; flmAssert( m_pCurrPred); m_pCurrOpt = &m_pCurrPred->OptInfo; break; } } else { if (m_pCurrContext->pLastChild) { m_pCurrContext = m_pCurrContext->pLastChild; } else { m_pCurrContextPath = m_pCurrContext->pLastPath; flmAssert( m_pCurrContextPath); // In a non-intersect context, pSelectedPred should NOT have // been set. flmAssert( !m_pCurrContextPath->pSelectedPred); m_pCurrPred = m_pCurrContextPath->pLastPred; flmAssert( m_pCurrPred); m_pCurrOpt = &m_pCurrPred->OptInfo; break; } } } } } /*************************************************************************** Desc: Get next predicate to evaluate for a query. ***************************************************************************/ FLMBOOL F_Query::useNextPredicate( void) { FLMBOOL bGotNext = FALSE; // If we are in a non-intersecting context, exhaust its // context paths and predicates before going up a level. if (!m_pCurrContext->bIntersect) { if (m_pCurrPred) { if (m_pCurrPred->pNext) { m_pCurrPred = m_pCurrPred->pNext; m_pCurrOpt = &m_pCurrPred->OptInfo; bGotNext = TRUE; goto Exit; } if (m_pCurrContextPath->pNext) { m_pCurrContextPath = m_pCurrContextPath->pNext; // In a non-intersect context, pSelectedPred should NOT have // been set. flmAssert( !m_pCurrContextPath->pSelectedPred); m_pCurrPred = m_pCurrContextPath->pFirstPred; m_pCurrOpt = &m_pCurrPred->OptInfo; bGotNext = TRUE; goto Exit; } } // Go up one context level, if there is one. if (!m_pCurrContext->pParent) { goto Exit; } // Parent better be an intersecting context m_pCurrContext = m_pCurrContext->pParent; flmAssert( m_pCurrContext->bIntersect); } // Go back up the context tree to get next context. for (;;) { OP_CONTEXT * pParent = m_pCurrContext->pParent; // If there is no parent context, we are done. if (!pParent) { break; } if (m_pCurrContext->bIntersect) { // Parent context should be non-intersecting, so we should // go to the sibling context, if there is one. flmAssert( !pParent->bIntersect); if (m_pCurrContext->pNextSib) { m_pCurrContext = m_pCurrContext->pNextSib; // Travel down this part of the context tree to get the // "leaf-most" context, context path, and predicate. useLeafContext( TRUE); bGotNext = TRUE; break; } } else { if (m_pCurrContext->pFirstPath) { m_pCurrContextPath = m_pCurrContext->pFirstPath; // In a non-intersect context, pSelectedPred should NOT have // been set. flmAssert( !m_pCurrContextPath->pSelectedPred); m_pCurrPred = m_pCurrContextPath->pFirstPred; m_pCurrOpt = &m_pCurrPred->OptInfo; bGotNext = TRUE; goto Exit; } // Parent context better be pointing to this context as its // "selected" context. flmAssert( pParent->pSelectedChild == m_pCurrContext); // Parent also better be an intersecting context. flmAssert( pParent->bIntersect); } m_pCurrContext = pParent; } Exit: return( bGotNext); } /*************************************************************************** Desc: Get previous predicate to evaluate for a query. ***************************************************************************/ FLMBOOL F_Query::usePrevPredicate( void) { FLMBOOL bGotPrev = FALSE; // If we are in a non-intersecting context, exhaust its // context paths and predicates before going up a level. if (!m_pCurrContext->bIntersect) { if (m_pCurrPred) { if (m_pCurrPred->pPrev) { m_pCurrPred = m_pCurrPred->pPrev; m_pCurrOpt = &m_pCurrPred->OptInfo; bGotPrev = TRUE; goto Exit; } if (m_pCurrContextPath->pPrev) { m_pCurrContextPath = m_pCurrContextPath->pPrev; // In a non-intersect context, pSelectedPred should NOT have // been set. flmAssert( !m_pCurrContextPath->pSelectedPred); m_pCurrPred = m_pCurrContextPath->pLastPred; m_pCurrOpt = &m_pCurrPred->OptInfo; bGotPrev = TRUE; goto Exit; } } // Go up one context level, if there is one. if (!m_pCurrContext->pParent) { goto Exit; } // Parent better be an intersecting context m_pCurrContext = m_pCurrContext->pParent; flmAssert( m_pCurrContext->bIntersect); } // Go back up the context tree to get previous context. for (;;) { OP_CONTEXT * pParent = m_pCurrContext->pParent; // If there is no parent context, we are done. if (!pParent) { break; } if (m_pCurrContext->bIntersect) { // Parent context should be non-intersecting, so we should // go to the sibling context, if there is one. flmAssert( !pParent->bIntersect); if (m_pCurrContext->pPrevSib) { m_pCurrContext = m_pCurrContext->pPrevSib; // Travel down this part of the context tree to get the // "leaf-most" context, context path, and predicate. useLeafContext( FALSE); bGotPrev = TRUE; break; } } else { if (m_pCurrContext->pLastPath) { m_pCurrContextPath = m_pCurrContext->pLastPath; // In a non-intersect context, pSelectedPred should NOT have // been set. flmAssert( !m_pCurrContextPath->pSelectedPred); m_pCurrPred = m_pCurrContextPath->pLastPred; m_pCurrOpt = &m_pCurrPred->OptInfo; bGotPrev = TRUE; goto Exit; } // Parent context better be pointing to this context as its // "selected" context. flmAssert( pParent->pSelectedChild == m_pCurrContext); // Parent also better be an intersecting context. flmAssert( pParent->bIntersect); } m_pCurrContext = pParent; } Exit: return( bGotPrev); } /*************************************************************************** Desc: Get the next/previous node for an application predicate. ***************************************************************************/ RCODE F_Query::getAppNode( FLMBOOL * pbFirstLast, FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent ) { RCODE rc = NE_XFLM_OK; IF_QueryNodeSource * pNodeSource = m_pCurrPred->pNodeSource; FLMUINT uiCurrTime; FLMUINT uiElapsedTime; FLMUINT uiTimeLimit = m_uiTimeLimit; FLMUINT64 ui64DocId; for (;;) { // Reset the timeout value everytime through the loop, if it // is non-zero. if (uiTimeLimit) { uiCurrTime = FLM_GET_TIMER(); uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiStartTime); if (uiElapsedTime >= m_uiTimeLimit) { rc = RC_SET( NE_XFLM_TIMEOUT); goto Exit; } else { uiTimeLimit = FLM_TIMER_UNITS_TO_MILLI( (m_uiTimeLimit - uiElapsedTime)); // Always give at least one milli-second. if (!uiTimeLimit) { uiTimeLimit = 1; } } } if (pXPathComponent->pKeyNode) { pXPathComponent->pKeyNode->Release(); pXPathComponent->pKeyNode = NULL; } // Get the next or previous key from the index. if (bForward) { rc = (RCODE)(*pbFirstLast ? pNodeSource->getFirst( (IF_Db *)m_pDb, NULL, &pXPathComponent->pKeyNode, uiTimeLimit, m_pQueryStatus) : pNodeSource->getNext( (IF_Db *)m_pDb, NULL, &pXPathComponent->pKeyNode, uiTimeLimit, m_pQueryStatus)); if (RC_BAD( rc)) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } } else { rc = (RCODE)(*pbFirstLast ? pNodeSource->getLast( (IF_Db *)m_pDb, NULL, &pXPathComponent->pKeyNode, uiTimeLimit, m_pQueryStatus) : pNodeSource->getPrev( (IF_Db *)m_pDb, NULL, &pXPathComponent->pKeyNode, uiTimeLimit, m_pQueryStatus)); if (RC_BAD( rc)) { if (rc == NE_XFLM_BOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } } *pbFirstLast = FALSE; // If we are eliminating duplicates, see if the document // has already been processed. If so, skip the key. if (RC_BAD( rc = pXPathComponent->pKeyNode->getDocumentId( (IF_Db *)m_pDb, &ui64DocId))) { goto Exit; } if (m_pDocIdSet) { if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) { if (rc != NE_FLM_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { // Document has already been passed, go to next/prev key. m_pCurrOpt->ui64KeyHadDupDoc++; if (RC_BAD( rc = queryStatus())) { goto Exit; } continue; } } // At this point, the key has passed at least the predicate function. // Get the document node. if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, ui64DocId, &m_pCurrDoc))) { goto Exit; } // Found one! break; } Exit: return( rc); } /*************************************************************************** Desc: Get an FQVALUE from a key. ***************************************************************************/ FSTATIC RCODE fqGetValueFromKey( FLMUINT uiDataType, F_DataVector * pKey, FQVALUE * pQValue, FLMBYTE ** ppucValue, FLMUINT uiValueBufSize ) { RCODE rc = NE_XFLM_OK; pQValue->uiFlags = 0; switch (uiDataType) { case XFLM_NODATA_TYPE: // Should have been set to missing on the outside flmAssert( pQValue->eValType == XFLM_MISSING_VAL); pQValue->eValType = XFLM_BOOL_VAL; pQValue->val.eBool = XFLM_TRUE; break; case XFLM_TEXT_TYPE: pQValue->uiDataLen = pKey->getDataLength( 0) + 1; if (pQValue->uiDataLen > uiValueBufSize) { if (RC_BAD( rc = f_alloc( pQValue->uiDataLen, ppucValue))) { goto Exit; } } pQValue->val.pucBuf = *ppucValue; if (RC_BAD( rc = pKey->getUTF8( 0, pQValue->val.pucBuf, &pQValue->uiDataLen))) { goto Exit; } pQValue->eValType = XFLM_UTF8_VAL; break; case XFLM_NUMBER_TYPE: // First, see if we can get it as a UINT - the most common // type. if (RC_OK( rc = pKey->getUINT( 0, &pQValue->val.uiVal))) { pQValue->eValType = XFLM_UINT_VAL; } else if (rc == NE_XFLM_CONV_NUM_OVERFLOW) { if (RC_BAD( rc = pKey->getUINT64( 0, &pQValue->val.ui64Val))) { goto Exit; } pQValue->eValType = XFLM_UINT64_VAL; } else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) { if (RC_OK( rc = pKey->getINT( 0, &pQValue->val.iVal))) { pQValue->eValType = XFLM_INT_VAL; } else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) { if (RC_BAD( rc = pKey->getINT64( 0, &pQValue->val.i64Val))) { goto Exit; } pQValue->eValType = XFLM_INT64_VAL; } } else { goto Exit; } break; case XFLM_BINARY_TYPE: pQValue->uiDataLen = pKey->getDataLength( 0) + 1; if (pQValue->uiDataLen > uiValueBufSize) { if (RC_BAD( rc = f_alloc( pQValue->uiDataLen, ppucValue))) { goto Exit; } } pQValue->val.pucBuf = *ppucValue; if (RC_BAD( rc = pKey->getBinary( 0, pQValue->val.pucBuf, &pQValue->uiDataLen))) { goto Exit; } pQValue->eValType = XFLM_BINARY_VAL; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Test a value against the specified predicate. ***************************************************************************/ FSTATIC RCODE fqPredCompare( FLMUINT uiLanguage, PATH_PRED * pPred, FQVALUE * pQValue, FLMBOOL * pbPasses ) { RCODE rc = NE_XFLM_OK; XFlmBoolType eBool; switch (pPred->eOperator) { case XFLM_EXISTS_OP: // We already know this one passes. *pbPasses = TRUE; goto Exit; case XFLM_NE_OP: case XFLM_APPROX_EQ_OP: if (RC_BAD( rc = fqCompareOperands( uiLanguage, pQValue, pPred->pFromValue, pPred->eOperator, pPred->uiCompareRules, pPred->pOpComparer, pPred->bNotted, &eBool))) { goto Exit; } break; case XFLM_MATCH_OP: // From value of predicate better be a constant // with wildcards set. flmAssert( pPred->pFromValue->uiFlags & VAL_IS_CONSTANT); flmAssert( pPred->pFromValue->uiFlags & VAL_HAS_WILDCARDS); if (RC_BAD( rc = fqCompareOperands( uiLanguage, pQValue, pPred->pFromValue, XFLM_EQ_OP, pPred->uiCompareRules, pPred->pOpComparer, pPred->bNotted, &eBool))) { goto Exit; } break; case XFLM_RANGE_OP: eBool = XFLM_TRUE; // Take care of ==, > and >= if (pPred->pFromValue) { eQueryOperators eOperator; if (pPred->pUntilValue == pPred->pFromValue) { eOperator = XFLM_EQ_OP; } else { eOperator = pPred->bInclFrom ? XFLM_GE_OP : XFLM_GT_OP; } if (RC_BAD( rc = fqCompareOperands( uiLanguage, pQValue, pPred->pFromValue, eOperator, pPred->uiCompareRules, pPred->pOpComparer, pPred->bNotted, &eBool))) { goto Exit; } } // Take care of < and <= if we are still TRUE if (eBool == XFLM_TRUE && pPred->pUntilValue && pPred->pUntilValue != pPred->pFromValue) { if (RC_BAD( rc = fqCompareOperands( uiLanguage, pQValue, pPred->pUntilValue, pPred->bInclUntil ? XFLM_LE_OP : XFLM_LT_OP, pPred->uiCompareRules, pPred->pOpComparer, pPred->bNotted, &eBool))) { goto Exit; } } break; default: *pbPasses = FALSE; rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If the value didn't pass the predicate, we should return // a FALSE in *pbPasses. *pbPasses = (eBool == XFLM_FALSE) ? FALSE : TRUE; Exit: return( rc); } /*************************************************************************** Desc: Test the key against the specified predicate. ***************************************************************************/ RCODE F_Query::testKey( F_DataVector * pKey, PATH_PRED * pPred, FLMBOOL * pbPasses, IF_DOMNode ** ppPassedNode) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NodeId; FQVALUE currVal; FLMUINT uiDataType; FLMBYTE ucKeyValue [128]; FLMBYTE * pucKeyValue = &ucKeyValue [0]; currVal.eValType = XFLM_MISSING_VAL; // At this point, all use of notted predicates should have been // weeded out. flmAssert( !pPred->bNotted); *pbPasses = TRUE; // First see if the key passes the criteria of the predicate. // If we can use the key, use it. Otherwise, fetch the node // and do the comparison. if (pPred->OptInfo.bCanCompareOnKey) { // No need to retrieve the data if it is an exists operator if (pPred->eOperator != XFLM_EXISTS_OP) { if ((uiDataType = pKey->getDataType( 0)) == XFLM_UNKNOWN_TYPE) { // No data - component was missing. *pbPasses = FALSE; goto Exit; } if (RC_BAD( rc = fqGetValueFromKey( uiDataType, pKey, &currVal, &pucKeyValue, sizeof( ucKeyValue)))) { goto Exit; } // Compare on the key if (RC_BAD( rc = fqPredCompare( m_uiLanguage, pPred, &currVal, pbPasses))) { goto Exit; } if (!(*pbPasses)) { goto Exit; } } } // Have to get the node whether we compare on it or not if ((ui64NodeId = pKey->getID( 0)) == 0) { // No data - component was missing. *pbPasses = FALSE; goto Exit; } if( pKey->isAttr( 0)) { rc = m_pDb->getAttribute( m_uiCollection, ui64NodeId, pKey->getNameId( 0), ppPassedNode); } else { rc = m_pDb->getNode( m_uiCollection, ui64NodeId, ppPassedNode); } if( RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } // See if we need to do the comparison on the node. if (pPred->OptInfo.bDoNodeMatch && pPred->eOperator != XFLM_EXISTS_OP) { if (RC_BAD( rc = fqGetValueFromNode( m_pDb, *ppPassedNode, &currVal, 0))) { goto Exit; } // Do the comparison if (RC_BAD( rc = fqPredCompare( m_uiLanguage, pPred, &currVal, pbPasses))) { goto Exit; } if (!(*pbPasses)) { goto Exit; } } // At this point, the key has passed at least the predicate comparison. // Get the document node if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, pKey->getDocumentID(), &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // If we cannot retrieve the node, we have a corruption // in the database. rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } Exit: if (pucKeyValue != &ucKeyValue [0]) { f_free( &pucKeyValue); } if ((currVal.eValType == XFLM_BINARY_VAL || currVal.eValType == XFLM_UTF8_VAL) && (currVal.uiFlags & VAL_IS_STREAM) && currVal.val.pIStream) { currVal.val.pIStream->Release(); } return( rc); } /*************************************************************************** Desc: Mark all of the XPATH nodes that are the same as the one in pXPathComponent as having a passing value. ***************************************************************************/ FSTATIC void fqMarkXPathNodeListPassed( PATH_PRED * pPred ) { PATH_PRED_NODE * pXPathNodeList; pXPathNodeList = pPred->pXPathNodeList; while (pXPathNodeList) { pXPathNodeList->pXPathNode->nd.pXPath->bHavePassingNode = TRUE; pXPathNodeList = pXPathNodeList->pNext; } } /*************************************************************************** Desc: Get the next/previous key for an xpath component. ***************************************************************************/ RCODE F_Query::getKey( FLMBOOL * pbFirstLast, FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent ) { RCODE rc = NE_XFLM_OK; PATH_PRED * pPred = pXPathComponent->pOptPred; FSIndexCursor * pFSIndexCursor = pPred->pFSIndexCursor; F_DataVector key; FLMBOOL bPassed; FLMBOOL bSkipCurrKey = FALSE; FLMBOOL bHasContextPosTest = hasContextPosTest( pXPathComponent); // Better be rightmost xpath component. flmAssert( !pXPathComponent->pNext); // Component could have an expression, but it should not be one // we are optimizing on this time around. flmAssert( !pXPathComponent->pExprXPathSource); for (;;) { // Release the current key node, if any if (pXPathComponent->pKeyNode) { pXPathComponent->pKeyNode->Release(); pXPathComponent->pKeyNode = NULL; } // Get the next or previous key from the index. if (bForward) { rc = (RCODE)(*pbFirstLast ? pFSIndexCursor->firstKey( m_pDb, &key) : pFSIndexCursor->nextKey( m_pDb, &key, bSkipCurrKey)); if (RC_BAD( rc)) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } } else { rc = (RCODE)(*pbFirstLast ? pFSIndexCursor->lastKey( m_pDb, &key) : pFSIndexCursor->prevKey( m_pDb, &key, bSkipCurrKey)); if (RC_BAD( rc)) { if (rc == NE_XFLM_BOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } } *pbFirstLast = FALSE; m_pCurrOpt->ui64KeysRead++; if (RC_BAD( rc = queryStatus())) { goto Exit; } // If we are eliminating duplicates, see if the document // has already been processed. If so, skip the key. if (m_pDocIdSet) { FLMUINT64 ui64DocId = key.getDocumentID(); if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) { if (rc != NE_FLM_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { // Document has already been passed, go to next/prev key. m_pCurrOpt->ui64KeyHadDupDoc++; if (RC_BAD( rc = queryStatus())) { goto Exit; } continue; } } // Evaluate the key. if (RC_BAD( rc = testKey( &key, pPred, &bPassed, &pXPathComponent->pKeyNode))) { goto Exit; } if (!bPassed) { // If this is a case-sensitive index or a case-insensitive compare, // we can skip the key. Otherwise, we cannot skip any keys. if (!(pFSIndexCursor->m_pIxd->pFirstKey->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) || (pPred->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE)) { bSkipCurrKey = TRUE; } continue; } bSkipCurrKey = FALSE; m_pCurrOpt->ui64KeysPassed++; if (RC_BAD( rc = queryStatus())) { goto Exit; } // If the xpath component has an expression, evaluate it if (pXPathComponent->pExpr) { if (RC_BAD( rc = evalExpr( pXPathComponent->pKeyNode, bForward, TRUE, pXPathComponent->pExpr, &bPassed, NULL))) { goto Exit; } if (!bPassed) { continue; } } if (bHasContextPosTest) { // Need to verify the context position of the found node. if (RC_BAD( rc = verifyOccurrence( FALSE, pXPathComponent, pXPathComponent->pKeyNode, &bPassed))) { goto Exit; } if (!bPassed) { continue; } } // There may be more than one XPATH that this predicate // covers. Set them up so they don't have to be evaluated // if at all possible. fqMarkXPathNodeListPassed( pPred); break; } Exit: return( rc); } /*************************************************************************** Desc: Test a node's metadata against the specified predicate. ***************************************************************************/ RCODE F_Query::testMetaData( IF_DOMNode * pNode, FLMUINT uiMetaDataType, PATH_PRED * pPred, FLMBOOL * pbPasses ) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64DocId; FQVALUE currVal; currVal.eValType = XFLM_MISSING_VAL; // At this point, all use of notted predicates should have been // weeded out. flmAssert( !pPred->bNotted); *pbPasses = TRUE; // See if we need to do the comparison on the node. if (pPred->eOperator != XFLM_EXISTS_OP) { if (RC_BAD( rc = fqGetValueFromNode( m_pDb, pNode, &currVal, uiMetaDataType))) { goto Exit; } // Do the comparison if (RC_BAD( rc = fqPredCompare( m_uiLanguage, pPred, &currVal, pbPasses))) { goto Exit; } if (!(*pbPasses)) { goto Exit; } } // At this point, the node has passed at least the predicate comparison. // Get the document node, if we don't already have it. if( !(((F_DOMNode *)pNode)->isRootNode())) { if (RC_BAD( rc = pNode->getDocumentId( m_pDb, &ui64DocId))) { goto Exit; } if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, ui64DocId, &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // If we cannot retrieve the node, we have a corruption // in the database. rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } else { m_pCurrDoc = pNode; m_pCurrDoc->AddRef(); } if (RC_BAD( rc = incrNodesRead())) { goto Exit; } Exit: if ((currVal.eValType == XFLM_BINARY_VAL || currVal.eValType == XFLM_UTF8_VAL) && (currVal.uiFlags & VAL_IS_STREAM) && currVal.val.pIStream) { currVal.val.pIStream->Release(); } return( rc); } /*************************************************************************** Desc: Get the next/previous node for an xpath component. ***************************************************************************/ RCODE F_Query::getANode( FLMBOOL * pbFirstLast, FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent ) { RCODE rc = NE_XFLM_OK; PATH_PRED * pPred = pXPathComponent->pOptPred; FSCollectionCursor * pFSCollectionCursor = pPred->pFSCollectionCursor; FLMBOOL bPassed; IF_DOMNode * pNode = NULL; FLMUINT64 ui64NodeId; FLMBOOL bHasContextPosTest = hasContextPosTest( pXPathComponent); // Better be rightmost xpath component. flmAssert( !pXPathComponent->pNext); // Component could have an expression, but it should not be one // we are optimizing on this time around. flmAssert( !pXPathComponent->pExprXPathSource); for (;;) { if (pNode) { pNode->Release(); pNode = NULL; } // See if we need to continue from the current node in the current // document, or release it and get another node. if ((pNode = pXPathComponent->pKeyNode) != NULL) { pXPathComponent->pKeyNode = NULL; // No need to do pNode->AddRef(), because we will just // steal the AddRef that was on pCurrNode. if (getMetaDataType( pXPathComponent) != XFLM_META_DOCUMENT_ID) { pNode->Release(); pNode = NULL; } else { // If we are doing document nodes, see if we can continue in the // document - because all of the nodes in the document should // be processed. if (RC_BAD( rc = walkDocument( bForward, TRUE, 0, &pNode))) { goto Exit; } } // If we didn't get a node back, and // If pFSCollectionCursor is NULL, it means we // are doing a single node, and we have already // done that single node (or document), so we can // quit. if (!pNode && !pFSCollectionCursor) { goto Exit; } } // Get the next or previous node from the collection, if we // didn't get a node from the loop above. if (!pNode) { if (pFSCollectionCursor) { if (bForward) { rc = (RCODE)(*pbFirstLast ? pFSCollectionCursor->firstNode( m_pDb, &pNode, &ui64NodeId) : pFSCollectionCursor->nextNode( m_pDb, &pNode, &ui64NodeId)); if (RC_BAD( rc)) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } } else { rc = (RCODE)(*pbFirstLast ? pFSCollectionCursor->lastNode( m_pDb, &pNode, &ui64NodeId) : pFSCollectionCursor->prevNode( m_pDb, &pNode, &ui64NodeId)); if (RC_BAD( rc)) { if (rc == NE_XFLM_BOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } } } else if (*pbFirstLast) { // Getting a single node. if (getMetaDataType( pXPathComponent) == XFLM_META_DOCUMENT_ID) { if (RC_BAD( rc = m_pDb->getDocument( m_uiCollection, XFLM_EXACT, pPred->OptInfo.ui64NodeId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } } else { if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, pPred->OptInfo.ui64NodeId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } } } else { goto Exit; } *pbFirstLast = FALSE; m_pCurrOpt->ui64NodesRead++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } // If we are eliminating duplicates, see if the document // has already been processed. If so, skip the key. if (m_pDocIdSet) { FLMUINT64 ui64DocId; if (RC_BAD( rc = pNode->getDocumentId( m_pDb, &ui64DocId))) { goto Exit; } if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) { if (rc != NE_FLM_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { // Document has already been passed, go to next/prev node. m_pCurrOpt->ui64KeyHadDupDoc++; if (RC_BAD( rc = queryStatus())) { goto Exit; } continue; } } // Evaluate the node. if (RC_BAD( rc = testMetaData( pNode, getMetaDataType( pXPathComponent), pPred, &bPassed))) { goto Exit; } if (bPassed) { pXPathComponent->pKeyNode = pNode; pXPathComponent->pKeyNode->AddRef(); } else { continue; } // If the xpath component has an expression, evaluate it if (pXPathComponent->pExpr) { if (RC_BAD( rc = evalExpr( pXPathComponent->pKeyNode, bForward, TRUE, pXPathComponent->pExpr, &bPassed, NULL))) { goto Exit; } if (!bPassed) { continue; } } if (bHasContextPosTest) { // Need to verify the context position of the found node. if (RC_BAD( rc = verifyOccurrence( FALSE, pXPathComponent, pXPathComponent->pKeyNode, &bPassed))) { goto Exit; } if (!bPassed) { continue; } } // There may be more than one XPATH that this predicate // covers. Set them up so they don't have to be evaluated // if at all possible. fqMarkXPathNodeListPassed( pPred); break; } Exit: if (pNode) { pNode->Release(); } return( rc); } /*************************************************************************** Desc: Get a context node for the passed in xpath component. ***************************************************************************/ RCODE F_Query::getContextNode( FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent ) { RCODE rc = NE_XFLM_OK; // Component better be the left-most xpath component. flmAssert( !pXPathComponent->pPrev); // See if we can now get a node for the XPATH context // component. if (RC_BAD( rc = getXPathComponentFromAxis( pXPathComponent->pKeyNode, bForward, TRUE, pXPathComponent->pXPathContext, &pXPathComponent->pXPathContext->pKeyNode, invertedAxis( pXPathComponent->eXPathAxis), TRUE, FALSE))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get the next value for an XPATH component that has an expression that is the source for the query. ***************************************************************************/ RCODE F_Query::getNextIndexNode( FLMBOOL * pbFirstLast, FLMBOOL bForward, FQNODE * pExprXPathSource, FLMBOOL bSkipCurrKey) { RCODE rc = NE_XFLM_OK; FXPATH * pSourceXPath; XPATH_COMPONENT * pXPathComp; FLMUINT64 ui64NodeId; FLMUINT64 ui64Tmp; // This routine should only be called for components that have an // expression that is serving as the source for the query. // Both pExpr and pXPathSource must be non-NULL. flmAssert( pExprXPathSource->eNodeType == FLM_XPATH_NODE); pSourceXPath = pExprXPathSource->nd.pXPath; flmAssert( pSourceXPath->bIsSource); // Release all pCurrNodes that come AFTER the source component. // Start from last component and go backwards. pXPathComp = pSourceXPath->pLastComponent; while (pXPathComp) { if (pXPathComp->bIsSource) { break; } if (pXPathComp->pCurrNode) { pXPathComp->pCurrNode->Release(); pXPathComp->pCurrNode = NULL; } pXPathComp = pXPathComp->pPrev; } // If this is the first time in, we need to go right to the // component in the XPath that is the key source. if (bSkipCurrKey) { // Release all of the DOM nodes up to and including the one // that is in the source component. pXPathComp should be // pointing to the source component when we are done. pXPathComp = pSourceXPath->pFirstComponent; for (;;) { if (pXPathComp == pSourceXPath->pSourceComponent) { // Only release pKeyNode if we are not on the // optimization predicate. Otherwise, we will // allow the call to getKey or getANode to do it. // This is mainly for the benefit of getANode, so it // can properly handle the document ID case, where it // needs to know the last node it was on inside a // document so it can continue walking from there. if (!pXPathComp->pOptPred && pXPathComp->pKeyNode) { pXPathComp->pKeyNode->Release(); pXPathComp->pKeyNode = NULL; } break; } if (pXPathComp->pKeyNode) { pXPathComp->pKeyNode->Release(); pXPathComp->pKeyNode = NULL; } pXPathComp = pXPathComp->pNext; } flmAssert( pXPathComp->bIsSource); } else if (!pSourceXPath->pFirstComponent->pKeyNode) { pXPathComp = pSourceXPath->pSourceComponent; flmAssert( pXPathComp->bIsSource); } else { pXPathComp = pSourceXPath->pFirstComponent; } for (;;) { if (pXPathComp->bIsSource) { if (pXPathComp->pOptPred) { if (pXPathComp->pOptPred->pFSIndexCursor) { if (RC_BAD( rc = getKey( pbFirstLast, bForward, pXPathComp))) { goto Exit; } } else if (pXPathComp->pOptPred->pNodeSource) { if (RC_BAD( rc = getAppNode( pbFirstLast, bForward, pXPathComp))) { goto Exit; } } else { // NOTE: pXPathComp->pOptPred->pFSCollectionCursor may be NULL // if getting a single node (eOptType == XFLM_QOPT_SINGLE_NODE_ID) if (RC_BAD( rc = getANode( pbFirstLast, bForward, pXPathComp))) { goto Exit; } } if (!pXPathComp->pKeyNode) { // Didn't get a node. // Cannot go any further than this - means we are // out of keys for this predicate. if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } goto Exit; } } else { flmAssert( pXPathComp->pExprXPathSource && pXPathComp->pExpr); // First see if we can get another context node without going // down to get another key. if (pXPathComp->pKeyNode) { // Got a node from expression, get context node for that node. if (RC_BAD( rc = getContextNode( bForward, pXPathComp->pExprXPathSource->nd.pXPath->pFirstComponent))) { goto Exit; } } while (!pXPathComp->pKeyNode) { if (RC_BAD( rc = getNextIndexNode( pbFirstLast, bForward, pXPathComp->pExprXPathSource, bSkipCurrKey))) { goto Exit; } // See if we got a node from below. If not, there is nothing // more we can do at this level. if (!pXPathComp->pExprXPathSource->nd.pXPath->pFirstComponent->pKeyNode) { if (pXPathComp->pKeyNode) { pXPathComp->pKeyNode->Release(); pXPathComp->pKeyNode = NULL; } goto Exit; } // Got a node from expression, get context node for that node. if (RC_BAD( rc = getContextNode( bForward, pXPathComp->pExprXPathSource->nd.pXPath->pFirstComponent))) { goto Exit; } } } } else { // There has to be a next to this node - because the source // component has to be somewhere after this component. flmAssert( pXPathComp->pNext); if (RC_BAD( rc = getXPathComponentFromAxis( pXPathComp->pNext->pKeyNode, bForward, TRUE, pXPathComp, &pXPathComp->pKeyNode, invertedAxis( pXPathComp->pNext->eXPathAxis), TRUE, FALSE))) { goto Exit; } // If we didn't get a node, go to next component and try to // get its next node. if (!pXPathComp->pKeyNode) { pXPathComp = pXPathComp->pNext; continue; } } // At this point we better have gotten a node. flmAssert( pXPathComp->pKeyNode); // If this is the left-most xpath component and we got a node // and the component's axis is the root axis, verify that the // node is, in fact the root node. if (!pXPathComp->pPrev && pXPathComp->eXPathAxis == ROOT_AXIS) { FLMUINT64 ui64DocId; if( RC_BAD( rc = m_pCurrDoc->getNodeId( m_pDb, &ui64DocId))) { goto Exit; } if( RC_BAD( rc = pXPathComp->pKeyNode->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } if( ui64DocId != ui64Tmp ) { if (RC_BAD( rc = pXPathComp->pKeyNode->getParentId( m_pDb, &ui64NodeId))) { goto Exit; } if ( ui64NodeId != ui64DocId || m_pCurrDoc->getNodeType() != DOCUMENT_NODE) { continue; } } } // See if we can go to a previous component. If not, we are done. if ((pXPathComp = pXPathComp->pPrev) == NULL) { break; } } Exit: return( rc); } /*************************************************************************** Desc: Set up to use the current predicate as the source for the query. ***************************************************************************/ RCODE F_Query::setupCurrPredicate( FLMBOOL bForward ) { RCODE rc = NE_XFLM_OK; FXPATH * pXPath; XPATH_COMPONENT * pXPathContextComponent; FLMBOOL bFirstLast; // Clear all state information. resetQuery(); if (RC_BAD( rc = newSource())) { goto Exit; } // m_pCurrPred has already been set. We just need to set up each // XPATH appropriately. // Could be multiple XPATHs associated with the predicate, but it // is only necessary to set up one of them to point to the predicate. // We could pick any of them, but we take the first one in the list. pXPath = m_pCurrPred->pXPathNodeList->pXPathNode->nd.pXPath; pXPath->bIsSource = TRUE; // The source component for this XPATH will always be the // last component, because predicates are always optimized on // the last component of an XPATH. pXPath->pSourceComponent = pXPath->pLastComponent; pXPath->pSourceComponent->bIsSource = TRUE; pXPath->pSourceComponent->pOptPred = m_pCurrPred; // See if this XPATH is nested inside of another XPATH // component. If so, that XPATH component must be marked // as the source, and its XPATH must also be marked as the // source. That XPATH component must also point to this // particular XPATH node as the expression XPATH source for // the context component. m_pExprXPathSource = m_pCurrPred->pXPathNodeList->pXPathNode; pXPathContextComponent = pXPath->pSourceComponent->pXPathContext; while (pXPathContextComponent) { // Get the context component's XPATH pXPath = pXPathContextComponent->pXPathNode->nd.pXPath; pXPath->bIsSource = TRUE; pXPath->pSourceComponent = pXPathContextComponent; pXPathContextComponent->bIsSource = TRUE; pXPathContextComponent->pExprXPathSource = m_pExprXPathSource; // Setup for the next higher context, if any m_pExprXPathSource = pXPathContextComponent->pXPathNode; pXPathContextComponent = pXPathContextComponent->pXPathContext; } // Now get the first or last index node bFirstLast = TRUE; if (RC_BAD( rc = getNextIndexNode( &bFirstLast, bForward, m_pExprXPathSource, FALSE))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: See if a node/document passes all of the conditions for a query. Also increment the necessary counters. If it passes and we are building a result set, add it to the result set. ***************************************************************************/ RCODE F_Query::testPassed( IF_DOMNode ** ppNode, FLMBOOL * pbPassed, FLMBOOL * pbEliminatedDup) { RCODE rc = NE_XFLM_OK; FLMBOOL bReportRSStatus = FALSE; FLMBOOL bReportQueryStatus = FALSE; *pbEliminatedDup = FALSE; if (!m_pQuery || m_pQuery->eNodeType != FLM_XPATH_NODE || m_bRemoveDups) { // NOTE: If the document passed, but was eliminated by // duplicate checking, we will NOT increment documents read, // because we know that we have read the document before. // If the document failed, we also need to increment documents read, // even though we don't really know if we read it before. In this // case, we may end up getting a larger document read count than // we should because we could count the same document as being // read more than once. if (*pbPassed) { if (RC_BAD( rc = checkIfDup( ppNode, pbPassed))) { goto Exit; } if (*pbPassed) { m_pCurrOpt->ui64DocsRead++; bReportQueryStatus = TRUE; if (m_pSortResultSet) { m_ui64RSDocsRead++; bReportRSStatus = TRUE; } } else { *pbEliminatedDup = TRUE; } } else { m_pCurrOpt->ui64DocsRead++; bReportQueryStatus = TRUE; if (m_pSortResultSet) { m_ui64RSDocsRead++; bReportRSStatus = TRUE; } } } if (RC_BAD( rc = validateNode( *ppNode, pbPassed))) { goto Exit; } if (*pbPassed) { m_pCurrOpt->ui64DocsPassed++; bReportQueryStatus = TRUE; // If we have a sort key and the sort order is different than // the optimization index, or the application requested that // we build a result set so it could do positioning, add the // document to the result set. if (m_pSortResultSet) { if (RC_BAD( rc = addToResultSet())) { goto Exit; } bReportRSStatus = TRUE; } } if (bReportQueryStatus) { if (RC_BAD( rc = queryStatus())) { goto Exit; } } if (bReportRSStatus && m_pQueryStatus) { if (RC_BAD( rc = m_pQueryStatus->resultSetStatus( m_ui64RSDocsRead, m_ui64RSDocsPassed, m_bEntriesAlreadyInOrder))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from the index. ***************************************************************************/ RCODE F_Query::nextFromIndex( FLMBOOL bEvalCurrDoc, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode ) { RCODE rc = NE_XFLM_OK; FLMBOOL bPassed; FLMBOOL bFirst; if (!bEvalCurrDoc) { bFirst = FALSE; if (RC_BAD( rc = getNextIndexNode( &bFirst, TRUE, m_pExprXPathSource, TRUE))) { goto Exit; } } for (;;) { // If m_pCurrDoc is non-NULL, it means we are set up // to call evalExpr. while (m_pCurrDoc) { FLMBOOL bPassedEval; FLMBOOL bEliminatedDup; if (RC_BAD( rc = evalExpr( NULL, TRUE, TRUE, m_pQuery, &bPassed, ppNode))) { if( rc == NE_XFLM_DOM_NODE_DELETED) { m_bResetAllXPaths = TRUE; rc = NE_XFLM_OK; goto Next_Index_Node; } goto Exit; } bPassedEval = bPassed; if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) { goto Exit; } if (bPassed) { m_eState = (m_eState == XFLM_QUERY_AT_BOF || m_eState == XFLM_QUERY_NOT_POSITIONED) ? XFLM_QUERY_AT_FIRST : XFLM_QUERY_POSITIONED; if (puiNumSkipped) { (*puiNumSkipped)++; } if (uiNumToSkip <= 1) { goto Exit; } else { // puiNumSkipped will always be non-NULL in the case // where uiNumToSkip > 1 flmAssert( puiNumSkipped); if (*puiNumSkipped >= uiNumToSkip) { goto Exit; } else { bPassed = FALSE; } } } // At this point we know that we failed to pass. If we // passed evalExpr, though, we need to call it again so it // can iterate through all possible values. No need to call // it again if it was eliminated because it was a duplicate howerver. if (bPassedEval && !bEliminatedDup) { continue; } Next_Index_Node: // Get the next node from the index. // NOTE: m_pCurrDoc will be set to NULL if there are no // more nodes to get from this particular predicate. bFirst = FALSE; if (RC_BAD( rc = getNextIndexNode( &bFirst, TRUE, m_pExprXPathSource, FALSE))) { goto Exit; } } // If we get here, the loop above failed to get // anything. flmAssert( !m_pCurrDoc); // Try the next predicate. if (!useNextPredicate()) { rc = RC_SET( NE_XFLM_EOF_HIT); m_eState = XFLM_QUERY_AT_EOF; goto Exit; } if (RC_BAD( rc = setupCurrPredicate( TRUE))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Get the previous node from the index. ***************************************************************************/ RCODE F_Query::prevFromIndex( FLMBOOL bEvalCurrDoc, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode ) { RCODE rc = NE_XFLM_OK; FLMBOOL bPassed; FLMBOOL bLast; if (!bEvalCurrDoc) { bLast = FALSE; if (RC_BAD( rc = getNextIndexNode( &bLast, FALSE, m_pExprXPathSource, TRUE))) { goto Exit; } } for (;;) { // If m_pCurrDoc is non-NULL, it means we are set up // to call evalExpr. while (m_pCurrDoc) { FLMBOOL bPassedEval; FLMBOOL bEliminatedDup; if (RC_BAD( rc = evalExpr( NULL, FALSE, TRUE, m_pQuery, &bPassed, ppNode))) { if( rc == NE_XFLM_DOM_NODE_DELETED) { m_bResetAllXPaths = TRUE; rc = NE_XFLM_OK; goto Prev_Index_Node; } goto Exit; } bPassedEval = bPassed; if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) { goto Exit; } if (bPassed) { m_eState = (m_eState == XFLM_QUERY_AT_EOF || m_eState == XFLM_QUERY_NOT_POSITIONED) ? XFLM_QUERY_AT_LAST : XFLM_QUERY_POSITIONED; if (puiNumSkipped) { (*puiNumSkipped)++; } if (uiNumToSkip <= 1) { goto Exit; } else { // puiNumSkipped will always be non-NULL in the case // where uiNumToSkip > 1 flmAssert( puiNumSkipped); if (*puiNumSkipped >= uiNumToSkip) { goto Exit; } else { bPassed = FALSE; } } } // At this point we know that we failed to pass. If we // passed evalExpr, though, we need to call it again so it // can iterate through all possible values. No need to call // it again if it was eliminated because it was a duplicate howerver. if (bPassedEval && !bEliminatedDup) { continue; } Prev_Index_Node: // Get the previous node from the index. // NOTE: m_pCurrDoc will be set to NULL if there are no // more nodes to get from this particular predicate. bLast = FALSE; if (RC_BAD( rc = getNextIndexNode( &bLast, FALSE, m_pExprXPathSource, FALSE))) { goto Exit; } } // If we get here, the loop above failed to get // anything. flmAssert( !m_pCurrDoc); // Try the previous predicate. if (!usePrevPredicate()) { rc = RC_SET( NE_XFLM_BOF_HIT); m_eState = XFLM_QUERY_AT_BOF; goto Exit; } if (RC_BAD( rc = setupCurrPredicate( FALSE))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Get the next/previous document from the index we are scanning. ***************************************************************************/ RCODE F_Query::getDocFromIndexScan( FLMBOOL bFirstLast, FLMBOOL bForward ) { RCODE rc = NE_XFLM_OK; F_DataVector key; FLMUINT64 ui64DocId; for (;;) { if (bForward) { rc = (RCODE)(bFirstLast ? m_pFSIndexCursor->firstKey( m_pDb, &key) : m_pFSIndexCursor->nextKey( m_pDb, &key, FALSE)); if (RC_BAD( rc)) { if (rc == NE_XFLM_EOF_HIT) { m_eState = XFLM_QUERY_AT_EOF; } goto Exit; } } else { rc = (RCODE)(bFirstLast ? m_pFSIndexCursor->lastKey( m_pDb, &key) : m_pFSIndexCursor->prevKey( m_pDb, &key, FALSE)); if (RC_BAD( rc)) { if (rc == NE_XFLM_BOF_HIT) { m_eState = XFLM_QUERY_AT_BOF; } goto Exit; } } m_pCurrOpt->ui64KeysRead++; if (RC_BAD( rc = queryStatus())) { goto Exit; } // If we do not have a duplicate checking set, we can // break out of the loop and get the document associated // with this key. Otherwise, we need to see if the // document associated with this key has already been // processed. If so, we will go to the next key. if (!m_pDocIdSet) { break; } // If we are eliminating duplicates, see if the document // has already been processed. If so, skip the key. ui64DocId = key.getDocumentID(); if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) { if (rc != NE_FLM_NOT_FOUND) { goto Exit; } // Document has not been processed. rc = NE_XFLM_OK; break; } // Document has already been passed, go to next/prev key. m_pCurrOpt->ui64KeyHadDupDoc++; if (RC_BAD( rc = queryStatus())) { goto Exit; } bFirstLast = FALSE; } // Retrieve the document node. if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, key.getDocumentID(), &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // If we cannot retrieve the node, we have a corruption // in the database. rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } else { if (RC_BAD( rc = incrNodesRead())) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Get the next node from scanning sequentially through documents. ***************************************************************************/ RCODE F_Query::nextFromScan( FLMBOOL bFirstDoc, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode ) { RCODE rc = NE_XFLM_OK; FLMBOOL bPassed; FLMBOOL bEliminatedDup; if (bFirstDoc || (m_pQuery && !m_bRemoveDups && m_pQuery->eNodeType == FLM_XPATH_NODE)) { goto Eval_Doc; } // Read until we get a document/node that passes. for (;;) { if (m_bScan) { if (RC_BAD( rc = m_pCurrDoc->getNextDocument( m_pDb, &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { m_eState = XFLM_QUERY_AT_EOF; rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } } else { if (RC_BAD( rc = getDocFromIndexScan( FALSE, TRUE))) { goto Exit; } } m_bResetAllXPaths = TRUE; Eval_Doc: // See if the document passes. if (RC_BAD( rc = evalExpr( NULL, TRUE, TRUE, m_pQuery, &bPassed, ppNode))) { if( rc == NE_XFLM_DOM_NODE_DELETED) { m_bResetAllXPaths = TRUE; rc = NE_XFLM_OK; continue; } goto Exit; } if (bPassed && m_bScanIndex) { m_pCurrOpt->ui64KeysPassed++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) { goto Exit; } if (bPassed) { m_eState = (m_eState == XFLM_QUERY_AT_BOF || m_eState == XFLM_QUERY_NOT_POSITIONED) ? XFLM_QUERY_AT_FIRST : XFLM_QUERY_POSITIONED; if (puiNumSkipped) { (*puiNumSkipped)++; } if (uiNumToSkip <= 1) { goto Exit; } else { // puiNumSkipped will always be non-NULL in the case // where uiNumToSkip > 1 flmAssert( puiNumSkipped); if (*puiNumSkipped >= uiNumToSkip) { goto Exit; } else { bPassed = FALSE; } } } } Exit: return( rc); } /*************************************************************************** Desc: Get the previous node from scanning sequentially through documents. ***************************************************************************/ RCODE F_Query::prevFromScan( FLMBOOL bLastDoc, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode ) { RCODE rc = NE_XFLM_OK; FLMBOOL bPassed; FLMBOOL bEliminatedDup; if (bLastDoc || (m_pQuery && !m_bRemoveDups && m_pQuery->eNodeType == FLM_XPATH_NODE)) { goto Eval_Doc; } // Read until we get a document/node that passes. for (;;) { // Go to the previous document if (m_bScan) { if (RC_BAD( rc = m_pCurrDoc->getPreviousDocument( m_pDb, &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { m_eState = XFLM_QUERY_AT_BOF; rc = RC_SET( NE_XFLM_BOF_HIT); } goto Exit; } } else { if (RC_BAD( rc = getDocFromIndexScan( FALSE, FALSE))) { goto Exit; } } m_bResetAllXPaths = TRUE; Eval_Doc: // See if the document passes. if (RC_BAD( rc = evalExpr( NULL, FALSE, TRUE, m_pQuery, &bPassed, ppNode))) { if( rc == NE_XFLM_DOM_NODE_DELETED) { m_bResetAllXPaths = TRUE; rc = NE_XFLM_OK; continue; } goto Exit; } if (bPassed && m_bScanIndex) { m_pCurrOpt->ui64KeysPassed++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) { goto Exit; } if (bPassed) { m_eState = (m_eState == XFLM_QUERY_AT_EOF || m_eState == XFLM_QUERY_NOT_POSITIONED) ? XFLM_QUERY_AT_LAST : XFLM_QUERY_POSITIONED; if (puiNumSkipped) { (*puiNumSkipped)++; } if (uiNumToSkip <= 1) { goto Exit; } else { // puiNumSkipped will always be non-NULL in the case // where uiNumToSkip > 1 flmAssert( puiNumSkipped); if (*puiNumSkipped >= uiNumToSkip) { goto Exit; } else { bPassed = FALSE; } } } } Exit: return( rc); } /*************************************************************************** Desc: Get first node/document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::getFirst( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit ) { RCODE rc = NE_XFLM_OK; // If we are building on a background thread and this is not the // background thread, we need to get the results from the result // set that is being built. if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getFirstFromResultSet( ifpDb, ppNode, uiTimeLimit); goto Exit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { if (RC_BAD( rc = optimize())) { goto Exit; } } // If the query can never evaluate to TRUE, return EOF without // doing anything. if (m_bEmpty) { m_eState = XFLM_QUERY_AT_EOF; rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // See if we need to build a result set. if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getFirstFromResultSet( ifpDb, ppNode, uiTimeLimit); goto Exit; } // Anytime we go back to the first, we must free whatever list // of document IDs we have collected so far. if (m_bRemoveDups && m_pDocIdSet) { m_pDocIdSet->Release(); m_pDocIdSet = NULL; } if ((m_uiTimeLimit = uiTimeLimit) != 0) { m_uiTimeLimit = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); m_uiStartTime = FLM_GET_TIMER(); } if (m_bScan) { // Start at the beginning of the collection. if (RC_BAD( rc = m_pDb->getFirstDocument( m_uiCollection, &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { m_eState = XFLM_QUERY_AT_EOF; rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } if (RC_BAD( rc = nextFromScan( TRUE, 0, NULL, ppNode))) { goto Exit; } } else if (m_bScanIndex) { if (RC_BAD( rc = getDocFromIndexScan( TRUE, TRUE))) { goto Exit; } if (RC_BAD( rc = nextFromScan( TRUE, 0, NULL, ppNode))) { goto Exit; } } else { m_pCurrContext = m_pQuery->pContext; // Position the context to lowest selected context, // context path, and predicate. // NOTE: Because the m_bScan flag is not set, we are guaranteed // that the following traversal scheme will not encounter any // contexts, context paths, or predicates that require scanning, even // though there may have been some during optimization. They cannot // have been the selected contexts, context paths, or predicates // without ultimately causing the m_bScan flag to have been set. useLeafContext( TRUE); // Setup predicate and get the first node. if (RC_BAD( rc = setupCurrPredicate( TRUE))) { goto Exit; } if (RC_BAD( rc = nextFromIndex( TRUE, 0, NULL, ppNode))) { goto Exit; } } Exit: if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } if (RC_BAD( rc)) { if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } } else { m_pCurrNode = *ppNode; m_pCurrNode->AddRef(); } m_uiTimeLimit = 0; return( rc); } /*************************************************************************** Desc: Get last node/document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::getLast( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit ) { RCODE rc = NE_XFLM_OK; // If we are building on a background thread and this is not the // background thread, we need to get the results from the result // set that is being built. if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getLastFromResultSet( ifpDb, ppNode, uiTimeLimit); goto Exit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { if (RC_BAD( rc = optimize())) { goto Exit; } } // If the query can never evaluate to TRUE, return EOF without // doing anything. if (m_bEmpty) { m_eState = XFLM_QUERY_AT_BOF; rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getLastFromResultSet( ifpDb, ppNode, uiTimeLimit); goto Exit; } // Anytime we go to the last, we must free whatever list // of document IDs we have collected so far. if (m_bRemoveDups && m_pDocIdSet) { m_pDocIdSet->Release(); m_pDocIdSet = NULL; } if ((m_uiTimeLimit = uiTimeLimit) != 0) { m_uiTimeLimit = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); m_uiStartTime = FLM_GET_TIMER(); } if (m_bScan) { // Start at the end of the collection. if (RC_BAD( rc = m_pDb->getLastDocument( m_uiCollection, &m_pCurrDoc))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { m_eState = XFLM_QUERY_AT_BOF; rc = RC_SET( NE_XFLM_BOF_HIT); } goto Exit; } if (RC_BAD( rc = prevFromScan( TRUE, 0, NULL, ppNode))) { goto Exit; } } else if (m_bScanIndex) { if (RC_BAD( rc = getDocFromIndexScan( TRUE, FALSE))) { goto Exit; } if (RC_BAD( rc = prevFromScan( TRUE, 0, NULL, ppNode))) { goto Exit; } } else { m_pCurrContext = m_pQuery->pContext; // Position the context to lowest selected context, // context path, and predicate. // NOTE: Because the m_bScan flag is not set, we are guaranteed // that the following traversal scheme will not encounter any // contexts, context paths, or predicates that require scanning, even // though there may have been some during optimization. They cannot // have been the selected contexts, context paths, or predicates // without ultimately causing the m_bScan flag to have been set. useLeafContext( FALSE); // Setup predicate and get the last node. if (RC_BAD( rc = setupCurrPredicate( FALSE))) { goto Exit; } if (RC_BAD( rc = prevFromIndex( TRUE, 0, NULL, ppNode))) { goto Exit; } } Exit: if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } if (RC_BAD( rc)) { if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } } else { m_pCurrNode = *ppNode; m_pCurrNode->AddRef(); } m_uiTimeLimit = 0; return( rc); } /*************************************************************************** Desc: Get next node/document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::getNext( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped ) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumSkipped; // If we are building on a background thread and this is not the // background thread, we need to get the results from the result // set that is being built. if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getNextFromResultSet( ifpDb, ppNode, uiTimeLimit, uiNumToSkip, puiNumSkipped); goto Exit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!puiNumSkipped) { // puiNumSkipped has to be non-NULL so it can be incremented only // if uiNumToSkip > 1 if (uiNumToSkip > 1) { uiNumSkipped = 0; puiNumSkipped = &uiNumSkipped; } } else { *puiNumSkipped = 0; } switch (m_eState) { case XFLM_QUERY_AT_EOF: rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; case XFLM_QUERY_AT_BOF: case XFLM_QUERY_NOT_POSITIONED: if (RC_OK( rc = getFirst( ifpDb, ppNode, uiTimeLimit))) { if (puiNumSkipped) { *puiNumSkipped = 1; } if (uiNumToSkip <= 1) { goto Exit; } } else { goto Exit; } break; default: if( !m_pCurrNode) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); goto Exit; } break; } // Optimization has to already have occurred. flmAssert( m_bOptimized); // Make sure the passed in F_Db matches the one associated with // the query. if (m_pDb->m_pDatabase != m_pDatabase) { rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // If we have been positioned, we better have a current document and node flmAssert( m_pCurrDoc && m_pCurrNode); if ((m_uiTimeLimit = uiTimeLimit) != 0) { m_uiTimeLimit = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); m_uiStartTime = FLM_GET_TIMER(); } if (m_bScan || m_bScanIndex) { if (RC_BAD( rc = nextFromScan( FALSE, uiNumToSkip, puiNumSkipped, ppNode))) { goto Exit; } } else { if (RC_BAD( rc = nextFromIndex( (m_pQuery && !m_bRemoveDups && m_pQuery->eNodeType == FLM_XPATH_NODE && !m_pQuery->nd.pXPath->pLastComponent->bIsSource) ? TRUE : FALSE, uiNumToSkip, puiNumSkipped, ppNode))) { goto Exit; } } Exit: if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } if (RC_BAD( rc)) { if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } } else { m_pCurrNode = *ppNode; m_pCurrNode->AddRef(); } m_uiTimeLimit = 0; return( rc); } /*************************************************************************** Desc: Get previous node/document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::getPrev( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped ) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumSkipped; // If we are building on a background thread and this is not the // background thread, we need to get the results from the result // set that is being built. if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getPrevFromResultSet( ifpDb, ppNode, uiTimeLimit, uiNumToSkip, puiNumSkipped); goto Exit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!puiNumSkipped) { // puiNumSkipped has to be non-NULL so it can be incremented only // if uiNumToSkip > 1 if (uiNumToSkip > 1) { uiNumSkipped = 0; puiNumSkipped = &uiNumSkipped; } } else { *puiNumSkipped = 0; } switch (m_eState) { case XFLM_QUERY_AT_BOF: rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; case XFLM_QUERY_AT_EOF: case XFLM_QUERY_NOT_POSITIONED: if (RC_OK( rc = getLast( ifpDb, ppNode, uiTimeLimit))) { if (puiNumSkipped) { *puiNumSkipped = 1; } if (uiNumToSkip <= 1) { goto Exit; } } else { goto Exit; } break; default: if( !m_pCurrNode) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); goto Exit; } break; } // Optimization has to already have occurred. flmAssert( m_bOptimized); // Make sure the passed in F_Db matches the one associated with // the query. if (m_pDb->m_pDatabase != m_pDatabase) { rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // If we have been positioned, we better have a current document and node flmAssert( m_pCurrDoc && m_pCurrNode); if ((m_uiTimeLimit = uiTimeLimit) != 0) { m_uiTimeLimit = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); m_uiStartTime = FLM_GET_TIMER(); } if (m_bScan || m_bScanIndex) { if (RC_BAD( rc = prevFromScan( FALSE, uiNumToSkip, puiNumSkipped, ppNode))) { goto Exit; } } else { if (RC_BAD( rc = prevFromIndex( (m_pQuery && !m_bRemoveDups && m_pQuery->eNodeType == FLM_XPATH_NODE && !m_pQuery->nd.pXPath->pLastComponent->bIsSource) ? TRUE : FALSE, uiNumToSkip, puiNumSkipped, ppNode))) { goto Exit; } } Exit: if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } if (RC_BAD( rc)) { if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } } else { m_pCurrNode = *ppNode; m_pCurrNode->AddRef(); } m_uiTimeLimit = 0; return( rc); } /*************************************************************************** Desc: Get current document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::getCurrent( IF_Db * ifpDb, IF_DOMNode ** ppNode) { RCODE rc = NE_XFLM_OK; // If we are building on a background thread and this is not the // background thread, we need to get the results from the result // set that is being built. if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || m_bResultSetPopulated) { rc = getCurrentFromResultSet( ifpDb, ppNode); goto Exit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } switch (m_eState) { case XFLM_QUERY_AT_BOF: case XFLM_QUERY_NOT_POSITIONED: rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; case XFLM_QUERY_AT_EOF: rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; default: if( !m_pCurrNode) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); goto Exit; } break; } // Optimization has to already have occurred. flmAssert( m_bOptimized); // Make sure the passed in F_Db matches the one associated with // the query. if (m_pDb->m_pDatabase != m_pDatabase) { rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // If we have been positioned, we better have a current document and // a current node id. flmAssert( m_pCurrDoc && m_pCurrNode); if( *ppNode) { (*ppNode)->Release(); } *ppNode = m_pCurrNode; (*ppNode)->AddRef(); Exit: if (RC_BAD( rc)) { if (m_pCurrDoc) { m_pCurrDoc->Release(); m_pCurrDoc = NULL; } if (m_pCurrNode) { m_pCurrNode->Release(); m_pCurrNode = NULL; } } m_uiTimeLimit = 0; return( rc); } /*************************************************************************** Desc: Get statistics and optimization information. ***************************************************************************/ RCODE FLMAPI F_Query::getStatsAndOptInfo( FLMUINT * puiNumOptInfos, XFLM_OPT_INFO ** ppOptInfo) { RCODE rc = NE_XFLM_OK; XFLM_OPT_INFO * pOptInfo; FLMUINT uiOptInfoCount; if (!m_bOptimized) { *puiNumOptInfos = 0; *ppOptInfo = NULL; goto Exit; } if (m_bScan || m_bEmpty) { if (RC_BAD( rc = f_alloc( sizeof( XFLM_OPT_INFO), ppOptInfo))) { goto Exit; } f_memcpy( *ppOptInfo, &m_scanOptInfo, sizeof( XFLM_OPT_INFO)); *puiNumOptInfos = 1; } else { OP_CONTEXT * pSaveCurrContext = m_pCurrContext; CONTEXT_PATH * pSaveCurrContextPath = m_pCurrContextPath; PATH_PRED * pSaveCurrPred = m_pCurrPred; FQNODE * pSaveExprXPathSource = m_pExprXPathSource; // Count the number of contexts. m_pCurrContext = m_pQuery->pContext; *puiNumOptInfos = 0; useLeafContext( TRUE); do { if (m_pCurrPred->pNodeSource) { if (RC_BAD( rc = m_pCurrPred->pNodeSource->getOptInfoCount( (IF_Db *)m_pDb, &uiOptInfoCount))) { goto Exit; } (*puiNumOptInfos) += uiOptInfoCount; } else { (*puiNumOptInfos)++; } } while (useNextPredicate()); // Allocate the opt info array. if (RC_OK( rc = f_alloc( sizeof( XFLM_OPT_INFO) * (*puiNumOptInfos), ppOptInfo))) { pOptInfo = *ppOptInfo; m_pCurrContext = m_pQuery->pContext; useLeafContext( TRUE); do { if (m_pCurrPred->pNodeSource) { if (RC_BAD( rc = m_pCurrPred->pNodeSource->getOptInfoCount( (IF_Db *)m_pDb, &uiOptInfoCount))) { goto Exit; } if (RC_BAD( rc = m_pCurrPred->pNodeSource->getOptInfo( (IF_Db *)m_pDb, pOptInfo, uiOptInfoCount))) { goto Exit; } pOptInfo += uiOptInfoCount; } else { f_memcpy( pOptInfo, m_pCurrOpt, sizeof( XFLM_OPT_INFO)); pOptInfo++; } } while (useNextPredicate()); } // Restore the current predicate. m_pCurrContext = pSaveCurrContext; m_pCurrContextPath = pSaveCurrContextPath; m_pCurrPred = pSaveCurrPred; m_pExprXPathSource = pSaveExprXPathSource; } Exit: return( rc); } /*************************************************************************** Desc: Free the optimization info structure. ***************************************************************************/ void FLMAPI F_Query::freeStatsAndOptInfo( XFLM_OPT_INFO ** ppOptInfo) { if (*ppOptInfo) { f_free( ppOptInfo); } } /**************************************************************************** Desc: Create an empty query object and return it's interface... ****************************************************************************/ RCODE FLMAPI F_DbSystem::createIFQuery( IF_Query ** ppQuery) { RCODE rc = NE_XFLM_OK; F_Query * pQuery = NULL; if ((pQuery = f_new F_Query) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } *ppQuery = pQuery; pQuery = NULL; Exit: if( pQuery) { pQuery->Release(); } return( rc); } /**************************************************************************** Desc: Parse a query using the passed in query string. The uiQueryType parameter is intended to identify the type of query syntax used. The XPATH syntax is currently the only sypported type. ****************************************************************************/ RCODE F_Query::setupQueryExpr( FLMBOOL bUnicode, IF_Db * ifpDb, const void * pvQuery) { RCODE rc = NE_XFLM_OK; F_XPath XPath; F_Db * pDb = (F_Db *)ifpDb; // Reset the query object totally. clearQuery(); if (!bUnicode) { if (RC_BAD( rc = XPath.parseQuery( pDb, (char *)pvQuery, this))) { goto Exit; } } else { flmAssert( 0); } // We need to make sure that from this point forward, we are using the same // database object. m_pDatabase = pDb->m_pDatabase; Exit: return rc; } /**************************************************************************** Desc: Comparison function for comparing node ids. ****************************************************************************/ FSTATIC int nodeIdCompareFunc( void * pvData1, void * pvData2, void * // pvUserData ) { if (*((FLMUINT64 *)pvData1) < *((FLMUINT64 *)pvData2)) { return( -1); } else if (*((FLMUINT64 *)pvData1) > *((FLMUINT64 *)pvData2)) { return( 1); } else { return( 0); } } /**************************************************************************** Desc: Allocate a result set for duplicate checking. ****************************************************************************/ RCODE F_Query::allocDupCheckSet( void) { RCODE rc = NE_XFLM_OK; char szTmpDir [F_PATH_MAX_SIZE]; if (m_pDocIdSet) { m_pDocIdSet->Release(); m_pDocIdSet = NULL; } if ((m_pDocIdSet = f_new F_DynSearchSet) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = gv_pXFlmDbSystem->getTempDir( szTmpDir))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = NE_XFLM_OK; } else { goto Exit; } } if (!szTmpDir [0]) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_pDb->m_pDatabase->m_pszDbPath, szTmpDir, NULL))) { goto Exit; } } if (RC_BAD( rc = m_pDocIdSet->setup( szTmpDir, sizeof( FLMUINT64)))) { goto Exit; } m_pDocIdSet->setCompareFunc( nodeIdCompareFunc, NULL); Exit: if (RC_BAD( rc)) { if (m_pDocIdSet) { m_pDocIdSet->Release(); m_pDocIdSet = NULL; } } return( rc); } /**************************************************************************** Desc: Check to see if we have already passed the document that *ppNode is in. If so, return FALSE in *pbPassed. NOTE: This routine will return the root node of the document if it still passes. ****************************************************************************/ RCODE F_Query::checkIfDup( IF_DOMNode ** ppNode, FLMBOOL * pbPassed) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64DocId; // If we have not yet allocated the result set, do it now. if (!m_pDocIdSet) { if (RC_BAD( rc = allocDupCheckSet())) { goto Exit; } } // See if we can add the document id to the result set if( RC_BAD( rc = m_pCurrDoc->getNodeId( m_pDb, &ui64DocId))) { goto Exit; } if (RC_BAD( rc = m_pDocIdSet->addEntry( &ui64DocId))) { if (rc == NE_FLM_EXISTS) { *pbPassed = FALSE; rc = NE_XFLM_OK; m_pCurrOpt->ui64DupDocsEliminated++; } goto Exit; } // When eliminating duplicates, we always return the root node // of the document. (*ppNode)->Release(); *ppNode = m_pCurrDoc; (*ppNode)->AddRef(); Exit: return( rc); } /**************************************************************************** Desc: Setup duplicate handling for a query. ****************************************************************************/ void FLMAPI F_Query::setDupHandling( FLMBOOL bRemoveDups ) { // Should not be able to change this after optimization has occurred. flmAssert( !m_bOptimized); m_bRemoveDups = bRemoveDups; if (!bRemoveDups && m_pDocIdSet) { m_pDocIdSet->Release(); m_pDocIdSet = NULL; } } /**************************************************************************** Desc: Set an index for the query. ****************************************************************************/ RCODE FLMAPI F_Query::setIndex( FLMUINT uiIndex ) { RCODE rc = NE_XFLM_OK; // Cannot set the index if we have already optimized the query if (m_bOptimized) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } m_uiIndex = uiIndex; m_bIndexSet = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Set an index for the query. ****************************************************************************/ RCODE FLMAPI F_Query::getIndex( IF_Db * ifpDb, FLMUINT * puiIndex, FLMBOOL * pbHaveMultiple ) { RCODE rc = NE_XFLM_OK; if (m_bIndexSet) { *puiIndex = m_uiIndex; *pbHaveMultiple = FALSE; goto Exit; } // Optimize the query to determine the index. m_pDb = (F_Db *)ifpDb; if (!m_bOptimized) { if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } if (RC_BAD( rc = optimize())) { goto Exit; } } *pbHaveMultiple = FALSE; if (m_bScan || m_bEmpty) { *puiIndex = 0; } else { OP_CONTEXT * pSaveCurrContext = m_pCurrContext; CONTEXT_PATH * pSaveCurrContextPath = m_pCurrContextPath; PATH_PRED * pSaveCurrPred = m_pCurrPred; FQNODE * pSaveExprXPathSource = m_pExprXPathSource; // See if more than one index is being used. m_pCurrContext = m_pQuery->pContext; useLeafContext( TRUE); *puiIndex = 0; do { if (m_pCurrPred->pNodeSource) { FLMUINT uiIndex; if (RC_BAD( rc = m_pCurrPred->pNodeSource->getIndex( ifpDb, &uiIndex, pbHaveMultiple))) { goto Exit; } if (uiIndex) { if (*puiIndex == 0) { *puiIndex = uiIndex; } if (*pbHaveMultiple) { break; } if (uiIndex != *puiIndex) { *pbHaveMultiple = TRUE; break; } } } else if (m_pCurrOpt->uiIxNum) { if (*puiIndex == 0) { *puiIndex = m_pCurrOpt->uiIxNum; } else if (m_pCurrOpt->uiIxNum != *puiIndex) { *pbHaveMultiple = TRUE; break; } } } while (useNextPredicate()); // Restore the current predicate. m_pCurrContext = pSaveCurrContext; m_pCurrContextPath = pSaveCurrContextPath; m_pCurrPred = pSaveCurrPred; m_pExprXPathSource = pSaveExprXPathSource; } Exit: return( rc); } /**************************************************************************** Desc: Make a copy of a value. ****************************************************************************/ RCODE F_Query::copyValue( FQVALUE * pDestValue, FQVALUE * pSrcValue ) { RCODE rc = NE_XFLM_OK; pDestValue->eValType = pSrcValue->eValType; pDestValue->uiFlags = pSrcValue->uiFlags; // Cannot copy stream values. flmAssert( !(pDestValue->uiFlags & VAL_IS_STREAM)); pDestValue->uiDataLen = pSrcValue->uiDataLen; switch (pDestValue->eValType) { case XFLM_BOOL_VAL: pDestValue->val.eBool = pSrcValue->val.eBool; break; case XFLM_UINT_VAL: pDestValue->val.uiVal = pSrcValue->val.uiVal; break; case XFLM_UINT64_VAL: pDestValue->val.ui64Val = pSrcValue->val.ui64Val; break; case XFLM_INT_VAL: pDestValue->val.iVal = pSrcValue->val.iVal; break; case XFLM_INT64_VAL: pDestValue->val.i64Val = pSrcValue->val.i64Val; break; case XFLM_BINARY_VAL: case XFLM_UTF8_VAL: if (pDestValue->uiDataLen) { if (RC_BAD( rc = m_pool.poolAlloc( pDestValue->uiDataLen, (void **)&pDestValue->val.pucBuf))) { goto Exit; } f_memcpy( pDestValue->val.pucBuf, pSrcValue->val.pucBuf, pDestValue->uiDataLen); } break; default: break; } Exit: return( rc); } /**************************************************************************** Desc: Make a copy of a value. ****************************************************************************/ RCODE F_Query::copyXPath( XPATH_COMPONENT * pXPathContext, FQNODE * pDestNode, FXPATH ** ppDestXPath, FXPATH * pSrcXPath ) { RCODE rc = NE_XFLM_OK; FXPATH * pDestXPath; XPATH_COMPONENT * pXPathComponent; XPATH_COMPONENT * pTmpXPathComponent; if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FXPATH), (void **)&pDestXPath))) { goto Exit; } *ppDestXPath = pDestXPath; pXPathComponent = pSrcXPath->pFirstComponent; while (pXPathComponent) { if (RC_BAD( rc = m_pool.poolCalloc( sizeof( XPATH_COMPONENT), (void **)&pTmpXPathComponent))) { goto Exit; } if ((pTmpXPathComponent->pPrev = pDestXPath->pLastComponent) != NULL) { pTmpXPathComponent->pPrev->pNext = pTmpXPathComponent; } else { pDestXPath->pFirstComponent = pTmpXPathComponent; } pDestXPath->pLastComponent = pTmpXPathComponent; pTmpXPathComponent->pXPathContext = pXPathContext; pTmpXPathComponent->pXPathNode = pDestNode; pTmpXPathComponent->eXPathAxis = pXPathComponent->eXPathAxis; pTmpXPathComponent->eNodeType = pXPathComponent->eNodeType; pTmpXPathComponent->uiDictNum = pXPathComponent->uiDictNum; pTmpXPathComponent->uiContextPosNeeded = pXPathComponent->uiContextPosNeeded; if (pXPathComponent->pNodeSource) { if (RC_BAD( rc = pXPathComponent->pNodeSource->copy( &pTmpXPathComponent->pNodeSource))) { goto Exit; } // Call objectAddRef to add the node source to the list of objects // we have an AddRef on. Then call Release, because the copy() // call above would have also done an AddRef() if (RC_BAD( rc = objectAddRef( pTmpXPathComponent->pNodeSource))) { goto Exit; } pTmpXPathComponent->pNodeSource->Release(); } if (pXPathComponent->pContextPosExpr) { if (RC_BAD( rc = copyExpr( pTmpXPathComponent, &pTmpXPathComponent->pContextPosExpr, pXPathComponent->pContextPosExpr))) { goto Exit; } } if (pXPathComponent->pExpr) { if (RC_BAD( rc = copyExpr( pTmpXPathComponent, &pTmpXPathComponent->pExpr, pXPathComponent->pExpr))) { goto Exit; } } pXPathComponent = pXPathComponent->pNext; } Exit: return( rc); } /**************************************************************************** Desc: Make a copy of a function. ****************************************************************************/ RCODE F_Query::copyFunction( XPATH_COMPONENT * pXPathContext, FQFUNCTION ** ppDestFunc, FQFUNCTION * pSrcFunc ) { RCODE rc = NE_XFLM_OK; FQFUNCTION * pDestFunc; FQEXPR * pExpr; FQEXPR * pTmpExpr; if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQFUNCTION), (void **)&pDestFunc))) { goto Exit; } *ppDestFunc = pDestFunc; pDestFunc->eFunction = pSrcFunc->eFunction; if (pSrcFunc->pFuncObj) { // Need to clone the object, because it may have state info. // on where it is at in iterating through values. if (RC_BAD( pSrcFunc->pFuncObj->cloneSelf( &pDestFunc->pFuncObj))) { goto Exit; } if (RC_BAD( rc = objectAddRef( pDestFunc->pFuncObj))) { goto Exit; } // Need to release once because cloneSelf will have done // an AddRef() - only need the AddRef done by objectAddRef() pDestFunc->pFuncObj->Release(); } pExpr = pSrcFunc->pFirstArg; while (pExpr) { if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQEXPR), (void **)&pTmpExpr))) { goto Exit; } if ((pTmpExpr->pPrev = pDestFunc->pLastArg) != NULL) { pTmpExpr->pPrev->pNext = pTmpExpr; } else { pDestFunc->pFirstArg = pTmpExpr; } pDestFunc->pLastArg = pTmpExpr; if (RC_BAD( rc = copyExpr( pXPathContext, &pTmpExpr->pExpr, pExpr->pExpr))) { goto Exit; } pExpr = pExpr->pNext; } Exit: return( rc); } /**************************************************************************** Desc: Make a copy of a node. ****************************************************************************/ RCODE F_Query::copyNode( XPATH_COMPONENT * pXPathContext, FQNODE ** ppDestNode, FQNODE * pSrcNode ) { RCODE rc = NE_XFLM_OK; FQNODE * pDestNode; if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FQNODE), (void **)&pDestNode))) { goto Exit; } *ppDestNode = pDestNode; pDestNode->eNodeType = pSrcNode->eNodeType; pDestNode->bNotted = pSrcNode->bNotted; switch (pSrcNode->eNodeType) { case FLM_OPERATOR_NODE: pDestNode->nd.op.eOperator = pSrcNode->nd.op.eOperator; pDestNode->nd.op.uiCompareRules = pSrcNode->nd.op.uiCompareRules; pDestNode->nd.op.pOpComparer = pSrcNode->nd.op.pOpComparer; if (pDestNode->nd.op.pOpComparer) { if (RC_BAD( rc = objectAddRef( pDestNode->nd.op.pOpComparer))) { goto Exit; } } break; case FLM_VALUE_NODE: if (RC_BAD( rc = copyValue( &pDestNode->currVal, &pSrcNode->currVal))) { goto Exit; } break; case FLM_XPATH_NODE: if (RC_BAD( rc = copyXPath( pXPathContext, pDestNode, &pDestNode->nd.pXPath, pSrcNode->nd.pXPath))) { goto Exit; } break; case FLM_FUNCTION_NODE: if (RC_BAD( rc = copyFunction( pXPathContext, &pDestNode->nd.pQFunction, pSrcNode->nd.pQFunction))) { goto Exit; } break; } Exit: return( rc); } /**************************************************************************** Desc: Copy an expression ****************************************************************************/ RCODE F_Query::copyExpr( XPATH_COMPONENT * pXPathContext, FQNODE ** ppDestExpr, FQNODE * pSrcExpr ) { RCODE rc = NE_XFLM_OK; FQNODE * pTmpQNode; FQNODE * pQNode = pSrcExpr; FQNODE * pParent = NULL; FQNODE * pPrevSib = NULL; if (!pQNode) { *ppDestExpr = NULL; goto Exit; // rc = NE_XFLM_OK; } for (;;) { if (RC_BAD( rc = copyNode( pXPathContext, &pTmpQNode, pQNode))) { goto Exit; } if (!(*ppDestExpr)) { *ppDestExpr = pTmpQNode; } pTmpQNode->pParent = pParent; if (pParent) { if ((pTmpQNode->pPrevSib = pPrevSib) == NULL) { pParent->pFirstChild = pTmpQNode; } else { pParent->pLastChild = pTmpQNode; } } if (pQNode->pFirstChild) { pParent = pTmpQNode; pPrevSib = NULL; pQNode = pQNode->pFirstChild; } else { while (!pQNode->pNextSib) { pParent = pTmpQNode->pParent; if ((pQNode = pQNode->pParent) == NULL) { flmAssert( !pParent); break; } } if (!pQNode) { break; } pQNode = pQNode->pNextSib; pPrevSib = pParent; pParent = pPrevSib->pParent; } } if (RC_BAD( rc = getPredicates( ppDestExpr, NULL, pXPathContext))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Copy criteria from another query object. ****************************************************************************/ RCODE FLMAPI F_Query::copyCriteria( IF_Query * pSrcQuery ) { RCODE rc = NE_XFLM_OK; EXPR_STATE * pExprState = ((F_Query *)pSrcQuery)->m_pCurExprState; // Verify that the source query is in a "copyable" state if (pExprState) { if (pExprState->pPrev || pExprState->uiNestLevel || (pExprState->pLastNode && pExprState->pLastNode->eNodeType == FLM_OPERATOR_NODE)) { rc = RC_SET( NE_XFLM_Q_INCOMPLETE_QUERY_EXPR); goto Exit; } } // Clear out the existing query, if any. clearQuery(); if (RC_BAD( rc = copyExpr( NULL, &m_pQuery, ((F_Query *)pSrcQuery)->m_pQuery))) { goto Exit; } Exit: return( rc); } libxflaim-5.1.969/src/fxml.h0000644000175000017500000002702410511001742017150 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the FLAIM XML wrapper class // // Tabs: 3 // // Copyright (c) 1999-2000, 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fxml.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FXML_H #define FXML_H /*============================================================================ Desc: FLAIM's XML namespace class ============================================================================*/ class F_XMLNamespace : public F_Object { public: FINLINE F_XMLNamespace() { m_puzPrefix = NULL; m_puzURI = NULL; m_pNext = NULL; } FINLINE ~F_XMLNamespace() { flmAssert( !m_pNext); if( m_puzPrefix) { f_free( &m_puzPrefix); } if( m_puzURI) { f_free( &m_puzURI); } } RCODE setPrefix( FLMUNICODE * puzPrefix); RCODE setURI( FLMUNICODE * puzURI); RCODE setup( FLMUNICODE * puzPrefix, FLMUNICODE * puzURI, F_XMLNamespace * pNext); FINLINE FLMUNICODE * getPrefixPtr( void) { return( m_puzPrefix); } FINLINE FLMUNICODE * getURIPtr( void) { return( m_puzURI); } private: FLMUNICODE * m_puzPrefix; FLMUNICODE * m_puzURI; F_XMLNamespace * m_pNext; friend class F_XMLNamespaceMgr; }; /*============================================================================ Desc: Namespace manager class ============================================================================*/ class F_XMLNamespaceMgr : public F_Object { public: F_XMLNamespaceMgr(); ~F_XMLNamespaceMgr(); RCODE findNamespace( FLMUNICODE * puzPrefix, F_XMLNamespace ** ppNamespace, FLMUINT uiMaxSearchSize = ~((FLMUINT)0)); RCODE pushNamespace( FLMUNICODE * puzPrefix, FLMUNICODE * puzNamespaceURI); RCODE pushNamespace( F_XMLNamespace * pNamespace); void popNamespaces( FLMUINT uiCount); FLMUINT getNamespaceCount( void) { return( m_uiNamespaceCount); } private: F_XMLNamespace * m_pFirstNamespace; FLMUINT m_uiNamespaceCount; }; // Typedefs typedef enum { XML_STATS } eXMLStatus; // This callback is currently only used by the non-com utilities, // which is why we haven't bothered to make it an interface typedef RCODE (* XML_STATUS_HOOK)( eXMLStatus eStatusType, void * pvArg1, void * pvArg2, void * pvArg3, void * pvUserData); /*============================================================================ Desc: FLAIM's XML import class ============================================================================*/ class F_XMLImport : public F_XMLNamespaceMgr { public: F_XMLImport(); ~F_XMLImport(); RCODE setup( void); void reset( void); RCODE import( IF_IStream * pStream, F_Db * pDb, FLMUINT uiCollection, FLMUINT uiFlags, F_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, F_DOMNode ** ppNewNode, XFLM_IMPORT_STATS * pImportStats); FINLINE void setStatusCallback( XML_STATUS_HOOK fnStatus, void * pvUserData) { m_fnStatus = fnStatus; m_pvCallbackData = pvUserData; } private: #define F_DEFAULT_NS_DECL 0x01 #define F_PREFIXED_NS_DECL 0x02 typedef struct xmlattr { FLMUINT uiLineNum; FLMUINT uiLineOffset; FLMUINT uiLineFilePos; FLMUINT uiLineBytes; FLMUINT uiValueLineNum; FLMUINT uiValueLineOffset; FLMUNICODE * puzPrefix; FLMUNICODE * puzLocalName; FLMUNICODE * puzVal; FLMUINT uiFlags; xmlattr * pPrev; xmlattr * pNext; } XML_ATTR; // Methods RCODE getFieldTagAndType( FLMUNICODE * puzName, FLMBOOL bOkToAdd, FLMUINT * puiTagNum, FLMUINT * puiDataType); RCODE getByte( FLMBYTE * pucByte); FINLINE void ungetByte( FLMBYTE ucByte) { // Can only unget a single byte. flmAssert( !m_ucUngetByte); m_ucUngetByte = ucByte; m_importStats.uiChars--; } RCODE getLine( void); FINLINE FLMUNICODE getChar( void) { if (m_uiCurrLineOffset == m_uiCurrLineNumChars) { return( (FLMUNICODE)0); } else { FLMUNICODE uzChar = m_puzCurrLineBuf [m_uiCurrLineOffset++]; return( uzChar); } } FINLINE FLMUNICODE peekChar( void) { if (m_uiCurrLineOffset == m_uiCurrLineNumChars) { return( (FLMUNICODE)0); } else { return( m_puzCurrLineBuf [m_uiCurrLineOffset]); } } FINLINE void ungetChar( void) { // There should never be a reason to unget past the beginning of the current // line. flmAssert( m_uiCurrLineOffset); m_uiCurrLineOffset--; } RCODE getName( FLMUINT * puiChars); RCODE getQualifiedName( FLMUINT * puiChars, FLMUNICODE ** ppuzPrefix, FLMUNICODE ** ppuzLocal, FLMBOOL * pbNamespaceDecl, FLMBOOL * pbDefaultNamespaceDecl); void getNmtoken( FLMUINT * puiChars); RCODE getPubidLiteral( void); RCODE getSystemLiteral( void); RCODE getElementValue( FLMUNICODE * puBuf, FLMUINT * puiMaxChars, FLMBOOL * pbEntity); RCODE processEntityValue( void); RCODE getEntity( FLMUNICODE * puBuf, FLMUINT * puiChars, FLMBOOL * pbTranslated, FLMUNICODE * puTransChar); RCODE processReference( FLMUNICODE * puChar = NULL); RCODE processCDATA( F_DOMNode * pParent, FLMUINT uiSavedLineNum, FLMUINT uiSavedOffset, FLMUINT uiSavedFilePos, FLMUINT uiSavedLineBytes); RCODE processAttributeList( void); RCODE processComment( F_DOMNode * pParent, FLMUINT uiSavedLineNum, FLMUINT uiSavedOffset, FLMUINT uiSavedFilePos, FLMUINT uiSavedLineBytes); RCODE processProlog( void); RCODE processXMLDecl( void); RCODE processVersion( void); RCODE processEncodingDecl( void); RCODE processSDDecl( void); RCODE processMisc( void); RCODE processDocTypeDecl( void); RCODE processPI( F_DOMNode * pParent, FLMUINT uiSavedLineNum, FLMUINT uiSavedOffset, FLMUINT uiSavedFilePos, FLMUINT uiSavedLineBytes); RCODE processElement( F_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, F_DOMNode ** ppNewNode); RCODE unicodeToNumber64( FLMUNICODE * puzVal, FLMUINT64 * pui64Val, FLMBOOL * pbNeg); RCODE flushElementValue( F_DOMNode * pParent, FLMBYTE * pucValue, FLMUINT uiValueLen); RCODE getBinaryVal( FLMUINT * puiLength); RCODE fixNamingTag( F_DOMNode * pNode); FLMBOOL lineHasToken( const char * pszToken); RCODE processMarkupDecl( void); RCODE processPERef( void); RCODE processElementDecl( void); RCODE processEntityDecl( void); RCODE processNotationDecl( void); RCODE processAttListDecl( void); RCODE processContentSpec( void); RCODE processMixedContent( void); RCODE processChildContent( void); RCODE processAttDef( void); RCODE processAttType( void); RCODE processAttValue( XML_ATTR * pAttr); RCODE processDefaultDecl( void); RCODE processID( FLMBOOL bPublicId); RCODE processSTag( F_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, FLMBOOL * pbHasContent, F_DOMNode ** ppElement); RCODE skipWhitespace( FLMBOOL bRequired); RCODE resizeValBuffer( FLMUINT uiSize); // Attribute management void resetAttrList( void) { m_pFirstAttr = NULL; m_pLastAttr = NULL; m_attrPool.poolReset( NULL); } RCODE allocAttribute( XML_ATTR ** ppAttr) { XML_ATTR * pAttr = NULL; RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = m_attrPool.poolCalloc( sizeof( XML_ATTR), (void **)&pAttr))) { goto Exit; } if( (pAttr->pPrev = m_pLastAttr) == NULL) { m_pFirstAttr = pAttr; } else { m_pLastAttr->pNext = pAttr; } m_pLastAttr = pAttr; Exit: *ppAttr = pAttr; return( rc); } RCODE setPrefix( XML_ATTR * pAttr, FLMUNICODE * puzPrefix) { RCODE rc = NE_XFLM_OK; FLMUINT uiStrLen; if( !puzPrefix) { pAttr->puzPrefix = NULL; goto Exit; } uiStrLen = f_unilen( puzPrefix); if( RC_BAD( rc = m_attrPool.poolAlloc( sizeof( FLMUNICODE) * (uiStrLen + 1), (void **)&pAttr->puzPrefix))) { goto Exit; } f_memcpy( pAttr->puzPrefix, puzPrefix, sizeof( FLMUNICODE) * (uiStrLen + 1)); Exit: return( rc); } RCODE setLocalName( XML_ATTR * pAttr, FLMUNICODE * puzLocalName) { RCODE rc = NE_XFLM_OK; FLMUINT uiStrLen; if( !puzLocalName) { pAttr->puzLocalName = NULL; goto Exit; } uiStrLen = f_unilen( puzLocalName); if( RC_BAD( rc = m_attrPool.poolAlloc( sizeof( FLMUNICODE) * (uiStrLen + 1), (void **)&pAttr->puzLocalName))) { goto Exit; } f_memcpy( pAttr->puzLocalName, puzLocalName, sizeof( FLMUNICODE) * (uiStrLen + 1)); Exit: return( rc); } RCODE setUnicode( XML_ATTR * pAttr, FLMUNICODE * puzUnicode) { RCODE rc = NE_XFLM_OK; FLMUINT uiStrLen; if( !puzUnicode) { pAttr->puzVal = NULL; goto Exit; } uiStrLen = f_unilen( puzUnicode); if( RC_BAD( rc = m_attrPool.poolAlloc( sizeof( FLMUNICODE) * (uiStrLen + 1), (void **)&pAttr->puzVal))) { goto Exit; } f_memcpy( pAttr->puzVal, puzUnicode, sizeof( FLMUNICODE) * (uiStrLen + 1)); Exit: return( rc); } RCODE addAttributesToElement( F_DOMNode * pElement); FINLINE void setErrInfo( FLMUINT uiErrLineNum, FLMUINT uiErrLineOffset, XMLParseError eErrorType, FLMUINT uiErrLineFilePos, FLMUINT uiErrLineBytes) { m_importStats.uiErrLineNum = uiErrLineNum; m_importStats.uiErrLineOffset = uiErrLineOffset; m_importStats.eErrorType = eErrorType; m_importStats.uiErrLineFilePos = uiErrLineFilePos; m_importStats.uiErrLineBytes = uiErrLineBytes; } // Data F_Db * m_pDb; FLMUINT m_uiCollection; FLMBYTE m_ucUngetByte; FLMUNICODE * m_puzCurrLineBuf; FLMUINT m_uiCurrLineBufMaxChars; FLMUINT m_uiCurrLineNumChars; FLMUINT m_uiCurrLineOffset; FLMUINT m_uiCurrLineNum; FLMUINT m_uiCurrLineFilePos; FLMUINT m_uiCurrLineBytes; #define FLM_XML_MAX_CHARS 128 FLMUNICODE m_uChars[ FLM_XML_MAX_CHARS]; FLMBOOL m_bSetup; IF_IStream * m_pStream; FLMBYTE * m_pucValBuf; FLMUINT m_uiValBufSize; // Number of Unicode characters FLMUINT m_uiFlags; FLMBOOL m_bExtendDictionary; XMLEncoding m_eXMLEncoding; XML_STATUS_HOOK m_fnStatus; void * m_pvCallbackData; XFLM_IMPORT_STATS m_importStats; F_Pool m_tmpPool; // Attribute management XML_ATTR * m_pFirstAttr; XML_ATTR * m_pLastAttr; F_Pool m_attrPool; }; #define FLM_XML_EXTEND_DICT_FLAG 0x00000001 #define FLM_XML_COMPRESS_WHITESPACE_FLAG 0x00000002 #define FLM_XML_TRANSLATE_ESC_FLAG 0x00000004 FINLINE FLMBOOL isXMLNS( FLMUNICODE * puzName) { return( (puzName [0] == FLM_UNICODE_x || puzName [0] == FLM_UNICODE_X) && (puzName [1] == FLM_UNICODE_m || puzName [1] == FLM_UNICODE_M) && (puzName [2] == FLM_UNICODE_l || puzName [2] == FLM_UNICODE_L) && (puzName [3] == FLM_UNICODE_n || puzName [3] == FLM_UNICODE_N) && (puzName [4] == FLM_UNICODE_s || puzName [4] == FLM_UNICODE_S) ? TRUE : FALSE); } #endif // FXML_H libxflaim-5.1.969/src/fsysdata.cpp0000644000175000017500000030053510511001742020354 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the routines that initialize and shut down FLAIM, // as well as routines for configuring FLAIM. // // Tabs: 3 // // Copyright (c) 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsysdata.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #define ALLOCATE_SYS_DATA #define ALLOC_ERROR_TABLES #include "flaimsys.h" #define FLM_MIN_FREE_BYTES (2 * 1024 * 1024) #ifdef FLM_32BIT #if defined( FLM_LINUX) // With mmap'd memory on Linux, you're effectively limited to about ~2 GB. // Userspace only gets ~3GB of usable address space anyway, and then you // have all of the thread stacks too, which you can't have // overlapping the heap. #define FLM_MAX_CACHE_SIZE (1500 * 1024 * 1024) #else #define FLM_MAX_CACHE_SIZE (2000 * 1024 * 1024) #endif #else #define FLM_MAX_CACHE_SIZE (~((FLMUINT)0)) #endif #define DEFAULT_OPEN_THRESHOLD 100 // 100 file handles to cache #define DEFAULT_MAX_AVAIL_TIME 900 // 15 minutes static FLMATOMIC gv_flmSysSpinLock = 0; static FLMBOOL gv_bFlmStarted = FALSE; static FLMBOOL gv_bToolkitStarted = FALSE; FSTATIC RCODE flmGetCacheBytes( FLMUINT uiPercent, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bCalcOnAvailMem, FLMUINT uiBytesCurrentlyInUse, FLMUINT * puiCacheBytes); FSTATIC RCODE flmVerifyDiskStructOffsets( void); FSTATIC void flmFreeEvent( FEVENT * pEvent, F_MUTEX hMutex, FEVENT ** ppEventListRV); FSTATIC void flmGetStringParam( const char * pszParamName, char ** ppszValue, IF_IniFile * pIniFile); FSTATIC void flmGetNumParam( char ** ppszParam, FLMUINT * puiNum); FSTATIC void flmGetUintParam( const char * pszParamName, FLMUINT uiDefaultValue, FLMUINT * puiUint, IF_IniFile * pIniFile); FSTATIC void lockSysData( void); FSTATIC void unlockSysData( void); void flmGetBoolParam( const char * pszParamName, FLMBOOL uiDefaultValue, FLMBOOL * pbBool, IF_IniFile * pIniFile); FSTATIC RCODE flmGetIniFileName( FLMBYTE * pszIniFileName, FLMUINT uiBufferSz); /**************************************************************************** Desc: This routine determines the number of cache bytes to use for caching based on a percentage of available physical memory or a percentage of physical memory (depending on bCalcOnAvailMem flag). uiBytesCurrentlyInUse indicates how many bytes are currently allocated by FLAIM - so it can factor that in if the calculation is to be based on the available memory. Lower limit is 1 megabyte. ****************************************************************************/ FSTATIC RCODE flmGetCacheBytes( FLMUINT uiPercent, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bCalcOnAvailMem, FLMUINT uiBytesCurrentlyInUse, FLMUINT * puiCacheBytes) { RCODE rc = NE_XFLM_OK; FLMUINT uiMem = 0; FLMUINT64 ui64TotalPhysMem; FLMUINT64 ui64AvailPhysMem; if( RC_BAD( rc = f_getMemoryInfo( &ui64TotalPhysMem, &ui64AvailPhysMem))) { goto Exit; } if( ui64TotalPhysMem > FLM_MAX_UINT) { ui64TotalPhysMem = FLM_MAX_UINT; } if( ui64AvailPhysMem > ui64TotalPhysMem) { ui64AvailPhysMem = ui64TotalPhysMem; } uiMem = (FLMUINT)((bCalcOnAvailMem) ? (FLMUINT)ui64TotalPhysMem : (FLMUINT)ui64AvailPhysMem); // If we are basing the calculation on available physical memory, // take in to account what has already been allocated. if (bCalcOnAvailMem) { if (uiMem > FLM_MAX_UINT - uiBytesCurrentlyInUse) { uiMem = FLM_MAX_UINT; } else { uiMem += uiBytesCurrentlyInUse; } } // If uiMax is zero, use uiMinToLeave to calculate the maximum. if (!uiMax) { if (!uiMinToLeave) { uiMax = uiMem; } else if (uiMinToLeave < uiMem) { uiMax = uiMem - uiMinToLeave; } else { uiMax = 0; } } // Calculate memory as a percentage of memory. uiMem = (FLMUINT)((uiMem > FLM_MAX_UINT / 100) ? (FLMUINT)(uiMem / 100) * uiPercent : (FLMUINT)(uiMem * uiPercent) / 100); // Don't go above the maximum. if (uiMem > uiMax) { uiMem = uiMax; } // Don't go below the minimum. if (uiMem < uiMin) { uiMem = uiMin; } Exit: *puiCacheBytes = uiMem; return( rc); } /*************************************************************************** Desc: Verify that the distance (in bytes) between pvStart and pvEnd is what was specified in uiOffset. ****************************************************************************/ FINLINE void flmVerifyOffset( FLMUINT uiCompilerOffset, FLMUINT uiOffset, RCODE * pRc) { if (RC_OK( *pRc)) { if ( uiCompilerOffset != uiOffset) { *pRc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); } } } /*************************************************************************** Desc: Verify the offsets of each member of every on-disk structure. This is a safety check to ensure that things work correctly on every platform. ****************************************************************************/ FSTATIC RCODE flmVerifyDiskStructOffsets( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiSizeOf; // Verify the XFLM_DB_HDR offsets. flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, szSignature[0]), XFLM_DB_HDR_szSignature_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui8IsLittleEndian), XFLM_DB_HDR_ui8IsLittleEndian_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui8DefaultLanguage), XFLM_DB_HDR_ui8DefaultLanguage_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui16BlockSize), XFLM_DB_HDR_ui16BlockSize_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32DbVersion), XFLM_DB_HDR_ui32DbVersion_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui8BlkChkSummingEnabled), XFLM_DB_HDR_ui8BlkChkSummingEnabled_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui8RflKeepFiles), XFLM_DB_HDR_ui8RflKeepFiles_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui8RflAutoTurnOffKeep), XFLM_DB_HDR_ui8RflAutoTurnOffKeep_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui8RflKeepAbortedTrans), XFLM_DB_HDR_ui8RflKeepAbortedTrans_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflCurrFileNum), XFLM_DB_HDR_ui32RflCurrFileNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui64LastRflCommitID), XFLM_DB_HDR_ui64LastRflCommitID_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflLastFileNumDeleted), XFLM_DB_HDR_ui32RflLastFileNumDeleted_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflLastTransOffset), XFLM_DB_HDR_ui32RflLastTransOffset_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflLastCPFileNum), XFLM_DB_HDR_ui32RflLastCPFileNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflLastCPOffset), XFLM_DB_HDR_ui32RflLastCPOffset_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui64RflLastCPTransID), XFLM_DB_HDR_ui64RflLastCPTransID_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflMinFileSize), XFLM_DB_HDR_ui32RflMinFileSize_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RflMaxFileSize), XFLM_DB_HDR_ui32RflMaxFileSize_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui64CurrTransID), XFLM_DB_HDR_ui64CurrTransID_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui64TransCommitCnt), XFLM_DB_HDR_ui64TransCommitCnt_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RblEOF), XFLM_DB_HDR_ui32RblEOF_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32RblFirstCPBlkAddr), XFLM_DB_HDR_ui32RblFirstCPBlkAddr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32FirstAvailBlkAddr), XFLM_DB_HDR_ui32FirstAvailBlkAddr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32FirstLFBlkAddr), XFLM_DB_HDR_ui32FirstLFBlkAddr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32LogicalEOF), XFLM_DB_HDR_ui32LogicalEOF_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32MaxFileSize), XFLM_DB_HDR_ui32MaxFileSize_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui64LastBackupTransID), XFLM_DB_HDR_ui64LastBackupTransID_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32IncBackupSeqNum), XFLM_DB_HDR_ui32IncBackupSeqNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32BlksChangedSinceBackup), XFLM_DB_HDR_ui32BlksChangedSinceBackup_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ucDbSerialNum[0]), XFLM_DB_HDR_ucDbSerialNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ucLastTransRflSerialNum[0]), XFLM_DB_HDR_ucLastTransRflSerialNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ucNextRflSerialNum[0]), XFLM_DB_HDR_ucNextRflSerialNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ucIncBackupSerialNum[0]), XFLM_DB_HDR_ucIncBackupSerialNum_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32DbKeyLen), XFLM_DB_HDR_ui32DbKeyLen, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ucReserved[0]), XFLM_DB_HDR_ucReserved_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, ui32HdrCRC), XFLM_DB_HDR_ui32HdrCRC_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(XFLM_DB_HDR, DbKey[0]), XFLM_DB_HDR_DbKey, &rc); // Have to use a variable for sizeof. If we don't, compiler barfs // because we are comparing two constants. uiSizeOf = XFLM_DB_HDR_DbKey + XFLM_MAX_ENC_KEY_SIZE; if( sizeof( XFLM_DB_HDR) != uiSizeOf) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); } // Verify the offsets in the F_BLK_HDR structure. flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32BlkAddr), F_BLK_HDR_ui32BlkAddr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32PrevBlkInChain), F_BLK_HDR_ui32PrevBlkInChain_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32NextBlkInChain), F_BLK_HDR_ui32NextBlkInChain_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32PriorBlkImgAddr), F_BLK_HDR_ui32PriorBlkImgAddr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui64TransID), F_BLK_HDR_ui64TransID_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32BlkCRC), F_BLK_HDR_ui32BlkCRC_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui16BlkBytesAvail), F_BLK_HDR_ui16BlkBytesAvail_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui8BlkFlags), F_BLK_HDR_ui8BlkFlags_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui8BlkType), F_BLK_HDR_ui8BlkType_OFFSET, &rc); // Have to use a variable for sizeof. If we don't, compiler barfs // because we are comparing two constants. uiSizeOf = SIZEOF_STD_BLK_HDR; if (sizeof( F_BLK_HDR) != uiSizeOf) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); } // Verify the offsets in the F_BTREE_BLK_HDR structure flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.stdBlkHdr), F_BTREE_BLK_HDR_stdBlkHdr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui16LogicalFile), F_BTREE_BLK_HDR_ui16LogicalFile_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui16NumKeys), F_BTREE_BLK_HDR_ui16NumKeys_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui8BlkLevel), F_BTREE_BLK_HDR_ui8BlkLevel_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui8BTreeFlags), F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui16HeapSize), F_BTREE_BLK_HDR_ui16HeapSize_OFFSET, &rc); // Have to use a variable for sizeof. If we don't, compiler barfs // because we are comparing two constants. uiSizeOf = 40; if (sizeof( F_BTREE_BLK_HDR) != uiSizeOf) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); } // Have to use a variable for sizeof. If we don't, compiler barfs // because we are comparing two constants. uiSizeOf = 40; if (sizeof( F_LARGEST_BLK_HDR) != uiSizeOf) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); } // Verify the offsets in the F_LF_HDR structure flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui32LfNumber), F_LF_HDR_ui32LfNumber_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui32LfType), F_LF_HDR_ui32LfType_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui32RootBlkAddr), F_LF_HDR_ui32RootBlkAddr_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui32EncId), F_LF_HDR_ui32EncId_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui64NextNodeId), F_LF_HDR_ui64NextNodeId_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui64FirstDocId), F_LF_HDR_ui64FirstDocId_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ui64LastDocId), F_LF_HDR_ui64LastDocId_OFFSET, &rc); flmVerifyOffset( (FLMUINT)f_offsetof(F_LF_HDR, ucZeroes[0]), F_LF_HDR_ucZeroes_OFFSET, &rc); // Have to use a variable for sizeof. If we don't, compiler barfs // because we are comparing two constants. uiSizeOf = 64; if (sizeof( F_LF_HDR) != uiSizeOf) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); } return( rc); } /**************************************************************************** Desc: Logs the reason for the "must close" flag being set ****************************************************************************/ void F_Database::logMustCloseReason( const char * pszFileName, FLMINT iLineNumber) { char * pszMsgBuf = NULL; IF_LogMessageClient * pLogMsg = NULL; // Log a message indicating why the "must close" flag was set if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) { if( RC_OK( f_alloc( F_PATH_MAX_SIZE + 512, &pszMsgBuf))) { f_sprintf( (char *)pszMsgBuf, "Database (%s) must be closed because of a 0x%04X error, " "File=%s, Line=%d.", (char *)(m_pszDbPath ? (char *)m_pszDbPath : (char *)""), (unsigned)m_rcMustClose, pszFileName, (int)iLineNumber); pLogMsg->changeColor( FLM_YELLOW, FLM_BLACK); pLogMsg->appendString( pszMsgBuf); } flmEndLogMessage( &pLogMsg); } if( pszMsgBuf) { f_free( &pszMsgBuf); } } /**************************************************************************** Desc: This shuts down the background threads Note: This routine assumes that the global mutex is locked. The mutex will be unlocked internally, but will always be locked on exit. ****************************************************************************/ void F_Database::shutdownDatabaseThreads( void) { RCODE rc = NE_XFLM_OK; F_BKGND_IX * pBackgroundIx; F_Db * pDb; IF_Thread * pThread; FLMUINT uiThreadId; FLMUINT uiThreadCount; FLMBOOL bMutexLocked = TRUE; // Signal all background indexing threads to shutdown and all // threads in the FLM_DB_THREAD_GROUP that are associated with // this F_Database. for( ;;) { uiThreadCount = 0; // Shut down all background threads. uiThreadId = 0; for( ;;) { if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_XFlmSysData.uiIndexingThreadGroup, &uiThreadId))) { if( rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { RC_UNEXPECTED_ASSERT( rc); } } else { pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); if( pBackgroundIx && pBackgroundIx->pDatabase == this) { // Set the thread's terminate flag. uiThreadCount++; pThread->setShutdownFlag(); } pThread->Release(); pThread = NULL; } } // Shut down all threads in the FLM_DB_THREAD_GROUP. uiThreadId = 0; for( ;;) { if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_XFlmSysData.uiDbThreadGroup, &uiThreadId))) { if( rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { RC_UNEXPECTED_ASSERT( rc); } } else { pDb = (F_Db *)pThread->getParm2(); if (pDb && pDb->m_pDatabase == this) { // Set the thread's terminate flag. uiThreadCount++; pThread->setShutdownFlag(); } pThread->Release(); pThread = NULL; } } if( !uiThreadCount) { break; } // Unlock the global mutex f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // Give the threads a chance to terminate f_sleep( 50); // Re-lock the mutex and see if any threads are still active f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } // Shut down the maintenance thread if( m_pMaintThrd) { flmAssert( bMutexLocked); m_pMaintThrd->setShutdownFlag(); f_semSignal( m_hMaintSem); f_mutexUnlock( gv_XFlmSysData.hShareMutex); m_pMaintThrd->stopThread(); f_mutexLock( gv_XFlmSysData.hShareMutex); m_pMaintThrd->Release(); m_pMaintThrd = NULL; f_semDestroy( &m_hMaintSem); } // Re-lock the mutex if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); } } /**************************************************************************** Desc: This routine frees a registered event. ****************************************************************************/ FSTATIC void flmFreeEvent( FEVENT * pEvent, F_MUTEX hMutex, FEVENT ** ppEventListRV) { pEvent->pEventClient->Release(); f_mutexLock( hMutex); if (pEvent->pPrev) { pEvent->pPrev->pNext = pEvent->pNext; } else { *ppEventListRV = pEvent->pNext; } if (pEvent->pNext) { pEvent->pNext->pPrev = pEvent->pPrev; } f_mutexUnlock( hMutex); f_free( &pEvent); } /**************************************************************************** Desc: This routine links an F_Database structure to its name hash bucket. NOTE: This function assumes that the global mutex has been locked. ****************************************************************************/ RCODE F_Database::linkToBucket( void) { RCODE rc = NE_XFLM_OK; F_Database * pTmpDatabase; F_BUCKET * pBucket; FLMUINT uiBucket; pBucket = gv_XFlmSysData.pDatabaseHashTbl; uiBucket = f_strHashBucket( m_pszDbPath, pBucket, FILE_HASH_ENTRIES); pBucket = &pBucket [uiBucket]; if (pBucket->pFirstInBucket) { pTmpDatabase = (F_Database *)pBucket->pFirstInBucket; pTmpDatabase->m_pPrev = this; } m_uiBucket = uiBucket; m_pPrev = NULL; m_pNext = (F_Database *)pBucket->pFirstInBucket; pBucket->pFirstInBucket = this; return( rc); } /**************************************************************************** Desc: This routine links an FDB structure to an F_Database structure. NOTE: This routine assumes that the global mutex has been locked. ****************************************************************************/ RCODE F_Db::linkToDatabase( F_Database * pDatabase) { RCODE rc = NE_XFLM_OK; IF_FileHdl * pTmpFileHdl = NULL; F_SuperFileClient * pSFileClient = NULL; // If the use count on the file used to be zero, unlink it from the // unused list. flmAssert( !m_pDatabase); m_pPrevForDatabase = NULL; if ((m_pNextForDatabase = pDatabase->m_pFirstDb) != NULL) { pDatabase->m_pFirstDb->m_pPrevForDatabase = this; } pDatabase->m_pFirstDb = this; m_pDatabase = pDatabase; if (!(m_uiFlags & FDB_INTERNAL_OPEN)) { pDatabase->incrOpenCount(); } // Allocate the super file object if( !m_pSFileHdl) { if( (m_pSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( (pSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( !pDatabase->m_uiMaxFileSize) { XFLM_DB_HDR tmpDbHdr; if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( pDatabase->m_pszDbPath, gv_XFlmSysData.uiFileOpenFlags, &pTmpFileHdl))) { goto Exit; } if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pTmpFileHdl, &tmpDbHdr))) { goto Exit; } pDatabase->m_uiMaxFileSize = tmpDbHdr.ui32MaxFileSize; pTmpFileHdl->Release(); pTmpFileHdl = NULL; } if( RC_BAD( rc = pSFileClient->setup( pDatabase->m_pszDbPath, pDatabase->m_pszDataDir, pDatabase->m_uiMaxFileSize))) { goto Exit; } if( RC_BAD( rc = m_pSFileHdl->setup( pSFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { goto Exit; } } Exit: if( pSFileClient) { pSFileClient->Release(); } if( pTmpFileHdl) { pTmpFileHdl->Release(); } return( rc); } /**************************************************************************** Desc: This routine unlinks F_Db object from its F_Database structure. NOTE: This routine assumes that the global mutex has been locked. ****************************************************************************/ void F_Db::unlinkFromDatabase( void) { if (!m_pDatabase) { return; } // Unlink the F_Db from the F_Database. if (m_pNextForDatabase) { m_pNextForDatabase->m_pPrevForDatabase = m_pPrevForDatabase; } if (m_pPrevForDatabase) { m_pPrevForDatabase->m_pNextForDatabase = m_pNextForDatabase; } else { m_pDatabase->m_pFirstDb = m_pNextForDatabase; } m_pNextForDatabase = m_pPrevForDatabase = NULL; // Decrement use counts in the F_Database, unless this was // an internal open. if (!(m_uiFlags & FDB_INTERNAL_OPEN)) { m_pDatabase->decrOpenCount(); } m_pDatabase = NULL; } /**************************************************************************** Desc: This routine dynamically adjusts the cache limit if that is the mode we are in. ****************************************************************************/ RCODE F_GlobalCacheMgr::adjustCache( FLMUINT * puiCurrTime, FLMUINT * puiLastCacheAdjustTime) { RCODE rc = NE_XFLM_OK; FLMUINT uiCurrTime = *puiCurrTime; FLMUINT uiLastCacheAdjustTime = *puiLastCacheAdjustTime; FLMBOOL bMutexLocked = FALSE; if (m_bDynamicCacheAdjust && FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= m_uiCacheAdjustInterval) { FLMUINT uiCacheBytes; lockMutex(); bMutexLocked = TRUE; // Make sure the dynamic adjust flag is still set. if (m_bDynamicCacheAdjust && FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= m_uiCacheAdjustInterval) { if( RC_BAD( rc = flmGetCacheBytes( m_uiCacheAdjustPercent, m_uiCacheAdjustMin, m_uiCacheAdjustMax, m_uiCacheAdjustMinToLeave, TRUE, totalBytes(), &uiCacheBytes))) { goto Exit; } if (RC_BAD( rc = setCacheLimit( uiCacheBytes, FALSE))) { unlockMutex(); goto Exit; } } unlockMutex(); bMutexLocked = FALSE; *puiCurrTime = *puiLastCacheAdjustTime = FLM_GET_TIMER(); } Exit: if( bMutexLocked) { unlockMutex(); } return( rc); } /**************************************************************************** Desc: This routine functions as a thread. It monitors open files and frees up files which have been closed longer than the maximum close time. ****************************************************************************/ RCODE FLMAPI F_DbSystem::monitorThrd( IF_Thread * pThread) { FLMUINT uiCurrTime; FLMUINT uiLastCacheAdjustTime = 0; for (;;) { // See if we should shut down if( pThread->getShutdownFlag()) { break; } uiCurrTime = FLM_GET_TIMER(); // Check the adjusting cache limit (void)gv_XFlmSysData.pGlobalCacheMgr->adjustCache( &uiCurrTime, &uiLastCacheAdjustTime); f_sleep( 250); } return( NE_XFLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_DbSystem::cacheCleanupThrd( IF_Thread * pThread) { FLMUINT uiCurrTime; FLMUINT uiLastDefragTime = 0; FLMUINT uiLastCleanupTime = 0; FLMUINT uiDefragInterval; FLMUINT uiCleanupInterval = gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheCleanupInterval; FLMBOOL bDoNodeCacheFirst = TRUE; uiDefragInterval = FLM_SECS_TO_TIMER_UNITS( 120); for (;;) { if( pThread->getShutdownFlag()) { break; } uiCurrTime = FLM_GET_TIMER(); // Alternate between reducing node cache and block cache first. if (gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || FLM_ELAPSED_TIME( uiCurrTime, uiLastCleanupTime) >= uiCleanupInterval) { if (bDoNodeCacheFirst) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->reduceCache(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); (void)gv_XFlmSysData.pBlockCacheMgr->reduceCache( NULL); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bDoNodeCacheFirst = FALSE; } else { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); (void)gv_XFlmSysData.pBlockCacheMgr->reduceCache( NULL); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->reduceCache(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); bDoNodeCacheFirst = TRUE; } uiLastCleanupTime = FLM_GET_TIMER(); } if( FLM_ELAPSED_TIME( uiCurrTime, uiLastDefragTime) >= uiDefragInterval) { gv_XFlmSysData.pBlockCacheMgr->defragmentMemory(); gv_XFlmSysData.pNodeCacheMgr->defragmentMemory(); uiLastDefragTime = FLM_GET_TIMER(); } f_sleep( 500); } return( NE_XFLM_OK); } /**************************************************************************** Desc: This routine does an event callback. Note that the mutex is locked during the callback. ****************************************************************************/ void flmDoEventCallback( eEventCategory eCategory, eEventType eEvent, IF_Db * pDb, FLMUINT uiThreadId, FLMUINT64 ui64TransID, FLMUINT uiIndexOrCollection, FLMUINT64 ui64NodeId, RCODE rc) { FEVENT * pEvent; f_mutexLock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); pEvent = gv_XFlmSysData.EventHdrs [eCategory].pEventCBList; while (pEvent) { pEvent->pEventClient->catchEvent( eEvent, pDb, uiThreadId, ui64TransID, uiIndexOrCollection, ui64NodeId, rc); pEvent = pEvent->pNext; } f_mutexUnlock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); } /**************************************************************************** Desc: This routine sets the "must close" flags on the F_Database and its FDBs ****************************************************************************/ void F_Database::setMustCloseFlags( RCODE rcMustClose, FLMBOOL bMutexLocked) { F_Db * pTmpDb; if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); } if( !m_bMustClose) { m_bMustClose = TRUE; m_rcMustClose = rcMustClose; pTmpDb = m_pFirstDb; while( pTmpDb) { pTmpDb->m_bMustClose = TRUE; pTmpDb = pTmpDb->m_pNextForDatabase; } // Log a message indicating why the "must close" flag has been // set. Calling checkState with the bMustClose flag // already set to TRUE will cause a message to be logged. (void)checkState( __FILE__, __LINE__); } if( !bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } } /*************************************************************************** Desc: Lock the system data structure for access - called only by startup and shutdown. NOTE: On platforms that do not support atomic exchange this is less than perfect - won't handle tight race conditions. ***************************************************************************/ void lockSysData( void) { while( f_atomicExchange( &gv_flmSysSpinLock, 1) == 1) { f_sleep( 10); } } /*************************************************************************** Desc: Unlock the system data structure for access - called only by startup and shutdown. ***************************************************************************/ void unlockSysData( void) { (void)f_atomicExchange( &gv_flmSysSpinLock, 0); } /**************************************************************************** Desc : Constructor for global cache manager. ****************************************************************************/ F_GlobalCacheMgr::F_GlobalCacheMgr() { m_pSlabManager = NULL; m_bCachePreallocated = FALSE; m_bDynamicCacheAdjust = f_canGetMemoryInfo(); m_uiCacheAdjustPercent = XFLM_DEFAULT_CACHE_ADJUST_PERCENT; m_uiCacheAdjustMin = XFLM_DEFAULT_CACHE_ADJUST_MIN; m_uiCacheAdjustMax = XFLM_DEFAULT_CACHE_ADJUST_MAX; m_uiCacheAdjustMinToLeave = XFLM_DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE; m_uiCacheAdjustInterval = FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_CACHE_ADJUST_INTERVAL); flmGetCacheBytes( m_uiCacheAdjustPercent, m_uiCacheAdjustMin, m_uiCacheAdjustMax, m_uiCacheAdjustMinToLeave, TRUE, 0, &m_uiMaxBytes); m_uiCacheCleanupInterval = FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL); m_uiUnusedCleanupInterval = FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_UNUSED_CLEANUP_INTERVAL); m_hMutex = F_MUTEX_NULL; m_uiMaxSlabs = 0; } /**************************************************************************** Desc: ****************************************************************************/ F_GlobalCacheMgr::~F_GlobalCacheMgr() { if (m_pSlabManager) { m_pSlabManager->Release(); } if (m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_GlobalCacheMgr::setup( void) { RCODE rc = NE_XFLM_OK; FLMBYTE szTmpBuffer[ 64]; flmAssert( !m_pSlabManager); if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } if( RC_BAD( rc = FlmAllocSlabManager( &m_pSlabManager))) { goto Exit; } f_getenv( "XFLM_PREALLOC_CACHE_SIZE", szTmpBuffer, sizeof( szTmpBuffer)); if( RC_BAD( rc = m_pSlabManager->setup( f_atoi( (char *)szTmpBuffer)))) { m_pSlabManager->Release(); m_pSlabManager = NULL; goto Exit; } // Need to set max slabs here because we didn't know the slab size // in the constructor. m_uiMaxSlabs = m_uiMaxBytes / m_pSlabManager->getSlabSize(); Exit: return( rc); } /**************************************************************************** Desc: This routine sets the limits for record cache and block cache - dividing the total cache between the two caches. It uses the same ratio currently in force. ****************************************************************************/ RCODE F_GlobalCacheMgr::setCacheLimit( FLMUINT uiNewTotalCacheSize, FLMBOOL bPreallocateCache) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldCacheSize = m_uiMaxBytes; if( uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE) { uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE; } if( bPreallocateCache) { if( m_bDynamicCacheAdjust) { // Can't pre-allocate and dynamically adjust. bPreallocateCache = FALSE; } else { if( RC_BAD( rc = m_pSlabManager->resize( uiNewTotalCacheSize, TRUE, &uiNewTotalCacheSize))) { bPreallocateCache = FALSE; } } } m_uiMaxBytes = uiNewTotalCacheSize; m_uiMaxSlabs = m_uiMaxBytes / m_pSlabManager->getSlabSize(); m_bCachePreallocated = bPreallocateCache; if( uiNewTotalCacheSize < uiOldCacheSize) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->reduceCache(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); gv_XFlmSysData.pBlockCacheMgr->reduceCache( NULL); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } if( !bPreallocateCache) { m_pSlabManager->resize( uiNewTotalCacheSize, FALSE); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_GlobalCacheMgr::clearCache( IF_Db * pDb) { RCODE rc = NE_XFLM_OK; FLMUINT uiSavedMaxBytes; FLMUINT uiSavedMaxSlabs; lockMutex(); uiSavedMaxBytes = m_uiMaxBytes; uiSavedMaxSlabs = m_uiMaxSlabs; m_uiMaxBytes = 0; m_uiMaxSlabs = 0; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->reduceCache(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( (F_Db *)pDb); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); if( RC_BAD( rc)) { goto Exit; } Exit: m_uiMaxBytes = uiSavedMaxBytes; m_uiMaxSlabs = uiSavedMaxSlabs; unlockMutex(); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ F_DbSystem::F_DbSystem() { m_refCnt = 1; LockModule(); } /**************************************************************************** Desc: ****************************************************************************/ F_DbSystem::~F_DbSystem() { lockSysData(); cleanup(); unlockSysData(); UnlockModule(); } /**************************************************************************** Desc : Startup the database engine. Notes: This routine may be called multiple times. However, if that is done exit() should be called for each time this is called successfully. This routine does not handle race conditions on platforms that do not support atomic increment. ****************************************************************************/ RCODE F_DbSystem::init( void) { RCODE rc = NE_XFLM_OK; FLMINT iEventCategory; #ifdef FLM_USE_NICI int iHandle; #endif flmAssert( !gv_bFlmStarted); // The memset needs to be first. f_memset( &gv_XFlmSysData, 0, sizeof( FLMSYSDATA)); gv_XFlmSysData.uiMaxFileSize = f_getMaxFileSize(); // Get the thread manager if( RC_BAD( rc = FlmGetThreadMgr( &gv_XFlmSysData.pThreadMgr))) { goto Exit; } // Get the file system manager if( RC_BAD( rc = FlmGetFileSystem( &gv_XFlmSysData.pFileSystem))) { goto Exit; } // Set up a file handle cache if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->allocFileHandleCache( DEFAULT_OPEN_THRESHOLD, DEFAULT_MAX_AVAIL_TIME, &gv_XFlmSysData.pFileHdlCache))) { goto Exit; } gv_XFlmSysData.uiIndexingThreadGroup = gv_XFlmSysData.pThreadMgr->allocGroupId(); gv_XFlmSysData.uiDbThreadGroup = gv_XFlmSysData.pThreadMgr->allocGroupId(); gv_XFlmSysData.uiCheckpointThreadGroup = gv_XFlmSysData.pThreadMgr->allocGroupId(); // Sanity check -- make sure we are using the correct // byte-swap macros for this platform flmAssert( FB2UD( (FLMBYTE *)"\x0A\x0B\x0C\x0D") == 0x0D0C0B0A); flmAssert( FB2UW( (FLMBYTE *)"\x0A\x0B") == 0x0B0A); #ifdef FLM_DEBUG // Variables for memory allocation tracking. gv_XFlmSysData.bTrackLeaks = TRUE; gv_XFlmSysData.hMemTrackingMutex = F_MUTEX_NULL; #endif gv_XFlmSysData.hNodeCacheMutex = F_MUTEX_NULL; gv_XFlmSysData.hBlockCacheMutex = F_MUTEX_NULL; gv_XFlmSysData.hShareMutex = F_MUTEX_NULL; gv_XFlmSysData.hStatsMutex = F_MUTEX_NULL; gv_XFlmSysData.hLoggerMutex = F_MUTEX_NULL; gv_XFlmSysData.hIniMutex = F_MUTEX_NULL; // Initialize the event categories to have no mutex. for (iEventCategory = 0; iEventCategory < XFLM_MAX_EVENT_CATEGORIES; iEventCategory++) { gv_XFlmSysData.EventHdrs [iEventCategory].hMutex = F_MUTEX_NULL; } if (RC_BAD( rc = flmVerifyDiskStructOffsets())) { goto Exit; } gv_XFlmSysData.uiFileOpenFlags = FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT; gv_XFlmSysData.uiFileCreateFlags = gv_XFlmSysData.uiFileOpenFlags | FLM_IO_EXCL | FLM_IO_CREATE_DIR; // Initialize all of the fields gv_XFlmSysData.uiMaxUnusedTime = FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_MAX_UNUSED_TIME); gv_XFlmSysData.uiMaxCPInterval = FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_MAX_CP_INTERVAL); gv_XFlmSysData.uiRehashAfterFailureBackoffTime = FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_REHASH_BACKOFF_INTERVAL); if ((gv_XFlmSysData.pGlobalCacheMgr = f_new F_GlobalCacheMgr) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pGlobalCacheMgr->setup())) { goto Exit; } // Create the mutexes for controlling access to cache and global structures. if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hNodeCacheMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hBlockCacheMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hShareMutex))) { goto Exit; } if ((gv_XFlmSysData.pBlockCacheMgr = f_new F_BlockCacheMgr) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->initCache())) { goto Exit; } if ((gv_XFlmSysData.pNodeCacheMgr = f_new F_NodeCacheMgr) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->initCache())) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hQueryMutex))) { goto Exit; } if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hIniMutex))) { goto Exit; } // Initialize a statistics structure. if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hStatsMutex))) { goto Exit; } f_memset( &gv_XFlmSysData.Stats, 0, sizeof( XFLM_STATS)); gv_XFlmSysData.bStatsInitialized = TRUE; // Initialize the logging mutex if( RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hLoggerMutex))) { goto Exit; } // Allocate memory for the file name hash table. if (RC_BAD(rc = f_allocHashTable( FILE_HASH_ENTRIES, &gv_XFlmSysData.pDatabaseHashTbl))) { goto Exit; } #ifdef FLM_DBG_LOG flmDbgLogInit(); #endif // Set up mutexes for the event table. for (iEventCategory = 0; iEventCategory < XFLM_MAX_EVENT_CATEGORIES; iEventCategory++) { if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.EventHdrs [iEventCategory].hMutex))) { goto Exit; } } // Start the monitor thread if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( &gv_XFlmSysData.pMonitorThrd, F_DbSystem::monitorThrd, "DB Monitor"))) { goto Exit; } // Start the cache cleanup thread if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( &gv_XFlmSysData.pCacheCleanupThrd, F_DbSystem::cacheCleanupThrd, "Cache Cleanup Thread"))) { goto Exit; } if ((gv_XFlmSysData.pBtPool = f_new F_BtPool()) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpInit())) { goto Exit; } if ((gv_XFlmSysData.pNodePool = f_new F_NodePool) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pNodePool->setup())) { goto Exit; } if( RC_BAD( rc = FlmGetXMLObject( &gv_XFlmSysData.pXml))) { goto Exit; } #ifdef FLM_USE_NICI iHandle = f_getpid(); // Initialize NICI if( RC_BAD( rc = CCS_Init( &iHandle))) { rc = RC_SET( NE_XFLM_NICI_INIT_FAILED); goto Exit; } #endif // Initialize the XFlaim cache settings from the .ini file (if present) readIniFile(); Exit: // If not successful, free up any resources that were allocated. if (RC_BAD( rc)) { cleanup(); } return( rc); } /************************************************************************ Desc : Cleans up - assumes that the spin lock has already been obtained. This allows it to be called directly from init() on error conditions. ************************************************************************/ void F_DbSystem::cleanup( void) { FLMUINT uiCnt; FLMINT iEventCategory; // If the toolkit wasn't started, nothing was done in XFLAIM if( !gv_bToolkitStarted) { return; } // Free any queries that have been saved in the query list. if (gv_XFlmSysData.hQueryMutex != F_MUTEX_NULL) { // Setting uiMaxQueries to zero will cause flmFreeSavedQueries // to free the entire list. Also, embedded queries will not be // added back into the list when uiMaxQueries is zero. gv_XFlmSysData.uiMaxQueries = 0; flmFreeSavedQueries( FALSE); } // Shut down the monitor thread, if there is one. if( gv_XFlmSysData.pMonitorThrd) { gv_XFlmSysData.pMonitorThrd->stopThread(); gv_XFlmSysData.pMonitorThrd->Release(); gv_XFlmSysData.pMonitorThrd = NULL; } // Shut down the cache reduce thread if( gv_XFlmSysData.pCacheCleanupThrd) { gv_XFlmSysData.pCacheCleanupThrd->stopThread(); gv_XFlmSysData.pCacheCleanupThrd->Release(); gv_XFlmSysData.pCacheCleanupThrd = NULL; } // Free all of the files and associated structures if (gv_XFlmSysData.pDatabaseHashTbl) { F_BUCKET * pDatabaseHashTbl; // F_Database destructor expects the global mutex to be locked // IMPORTANT NOTE: pDatabaseHashTbl is ALWAYS allocated // AFTER the mutex is allocated, so we are guaranteed // to have a mutex if pDatabaseHashTbl is non-NULL. f_mutexLock( gv_XFlmSysData.hShareMutex); for (uiCnt = 0, pDatabaseHashTbl = gv_XFlmSysData.pDatabaseHashTbl; uiCnt < FILE_HASH_ENTRIES; uiCnt++, pDatabaseHashTbl++) { F_Database * pDatabase = (F_Database *)pDatabaseHashTbl->pFirstInBucket; F_Database * pTmpDatabase; while (pDatabase) { pTmpDatabase = pDatabase; pDatabase = pDatabase->m_pNext; pTmpDatabase->freeDatabase(); } pDatabaseHashTbl->pFirstInBucket = NULL; } // Unlock the global mutex f_mutexUnlock( gv_XFlmSysData.hShareMutex); // Free the hash table f_free( &gv_XFlmSysData.pDatabaseHashTbl); } // Free the statistics. if (gv_XFlmSysData.bStatsInitialized) { f_mutexLock( gv_XFlmSysData.hStatsMutex); flmStatFree( &gv_XFlmSysData.Stats); f_mutexUnlock( gv_XFlmSysData.hStatsMutex); gv_XFlmSysData.bStatsInitialized = FALSE; } if (gv_XFlmSysData.hStatsMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hStatsMutex); } // Make sure the purge list is empty if( gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->cleanupPurgedCache(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); flmAssert( !gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList); } // Free the node cache manager if( gv_XFlmSysData.pNodeCacheMgr) { gv_XFlmSysData.pNodeCacheMgr->Release(); gv_XFlmSysData.pNodeCacheMgr = NULL; } // Free the block cache manager if( gv_XFlmSysData.pBlockCacheMgr) { gv_XFlmSysData.pBlockCacheMgr->Release(); gv_XFlmSysData.pBlockCacheMgr = NULL; } // Free up callbacks that have been registered for events. for (iEventCategory = 0; iEventCategory < XFLM_MAX_EVENT_CATEGORIES; iEventCategory++) { if (gv_XFlmSysData.EventHdrs [iEventCategory].hMutex != F_MUTEX_NULL) { while (gv_XFlmSysData.EventHdrs [iEventCategory].pEventCBList) { flmFreeEvent( gv_XFlmSysData.EventHdrs [iEventCategory].pEventCBList, gv_XFlmSysData.EventHdrs [iEventCategory].hMutex, &gv_XFlmSysData.EventHdrs [iEventCategory].pEventCBList); } f_mutexDestroy( &gv_XFlmSysData.EventHdrs [iEventCategory].hMutex); } } // Release the thread manager if( gv_XFlmSysData.pThreadMgr) { gv_XFlmSysData.pThreadMgr->Release(); gv_XFlmSysData.pThreadMgr = NULL; } // Release the file handle cache if( gv_XFlmSysData.pFileHdlCache) { gv_XFlmSysData.pFileHdlCache->Release(); gv_XFlmSysData.pFileHdlCache = NULL; } // Release the file system object if (gv_XFlmSysData.pFileSystem) { gv_XFlmSysData.pFileSystem->Release(); gv_XFlmSysData.pFileSystem = NULL; } #ifdef FLM_DBG_LOG flmDbgLogExit(); #endif // Release the logger if( gv_XFlmSysData.pLogger) { flmAssert( !gv_XFlmSysData.uiPendingLogMessages); gv_XFlmSysData.pLogger->Release(); gv_XFlmSysData.pLogger = NULL; } if( gv_XFlmSysData.hLoggerMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hLoggerMutex); } // Free the btree pool if( gv_XFlmSysData.pBtPool) { gv_XFlmSysData.pBtPool->Release(); gv_XFlmSysData.pBtPool = NULL; } // Free the node pool if( gv_XFlmSysData.pNodePool) { gv_XFlmSysData.pNodePool->Release(); gv_XFlmSysData.pNodePool = NULL; } // Free the XML object if( gv_XFlmSysData.pXml) { gv_XFlmSysData.pXml->Release(); gv_XFlmSysData.pXml = NULL; } // Release the global cache manager. NOTE: This must happen // only AFTER releasing the node cache manager and block cache // manager. if( gv_XFlmSysData.pGlobalCacheMgr) { gv_XFlmSysData.pGlobalCacheMgr->Release(); gv_XFlmSysData.pGlobalCacheMgr = NULL; } #ifdef FLM_USE_NICI CCS_Shutdown(); #endif // Free the mutexes last of all. if (gv_XFlmSysData.hQueryMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hQueryMutex); } if (gv_XFlmSysData.hNodeCacheMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hNodeCacheMutex); } if (gv_XFlmSysData.hBlockCacheMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hBlockCacheMutex); } if (gv_XFlmSysData.hShareMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hShareMutex); } if (gv_XFlmSysData.hIniMutex != F_MUTEX_NULL) { f_mutexDestroy( &gv_XFlmSysData.hIniMutex); } if( gv_bToolkitStarted) { ftkShutdown(); gv_bToolkitStarted = FALSE; } gv_bFlmStarted = FALSE; } /**************************************************************************** Desc: Configures how memory will be dynamically regulated. ****************************************************************************/ RCODE F_GlobalCacheMgr::setDynamicMemoryLimit( FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave) { RCODE rc = NE_XFLM_OK; FLMUINT uiCacheBytes; FLMBOOL bMutexLocked = FALSE; if( !f_canGetMemoryInfo()) { rc = RC_SET( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } lockMutex(); bMutexLocked = TRUE; m_bDynamicCacheAdjust = TRUE; flmAssert( uiCacheAdjustPercent > 0 && uiCacheAdjustPercent <= 100); m_uiCacheAdjustPercent = uiCacheAdjustPercent; m_uiCacheAdjustMin = uiCacheAdjustMin; m_uiCacheAdjustMax = uiCacheAdjustMax; m_uiCacheAdjustMinToLeave = uiCacheAdjustMinToLeave; if( RC_BAD( rc = flmGetCacheBytes( m_uiCacheAdjustPercent, m_uiCacheAdjustMin, m_uiCacheAdjustMax, m_uiCacheAdjustMinToLeave, TRUE, totalBytes(), &uiCacheBytes))) { goto Exit; } if( RC_BAD( rc = setCacheLimit( uiCacheBytes, FALSE))) { goto Exit; } Exit: if( bMutexLocked) { unlockMutex(); } return( rc); } /**************************************************************************** Desc: Sets a hard memory limit for cache. ****************************************************************************/ RCODE F_GlobalCacheMgr::setHardMemoryLimit( FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; lockMutex(); bMutexLocked = TRUE; m_bDynamicCacheAdjust = FALSE; if (uiPercent) { FLMUINT uiCacheBytes; if( RC_BAD( rc = flmGetCacheBytes( uiPercent, uiMin, uiMax, uiMinToLeave, bPercentOfAvail, totalBytes(), &uiCacheBytes))) { goto Exit; } if( RC_BAD( rc = setCacheLimit( uiCacheBytes, bPreallocate))) { goto Exit; } } else { if( RC_BAD( rc = setCacheLimit( uiMax, bPreallocate))) { goto Exit; } } Exit: if( bMutexLocked) { unlockMutex(); } return( rc); } /**************************************************************************** Desc: Returns information about memory usage ****************************************************************************/ void F_GlobalCacheMgr::getCacheInfo( XFLM_CACHE_INFO * pCacheInfo) { f_memset( pCacheInfo, 0, sizeof( XFLM_CACHE_INFO)); lockMutex(); pCacheInfo->uiMaxBytes = m_uiMaxBytes; pCacheInfo->uiTotalBytesAllocated = totalBytes(); pCacheInfo->bDynamicCacheAdjust = m_bDynamicCacheAdjust; pCacheInfo->uiCacheAdjustPercent = m_uiCacheAdjustPercent; pCacheInfo->uiCacheAdjustMin = m_uiCacheAdjustMin; pCacheInfo->uiCacheAdjustMax = m_uiCacheAdjustMax; pCacheInfo->uiCacheAdjustMinToLeave = m_uiCacheAdjustMinToLeave; pCacheInfo->bPreallocatedCache = m_bCachePreallocated; unlockMutex(); // Return block cache information. f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); f_memcpy( &pCacheInfo->BlockCache, &gv_XFlmSysData.pBlockCacheMgr->m_Usage, sizeof( XFLM_CACHE_USAGE)); pCacheInfo->uiFreeBytes = gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes; pCacheInfo->uiFreeCount = gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount; pCacheInfo->uiReplaceableCount = gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount; pCacheInfo->uiReplaceableBytes = gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes; f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); // Return node cache information. f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); f_memcpy( &pCacheInfo->NodeCache, &gv_XFlmSysData.pNodeCacheMgr->m_Usage, sizeof( XFLM_CACHE_USAGE)); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); // Gather per-database statistics f_mutexLock( gv_XFlmSysData.hShareMutex); if( gv_XFlmSysData.pDatabaseHashTbl) { FLMUINT uiLoop; F_Database * pDatabase; for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) { if( (pDatabase = (F_Database *)gv_XFlmSysData.pDatabaseHashTbl[ uiLoop].pFirstInBucket) != NULL) { while( pDatabase) { if( pDatabase->m_uiDirtyCacheCount) { pCacheInfo->uiDirtyBytes += pDatabase->m_uiDirtyCacheCount * pDatabase->m_uiBlockSize; pCacheInfo->uiDirtyCount += pDatabase->m_uiDirtyCacheCount; } if( pDatabase->m_uiNewCount) { pCacheInfo->uiNewBytes += pDatabase->m_uiNewCount * pDatabase->m_uiBlockSize; pCacheInfo->uiNewCount += pDatabase->m_uiNewCount; } if( pDatabase->m_uiLogCacheCount) { pCacheInfo->uiLogBytes += pDatabase->m_uiLogCacheCount * pDatabase->m_uiBlockSize; pCacheInfo->uiLogCount += pDatabase->m_uiLogCacheCount; } pDatabase = pDatabase->m_pNext; } } } } f_mutexUnlock( gv_XFlmSysData.hShareMutex); } /**************************************************************************** Desc: Close all files in the file handle cache that have not been used for the specified number of seconds. ****************************************************************************/ RCODE FLMAPI F_DbSystem::closeUnusedFiles( FLMUINT uiSeconds) { if( gv_XFlmSysData.pFileHdlCache) { gv_XFlmSysData.pFileHdlCache->closeUnusedFiles( uiSeconds); } return( NE_XFLM_OK); } /**************************************************************************** Desc: Enable/disable cache debugging mode ****************************************************************************/ void FLMAPI F_DbSystem::enableCacheDebug( FLMBOOL bDebug) { #ifdef FLM_DEBUG gv_XFlmSysData.pBlockCacheMgr->m_bDebug = bDebug; gv_XFlmSysData.pNodeCacheMgr->m_bDebug = bDebug; #else F_UNREFERENCED_PARM( bDebug); #endif } /**************************************************************************** Desc: Returns cache debugging mode ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::cacheDebugEnabled( void) { #ifdef FLM_DEBUG return( gv_XFlmSysData.pBlockCacheMgr->m_bDebug || gv_XFlmSysData.pNodeCacheMgr->m_bDebug); #else return( FALSE); #endif } /**************************************************************************** Desc: Start gathering statistics. ****************************************************************************/ void FLMAPI F_DbSystem::startStats( void) { f_mutexLock( gv_XFlmSysData.hStatsMutex); flmStatStart( &gv_XFlmSysData.Stats); f_mutexUnlock( gv_XFlmSysData.hStatsMutex); // Start query statistics, if they have not // already been started. f_mutexLock( gv_XFlmSysData.hQueryMutex); if (!gv_XFlmSysData.uiMaxQueries) { gv_XFlmSysData.uiMaxQueries = 20; gv_XFlmSysData.bNeedToUnsetMaxQueries = TRUE; } f_mutexUnlock( gv_XFlmSysData.hQueryMutex); } /**************************************************************************** Desc: Stop gathering statistics. ****************************************************************************/ void FLMAPI F_DbSystem::stopStats( void) { f_mutexLock( gv_XFlmSysData.hStatsMutex); flmStatStop( &gv_XFlmSysData.Stats); f_mutexUnlock( gv_XFlmSysData.hStatsMutex); // Stop query statistics, if they were // started by a call to FLM_START_STATS. f_mutexLock( gv_XFlmSysData.hQueryMutex); if (gv_XFlmSysData.bNeedToUnsetMaxQueries) { gv_XFlmSysData.uiMaxQueries = 0; flmFreeSavedQueries( TRUE); // NOTE: flmFreeSavedQueries unlocks the mutex. } else { f_mutexUnlock( gv_XFlmSysData.hQueryMutex); } } /**************************************************************************** Desc: Reset statistics. ****************************************************************************/ void FLMAPI F_DbSystem::resetStats( void) { FLMUINT uiSaveMax; // Reset the block cache statistics. f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); gv_XFlmSysData.pBlockCacheMgr->m_uiIoWaits = 0; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHits = 0; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHitLooks = 0; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaults = 0; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaultLooks = 0; f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); // Reset the node cache statistics. f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->m_uiIoWaits = 0; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheHits = 0; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheHitLooks = 0; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheFaults = 0; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheFaultLooks = 0; f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); f_mutexLock( gv_XFlmSysData.hStatsMutex); flmStatReset( &gv_XFlmSysData.Stats, TRUE); f_mutexUnlock( gv_XFlmSysData.hStatsMutex); f_mutexLock( gv_XFlmSysData.hQueryMutex); uiSaveMax = gv_XFlmSysData.uiMaxQueries; gv_XFlmSysData.uiMaxQueries = 0; flmFreeSavedQueries( TRUE); // NOTE: flmFreeSavedQueries unlocks the mutex. // Restore the old maximum if( uiSaveMax) { // flmFreeSavedQueries unlocks the mutex, so we // must relock it to restore the old maximum. f_mutexLock( gv_XFlmSysData.hQueryMutex); gv_XFlmSysData.uiMaxQueries = uiSaveMax; f_mutexUnlock( gv_XFlmSysData.hQueryMutex); } } /**************************************************************************** Desc: Returns statistics that have been collected for a share. Notes: The statistics returned will be the statistics for ALL databases ****************************************************************************/ RCODE FLMAPI F_DbSystem::getStats( XFLM_STATS * pFlmStats) { RCODE rc = NE_XFLM_OK; // Get the statistics f_mutexLock( gv_XFlmSysData.hStatsMutex); rc = flmStatCopy( pFlmStats, &gv_XFlmSysData.Stats); f_mutexUnlock( gv_XFlmSysData.hStatsMutex); if (RC_BAD( rc)) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Frees memory allocated to a FLM_STATS structure ****************************************************************************/ void FLMAPI F_DbSystem::freeStats( XFLM_STATS * pFlmStats) { flmStatFree( pFlmStats); } /**************************************************************************** Desc: Sets the path for all temporary files that come into use within a FLAIM share structure. The share mutex should be locked when settting when called from FlmConfig(). ****************************************************************************/ RCODE FLMAPI F_DbSystem::setTempDir( const char * pszPath) { RCODE rc = NE_XFLM_OK; f_mutexLock( gv_XFlmSysData.hShareMutex); // First, test the path if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->doesFileExist( pszPath))) { goto Exit; } f_strcpy( gv_XFlmSysData.szTempDir, pszPath); gv_XFlmSysData.bTempDirSet = TRUE; Exit: f_mutexUnlock( gv_XFlmSysData.hShareMutex); return( rc); } /**************************************************************************** Desc: Get the temporary directory. ****************************************************************************/ RCODE FLMAPI F_DbSystem::getTempDir( char * pszPath) { RCODE rc = NE_XFLM_OK; f_mutexLock( gv_XFlmSysData.hShareMutex); if( !gv_XFlmSysData.bTempDirSet ) { *pszPath = 0; rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND); goto Exit; } else { f_strcpy( pszPath, gv_XFlmSysData.szTempDir); } Exit: f_mutexUnlock( gv_XFlmSysData.hShareMutex); return( rc); } /**************************************************************************** Desc: Sets the maximum seconds between checkpoints ****************************************************************************/ void FLMAPI F_DbSystem::setCheckpointInterval( FLMUINT uiSeconds) { gv_XFlmSysData.uiMaxCPInterval = FLM_SECS_TO_TIMER_UNITS( uiSeconds); } /**************************************************************************** Desc: Gets the maximum seconds between checkpoints ****************************************************************************/ FLMUINT FLMAPI F_DbSystem::getCheckpointInterval( void) { return( FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.uiMaxCPInterval)); } /**************************************************************************** Desc: Sets the interval for dynamically adjusting the cache limit. ****************************************************************************/ void FLMAPI F_DbSystem::setCacheAdjustInterval( FLMUINT uiSeconds) { gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheAdjustInterval = FLM_SECS_TO_TIMER_UNITS( uiSeconds); } /**************************************************************************** Desc: Sets the interval for dynamically adjusting the cache limit. ****************************************************************************/ FLMUINT FLMAPI F_DbSystem::getCacheAdjustInterval( void) { return( FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheAdjustInterval)); } /**************************************************************************** Desc: Sets the interval for dynamically cleaning out old cache blocks and records ****************************************************************************/ void FLMAPI F_DbSystem::setCacheCleanupInterval( FLMUINT uiSeconds) { gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheCleanupInterval = FLM_SECS_TO_TIMER_UNITS( uiSeconds); } /**************************************************************************** Desc: Gets the interval for dynamically cleaning out old cache blocks and records ****************************************************************************/ FLMUINT FLMAPI F_DbSystem::getCacheCleanupInterval( void) { return( FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheCleanupInterval)); } /**************************************************************************** Desc: Set interval for cleaning up unused structures ****************************************************************************/ void FLMAPI F_DbSystem::setUnusedCleanupInterval( FLMUINT uiSeconds) { gv_XFlmSysData.pGlobalCacheMgr->m_uiUnusedCleanupInterval = FLM_SECS_TO_TIMER_UNITS( uiSeconds); } /**************************************************************************** Desc: Gets the interval for cleaning up unused structures ****************************************************************************/ FLMUINT FLMAPI F_DbSystem::getUnusedCleanupInterval( void) { return( FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.pGlobalCacheMgr->m_uiUnusedCleanupInterval)); } /**************************************************************************** Desc: Set the maximum time for an item to be unused before it is cleaned up ****************************************************************************/ void FLMAPI F_DbSystem::setMaxUnusedTime( FLMUINT uiSeconds) { gv_XFlmSysData.uiMaxUnusedTime = FLM_SECS_TO_TIMER_UNITS( uiSeconds); } /**************************************************************************** Desc: Gets the maximum time for an item to be unused ****************************************************************************/ FLMUINT FLMAPI F_DbSystem::getMaxUnusedTime( void) { return( FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.uiMaxUnusedTime)); } /**************************************************************************** Desc: Sets the logging object to be used for internal status messages ****************************************************************************/ void FLMAPI F_DbSystem::setLogger( IF_LoggerClient * pLogger) { IF_LoggerClient * pOldLogger = NULL; f_mutexLock( gv_XFlmSysData.hLoggerMutex); for( ;;) { // While waiting for the pending message count to go to zero, other // threads may have come in to set different logger objects. To handle // this case, we need to check gv_XFlmSysData.pLogger and release it // if non-NULL. if( gv_XFlmSysData.pLogger) { if( pOldLogger) { pOldLogger->Release(); } pOldLogger = gv_XFlmSysData.pLogger; gv_XFlmSysData.pLogger = NULL; } if( !gv_XFlmSysData.uiPendingLogMessages) { break; } f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); f_sleep( 100); f_mutexLock( gv_XFlmSysData.hLoggerMutex); } if( pOldLogger) { pOldLogger->Release(); } if( (gv_XFlmSysData.pLogger = pLogger) != NULL) { gv_XFlmSysData.pLogger->AddRef(); } f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); } /**************************************************************************** Desc: Enables or disables the use of ESM (if available) ****************************************************************************/ void FLMAPI F_DbSystem::enableExtendedServerMemory( FLMBOOL bEnable) { gv_XFlmSysData.bOkToUseESM = bEnable; } /**************************************************************************** Desc: Returns the state of ESM ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::extendedServerMemoryEnabled( void) { return( gv_XFlmSysData.bOkToUseESM); } /**************************************************************************** Desc: Deactivates open database handles, forcing the database to be (eventually) closed Notes: Passing NULL for the path values will cause all active database handles to be deactivated ****************************************************************************/ void FLMAPI F_DbSystem::deactivateOpenDb( const char * pszDbFileName, const char * pszDataDir) { F_Database * pTmpDatabase; f_mutexLock( gv_XFlmSysData.hShareMutex); if( pszDbFileName) { // Look up the file using findDatabase to see if we have the // file open. May unlock and re-lock the global mutex. if( RC_OK( findDatabase( pszDbFileName, pszDataDir, &pTmpDatabase)) && pTmpDatabase) { pTmpDatabase->setMustCloseFlags( NE_XFLM_OK, TRUE); } } else { if( gv_XFlmSysData.pDatabaseHashTbl) { FLMUINT uiLoop; for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) { pTmpDatabase = (F_Database *)gv_XFlmSysData.pDatabaseHashTbl[ uiLoop].pFirstInBucket; while( pTmpDatabase) { pTmpDatabase->setMustCloseFlags( NE_XFLM_OK, TRUE); pTmpDatabase = pTmpDatabase->m_pNext; } } } } f_mutexUnlock( gv_XFlmSysData.hShareMutex); } /**************************************************************************** Desc: Sets the maximum number of queries to save ****************************************************************************/ void FLMAPI F_DbSystem::setQuerySaveMax( FLMUINT uiMaxToSave) { f_mutexLock( gv_XFlmSysData.hQueryMutex); gv_XFlmSysData.uiMaxQueries = uiMaxToSave; gv_XFlmSysData.bNeedToUnsetMaxQueries = FALSE; flmFreeSavedQueries( TRUE); } /**************************************************************************** Desc: Gets the maximum number of queries to save ****************************************************************************/ FLMUINT FLMAPI F_DbSystem::getQuerySaveMax( void) { return( gv_XFlmSysData.uiMaxQueries); } /**************************************************************************** Desc: Sets the maximum amount of dirty cache allowed ****************************************************************************/ void FLMAPI F_DbSystem::setDirtyCacheLimits( FLMUINT uiMaxDirty, FLMUINT uiLowDirty) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); if( !uiMaxDirty) { gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty = TRUE; gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache = 0; gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = 0; } else { gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty = FALSE; gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache = uiMaxDirty; // Low threshhold must be no higher than maximum! if ((gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = uiLowDirty) > gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache) { gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache; } } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: Gets the maximum amount of dirty cache allowed ****************************************************************************/ void FLMAPI F_DbSystem::getDirtyCacheLimits( FLMUINT * puiMaxDirty, FLMUINT * puiLowDirty) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); if( puiMaxDirty) { *puiMaxDirty = gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache; } if( puiLowDirty) { *puiLowDirty = gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: Returns information about threads owned by the database engine ****************************************************************************/ RCODE FLMAPI F_DbSystem::getThreadInfo( IF_ThreadInfo ** ppThreadInfo) { return( FlmGetThreadInfo( ppThreadInfo)); } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI F_DbSystem::getFileSystem( IF_FileSystem ** ppFileSystem) { FlmGetFileSystem( ppFileSystem); } /**************************************************************************** Desc: Registers for an event ****************************************************************************/ RCODE FLMAPI F_DbSystem::registerForEvent( eEventCategory eCategory, IF_EventClient * pEventClient) { RCODE rc = NE_XFLM_OK; FEVENT * pEvent; // Make sure it is a legal event category to register for. if (eCategory >= XFLM_MAX_EVENT_CATEGORIES) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Allocate an event structure if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( FEVENT)), &pEvent))) { goto Exit; } // Initialize the structure members and linkt to the // list of events off of the event category. pEvent->pEventClient = pEventClient; pEvent->pEventClient->AddRef(); // pEvent->pPrev = NULL; // done by f_calloc above. // Mutex should be locked to link into list. f_mutexLock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); if ((pEvent->pNext = gv_XFlmSysData.EventHdrs [eCategory].pEventCBList) != NULL) { pEvent->pNext->pPrev = pEvent; } gv_XFlmSysData.EventHdrs [eCategory].pEventCBList = pEvent; f_mutexUnlock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); Exit: return( rc); } /**************************************************************************** Desc: De-registers for an event ****************************************************************************/ void FLMAPI F_DbSystem::deregisterForEvent( eEventCategory eCategory, IF_EventClient * pEventClient) { FEVENT * pEvent = gv_XFlmSysData.EventHdrs [eCategory].pEventCBList; // Find the event and remove from the list. If it is not // there, don't worry about it. while (pEvent) { if (pEventClient == pEvent->pEventClient) { flmFreeEvent( pEvent, gv_XFlmSysData.EventHdrs[ eCategory].hMutex, &gv_XFlmSysData.EventHdrs[ eCategory].pEventCBList); break; } pEvent = pEvent->pNext; } } /**************************************************************************** Desc: Returns TRUE if the specified error indicates that the database is corrupt ****************************************************************************/ RCODE FLMAPI F_DbSystem::getNextMetaphone( IF_IStream * pIStream, FLMUINT * puiMetaphone, FLMUINT * puiAltMetaphone) { return( f_getNextMetaphone( pIStream, puiMetaphone, puiAltMetaphone)); } /**************************************************************************** Desc: Returns TRUE if the specified error indicates that the database is corrupt ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::errorIsFileCorrupt( RCODE rc) { FLMBOOL bIsCorrupt = FALSE; switch( rc) { case NE_XFLM_BTREE_ERROR : case NE_XFLM_DATA_ERROR : case NE_XFLM_NOT_FLAIM : case NE_XFLM_BLOCK_CRC : case NE_XFLM_HDR_CRC : case NE_XFLM_INCOMPLETE_LOG : bIsCorrupt = TRUE; break; default : break; } return( bIsCorrupt); } /**************************************************************************** Desc: Increment the database system use count ****************************************************************************/ FLMINT FLMAPI F_DbSystem::AddRef( FLMBOOL bSysDataLocked) { FLMINT iRefCnt; if( !bSysDataLocked) { lockSysData(); } iRefCnt = ++m_refCnt; if( !bSysDataLocked) { unlockSysData(); } LockModule(); return( iRefCnt); } /**************************************************************************** Desc: Decrement the database system use count ****************************************************************************/ FLMINT FLMAPI F_DbSystem::Release(void) { FLMINT iRefCnt; lockSysData(); iRefCnt = --m_refCnt; if( iRefCnt == 0) { flmAssert( !gv_pXFlmDbSystem); unlockSysData(); delete this; } else if( iRefCnt == 1) { flmAssert( this == gv_pXFlmDbSystem); iRefCnt = 0; m_refCnt = 0; gv_pXFlmDbSystem = NULL; unlockSysData(); delete this; UnlockModule(); } else { unlockSysData(); } UnlockModule(); return( iRefCnt); } /**************************************************************************** Desc: Allocates an F_DbSystem object for non-COM applications ****************************************************************************/ FLMEXP RCODE FLMAPI FlmAllocDbSystem( IF_DbSystem ** ppDbSystem) { RCODE rc = NE_XFLM_OK; F_DbSystem * pDbSystem = NULL; flmAssert( ppDbSystem && *ppDbSystem == NULL); lockSysData(); if( !gv_pXFlmDbSystem) { flmAssert( !gv_bToolkitStarted); if( RC_BAD( rc = ftkStartup())) { goto Exit; } gv_bToolkitStarted = TRUE; if( (pDbSystem = f_new F_DbSystem) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pDbSystem->init())) { goto Exit; } gv_pXFlmDbSystem = pDbSystem; pDbSystem = NULL; } gv_pXFlmDbSystem->AddRef( TRUE); *ppDbSystem = gv_pXFlmDbSystem; Exit: if( pDbSystem) { pDbSystem->Release(); } unlockSysData(); return( rc); } /**************************************************************************** Desc: Check for the existence of FLAIM performance tuning file. If found this routine will parse the contents and set the global performance tuning variables. Looks for the following strings within the .ini file cache= # Set a hard memory limit cache= # Set a hard memory limit or dynamically # adjusting limit. # Multiple options may be specified, in # any order, separated by commas. All # are optional. %: # percentage of available or physical memory. AVAIL or TOTAL # percentage is for available physical # memory or total physical memory. NOTE: # these are ignored for a dynamic limit. MIN: # minimum number of bytes MAX: # maximum number of bytes LEAVE: # minimum nmber of bytes to leave DYN or HARD # dynamic or hard limit PRE # preallocate cache - valid only with HARD setting. Examples: cache=DYN,%:75,MIN:16000000 # Dynamic limit of 75% available memory, # minimum of 16 megabytes. cache=HARD,%:75,MIN:16000000 # Hard limit of 75% total physical memory, # minimum of 16 megabytes. cache=HARD,%:75,MIN:16000000,PRE # Hard limit of 75% total physical memory, # minimum of 16 megabytes, preallocated. cache=8000000 # Old style - hard limit of 8 megabytes. cacheadjustinterval= cachecleanupinterval= ****************************************************************************/ #define XFLM_INI_FILE_NAME "_xflm.ini" RCODE F_DbSystem::readIniFile() { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; IF_IniFile * pIniFile = NULL; char szIniFileName [F_PATH_MAX_SIZE]; FLMUINT uiParamValue; FLMUINT uiMaxDirtyCache; FLMUINT uiLowDirtyCache; // Initialize the ini file object... if( RC_BAD( rc = FlmAllocIniFile( &pIniFile))) { goto Exit; } // Lock the ini file mutex f_mutexLock( gv_XFlmSysData.hIniMutex); bMutexLocked = TRUE; if (RC_BAD( rc = flmGetIniFileName( (FLMBYTE *)szIniFileName, F_PATH_MAX_SIZE))) { goto Exit; } if (RC_BAD( rc = pIniFile->read( szIniFileName))) { goto Exit; } // Set FLAIM parameters from the buffer read from the .ini file. setCacheParams( pIniFile); flmGetUintParam( XFLM_INI_CACHE_ADJUST_INTERVAL, XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL, &uiParamValue, pIniFile); setCacheAdjustInterval( uiParamValue); flmGetUintParam( XFLM_INI_CACHE_CLEANUP_INTERVAL, XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL, &uiParamValue, pIniFile); setCacheCleanupInterval( uiParamValue); flmGetUintParam( XFLM_INI_MAX_DIRTY_CACHE, 0, &uiMaxDirtyCache, pIniFile); flmGetUintParam( XFLM_INI_LOW_DIRTY_CACHE, 0, &uiLowDirtyCache, pIniFile); // Use FLAIM's default behavior if uiMaxDirtyCache is zero. FLAIM's // default behavior is to use a maximum value for dirty cache. if (uiMaxDirtyCache) { setDirtyCacheLimits( uiMaxDirtyCache, uiLowDirtyCache); } Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hIniMutex); } pIniFile->Release(); return( rc); } /**************************************************************************** Desc: Given a tag and a value this function will open/create the ini file and replace or insert the tag with it's value. NOTE: This function expects gv_XFlmSysData.hIniMutex to already be locked! ****************************************************************************/ RCODE FLMAPI F_DbSystem::updateIniFile( const char * pszParamName, const char * pszValue) { RCODE rc = NE_XFLM_OK; IF_IniFile * pIniFile = NULL; char szIniFileName [F_PATH_MAX_SIZE]; if( RC_BAD( rc = FlmAllocIniFile( &pIniFile))) { goto Exit; } if (RC_BAD( rc = flmGetIniFileName( (FLMBYTE *)szIniFileName, F_PATH_MAX_SIZE))) { goto Exit; } if (RC_BAD( rc = pIniFile->read( szIniFileName))) { goto Exit; } if (RC_BAD( rc = pIniFile->setParam( pszParamName, pszValue))) { goto Exit; } if (RC_BAD( rc = pIniFile->write())) { goto Exit; } Exit: pIniFile->Release(); return( rc); } /**************************************************************************** Desc: Sets the parameters for controlling cache. ****************************************************************************/ RCODE F_DbSystem::setCacheParams( IF_IniFile * pIniFile) { RCODE rc = NE_XFLM_OK; char * pszCacheParam = NULL; char cChar; char * pszSave; char * pszValueName; FLMBOOL bDynamicCacheAdjust; FLMBOOL bCalcLimitOnAvail = FALSE; FLMUINT uiCachePercent; FLMUINT uiCacheMin; FLMUINT uiCacheMax; FLMUINT uiCacheMinToLeave; FLMBOOL bPreallocated = FALSE; // Set up appropriate defaults. bDynamicCacheAdjust = getDynamicCacheSupported(); if( bDynamicCacheAdjust) { uiCachePercent = XFLM_DEFAULT_CACHE_ADJUST_PERCENT; bCalcLimitOnAvail = TRUE; uiCacheMin = XFLM_DEFAULT_CACHE_ADJUST_MIN; uiCacheMax = XFLM_DEFAULT_CACHE_ADJUST_MAX; uiCacheMinToLeave = 0; } else { uiCachePercent = 0; uiCacheMin = uiCacheMax = XFLM_DEFAULT_CACHE_ADJUST_MIN; uiCacheMinToLeave = 0; } // Extract values from the cache parameter flmGetStringParam( XFLM_INI_CACHE, &pszCacheParam, pIniFile); if ((pszCacheParam != NULL) && (pszCacheParam[0] != '\0')) { // Parse until we hit white space. while (*pszCacheParam && *pszCacheParam != ' ' && *pszCacheParam != '\n' && *pszCacheParam != '\r' && *pszCacheParam != '\t') { // Get the value name. pszValueName = pszCacheParam; while (*pszCacheParam && *pszCacheParam != ' ' && *pszCacheParam != '\n' && *pszCacheParam != '\r' && *pszCacheParam != '\t' && *pszCacheParam != ':' && *pszCacheParam != ',' && *pszCacheParam != ';') { pszCacheParam++; } pszSave = pszCacheParam; cChar = *pszSave; *pszSave = 0; // New options only supported on platforms that have ways // of getting physical memory size and available physical // memory size. if ((f_stricmp( pszValueName, "MAX") == 0) || (f_stricmp( pszValueName, "MAXIMUM") == 0)) { if (cChar == ':') { pszCacheParam++; flmGetNumParam( &pszCacheParam, &uiCacheMax); } } else if ((f_stricmp( pszValueName, "MIN") == 0) || (f_stricmp( pszValueName, "MINIMUM") == 0)) { if (cChar == ':') { pszCacheParam++; flmGetNumParam( &pszCacheParam, &uiCacheMin); } } else if ((f_stricmp( pszValueName, "%") == 0) || (f_stricmp( pszValueName, "PERCENT") == 0)) { if (cChar == ':') { pszCacheParam++; flmGetNumParam( &pszCacheParam, &uiCachePercent); } } else if ((f_stricmp( pszValueName, "DYNAMIC") == 0) || (f_stricmp( pszValueName, "DYN") == 0)) { if (cChar == ':' || cChar == ',' || cChar == ';') { pszCacheParam++; } bDynamicCacheAdjust = TRUE; } else if (f_stricmp( pszValueName, "AVAIL") == 0) { if (cChar == ':' || cChar == ',' || cChar == ';') { pszCacheParam++; } bCalcLimitOnAvail = TRUE; } else if (f_stricmp( pszValueName, "TOTAL") == 0) { if (cChar == ':' || cChar == ',' || cChar == ';') { pszCacheParam++; } bCalcLimitOnAvail = FALSE; } else if (f_stricmp( pszValueName, "LEAVE") == 0) { if (cChar == ':') { pszCacheParam++; flmGetNumParam( &pszCacheParam, &uiCacheMinToLeave); } } else if (f_stricmp( pszValueName, "HARD") == 0) { if (cChar == ':' || cChar == ',' || cChar == ';') { pszCacheParam++; } bDynamicCacheAdjust = FALSE; } else if (f_stricmp( pszValueName, "PRE") == 0) { if (cChar == ':' || cChar == ',' || cChar == ';') { pszCacheParam++; } bPreallocated = TRUE; } else { *pszSave = cChar; // This will handle the old way where they just put in a // number for the byte maximum. bDynamicCacheAdjust = FALSE; uiCachePercent = 0; uiCacheMax = f_atol( pszValueName); // Don't really have to set these, but do it just to // be clean. uiCacheMin = 0; uiCacheMinToLeave = 0; bCalcLimitOnAvail = FALSE; break; } *pszSave = cChar; } } // Set up the stuff for controlling cache - hard limit or dynamically // adjusting limit. if( bDynamicCacheAdjust) { if (RC_BAD( rc = setDynamicMemoryLimit( uiCachePercent, uiCacheMin, uiCacheMax, uiCacheMinToLeave))) { goto Exit; } } else { if( RC_BAD( rc = setHardMemoryLimit( uiCachePercent, bCalcLimitOnAvail, uiCacheMin, uiCacheMax, uiCacheMinToLeave, bPreallocated))) { goto Exit; } } Exit: return rc; } /**************************************************************************** Desc: Gets a string parameter from the .ini file. ****************************************************************************/ FSTATIC void flmGetStringParam( const char * pszParamName, char ** ppszValue, IF_IniFile * pIniFile) { flmAssert( pIniFile); (void)pIniFile->getParam( pszParamName, ppszValue); } /**************************************************************************** Desc: Gets one of the number parameters for setting cache stuff. ****************************************************************************/ FSTATIC void flmGetNumParam( char ** ppszParam, FLMUINT * puiNum) { char * pszTmp = *ppszParam; FLMUINT uiNum = 0; while ((*pszTmp >= '0') && (*pszTmp <= '9')) { uiNum *= 10; uiNum += (FLMUINT)(*pszTmp - '0'); pszTmp++; } // Run past any other junk up to allowed terminators. while (*pszTmp && *pszTmp != ' ' && *pszTmp != '\n' && *pszTmp != '\r' && *pszTmp != '\t' && *pszTmp != ':' && *pszTmp != ',' && *pszTmp != ';') { pszTmp++; } // Skip past trailing colon, comma, or semicolon. if (*pszTmp == ':' || *pszTmp == ',' || *pszTmp == ';') { pszTmp++; } *puiNum = uiNum; *ppszParam = pszTmp; } /**************************************************************************** Desc: Gets a FLMUINT parameter from the .ini file. ****************************************************************************/ FSTATIC void flmGetUintParam( const char * pszParamName, FLMUINT uiDefaultValue, FLMUINT * puiUint, IF_IniFile * pIniFile) { flmAssert( pIniFile); if (!pIniFile->getParam( pszParamName, puiUint)) { *puiUint = uiDefaultValue; } } /**************************************************************************** Desc: Gets a FLMBOOL parameter from the .ini file. ****************************************************************************/ void flmGetBoolParam( const char * pszParamName, FLMBOOL bDefaultValue, FLMBOOL * pbBool, IF_IniFile * pIniFile) { flmAssert( pIniFile); if (!pIniFile->getParam( pszParamName, pbBool)) { *pbBool = bDefaultValue; } } /**************************************************************************** Desc: ****************************************************************************/ FSTATIC RCODE flmGetIniFileName( FLMBYTE * pszIniFileName, FLMUINT uiBufferSz) { RCODE rc = NE_XFLM_OK; FLMUINT uiFileNameLen = 0; IF_DirHdl * pDirHdl = NULL; if (!uiBufferSz) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } // See if we have an environment variable to tell us wher the ini file should be. f_getenv( "XFLM_INI_PATH", pszIniFileName, uiBufferSz, &uiFileNameLen); if( !uiFileNameLen) { // Perhaps we can find a data directory. If there is one, we will // look in there. if( RC_OK( rc = gv_XFlmSysData.pFileSystem->openDir( (char *)".", (char *)"data", &pDirHdl))) { if (RC_OK( rc = pDirHdl->next())) { if (pDirHdl->currentItemIsDir()) { // Directory exists. We will look there. f_strcpy( (char *)pszIniFileName, "data"); } else { goto NoDataDir; } } else { rc = NE_XFLM_OK; goto NoDataDir; } } else { NoDataDir: // Data directory does not exist. We will look in the // current (default) dir. f_strcpy( (char *)pszIniFileName, "."); } } gv_XFlmSysData.pFileSystem->pathAppend( (char *)pszIniFileName, XFLM_INI_FILE_NAME); Exit: if (pDirHdl) { pDirHdl->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openBufferIStream( const char * pucBuffer, FLMUINT uiLength, IF_PosIStream ** ppIStream) { return( FlmOpenBufferIStream( pucBuffer, uiLength, ppIStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openFileIStream( const char * pszPath, IF_PosIStream ** ppIStream) { return( FlmOpenFileIStream( pszPath, ppIStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openMultiFileIStream( const char * pszDirectory, const char * pszBaseName, IF_IStream ** ppIStream) { return( FlmOpenMultiFileIStream( pszDirectory, pszBaseName, ppIStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openBufferedIStream( IF_IStream * pSourceIStream, FLMUINT uiBufferSize, IF_IStream ** ppIStream) { return( FlmOpenBufferedIStream( pSourceIStream, uiBufferSize, ppIStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openUncompressingIStream( IF_IStream * pIStream, IF_IStream ** ppIStream) { return( FlmOpenUncompressingIStream( pIStream, ppIStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openFileOStream( const char * pszFileName, FLMBOOL bTruncateIfExists, IF_OStream ** ppOStream) { return( FlmOpenFileOStream( pszFileName, bTruncateIfExists, ppOStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openMultiFileOStream( const char * pszDirectory, const char * pszBaseName, FLMUINT uiMaxFileSize, FLMBOOL bOkToOverwrite, IF_OStream ** ppStream) { return( FlmOpenMultiFileOStream( pszDirectory, pszBaseName, uiMaxFileSize, bOkToOverwrite, ppStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::removeMultiFileStream( const char * pszDirectory, const char * pszBaseName) { return( FlmRemoveMultiFileStream( pszDirectory, pszBaseName)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openBufferedOStream( IF_OStream * pOStream, FLMUINT uiBufferSize, IF_OStream ** ppOStream) { return( FlmOpenBufferedOStream( pOStream, uiBufferSize, ppOStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openCompressingOStream( IF_OStream * pOStream, IF_OStream ** ppOStream) { return( FlmOpenCompressingOStream( pOStream, ppOStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::writeToOStream( IF_IStream * pIStream, IF_OStream * pOStream) { return( FlmWriteToOStream( pIStream, pOStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openBase64Encoder( IF_IStream * pInputStream, FLMBOOL bInsertLineBreaks, IF_IStream ** ppEncodedStream) { return( FlmOpenBase64EncoderIStream( pInputStream, bInsertLineBreaks, ppEncodedStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::openBase64Decoder( IF_IStream * pInputStream, IF_IStream ** ppDecodedStream) { return( FlmOpenBase64DecoderIStream( pInputStream, ppDecodedStream)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::createIFResultSet( IF_ResultSet ** ifppResultSet) { return( FlmAllocResultSet( ifppResultSet)); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::uniIsUpper( FLMUNICODE uChar) { return( f_uniIsUpper( uChar)); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::uniIsLower( FLMUNICODE uChar) { return( f_uniIsLower( uChar)); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::uniIsAlpha( FLMUNICODE uChar) { return( f_uniIsAlpha( uChar)); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FLMAPI F_DbSystem::uniIsDecimalDigit( FLMUNICODE uChar) { return( f_uniIsDecimalDigit( uChar)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUNICODE FLMAPI F_DbSystem::uniToLower( FLMUNICODE uChar) { return( f_uniToLower( uChar)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::nextUCS2Char( const FLMBYTE ** ppszUTF8, const FLMBYTE * pszEndOfUTF8String, FLMUNICODE * puzChar) { return( f_nextUCS2Char( ppszUTF8, pszEndOfUTF8String, puzChar)); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::numUCS2Chars( const FLMBYTE * pszUTF8, FLMUINT * puiNumChars) { return( f_numUCS2Chars( pszUTF8, puiNumChars)); } /**************************************************************************** Desc: ****************************************************************************/ F_SuperFileClient::F_SuperFileClient() { m_pszCFileName = NULL; m_pszDataFileBaseName = NULL; m_uiExtOffset = 0; m_uiDataExtOffset = 0; m_uiMaxFileSize = 0; } /**************************************************************************** Desc: ****************************************************************************/ F_SuperFileClient::~F_SuperFileClient() { if( m_pszCFileName) { f_free( &m_pszCFileName); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_SuperFileClient::setup( const char * pszCFileName, const char * pszDataDir, FLMUINT uiMaxFileSize) { RCODE rc = NE_XFLM_OK; FLMUINT uiNameLen; FLMUINT uiDataNameLen; char szDir[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; if( !pszCFileName && *pszCFileName == 0) { rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); goto Exit; } uiNameLen = f_strlen( pszCFileName); if (pszDataDir && *pszDataDir) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( pszCFileName, szDir, szBaseName))) { goto Exit; } f_strcpy( szDir, pszDataDir); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( szDir, szBaseName))) { goto Exit; } uiDataNameLen = f_strlen( szDir); if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1), &m_pszCFileName))) { goto Exit; } f_memcpy( m_pszCFileName, pszCFileName, uiNameLen + 1); m_pszDataFileBaseName = m_pszCFileName + uiNameLen + 1; flmGetDbBasePath( m_pszDataFileBaseName, szDir, &m_uiDataExtOffset); m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset); } else { if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszCFileName))) { goto Exit; } f_memcpy( m_pszCFileName, pszCFileName, uiNameLen + 1); m_pszDataFileBaseName = m_pszCFileName + uiNameLen + 1; flmGetDbBasePath( m_pszDataFileBaseName, m_pszCFileName, &m_uiDataExtOffset); m_uiExtOffset = m_uiDataExtOffset; } m_uiMaxFileSize = uiMaxFileSize; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_SuperFileClient::getFileNumber( FLMUINT uiBlockAddr) { return( FSGetFileNumber( uiBlockAddr)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_SuperFileClient::getFileOffset( FLMUINT uiBlockAddr) { return( FSGetFileOffset( uiBlockAddr)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT FLMAPI F_SuperFileClient::getBlockAddress( FLMUINT uiFileNumber, FLMUINT uiFileOffset) { return( FSBlkAddress( uiFileNumber, uiFileOffset)); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT64 FLMAPI F_SuperFileClient::getMaxFileSize( void) { f_assert( m_uiMaxFileSize); return( m_uiMaxFileSize); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_SuperFileClient::getFilePath( FLMUINT uiFileNumber, char * pszPath) { RCODE rc = NE_XFLM_OK; FLMUINT uiExtOffset; if( !uiFileNumber) { f_strcpy( pszPath, m_pszCFileName); goto Exit; } if( uiFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER) { f_memcpy( pszPath, m_pszDataFileBaseName, m_uiDataExtOffset); uiExtOffset = m_uiDataExtOffset; } else { f_memcpy( pszPath, m_pszCFileName, m_uiExtOffset); uiExtOffset = m_uiExtOffset; } // Modify the file's extension. bldSuperFileExtension( uiFileNumber, &pszPath[ uiExtOffset]); Exit: return( rc); } /**************************************************************************** Desc: Generates a file name given a super file number. Adds ".xx" to pFileExtension. Use lower case characters. Notes: This is a base 24 alphanumeric value where { a, b, c, d, e, f, i, l, o, r, u, v } values are removed. ****************************************************************************/ void F_SuperFileClient::bldSuperFileExtension( FLMUINT uiFileNum, char * pszFileExtension) { char ucLetter; if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER - 1536) { // No additional letter - File numbers 1 to 511 // This is just like pre-4.3 numbering. ucLetter = 0; } else if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER - 1024) { // File numbers 512 to 1023 ucLetter = 'r'; } else if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER - 512) { // File numbers 1024 to 1535 ucLetter = 's'; } else if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER) { // File numbers 1536 to 2047 ucLetter = 't'; } else if (uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER - 1536) { // File numbers 2048 to 2559 ucLetter = 'v'; } else if (uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER - 1024) { // File numbers 2560 to 3071 ucLetter = 'w'; } else if (uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER - 512) { // File numbers 3072 to 3583 ucLetter = 'x'; } else { flmAssert( uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER); // File numbers 3584 to 4095 ucLetter = 'z'; } *pszFileExtension++ = '.'; *pszFileExtension++ = (char)(f_getBase24DigitChar( (FLMBYTE)((uiFileNum & 511) / 24))); *pszFileExtension++ = (char)(f_getBase24DigitChar( (FLMBYTE)((uiFileNum & 511) % 24))); *pszFileExtension++ = ucLetter; *pszFileExtension = 0; } libxflaim-5.1.969/src/fqeval.cpp0000644000175000017500000012276410511001742020022 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the methods for doing evaluation of query expressions. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqeval.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "fquery.h" FSTATIC RCODE fqApproxCompare( FQVALUE * pLValue, FQVALUE * pRValue, FLMINT * piResult); FSTATIC RCODE fqCompareBinary( IF_OperandComparer * pOpComparer, FQVALUE * pLValue, FQVALUE * pRValue, FLMINT * piResult); FSTATIC RCODE fqCompareText( IF_OperandComparer * pOpComparer, FQVALUE * pLValue, FQVALUE * pRValue, FLMUINT uiCompareRules, FLMBOOL bOpIsMatch, FLMUINT uiLanguage, FLMINT * piResult); FSTATIC void fqOpUUBitAND( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUBitOR( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUBitXOR( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUSMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSSMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSUMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUSDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSSDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSUDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUSMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSSMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSUMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUSPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSSPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSUPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUUMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpUSMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSSMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FSTATIC void fqOpSUMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); typedef void FQ_OPERATION( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult); FQ_OPERATION * FQ_ArithOpTable[ ((XFLM_LAST_ARITH_OP - XFLM_FIRST_ARITH_OP) + 1) * 4 ] = { /* U = Unsigned S = Signed U + U U + S S + U S + S */ /* BITAND */ fqOpUUBitAND, fqOpUUBitAND, fqOpUUBitAND, fqOpUUBitAND, /* BITOR */ fqOpUUBitOR, fqOpUUBitOR, fqOpUUBitOR, fqOpUUBitOR, /* BITXOR */ fqOpUUBitXOR, fqOpUUBitXOR, fqOpUUBitXOR, fqOpUUBitXOR, /* MULT */ fqOpUUMult, fqOpUSMult, fqOpSUMult, fqOpSSMult, /* DIV */ fqOpUUDiv, fqOpUSDiv, fqOpSUDiv, fqOpSSDiv, /* MOD */ fqOpUUMod, fqOpUSMod, fqOpSUMod, fqOpSSMod, /* PLUS */ fqOpUUPlus, fqOpUSPlus, fqOpSUPlus, fqOpSSPlus, /* MINUS */ fqOpUUMinus, fqOpUSMinus, fqOpSUMinus, fqOpSSMinus }; /*************************************************************************** Desc: Returns a 64-bit unsigned integer ***************************************************************************/ FINLINE FLMUINT64 fqGetUInt64( FQVALUE * pValue) { if (pValue->eValType == XFLM_UINT_VAL) { return( (FLMUINT64)pValue->val.uiVal); } else if( pValue->eValType == XFLM_UINT64_VAL) { return( pValue->val.ui64Val); } else if( pValue->eValType == XFLM_INT64_VAL) { if( pValue->val.i64Val >= 0) { return( (FLMUINT64)pValue->val.i64Val); } } else if( pValue->eValType == XFLM_INT_VAL) { if( pValue->val.iVal >= 0) { return( (FLMUINT64)pValue->val.iVal); } } flmAssert( 0); return( 0); } /*************************************************************************** Desc: Returns a 64-bit signed integer ***************************************************************************/ FINLINE FLMINT64 fqGetInt64( FQVALUE * pValue) { if (pValue->eValType == XFLM_INT_VAL) { return( (FLMINT64)pValue->val.iVal); } else if( pValue->eValType == XFLM_INT64_VAL) { return( pValue->val.i64Val); } else if( pValue->eValType == XFLM_UINT_VAL) { return( (FLMINT64)pValue->val.uiVal); } else if( pValue->eValType == XFLM_UINT64_VAL) { if( pValue->val.ui64Val <= (FLMUINT64)FLM_MAX_INT64) { return( (FLMINT64)pValue->val.ui64Val); } } flmAssert( 0); return( 0); } /*************************************************************************** Desc: Performs the bit and operation ***************************************************************************/ FSTATIC void fqOpUUBitAND( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.uiVal = pLValue->val.uiVal & pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) & fqGetUInt64( pRValue); pResult->eValType = XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the bit or operation ***************************************************************************/ FSTATIC void fqOpUUBitOR( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.uiVal = pLValue->val.uiVal | pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) | fqGetUInt64( pRValue); pResult->eValType = XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the bit xor operation ***************************************************************************/ FSTATIC void fqOpUUBitXOR( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.uiVal = pLValue->val.uiVal ^ pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) ^ fqGetUInt64( pRValue); pResult->eValType = XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpUUMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.uiVal = pLValue->val.uiVal * pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) * fqGetUInt64( pRValue); pResult->eValType = XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpUSMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.iVal = (FLMINT)pLValue->val.uiVal * pRValue->val.iVal; pResult->eValType = XFLM_INT_VAL; } else { pResult->val.i64Val = (FLMINT64) fqGetUInt64( pLValue) * fqGetInt64( pRValue); pResult->eValType = XFLM_INT64_VAL; } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpSSMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.iVal = pLValue->val.iVal * pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } else { pResult->val.i64Val = (FLMINT64)(fqGetInt64( pLValue) * fqGetInt64( pRValue)); pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs the multiply operation ***************************************************************************/ FSTATIC void fqOpSUMult( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.iVal = pLValue->val.iVal * (FLMINT)pRValue->val.uiVal; pResult->eValType = XFLM_INT_VAL; } else { pResult->val.i64Val = (FLMINT64) (fqGetInt64( pLValue) * fqGetUInt64( pRValue)); pResult->eValType = XFLM_INT64_VAL; } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpUUDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.uiVal) { pResult->val.uiVal = pLValue->val.uiVal / pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( ui64RValue) { pResult->val.ui64Val = ui64LValue / ui64RValue; pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpUSDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.iVal) { pResult->val.iVal = pLValue->val.uiVal / pRValue->val.iVal; pResult->eValType = XFLM_INT_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( i64RValue) { pResult->val.i64Val = ui64LValue / i64RValue; pResult->eValType = XFLM_INT64_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpSSDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.iVal) { pResult->val.iVal = pLValue->val.iVal / pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( i64RValue) { pResult->val.i64Val = i64LValue / i64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the divide operation ***************************************************************************/ FSTATIC void fqOpSUDiv( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.uiVal) { pResult->val.iVal = pLValue->val.iVal / pRValue->val.uiVal; pResult->eValType = XFLM_INT_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( ui64RValue) { pResult->val.i64Val = i64LValue / ui64RValue; pResult->eValType = XFLM_INT64_VAL; } else { pResult->val.uiVal = 0; // Divide by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpUUMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.uiVal) { pResult->val.uiVal = pLValue->val.uiVal % pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( ui64RValue) { pResult->val.ui64Val = ui64LValue % ui64RValue; pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpUSMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.iVal) { pResult->val.iVal = pLValue->val.uiVal % pRValue->val.iVal; pResult->eValType = XFLM_INT_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( i64RValue) { pResult->val.i64Val = ui64LValue % i64RValue; pResult->eValType = XFLM_INT64_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpSSMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.iVal) { pResult->val.iVal = pLValue->val.iVal % pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( i64RValue) { pResult->val.i64Val = i64LValue % i64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs the modulo operation ***************************************************************************/ FSTATIC void fqOpSUMod( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.uiVal) { pResult->val.iVal = pLValue->val.iVal % pRValue->val.uiVal; pResult->eValType = XFLM_INT_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( ui64RValue) { pResult->val.i64Val = i64LValue % ui64RValue; pResult->eValType = XFLM_INT64_VAL; } else { pResult->val.uiVal = 0; // MOD by ZERO case. pResult->eValType = XFLM_MISSING_VAL; } } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpUUPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.uiVal = pLValue->val.uiVal + pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.ui64Val = fqGetUInt64( pLValue) + fqGetUInt64( pRValue); pResult->eValType = XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpUSPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( (pRValue->val.iVal >= 0) || (pLValue->val.uiVal > gv_uiMaxSignedIntVal)) { pResult->val.uiVal = pLValue->val.uiVal + (FLMUINT)pRValue->val.iVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.iVal = (FLMINT)pLValue->val.uiVal + pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( (i64RValue >= 0) || (ui64LValue > gv_ui64MaxSignedIntVal)) { pResult->val.ui64Val = ui64LValue + (FLMUINT64)i64RValue; pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.i64Val = (FLMINT64)ui64LValue + i64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpSSPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { pResult->val.iVal = pLValue->val.iVal + pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } else { pResult->val.i64Val = fqGetInt64( pLValue) + fqGetInt64( pRValue); pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } /*************************************************************************** Desc: Performs an addition operation ***************************************************************************/ FSTATIC void fqOpSUPlus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( (pLValue->val.iVal >= 0) || (pRValue->val.uiVal > gv_uiMaxSignedIntVal)) { pResult->val.uiVal = (FLMUINT)pLValue->val.iVal + pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.iVal = pLValue->val.iVal + (FLMINT)pRValue->val.uiVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( (i64LValue >= 0) || (ui64RValue > gv_ui64MaxSignedIntVal)) { pResult->val.ui64Val = (FLMUINT64)i64LValue + ui64RValue; pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.i64Val = i64LValue + (FLMINT64)ui64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpUUMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pLValue->val.uiVal >= pRValue->val.uiVal) { pResult->val.uiVal = pLValue->val.uiVal - pRValue->val.uiVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.iVal = (FLMINT)(pLValue->val.uiVal - pRValue->val.uiVal); pResult->eValType = XFLM_INT_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( ui64LValue >= ui64RValue) { pResult->val.ui64Val = ui64LValue - ui64RValue; pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.i64Val = (FLMINT64)(ui64LValue - ui64RValue); pResult->eValType = XFLM_INT64_VAL; } } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpUSMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.iVal < 0) { pResult->val.uiVal = pLValue->val.uiVal - pRValue->val.iVal; pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.iVal = (FLMINT)pLValue->val.uiVal - pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } } else { FLMUINT64 ui64LValue = fqGetUInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( i64RValue < 0) { pResult->val.ui64Val = ui64LValue - i64RValue; pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.i64Val = (FLMINT64)ui64LValue - i64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpSSMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if(( pLValue->val.iVal > 0) && ( pRValue->val.iVal < 0)) { pResult->val.uiVal = (FLMUINT)(pLValue->val.iVal - pRValue->val.iVal); pResult->eValType = XFLM_UINT_VAL; } else { pResult->val.iVal = pLValue->val.iVal - pRValue->val.iVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMINT64 i64RValue = fqGetInt64( pRValue); if( (i64LValue > 0) && (i64RValue < 0)) { pResult->val.ui64Val = (FLMUINT64)( i64LValue - i64RValue); pResult->eValType = XFLM_UINT64_VAL; } else { pResult->val.i64Val = i64LValue - i64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } } /*************************************************************************** Desc: Performs a subtraction operation ***************************************************************************/ FSTATIC void fqOpSUMinus( FQVALUE * pLValue, FQVALUE * pRValue, FQVALUE * pResult) { if( isNativeNum( pLValue->eValType) && isNativeNum( pRValue->eValType)) { if( pRValue->val.uiVal > gv_uiMaxSignedIntVal) { pResult->val.iVal = (pLValue->val.iVal - gv_uiMaxSignedIntVal) - (FLMINT)(pRValue->val.uiVal - gv_uiMaxSignedIntVal); pResult->eValType = XFLM_INT_VAL; } else { pResult->val.iVal = pLValue->val.iVal - (FLMINT)pRValue->val.uiVal; pResult->eValType = (pResult->val.iVal < 0) ? XFLM_INT_VAL : XFLM_UINT_VAL; } } else { FLMINT64 i64LValue = fqGetInt64( pLValue); FLMUINT64 ui64RValue = fqGetUInt64( pRValue); if( ui64RValue > gv_ui64MaxSignedIntVal) { pResult->val.i64Val = (i64LValue - gv_ui64MaxSignedIntVal) - (FLMINT64)(ui64RValue - gv_ui64MaxSignedIntVal); pResult->eValType = XFLM_INT64_VAL; } else { pResult->val.i64Val = i64LValue - (FLMINT64)ui64RValue; pResult->eValType = (pResult->val.i64Val < 0) ? XFLM_INT64_VAL : XFLM_UINT64_VAL; } } } /*************************************************************************** Desc: Compare two entire strings. ****************************************************************************/ FSTATIC RCODE fqCompareText( IF_OperandComparer * pOpComparer, FQVALUE * pLValue, FQVALUE * pRValue, FLMUINT uiCompareRules, FLMBOOL bOpIsMatch, FLMUINT uiLanguage, FLMINT * piResult) { RCODE rc = NE_XFLM_OK; IF_BufferIStream * pBufferLStream = NULL; IF_PosIStream * pLStream; IF_BufferIStream * pBufferRStream = NULL; IF_PosIStream * pRStream; // Types must be text if (pLValue->eValType != XFLM_UTF8_VAL || pRValue->eValType != XFLM_UTF8_VAL) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Open the streams if( !(pLValue->uiFlags & VAL_IS_STREAM)) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferLStream))) { goto Exit; } if (RC_BAD( rc = pBufferLStream->openStream( (const char *)pLValue->val.pucBuf, pLValue->uiDataLen))) { goto Exit; } pLStream = pBufferLStream; } else { pLStream = pLValue->val.pIStream; } if( !(pRValue->uiFlags & VAL_IS_STREAM)) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferRStream))) { goto Exit; } if( RC_BAD( rc = pBufferRStream->openStream( (const char *)pRValue->val.pucBuf, pRValue->uiDataLen))) { goto Exit; } pRStream = pBufferRStream; } else { pRStream = pRValue->val.pIStream; } if (pOpComparer) { rc = pOpComparer->compare( pLStream, pRStream, piResult); goto Exit; } if( RC_BAD( rc = f_compareUTF8Streams( pLStream, (bOpIsMatch && (pLValue->uiFlags & VAL_IS_CONSTANT)) ? TRUE : FALSE, pRStream, (bOpIsMatch && (pRValue->uiFlags & VAL_IS_CONSTANT)) ? TRUE : FALSE, uiCompareRules, uiLanguage, piResult))) { goto Exit; } Exit: if( pBufferLStream) { pBufferLStream->Release(); } if( pBufferRStream) { pBufferRStream->Release(); } return( rc); } /*************************************************************************** Desc: Approximate compare - only works for strings right now. ****************************************************************************/ FSTATIC RCODE fqApproxCompare( FQVALUE * pLValue, FQVALUE * pRValue, FLMINT * piResult) { RCODE rc = NE_XFLM_OK; FLMUINT uiLMeta; FLMUINT uiRMeta; FLMUINT64 ui64StartPos; IF_BufferIStream * pBufferLStream = NULL; IF_PosIStream * pLStream; IF_BufferIStream * pBufferRStream = NULL; IF_PosIStream * pRStream; // Types must be text if (pLValue->eValType != XFLM_UTF8_VAL || pRValue->eValType != XFLM_UTF8_VAL) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Open the streams if (!(pLValue->uiFlags & VAL_IS_STREAM)) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferLStream))) { goto Exit; } if (RC_BAD( rc = pBufferLStream->openStream( (const char *)pLValue->val.pucBuf, pLValue->uiDataLen))) { goto Exit; } pLStream = pBufferLStream; } else { pLStream = pLValue->val.pIStream; } if (!(pRValue->uiFlags & VAL_IS_STREAM)) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferRStream))) { goto Exit; } if( RC_BAD( rc = pBufferRStream->openStream( (const char *)pRValue->val.pucBuf, pRValue->uiDataLen))) { goto Exit; } pRStream = pBufferRStream; } else { pRStream = pRValue->val.pIStream; } if ((pLValue->uiFlags & VAL_IS_CONSTANT) || !(pRValue->uiFlags & VAL_IS_CONSTANT)) { for( ;;) { if( RC_BAD( rc = f_getNextMetaphone( pLStream, &uiLMeta))) { if( rc == NE_XFLM_EOF_HIT) { *piResult = 0; rc = NE_XFLM_OK; } goto Exit; } ui64StartPos = pRStream->getCurrPosition(); for( ;;) { if( RC_BAD( rc = f_getNextMetaphone( pRStream, &uiRMeta))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; *piResult = -1; } goto Exit; } if( uiLMeta == uiRMeta) { break; } } if( RC_BAD( rc = pRStream->positionTo( ui64StartPos))) { goto Exit; } } } else { for( ;;) { if( RC_BAD( rc = f_getNextMetaphone( pRStream, &uiRMeta))) { if( rc == NE_XFLM_EOF_HIT) { *piResult = 0; rc = NE_XFLM_OK; } goto Exit; } ui64StartPos = pLStream->getCurrPosition(); for( ;;) { if( RC_BAD( rc = f_getNextMetaphone( pLStream, &uiLMeta))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; *piResult = 1; } goto Exit; } if( uiLMeta == uiRMeta) { break; } } if( RC_BAD( rc = pLStream->positionTo( ui64StartPos))) { goto Exit; } } } Exit: if( pBufferLStream) { pBufferLStream->Release(); } if( pBufferRStream) { pBufferRStream->Release(); } return( rc); } /*************************************************************************** Desc: Performs binary comparison on two streams - may be text or binary, it really doesn't matter. Returns XFLM_TRUE or XFLM_FALSE. ***************************************************************************/ FSTATIC RCODE fqCompareBinary( IF_OperandComparer * pOpComparer, FQVALUE * pLValue, FQVALUE * pRValue, FLMINT * piResult) { RCODE rc = NE_XFLM_OK; IF_BufferIStream * pBufferLStream = NULL; IF_PosIStream * pLStream; IF_BufferIStream * pBufferRStream = NULL; IF_PosIStream * pRStream; FLMBYTE ucLByte; FLMBYTE ucRByte; FLMUINT uiOffset = 0; FLMBOOL bLEmpty = FALSE; *piResult = 0; // Types must be binary if ( pLValue->eValType != XFLM_BINARY_VAL || pRValue->eValType != XFLM_BINARY_VAL) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Open the streams if( !(pLValue->uiFlags & VAL_IS_STREAM)) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferLStream))) { goto Exit; } if (RC_BAD( rc = pBufferLStream->openStream( (const char *)pLValue->val.pucBuf, pLValue->uiDataLen))) { goto Exit; } pLStream = pBufferLStream; } else { pLStream = pLValue->val.pIStream; } if( !(pRValue->uiFlags & VAL_IS_STREAM)) { if( RC_BAD( rc = pBufferRStream->openStream( (const char *)pRValue->val.pucBuf, pRValue->uiDataLen))) { goto Exit; } pRStream = pBufferRStream; } else { pRStream = pRValue->val.pIStream; } if (pOpComparer) { rc = pOpComparer->compare( pLStream, pRStream, piResult); goto Exit; } for (;;) { if (RC_BAD( rc = flmReadStorageAsBinary( pLStream, &ucLByte, 1, uiOffset, NULL))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; bLEmpty = TRUE; } else { goto Exit; } } if (RC_BAD( rc = flmReadStorageAsBinary( pRStream, &ucRByte, 1, uiOffset, NULL))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; if( bLEmpty) { *piResult = 0; } else { *piResult = 1; } } goto Exit; } else if( bLEmpty) { *piResult = -1; goto Exit; } if( ucLByte != ucRByte) { *piResult = ucLByte < ucRByte ? -1 : 1; goto Exit; } uiOffset++; } Exit: if( pBufferLStream) { pBufferLStream->Release(); } if( pBufferRStream) { pBufferRStream->Release(); } return( rc); } /*************************************************************************** Desc: Compare two values. This routine assumes that pValue1 and pValue2 are non-null. ***************************************************************************/ RCODE fqCompare( FQVALUE * pValue1, FQVALUE * pValue2, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FLMUINT uiLanguage, FLMINT * piCmp) { RCODE rc = NE_XFLM_OK; // We have already called fqCanCompare, so no need to do it here switch (pValue1->eValType) { case XFLM_BOOL_VAL: *piCmp = pValue1->val.eBool > pValue2->val.eBool ? 1 : pValue1->val.eBool < pValue2->val.eBool ? -1 : 0; break; case XFLM_UINT_VAL: switch (pValue2->eValType) { case XFLM_UINT_VAL: *piCmp = pValue1->val.uiVal > pValue2->val.uiVal ? 1 : pValue1->val.uiVal < pValue2->val.uiVal ? -1 : 0; break; case XFLM_UINT64_VAL: *piCmp = (FLMUINT64)pValue1->val.uiVal > pValue2->val.ui64Val ? 1 : (FLMUINT64)pValue1->val.uiVal < pValue2->val.ui64Val ? -1 : 0; break; case XFLM_INT_VAL: *piCmp = pValue2->val.iVal < 0 || pValue1->val.uiVal > (FLMUINT)pValue2->val.iVal ? 1 : pValue1->val.uiVal < (FLMUINT)pValue2->val.iVal ? -1 : 0; break; case XFLM_INT64_VAL: *piCmp = pValue2->val.i64Val < 0 || (FLMUINT64)pValue1->val.uiVal > (FLMUINT64)pValue2->val.i64Val ? 1 : (FLMUINT64)pValue1->val.uiVal < (FLMUINT64)pValue2->val.i64Val ? -1 : 0; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } break; case XFLM_UINT64_VAL: switch (pValue2->eValType) { case XFLM_UINT_VAL: *piCmp = pValue1->val.ui64Val > (FLMUINT64)pValue2->val.uiVal ? 1 : pValue1->val.ui64Val < (FLMUINT64)pValue2->val.uiVal ? -1 : 0; break; case XFLM_UINT64_VAL: *piCmp = pValue1->val.ui64Val > pValue2->val.ui64Val ? 1 : pValue1->val.ui64Val < pValue2->val.ui64Val ? -1 : 0; break; case XFLM_INT_VAL: *piCmp = pValue2->val.iVal < 0 || pValue1->val.ui64Val > (FLMUINT64)pValue2->val.iVal ? 1 : pValue1->val.ui64Val < (FLMUINT64)pValue2->val.iVal ? -1 : 0; break; case XFLM_INT64_VAL: *piCmp = pValue2->val.i64Val < 0 || pValue1->val.ui64Val > (FLMUINT64)pValue2->val.i64Val ? 1 : pValue1->val.ui64Val < (FLMUINT64)pValue2->val.i64Val ? -1 : 0; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } break; case XFLM_INT_VAL: switch (pValue2->eValType) { case XFLM_UINT_VAL: *piCmp = pValue1->val.iVal < 0 || (FLMUINT)pValue1->val.iVal < pValue2->val.uiVal ? -1 : (FLMUINT)pValue1->val.iVal > pValue2->val.uiVal ? 1 : 0; break; case XFLM_UINT64_VAL: *piCmp = pValue1->val.iVal < 0 || (FLMUINT64)pValue1->val.iVal < pValue2->val.ui64Val ? -1 : (FLMUINT64)pValue1->val.iVal > pValue2->val.ui64Val ? 1 : 0; break; case XFLM_INT_VAL: *piCmp = pValue1->val.iVal < pValue2->val.iVal ? -1 : pValue1->val.iVal > pValue2->val.iVal ? 1 : 0; break; case XFLM_INT64_VAL: *piCmp = (FLMINT64)pValue1->val.iVal < pValue2->val.i64Val ? -1 : (FLMINT64)pValue1->val.iVal > pValue2->val.i64Val ? 1 : 0; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } break; case XFLM_INT64_VAL: switch (pValue2->eValType) { case XFLM_UINT_VAL: *piCmp = pValue1->val.i64Val < 0 || (FLMUINT64)pValue1->val.i64Val < (FLMUINT64)pValue2->val.uiVal ? -1 : (FLMUINT64)pValue1->val.i64Val > (FLMUINT64)pValue2->val.uiVal ? 1 : 0; break; case XFLM_UINT64_VAL: *piCmp = pValue1->val.i64Val < 0 || (FLMUINT64)pValue1->val.i64Val < pValue2->val.ui64Val ? -1 : (FLMUINT64)pValue1->val.i64Val > pValue2->val.ui64Val ? 1 : 0; break; case XFLM_INT_VAL: *piCmp = pValue1->val.i64Val < (FLMINT64)pValue2->val.iVal ? -1 : pValue1->val.i64Val > (FLMINT64)pValue2->val.iVal ? 1 : 0; break; case XFLM_INT64_VAL: *piCmp = pValue1->val.i64Val < pValue2->val.i64Val ? -1 : pValue1->val.i64Val > pValue2->val.i64Val ? 1 : 0; break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } break; case XFLM_BINARY_VAL: if (RC_BAD( rc = fqCompareBinary( pOpComparer, pValue1, pValue2, piCmp))) { goto Exit; } break; case XFLM_UTF8_VAL: if (RC_BAD( rc = fqCompareText( pOpComparer, pValue1, pValue2, uiCompareRules, FALSE, uiLanguage, piCmp))) { goto Exit; } break; default: break; } Exit: return( rc); } /*************************************************************************** Desc: Do a comparison operator. ***************************************************************************/ RCODE fqCompareOperands( FLMUINT uiLanguage, FQVALUE * pLValue, FQVALUE * pRValue, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FLMBOOL bNotted, XFlmBoolType * peBool) { RCODE rc = NE_XFLM_OK; FLMINT iCmp; if (!pLValue || pLValue->eValType == XFLM_MISSING_VAL || !pRValue || pRValue->eValType == XFLM_MISSING_VAL || !fqCanCompare( pLValue, pRValue)) { *peBool = (bNotted ? XFLM_TRUE : XFLM_FALSE); } // At this point, both operands are known to be present and are of // types that can be compared. The comparison // will therefore be performed according to the // operator specified. else { switch (eOperator) { case XFLM_EQ_OP: case XFLM_NE_OP: if (pLValue->eValType == XFLM_UTF8_VAL || pRValue->eValType == XFLM_UTF8_VAL) { if (RC_BAD( rc = fqCompareText( pOpComparer, pLValue, pRValue, uiCompareRules, TRUE, uiLanguage, &iCmp))) { goto Exit; } } else { if (RC_BAD( rc = fqCompare( pLValue, pRValue, uiCompareRules, pOpComparer, uiLanguage, &iCmp))) { goto Exit; } } if (eOperator == XFLM_EQ_OP) { *peBool = (iCmp == 0 ? XFLM_TRUE : XFLM_FALSE); } else { *peBool = (iCmp != 0 ? XFLM_TRUE : XFLM_FALSE); } break; case XFLM_APPROX_EQ_OP: if (RC_BAD( rc = fqApproxCompare( pLValue, pRValue, &iCmp))) { goto Exit; } *peBool = (iCmp == 0 ? XFLM_TRUE : XFLM_FALSE); break; case XFLM_LT_OP: if (RC_BAD( rc = fqCompare( pLValue, pRValue, uiCompareRules, pOpComparer, uiLanguage, &iCmp))) { goto Exit; } *peBool = (iCmp < 0 ? XFLM_TRUE : XFLM_FALSE); break; case XFLM_LE_OP: if (RC_BAD( rc = fqCompare( pLValue, pRValue, uiCompareRules, pOpComparer, uiLanguage, &iCmp))) { goto Exit; } *peBool = (iCmp <= 0 ? XFLM_TRUE : XFLM_FALSE); break; case XFLM_GT_OP: if (RC_BAD( rc = fqCompare( pLValue, pRValue, uiCompareRules, pOpComparer, uiLanguage, &iCmp))) { goto Exit; } *peBool = (iCmp > 0 ? XFLM_TRUE : XFLM_FALSE); break; case XFLM_GE_OP: if (RC_BAD( rc = fqCompare( pLValue, pRValue, uiCompareRules, pOpComparer, uiLanguage, &iCmp))) { goto Exit; } *peBool = (iCmp >= 0 ? XFLM_TRUE : XFLM_FALSE); break; default: *peBool = XFLM_UNKNOWN; rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Do an arithmetic operator. ***************************************************************************/ RCODE fqArithmeticOperator( FQVALUE * pLValue, FQVALUE * pRValue, eQueryOperators eOperator, FQVALUE * pResult) { RCODE rc = NE_XFLM_OK; FQ_OPERATION * fnOp; FLMUINT uiOffset = 0; if( !isArithOp( eOperator)) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if (pLValue->eValType == XFLM_MISSING_VAL || pRValue->eValType == XFLM_MISSING_VAL) { pResult->eValType = XFLM_MISSING_VAL; goto Exit; } if( isUnsigned( pLValue)) { if( isUnsigned( pRValue)) { uiOffset = 0; } else if( isSigned( pRValue)) { uiOffset = 1; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } else if( isSigned( pLValue)) { if( isUnsigned( pRValue)) { uiOffset = 2; } else if( isSigned( pRValue)) { uiOffset = 3; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } fnOp = FQ_ArithOpTable[ ((((FLMUINT)eOperator) - XFLM_FIRST_ARITH_OP) * 4) + uiOffset]; fnOp( pLValue, pRValue, pResult); Exit: return( rc); } libxflaim-5.1.969/src/fdbrenam.cpp0000644000175000017500000002550110511001742020311 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the F_DbSystem::dbRename method. // // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbrenam.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" typedef struct { char szSrcFileName [F_PATH_MAX_SIZE]; char szDstFileName [F_PATH_MAX_SIZE]; } DB_RENAME_INFO, * DB_RENAME_INFO_p; typedef struct DBRenameInfoTag { DB_RENAME_INFO Info; DBRenameInfoTag * pNext; } DBRenameInfo; FSTATIC RCODE flmRenameFile( const char * pszSrcFileName, const char * pszDstFileName, FLMBOOL bOverwriteDestOk, FLMBOOL bPathNotFoundOk, DBRenameInfo ** ppRenameList, FLMBOOL * pbFileFound, IF_DbRenameStatus * ifpStatus); /**************************************************************************** Desc: Renames all files of a database ****************************************************************************/ RCODE F_DbSystem::dbRename( const char * pszDbName, // [IN] Database to be renamed. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory of database. NULL can be // passed to indicate that the log files are located // in the same directory as the other database files. const char * pszNewDbName, // [IN] New name to be given to the database. May be // the short name only, or include a directory. If it // includes a directory, it must be the same directory // as the directory given in pszDbName. FLMBOOL bOverwriteDestOk, // [IN] Ok to overwrite existing file with rename? IF_DbRenameStatus * ifpStatus) // [IN] Status callback function. { RCODE rc = NE_XFLM_OK; FLMUINT uiFileNumber; DBRenameInfo * pRenameList = NULL; FLMBOOL bFileFound; char * pszOldName = NULL; char * pszNewName; char * pszOldDataName; char * pszNewDataName; char * pszFullNewName; char szOldBase [F_FILENAME_SIZE]; char szNewBase [F_FILENAME_SIZE]; char * pszExtOld; char * pszExtNew; char * pszDataExtOld; char * pszDataExtNew; // Cannot handle empty database name. flmAssert( pszDbName && *pszDbName); flmAssert( pszNewDbName && *pszNewDbName); // Allocate memory for a read buffer, the log header, and various // file names. if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 5, &pszOldName))) { goto Exit; } pszNewName = pszOldName + F_PATH_MAX_SIZE; pszOldDataName = pszNewName + F_PATH_MAX_SIZE; pszNewDataName = pszOldDataName + F_PATH_MAX_SIZE; pszFullNewName = pszNewDataName + F_PATH_MAX_SIZE; // There must be either no directory specified for the new name, or // it must be identical to the old directory. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( pszDbName, pszOldName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( pszNewDbName, pszNewName, szNewBase))) { goto Exit; } // Directories must be the same. if (*pszNewName && f_stricmp( pszOldName, pszNewName) != 0) { rc = RC_SET( NE_XFLM_INVALID_PARM); goto Exit; } f_strcpy( pszNewName, pszOldName); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( pszNewName, szNewBase))) { goto Exit; } f_strcpy( pszFullNewName, pszNewName); f_strcpy( pszOldName, pszDbName); if (pszDataDir && *pszDataDir) { f_strcpy( pszOldDataName, pszDataDir); f_strcpy( pszNewDataName, pszDataDir); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( pszOldDataName, szOldBase))) { goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( pszNewDataName, szNewBase))) { goto Exit; } } else { f_strcpy( pszNewDataName, pszNewName); f_strcpy( pszOldDataName, pszOldName); } // First make sure we have closed the databases and gotten rid of // them from our internal memory tables - in case they had been open. if (RC_BAD( rc = checkDatabaseClosed( pszDbName, pszDataDir))) { goto Exit; } if (RC_BAD( rc = checkDatabaseClosed( pszFullNewName, pszDataDir))) { goto Exit; } // Close all unused file handles if( gv_XFlmSysData.pFileHdlCache) { gv_XFlmSysData.pFileHdlCache->closeUnusedFiles(); } // Start renaming files, beginning with the main DB file. if (RC_BAD( rc = flmRenameFile( pszDbName, pszFullNewName, bOverwriteDestOk, FALSE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } // Find where the extension of the old and new database names are pszExtOld = pszOldName + f_strlen( pszOldName) - 1; pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName) - 1; while (pszExtOld != pszOldName && *pszExtOld != '.') { pszExtOld--; // Both the old db name and old data name have the same // base name, so we can decrement pszDataExtOld // at the same time we decrement pszExtOld. pszDataExtOld--; } if (*pszExtOld != '.') { pszExtOld = pszOldName + f_strlen( pszOldName); pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName); } pszExtNew = pszNewName + f_strlen( pszNewName) - 1; pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName) - 1; while (pszExtNew != pszOldName && *pszExtNew != '.') { pszExtNew--; // Both the new db name and new data name have the same // base name, so we can decrement pszDataExtNew // at the same time we decrement pszExtNew. pszDataExtNew--; } if (*pszExtNew != '.') { pszExtNew = pszNewName + f_strlen( pszNewName); pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName); } // Rename the .lck file, if any. This is necessary for UNIX. f_strcpy( pszExtOld, ".lck"); f_strcpy( pszExtNew, ".lck"); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } // Rename block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszDataExtOld); F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszDataExtNew); if (RC_BAD( rc = flmRenameFile( pszOldDataName, pszNewDataName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) { break; } uiFileNumber++; } // Rename rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszExtOld); F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszExtNew); if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } if (!bFileFound) { break; } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) { break; } uiFileNumber++; } // Rename the RFL directory. if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, pszOldName))) { goto Exit; } if (RC_BAD( rc = rflGetDirAndPrefix( pszFullNewName, pszRflDir, pszNewName))) { goto Exit; } if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, bOverwriteDestOk, TRUE, &pRenameList, &bFileFound, ifpStatus))) { goto Exit; } Exit: if (pszOldName) { f_free( &pszOldName); } // Free the list of renamed files. while (pRenameList) { DBRenameInfo * pRenameFile; pRenameFile = pRenameList; pRenameList = pRenameList->pNext; // If we had an error of some sort, attempt to un-rename // the file that had been renamed. if (RC_BAD( rc)) { gv_XFlmSysData.pFileSystem->renameFile( pRenameFile->Info.szDstFileName, pRenameFile->Info.szSrcFileName); } f_free( &pRenameFile); } return( rc); } /**************************************************************************** Desc: Rename a database file and add to list of renamed files. ****************************************************************************/ FSTATIC RCODE flmRenameFile( const char * pszSrcFileName, const char * pszDstFileName, FLMBOOL bOverwriteDestOk, FLMBOOL bPathNotFoundOk, DBRenameInfo ** ppRenameList, FLMBOOL * pbFileFound, IF_DbRenameStatus * ifpStatus) { RCODE rc = NE_XFLM_OK; DBRenameInfo * pRenameFile = NULL; *pbFileFound = FALSE; // Should not do anything if the source and destination names // are the same. if (f_stricmp( pszSrcFileName, pszDstFileName) == 0) { if (gv_XFlmSysData.pFileSystem->doesFileExist( pszSrcFileName) == NE_XFLM_OK) { *pbFileFound = TRUE; } goto Exit; } if (RC_BAD( rc = f_alloc( sizeof( DBRenameInfo), &pRenameFile))) { goto Exit; } // If a destination file exists, and it is OK to overwrite // it, it must be deleted. if (bOverwriteDestOk) { if (gv_XFlmSysData.pFileSystem->isDir( pszDstFileName)) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->removeDir( pszDstFileName, TRUE))) { goto Exit; } } else { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->deleteFile( pszDstFileName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } else { goto Exit; } } } } // If names are the same, no need to actually do the // rename. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->renameFile( pszSrcFileName, pszDstFileName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (bPathNotFoundOk) { rc = NE_XFLM_OK; } else { goto Exit; } } else { goto Exit; } } else { *pbFileFound = TRUE; pRenameFile->pNext = *ppRenameList; *ppRenameList = pRenameFile; // Do user callback. User could choose to stop the rename // from continuing. if (ifpStatus) { f_strcpy( pRenameFile->Info.szSrcFileName, pszSrcFileName); f_strcpy( pRenameFile->Info.szDstFileName, pszDstFileName); if (RC_BAD( rc = ifpStatus->dbRenameStatus( pRenameFile->Info.szSrcFileName, pRenameFile->Info.szDstFileName))) { goto Exit; } } // So it won't get deallocated at exit. pRenameFile = NULL; } Exit: if (pRenameFile) { f_free( &pRenameFile); } return( rc); } libxflaim-5.1.969/src/fbtrset.h0000644000175000017500000001317210511001742017652 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This File contains routines which do certain types of verifications // on objects in a FLAIM database. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fbtrset.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef BTRSET_H #define BTRSET_H #include "f_btpool.h" #include "f_btree.h" class IXKeyCompare; typedef struct BtCollXref { FLMUINT uiKeyNum; FLMUINT uiCollection; F_COLLECTION Collection; struct BtCollXref * pNext; IXKeyCompare * pCompare; } BT_COLLECTION_XREF; #define BT_MAX_COLLECTION_TBL_SIZ 256 /*============================================================================= Desc: Result set class that uses an independant database. The name is randomly generated. =============================================================================*/ class F_BtResultSet : public F_Object { public: F_BtResultSet( F_Db * pResultSetDb, F_BtPool * pBtPool) { m_pBtPool = pBtPool; m_pResultSetDb = pResultSetDb; f_memset( &m_Collection, 0, sizeof( m_Collection)); m_ppCollectionTbl = NULL; } ~F_BtResultSet(); // Entry Add and Sort Methods RCODE addEntry( // Variable or fixed length entry coming in F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys FLMBYTE * pucKey, // key for sorting. FLMUINT uiKeyLength, FLMBYTE * pEntry, FLMUINT uiEntryLength); // If length is zero then ignore entry. RCODE modifyEntry( // Modify current entry. F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pEntry, // Points to entry buffer FLMUINT uiEntryLength); // Methods to read entries. RCODE getCurrent( // Return current entry F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength, // Size of Entry buffer. FLMUINT * puiReturnLength); RCODE getNext( F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys F_Btree * pBTree, // Preserves the context from one call to // the next. May be null if not needed. FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength); RCODE getPrev( // Position to previous entry and return F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys F_Btree * pBTree, // Preserves the context from one call to // the next. May be null if not needed. FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength); RCODE getFirst( // Position to the first entry and return F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys F_Btree * pBTree, // Preserves the context from one call to // the next. May be null if not needed. FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength); RCODE getLast( // Position to the last entry and return F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys F_Btree * pBTree, // Preserves the context from one call to // the next. May be null if not needed. FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength); RCODE findEntry( // Locate an entry F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength); RCODE deleteEntry( F_Db * pSrcDb, // Set for when we are keeping index keys IXD * pSrcIxd, // Set for when we are keeping index keys FLMBYTE * pucKey, FLMUINT uiKeyLength); // Methods for managing context RCODE getBTree( F_Db * pSrcDb, IXD * pSrcIxd, F_Btree ** ppBtree); FINLINE void freeBTree( F_Btree ** ppBTree) { flmAssert( *ppBTree); m_pBtPool->btpReturnBtree( ppBTree); *ppBTree = NULL; } private: F_BtPool * m_pBtPool; F_Db * m_pResultSetDb; F_COLLECTION m_Collection; BT_COLLECTION_XREF ** m_ppCollectionTbl; friend class F_DbCheck; }; #endif libxflaim-5.1.969/src/fltrabrt.cpp0000644000175000017500000002732610511001742020362 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains routines for aborting a transaction. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fltrabrt.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: This routine aborts an active transaction for a particular database. If the database is open via a server, a message is sent to the server to abort the transaction. Otherwise, the transaction is rolled back locally. ****************************************************************************/ RCODE F_Db::abortTrans( FLMBOOL bOkToLogAbort) { RCODE rc = NE_XFLM_OK; eDbTransType eSaveTransType; XFLM_DB_HDR * pLastCommittedDbHdr; XFLM_DB_HDR * pUncommittedDbHdr; FLMBOOL bDumpedCache = FALSE; FLMBOOL bKeepAbortedTrans; FLMUINT64 ui64TransId; F_Rfl * pRfl = m_pDatabase->m_pRfl; RCODE tmpRc; // Should never be calling on a temporary database. flmAssert( !m_pDatabase->m_bTempDb); // Get transaction type if (m_eTransType == XFLM_NO_TRANS) { goto Exit; // Will return SUCCESS. } // No recovery required if it is a read transaction. if (m_eTransType == XFLM_READ_TRANS) { if (m_bKrefSetup) { // krefCntrlFree could be called w/o checking bKrefSetup because // it checks the flag, but it is more optimal to check the // flag before making the call because most of the time it will // be false. krefCntrlFree(); } goto Unlink_From_Trans; } #ifdef FLM_DBG_LOG flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID, 0, 0, NE_XFLM_OK, "TAbrt"); #endif // Disable DB header writes pRfl->clearDbHdrs(); // End any pending input operations m_pDatabase->endPendingInput(); // Clear the document list m_pDatabase->m_DocumentList.clearNodes(); // If the transaction had no update operations, restore it // to its pre-transaction state - make it appear that no // transaction ever happened. pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr; pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; ui64TransId = m_ui64CurrTransID; // Free up all keys associated with this database. This is done even // if we didn't have any update operations because the KREF may // have been initialized by key generation operations performed // by cursors, etc. krefCntrlFree(); if (m_bHadUpdOper) { // Dump any start and stop indexing stubs that should be aborted. indexingAfterAbort(); // Log the abort record to the rfl file, or throw away the logged // records altogether, depending on the LOG_KEEP_ABORTED_TRANS_IN_RFL // flag. If the RFL volume is bad, we will not attempt to keep this // transaction in the RFL. if (!pRfl->seeIfRflVolumeOk()) { bKeepAbortedTrans = FALSE; } else { bKeepAbortedTrans = (pUncommittedDbHdr->ui8RflKeepAbortedTrans) ? TRUE : FALSE; } } else { bKeepAbortedTrans = FALSE; } // Log an abort transaction record to the roll-forward log or // throw away the entire transaction, depending on the // bKeepAbortedTrans flag. // If the transaction is being "dumped" because of a failed commit, // don't log anything to the RFL. if (bOkToLogAbort) { #ifdef FLM_DEBUG if( pRfl->isLoggingEnabled()) { flmAssert( m_ui64CurrTransID == pRfl->getCurrTransID()); } #endif if (RC_BAD( rc = pRfl->logEndTransaction( this, RFL_TRNS_ABORT_PACKET, !bKeepAbortedTrans))) { goto Exit1; } } #ifdef FLM_DEBUG else { // If bOkToLogAbort is FALSE, this always means that either a // commit failed while trying to log an end transaction packet or a // commit packet was logged and the transaction commit subsequently // failed for some other reason. In either case, the RFL should be // in a good state, with its current transaction ID reset to 0. If // not, either bOkToLogAbort is being used incorrectly by the caller // or there is a bug in the RFL logic. flmAssert( pRfl->getCurrTransID() == 0); } #endif // If there were no operations in the transaction, restore // everything as if the transaction never happened. // Even empty transactions can have modified nodes to clean up // so we need to call this no matter what. m_pDatabase->freeModifiedNodes( this, m_ui64CurrTransID - 1); if (!m_bHadUpdOper) { // Pretend we dumped cache - shouldn't be any to worry about at // this point. bDumpedCache = TRUE; goto Exit1; } // Dump ALL modified cache blocks associated with the DB. // NOTE: This needs to be done BEFORE the call to flmGetDbHdrInfo // below, because that call will change pDb->m_ui64CurrTransID, // and that value is used by freeModifiedNodes. m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID); bDumpedCache = TRUE; // Reset the Db header from the last committed DB header in pFile. getDbHdrInfo( pLastCommittedDbHdr); if (RC_BAD( rc = physRollback( (FLMUINT)pUncommittedDbHdr->ui32RblEOF, m_pDatabase->m_uiFirstLogBlkAddress, FALSE, 0))) { goto Exit1; } m_pDatabase->lockMutex(); // Put the new transaction ID into the log header even though // we are not committing. We want to keep the transaction IDs // incrementing even though we aborted. pLastCommittedDbHdr->ui64CurrTransID = ui64TransId; // Preserve where we are at in the roll-forward log. Even though // the transaction aborted, we may have kept it in the RFL instead of // throw it away. pLastCommittedDbHdr->ui32RflCurrFileNum = pUncommittedDbHdr->ui32RflCurrFileNum; pLastCommittedDbHdr->ui32RflLastTransOffset = pUncommittedDbHdr->ui32RflLastTransOffset; f_memcpy( pLastCommittedDbHdr->ucLastTransRflSerialNum, pUncommittedDbHdr->ucLastTransRflSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( pLastCommittedDbHdr->ucNextRflSerialNum, pUncommittedDbHdr->ucNextRflSerialNum, XFLM_SERIAL_NUM_SIZE); // The following items tell us where we are at in the roll-back log. // During a transaction we may log blocks for the checkpoint or for // read transactions. So, even though we are aborting this transaction, // there may be other things in the roll-back log that we don't want // to lose. These items should not be reset until we do a checkpoint, // which is when we know it is safe to throw away the entire roll-back log. pLastCommittedDbHdr->ui32RblEOF = pUncommittedDbHdr->ui32RblEOF; pLastCommittedDbHdr->ui32RblFirstCPBlkAddr = pUncommittedDbHdr->ui32RblFirstCPBlkAddr; m_pDatabase->unlockMutex(); pRfl->commitDbHdrs( pLastCommittedDbHdr, &m_pDatabase->m_checkpointDbHdr); Exit1: // Dump cache, if not done above. if (!bDumpedCache) { m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID); m_pDatabase->freeModifiedNodes( this, m_ui64CurrTransID - 1); bDumpedCache = TRUE; } // Throw away IXD_FIXUPs if (m_pIxdFixups) { IXD_FIXUP * pIxdFixup; IXD_FIXUP * pDeleteIxdFixup; pIxdFixup = m_pIxdFixups; while (pIxdFixup) { pDeleteIxdFixup = pIxdFixup; pIxdFixup = pIxdFixup->pNext; f_free( &pDeleteIxdFixup); } m_pIxdFixups = NULL; } if (m_eTransType == XFLM_UPDATE_TRANS && gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList) { flmTransEventCallback( XFLM_EVENT_ABORT_TRANS, this, rc, ui64TransId); } Unlink_From_Trans: eSaveTransType = m_eTransType; if (m_uiFlags & FDB_HAS_WRITE_LOCK) { if (RC_BAD( tmpRc = pRfl->completeTransWrites( this, FALSE, FALSE))) { if (RC_OK( rc)) { rc = tmpRc; } } } if (eSaveTransType == XFLM_UPDATE_TRANS) { // Before unlocking, restore collection information. if (m_uiFlags & FDB_UPDATED_DICTIONARY) { m_pDatabase->lockMutex(); flmAssert( m_pDict); unlinkFromDict(); if (m_pDatabase->m_pDictList) { // Link the F_Db to the right F_Dict object so it will // fixup the correct F_COLLECTION structures. linkToDict( m_pDatabase->m_pDictList); } m_pDatabase->unlockMutex(); } if (m_pDict) { F_COLLECTION * pCollection; FLMUINT uiLfNum; #ifdef FLM_DEBUG IXD * pIxd; FLMUINT uiRootBlk; #endif // Only need to do collections. Nothing from the LFH of // an index is stored in memory except for the root block // address, and whenever that is changed, we get a new // dictionary. Since the new dictionary will be discarded // in that case, there is nothing to restore for an index. uiLfNum = 0; while ((pCollection = m_pDict->getNextCollection( uiLfNum, TRUE)) != NULL) { #ifdef FLM_DEBUG uiRootBlk = pCollection->lfInfo.uiRootBlk; #endif if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this, &pCollection->lfInfo, pCollection))) { if (RC_OK( rc)) { rc = tmpRc; } } #ifdef FLM_DEBUG else { // Make sure root block did not change - should not // have because root block changes are done by creating // a new dictionary, and we have already discarded // any new dictionary. Hence, root block address should // be the same in memory as it is no disk. flmAssert( uiRootBlk == pCollection->lfInfo.uiRootBlk); } #endif uiLfNum = pCollection->lfInfo.uiLfNum; } // Do indexes in debug mode to make sure uiRootBlk is correct #ifdef FLM_DEBUG uiLfNum = 0; while ((pIxd = m_pDict->getNextIndex( uiLfNum, TRUE)) != NULL) { uiRootBlk = pIxd->lfInfo.uiRootBlk; if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this, &pIxd->lfInfo, NULL))) { if (RC_OK( rc)) { rc = tmpRc; } } else { // Make sure root block did not change - should not // have because root block changes are done by creating // a new dictionary, and we have already discarded // any new dictionary. Hence, root block address should // be the same in memory as it is no disk. flmAssert( uiRootBlk == pIxd->lfInfo.uiRootBlk); } uiLfNum = pIxd->lfInfo.uiLfNum; } #endif } } // Unlink the database from the transaction list. unlinkFromTransList( FALSE); if (m_pDbStats) { FLMUINT64 ui64ElapMilli = 0; flmAddElapTime( &m_TransStartTime, &ui64ElapMilli); m_pDbStats->bHaveStats = TRUE; if (eSaveTransType == XFLM_READ_TRANS) { m_pDbStats->ReadTransStats.AbortedTrans.ui64Count++; m_pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli += ui64ElapMilli; } else { m_pDbStats->UpdateTransStats.AbortedTrans.ui64Count++; m_pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli += ui64ElapMilli; } } if (m_pStats) { (void)flmStatUpdate( &m_Stats); } Exit: m_AbortRc = NE_XFLM_OK; return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Aborts an active transaction. *END************************************************************************/ RCODE F_Db::transAbort( void) { RCODE rc = NE_XFLM_OK; if (m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } rc = abortTrans(); Exit: if (RC_OK( rc)) { rc = checkState( __FILE__, __LINE__); } return( rc); } libxflaim-5.1.969/src/ncache.cpp0000644000175000017500000037613610511001742017771 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This is the DOM Node cache for XFLAIM // // Tabs: 3 // // Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: ncache.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #if defined( FLM_NLM) && !defined( __MWERKS__) // Disable "Warning! W549: col(XX) 'sizeof' operand contains // compiler generated information" #pragma warning 549 9 #endif /**************************************************************************** Desc: Constructor ****************************************************************************/ F_NodeCacheMgr::F_NodeCacheMgr() { m_pNodeAllocator = NULL; m_pBufAllocator = NULL; m_pAttrItemAllocator = NULL; m_pPurgeList = NULL; m_pHeapList = NULL; m_pOldList = NULL; f_memset( &m_Usage, 0, sizeof( m_Usage)); m_ppHashBuckets = NULL; m_uiNumBuckets = 0; m_uiHashFailTime = 0; m_uiHashMask = 0; m_uiPendingReads = 0; m_uiIoWaits = 0; m_pFirstNode = NULL; m_bReduceInProgress = FALSE; #ifdef FLM_DEBUG m_bDebug = FALSE; #endif } /**************************************************************************** Desc: Constructor for F_CachedNode ****************************************************************************/ F_CachedNode::F_CachedNode() { m_pPrevInBucket = NULL; m_pNextInBucket = NULL; m_pPrevInDatabase = NULL; m_pNextInDatabase = NULL; m_pOlderVersion = NULL; m_pNewerVersion = NULL; m_pPrevInHeapList = NULL; m_pNextInHeapList = NULL; m_pPrevInOldList = NULL; m_pNextInOldList = NULL; m_ui64LowTransId = 0; // Set the high transaction ID to FLM_MAX_UINT64 so that this will NOT // be treated as one that had memory assigned to the old version nodes. m_ui64HighTransId = FLM_MAX_UINT64; m_pNotifyList = NULL; m_uiCacheFlags = 0; m_uiStreamUseCount = 0; // Items initialized in constructor m_uiDataBufSize = 0; m_pucData = NULL; m_pNodeList = NULL; m_ppAttrList = NULL; m_uiAttrCount = 0; m_uiTotalAttrSize = 0; m_uiFlags = 0; f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); } /**************************************************************************** Desc: Destructor for F_CachedNode object. This routine assumes the global mutex is already locked. ****************************************************************************/ F_CachedNode::~F_CachedNode() { // Don't include attribute size, because it will be subtracted out // when we delete the attr items. FLMUINT uiSize = memSize() - m_uiTotalAttrSize; FLMBYTE * pucActualAlloc; flmAssert( !m_uiStreamUseCount); f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); // If this is an old version, decrement the old version counters. if (m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize && gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount--; unlinkFromOldList(); } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize && gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount--; if( m_uiFlags & FDOM_HEAP_ALLOC) { unlinkFromHeapList(); } // Free the m_pucData, if any if (m_pucData) { pucActualAlloc = getActualPointer( m_pucData); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( m_uiDataBufSize, &pucActualAlloc); m_pucData = NULL; } if (m_pNodeList) { pucActualAlloc = getActualPointer( m_pNodeList); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( calcNodeListBufSize( m_nodeInfo.uiChildElmCount), &pucActualAlloc); m_pNodeList = NULL; } if (m_uiAttrCount) { FLMUINT uiLoop; for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) { delete m_ppAttrList [uiLoop]; } flmAssert( !m_uiTotalAttrSize); pucActualAlloc = getActualPointer( m_ppAttrList); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( calcAttrListBufSize( m_uiAttrCount), &pucActualAlloc); m_ppAttrList = NULL; m_uiAttrCount = 0; } if (shouldRehash( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount, gv_XFlmSysData.pNodeCacheMgr->m_uiNumBuckets)) { if (checkHashFailTime( &gv_XFlmSysData.pNodeCacheMgr->m_uiHashFailTime)) { (void)gv_XFlmSysData.pNodeCacheMgr->rehash(); } } } /**************************************************************************** Desc: This routine frees a purged node from node cache. This routine assumes that the node cache mutex has already been locked. ****************************************************************************/ void F_CachedNode::freePurged( void) { // Unlink the node from the purged list. unlinkFromPurged(); // Free the F_CachedNode object. unsetPurged(); delete this; } /**************************************************************************** Desc: This routine frees a node in the node cache. This routine assumes that the node cache mutex has already been locked. ****************************************************************************/ void F_CachedNode::freeCache( FLMBOOL bPutInPurgeList) { FLMBOOL bOldVersion; bOldVersion = (FLMBOOL)((m_ui64HighTransId != FLM_MAX_UINT64) ? TRUE : FALSE); // Unlink the node from its various lists. gv_XFlmSysData.pNodeCacheMgr->m_MRUList.unlinkGlobal( (F_CachedItem *)this); unlinkFromDatabase(); if (!m_pNewerVersion) { F_CachedNode * pOlderVersion = m_pOlderVersion; unlinkFromHashBucket(); // If there was an older version, it now needs to be // put into the hash bucket. if (pOlderVersion) { unlinkFromVerList(); pOlderVersion->linkToHashBucket(); } } else { unlinkFromVerList(); } if( m_uiFlags & FDOM_HEAP_ALLOC) { unlinkFromHeapList(); } // Free the F_CachedNode structure if not putting in purge list. if (!bPutInPurgeList) { delete this; } else { if ((m_pNextInGlobal = gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList) != NULL) { m_pNextInGlobal->m_pPrevInGlobal = this; } gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList = this; // Unset the dirty flags - don't want anything in the purge list // to be dirty. m_uiFlags &= ~(FDOM_DIRTY | FDOM_NEW); setPurged(); flmAssert( !m_pPrevInGlobal); } } /**************************************************************************** Desc: This routine initializes node cache manager. ****************************************************************************/ RCODE F_NodeCacheMgr::initCache( void) { RCODE rc = NE_XFLM_OK; // Allocate the hash buckets. if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_CachedNode *) * (FLMUINT)MIN_HASH_BUCKETS, &m_ppHashBuckets))) { goto Exit; } m_uiNumBuckets = MIN_HASH_BUCKETS; m_uiHashMask = m_uiNumBuckets - 1; gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); // Set up the F_CachedNode object allocator if( RC_BAD( rc = FlmAllocFixedAllocator( &m_pNodeAllocator))) { goto Exit; } if (RC_BAD( rc = m_pNodeAllocator->setup( FALSE, gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, &m_nodeRelocator, sizeof( F_CachedNode), &m_Usage.slabUsage, NULL))) { goto Exit; } // Set up the buffer allocator for F_CachedNode objects if( RC_BAD( rc = FlmAllocBufferAllocator( &m_pBufAllocator))) { goto Exit; } if (RC_BAD( rc = m_pBufAllocator->setup( FALSE, gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, NULL, &m_Usage.slabUsage, NULL))) { goto Exit; } // Set up the allocator for attribute items if( RC_BAD( rc = FlmAllocFixedAllocator( &m_pAttrItemAllocator))) { goto Exit; } if( RC_BAD( rc = m_pAttrItemAllocator->setup( FALSE, gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, &m_attrItemRelocator, sizeof( F_AttrItem), &m_Usage.slabUsage, NULL))) { goto Exit; } #ifdef FLM_DEBUG m_bDebug = TRUE; #endif Exit: return( rc); } /**************************************************************************** Desc: Determine if a node can be moved. Notes: This routine assumes the node cache mutex is locked This is a static method, so there is no "this" pointer to the F_NodeCacheMgr object. ****************************************************************************/ FLMBOOL F_NodeRelocator::canRelocate( void * pvAlloc) { return( ((F_CachedNode *)pvAlloc)->nodeInUse() ? FALSE : TRUE); } /**************************************************************************** Desc: Fixes up all pointers needed to allow an F_CachedNode object to be moved to a different location in memory Notes: This routine assumes the node cache mutex is locked. This is a static method, so there is no "this" pointer to the F_NodeCacheMgr object. ****************************************************************************/ void F_NodeRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_CachedNode * pOldNode = (F_CachedNode *)pvOldAlloc; F_CachedNode * pNewNode = (F_CachedNode *)pvNewAlloc; F_CachedNode ** ppBucket; F_Database * pDatabase = pOldNode->m_pDatabase; F_NodeCacheMgr * pNodeCacheMgr = gv_XFlmSysData.pNodeCacheMgr; FLMBYTE * pucActualAlloc; flmAssert( !pOldNode->nodeInUse()); flmAssert( pvNewAlloc < pvOldAlloc); // Update the F_CachedNode pointer in the data buffer if( pNewNode->m_pucData) { pucActualAlloc = getActualPointer( pNewNode->m_pucData); flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode); pNewNode->setNodeAndDataPtr( pucActualAlloc); } if( pNewNode->m_pNodeList) { pucActualAlloc = getActualPointer( pNewNode->m_pNodeList); flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode); pNewNode->setNodeListPtr( pucActualAlloc); } if( pNewNode->m_ppAttrList) { FLMUINT uiLoop; pucActualAlloc = getActualPointer( pNewNode->m_ppAttrList); flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode); pNewNode->setAttrListPtr( pucActualAlloc); for (uiLoop = 0; uiLoop < pNewNode->m_uiAttrCount; uiLoop++) { pNewNode->m_ppAttrList [uiLoop]->m_pCachedNode = pNewNode; } } if (pNewNode->m_pPrevInDatabase) { pNewNode->m_pPrevInDatabase->m_pNextInDatabase = pNewNode; } if (pNewNode->m_pNextInDatabase) { pNewNode->m_pNextInDatabase->m_pPrevInDatabase = pNewNode; } if (pNewNode->m_pPrevInGlobal) { pNewNode->m_pPrevInGlobal->m_pNextInGlobal = pNewNode; } if (pNewNode->m_pNextInGlobal) { pNewNode->m_pNextInGlobal->m_pPrevInGlobal = pNewNode; } if (pNewNode->m_pPrevInBucket) { pNewNode->m_pPrevInBucket->m_pNextInBucket = pNewNode; } if (pNewNode->m_pNextInBucket) { pNewNode->m_pNextInBucket->m_pPrevInBucket = pNewNode; } if (pNewNode->m_pOlderVersion) { pNewNode->m_pOlderVersion->m_pNewerVersion = pNewNode; } if (pNewNode->m_pNewerVersion) { pNewNode->m_pNewerVersion->m_pOlderVersion = pNewNode; } if (pNewNode->m_pPrevInHeapList) { pNewNode->m_pPrevInHeapList->m_pNextInHeapList = pNewNode; } if (pNewNode->m_pNextInHeapList) { pNewNode->m_pNextInHeapList->m_pPrevInHeapList = pNewNode; } if (pNewNode->m_pPrevInOldList) { pNewNode->m_pPrevInOldList->m_pNextInOldList = pNewNode; } if (pNewNode->m_pNextInOldList) { pNewNode->m_pNextInOldList->m_pPrevInOldList = pNewNode; } if( pDatabase) { if (pDatabase->m_pFirstNode == pOldNode) { pDatabase->m_pFirstNode = pNewNode; } if( pDatabase->m_pLastNode == pOldNode) { pDatabase->m_pLastNode = pNewNode; } if( pDatabase->m_pLastDirtyNode == pOldNode) { pDatabase->m_pLastDirtyNode = pNewNode; } } ppBucket = pNodeCacheMgr->nodeHash( pOldNode->m_nodeInfo.ui64NodeId); if( *ppBucket == pOldNode) { *ppBucket = pNewNode; } if (pNodeCacheMgr->m_MRUList.m_pMRUItem == (F_CachedItem *)pOldNode) { pNodeCacheMgr->m_MRUList.m_pMRUItem = pNewNode; } if (pNodeCacheMgr->m_MRUList.m_pLRUItem == (F_CachedItem *)pOldNode) { pNodeCacheMgr->m_MRUList.m_pLRUItem = pNewNode; } if (pNodeCacheMgr->m_pHeapList == pOldNode) { pNodeCacheMgr->m_pHeapList = pNewNode; } if (pNodeCacheMgr->m_pOldList == pOldNode) { pNodeCacheMgr->m_pOldList = pNewNode; } if (pNodeCacheMgr->m_pPurgeList == pOldNode) { pNodeCacheMgr->m_pPurgeList = pNewNode; } } /**************************************************************************** Desc: Determine if a data buffer of an F_CachedNode object can be moved. This routine assumes that the node cache mutex is locked. ****************************************************************************/ FLMBOOL F_NodeDataRelocator::canRelocate( void * pvAlloc) { F_CachedNode * pNode = *((F_CachedNode **)pvAlloc); if( pNode->nodeInUse()) { return( FALSE); } else { flmAssert( getActualPointer( pNode->m_pucData) == (FLMBYTE *)pvAlloc); return( TRUE); } } /**************************************************************************** Desc: Relocate the data buffer of an F_CachedNode object. This routine assumes that the node cache mutex is locked. ****************************************************************************/ void F_NodeDataRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc); flmAssert( !pNode->nodeInUse()); flmAssert( pvNewAlloc < pvOldAlloc); flmAssert( getActualPointer( pNode->m_pucData) == (FLMBYTE *)pvOldAlloc); pNode->setNodeAndDataPtr( (FLMBYTE *)pvNewAlloc); } /**************************************************************************** Desc: Determine if a node list of an F_CachedNode object can be moved. This routine assumes that the node cache mutex is locked. ****************************************************************************/ FLMBOOL F_NodeListRelocator::canRelocate( void * pvAlloc) { F_CachedNode * pNode = *((F_CachedNode **)pvAlloc); if( pNode->nodeInUse()) { return( FALSE); } else { flmAssert( getActualPointer( pNode->m_pNodeList) == (FLMBYTE *)pvAlloc); return( TRUE); } } /**************************************************************************** Desc: Relocate the node list of an F_CachedNode object. This routine assumes that the node cache mutex is locked. ****************************************************************************/ void F_NodeListRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc); flmAssert( !pNode->nodeInUse()); flmAssert( pvNewAlloc < pvOldAlloc); flmAssert( getActualPointer( pNode->m_pNodeList) == (FLMBYTE *)pvOldAlloc); pNode->setNodeListPtr( (FLMBYTE *)pvNewAlloc); } /**************************************************************************** Desc: Determine if an attr list of an F_CachedNode object can be moved. This routine assumes that the node cache mutex is locked. ****************************************************************************/ FLMBOOL F_AttrListRelocator::canRelocate( void * pvAlloc) { F_CachedNode * pNode = *((F_CachedNode **)pvAlloc); if( pNode->nodeInUse()) { return( FALSE); } else { flmAssert( getActualPointer( pNode->m_ppAttrList) == (FLMBYTE *)pvAlloc); return( TRUE); } } /**************************************************************************** Desc: Relocate the attr list of an F_CachedNode object. This routine assumes that the node cache mutex is locked. ****************************************************************************/ void F_AttrListRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc); flmAssert( !pNode->nodeInUse()); flmAssert( pvNewAlloc < pvOldAlloc); flmAssert( getActualPointer( pNode->m_ppAttrList) == (FLMBYTE *)pvOldAlloc); pNode->setAttrListPtr( (FLMBYTE *)pvNewAlloc); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL F_AttrItemRelocator::canRelocate( void * pvAlloc) { F_AttrItem * pAttrItem = (F_AttrItem *)pvAlloc; if( pAttrItem->m_pCachedNode && !pAttrItem->m_pCachedNode->nodeInUse()) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: ****************************************************************************/ void F_AttrItemRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_AttrItem * pOldAttrItem = (F_AttrItem *)pvOldAlloc; F_AttrItem * pNewAttrItem = (F_AttrItem *)pvNewAlloc; F_CachedNode * pCachedNode = pNewAttrItem->m_pCachedNode; FLMUINT uiPos; flmAssert( !pCachedNode->nodeInUse()); // Find the new attr item slot if (pCachedNode->getAttribute( pNewAttrItem->m_uiNameId, &uiPos) == pOldAttrItem) { pCachedNode->m_ppAttrList [uiPos] = pNewAttrItem; } else { flmAssert( 0); } if( pOldAttrItem->m_uiPayloadLen > sizeof( FLMBYTE *)) { *((F_AttrItem **)(pNewAttrItem->m_pucPayload - sizeof( F_AttrItem *))) = pNewAttrItem; } } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL F_AttrBufferRelocator::canRelocate( void * pvAlloc) { F_AttrItem * pAttrItem = *((F_AttrItem **)pvAlloc); flmAssert( pAttrItem->m_pucPayload == (FLMBYTE *)pvAlloc + sizeof( F_AttrItem *)); if( pAttrItem->m_pCachedNode && !pAttrItem->m_pCachedNode->nodeInUse()) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: ****************************************************************************/ void F_AttrBufferRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_AttrItem * pAttrItem = *((F_AttrItem **)pvOldAlloc); flmAssert( !pAttrItem->m_pCachedNode->nodeInUse()); flmAssert( pvNewAlloc < pvOldAlloc); flmAssert( pAttrItem->m_pucPayload == (FLMBYTE *)pvOldAlloc + sizeof( F_AttrItem *)); pAttrItem->m_pucPayload = ((FLMBYTE *)pvNewAlloc) + sizeof( F_AttrItem *); } /**************************************************************************** Desc: This routine resizes the hash table for the cache manager. NOTE: This routine assumes that the node cache mutex has been locked. ****************************************************************************/ RCODE F_NodeCacheMgr::rehash( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiNewHashTblSize; F_CachedNode ** ppOldHashTbl; FLMUINT uiOldHashTblSize; F_CachedNode ** ppBucket; FLMUINT uiLoop; F_CachedNode * pTmpNode; F_CachedNode * pTmpNextNode; FLMUINT uiOldMemSize; uiNewHashTblSize = caGetBestHashTblSize( m_Usage.uiCount); // At this point we better have a different hash table size // or something is mucked up! flmAssert( uiNewHashTblSize != m_uiNumBuckets); // Save the old hash table and its size. if ((ppOldHashTbl = m_ppHashBuckets) != NULL) { uiOldMemSize = f_msize( ppOldHashTbl); } else { uiOldMemSize = 0; } uiOldHashTblSize = m_uiNumBuckets; // Allocate a new hash table. if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_CachedNode *) * (FLMUINT)uiNewHashTblSize, &m_ppHashBuckets))) { m_uiHashFailTime = FLM_GET_TIMER(); m_ppHashBuckets = ppOldHashTbl; goto Exit; } // Subtract off old size and add in new size. gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiOldMemSize); gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); m_uiNumBuckets = uiNewHashTblSize; m_uiHashMask = uiNewHashTblSize - 1; // Relink all of the nodes into the new hash table. for (uiLoop = 0, ppBucket = ppOldHashTbl; uiLoop < uiOldHashTblSize; uiLoop++, ppBucket++) { pTmpNode = *ppBucket; while (pTmpNode) { pTmpNextNode = pTmpNode->m_pNextInBucket; pTmpNode->linkToHashBucket(); pTmpNode = pTmpNextNode; } } // Throw away the old hash table. f_free( &ppOldHashTbl); Exit: return( rc); } /**************************************************************************** Desc: This routine shuts down the node cache manager and frees all resources allocated by it. NOTE: Node cache mutex must be locked already, or we must be shutting down so that only one thread is calling this routine. ****************************************************************************/ F_NodeCacheMgr::~F_NodeCacheMgr() { F_CachedItem * pItem; F_CachedItem * pNextItem; F_DOMNode * pTmp; // Free the DOM Node Pool while( (pTmp = m_pFirstNode) != NULL) { m_pFirstNode = m_pFirstNode->m_pNextInPool; pTmp->m_refCnt = 0; pTmp->m_pNextInPool = NULL; pTmp->m_pCachedNode = NULL; delete pTmp; } // Free all of the node cache objects. pItem = m_MRUList.m_pMRUItem; while (pItem) { pNextItem = pItem->m_pNextInGlobal; ((F_CachedNode *)pItem)->freeCache( FALSE); pItem = pNextItem; } flmAssert( !m_MRUList.m_pMRUItem && !m_MRUList.m_pLRUItem); // Must free those in the purge list too. while (m_pPurgeList) { m_pPurgeList->freePurged(); } // The math better be consistent! flmAssert( m_Usage.uiCount == 0); flmAssert( m_Usage.uiOldVerCount == 0); flmAssert( m_Usage.uiOldVerBytes == 0); // Free the hash bucket array if (m_ppHashBuckets) { FLMUINT uiTotalMemory = f_msize( m_ppHashBuckets); f_free( &m_ppHashBuckets); gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiTotalMemory); } // Free the allocators if (m_pNodeAllocator) { m_pNodeAllocator->Release(); } if( m_pBufAllocator) { m_pBufAllocator->Release(); } if( m_pAttrItemAllocator) { m_pAttrItemAllocator->Release(); } } /**************************************************************************** Desc: This routine links a notify request into a node's notification list and then waits to be notified that the event has occurred. NOTE: This routine assumes that the node cache mutex is locked and that it is supposed to unlock it. It will relock the mutex on its way out. ****************************************************************************/ RCODE F_NodeCacheMgr::waitNotify( F_Db * pDb, F_CachedNode ** ppNode) { return( f_notifyWait( gv_XFlmSysData.hNodeCacheMutex, pDb->m_hWaitSem, ppNode, &((*ppNode)->m_pNotifyList))); } /**************************************************************************** Desc: This routine notifies threads waiting for a pending read to complete. NOTE: This routine assumes that the node cache mutex is already locked. ****************************************************************************/ void F_NodeCacheMgr::notifyWaiters( F_NOTIFY_LIST_ITEM * pNotify, F_CachedNode * pUseNode, RCODE NotifyRc) { while (pNotify) { F_SEM hSem; *(pNotify->pRc) = NotifyRc; if (RC_OK( NotifyRc)) { *((F_CachedNode **)pNotify->pvData) = pUseNode; pUseNode->incrNodeUseCount(); } hSem = pNotify->hSem; pNotify = pNotify->pNext; f_semSignal( hSem); } } /**************************************************************************** Desc: Allocate an F_CachedNode object. ****************************************************************************/ RCODE F_NodeCacheMgr::allocNode( F_CachedNode ** ppNode, FLMBOOL bMutexLocked) { RCODE rc = NE_XFLM_OK; FLMBOOL bUnlockMutex = FALSE; if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bUnlockMutex = TRUE; } if ((*ppNode = new F_CachedNode) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } // Increment statistics. m_Usage.uiCount++; m_Usage.uiByteCount += (*ppNode)->memSize(); if (shouldRehash( m_Usage.uiCount, m_uiNumBuckets)) { if (checkHashFailTime( &m_uiHashFailTime)) { if (RC_BAD( rc = rehash())) { goto Exit; } } } Exit: if( bUnlockMutex) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /**************************************************************************** Desc: Cleanup old nodes in cache that are no longer needed by any transaction. This routine assumes that the node cache mutex has been locked. ****************************************************************************/ void F_NodeCacheMgr::cleanupOldCache( void) { F_CachedNode * pCurNode; F_CachedNode * pNextNode; pCurNode = m_pOldList; // Stay in the loop until we have freed all old nodes, or // we have run through the entire list. while( pCurNode) { flmAssert( pCurNode->m_ui64HighTransId != FLM_MAX_UINT64); // Save the pointer to the next entry in the list because // we may end up unlinking pCurNode below, in which case we would // have lost the next node. pNextNode = pCurNode->m_pNextInOldList; if (!pCurNode->nodeInUse() && !pCurNode->readingInNode() && (!pCurNode->nodeLinkedToDatabase() || !pCurNode->m_pDatabase->neededByReadTrans( pCurNode->m_ui64LowTransId, pCurNode->m_ui64HighTransId))) { pCurNode->freeNode(); } pCurNode = pNextNode; } } /**************************************************************************** Desc: Cleanup nodes that have been purged. This routine assumes that the node cache mutex has been locked. ****************************************************************************/ void F_NodeCacheMgr::cleanupPurgedCache( void) { F_CachedNode * pCurNode; F_CachedNode * pNextNode; pCurNode = m_pPurgeList; // Stay in the loop until we have freed all purged nodes, or // we have run through the entire list. while( pCurNode) { // Save the pointer to the next entry in the list because // we may end up unlinking pCurNode below, in which case we would // have lost the next node. pNextNode = (F_CachedNode *)pCurNode->m_pNextInGlobal; flmAssert( pCurNode->nodePurged()); if (!pCurNode->nodeInUse()) { pCurNode->freePurged(); } pCurNode = pNextNode; } } /**************************************************************************** Desc: Reduce node cache to below the cache limit. NOTE: This routine assumes that the node cache mutex is locked upon entering the routine, but it may unlock and re-lock the mutex. ****************************************************************************/ void F_NodeCacheMgr::reduceCache( void) { F_CachedNode * pTmpNode; F_CachedNode * pPrevNode; F_CachedNode * pNextNode; FLMUINT uiSlabSize; FLMUINT uiByteThreshold; FLMUINT uiSlabThreshold; FLMBOOL bDoingReduce = FALSE; // Discard items that are allocated on the heap. These are large // allocations that could not be satisfied by the buffer allocator and have // the side effect of causing memory fragmentation. pTmpNode = m_pHeapList; while( pTmpNode) { // Need to save the pointer to the next entry in the list because // we may end up freeing pTmpNode below. pNextNode = pTmpNode->m_pNextInHeapList; // See if the item can be freed. if( pTmpNode->canBeFreed()) { // NOTE: This call will free the memory pointed to by // pTmpNode. Hence, pTmpNode should NOT be used after // this point. pTmpNode->freeNode(); } pTmpNode = pNextNode; } // If cache is not full, we are done. if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || m_bReduceInProgress) { goto Exit; } m_bReduceInProgress = TRUE; bDoingReduce = TRUE; // Cleanup cache that is no longer needed by anyone cleanupOldCache(); cleanupPurgedCache(); // Determine the cache threshold uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); // Are we over the threshold? if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) { goto Exit; } // Remove items from cache starting from the LRU pTmpNode = (F_CachedNode *)m_MRUList.m_pLRUItem; uiByteThreshold = m_Usage.uiByteCount > uiSlabSize ? m_Usage.uiByteCount - uiSlabSize : 0; while( pTmpNode) { // Need to save the pointer to the next entry in the list because // we may end up freeing pTmpNode below. pPrevNode = (F_CachedNode *)pTmpNode->m_pPrevInGlobal; // See if the item can be freed. if( pTmpNode->canBeFreed()) { pTmpNode->freeNode(); if( m_Usage.uiByteCount <= uiByteThreshold) { if( pPrevNode) { pPrevNode->incrNodeUseCount(); } gv_XFlmSysData.pNodeCacheMgr->defragmentMemory( TRUE); if( !pPrevNode) { break; } pPrevNode->decrNodeUseCount(); // We're going to quit when we get under 50 percent for node cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving node // cache because block cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } uiByteThreshold = uiByteThreshold > uiSlabSize ? uiByteThreshold - uiSlabSize : 0; } } pTmpNode = pPrevNode; } Exit: if( bDoingReduce) { m_bReduceInProgress = FALSE; } return; } /**************************************************************************** Desc: This routine finds a node in the node cache. If it cannot find the node, it will return the position where the node should be inserted. NOTE: This routine assumes that the node cache mutex has been locked. ****************************************************************************/ void F_NodeCacheMgr::findNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64VersionNeeded, FLMBOOL bDontPoisonCache, FLMUINT * puiNumLooks, F_CachedNode ** ppNode, F_CachedNode ** ppNewerNode, F_CachedNode ** ppOlderNode) { F_CachedNode * pNode; FLMUINT uiNumLooks = 0; FLMBOOL bFound; F_CachedNode * pNewerNode; F_CachedNode * pOlderNode; F_Database * pDatabase = pDb->m_pDatabase; // Search down the hash bucket for the matching item. Start_Find: // NOTE: Need to always calculate hash bucket because // the hash table may have been changed while we // were waiting to be notified below - mutex can // be unlocked, but it is guaranteed to be locked // here. pNode = *(nodeHash( ui64NodeId)); bFound = FALSE; uiNumLooks = 1; while (pNode && (pNode->m_nodeInfo.ui64NodeId != ui64NodeId || pNode->m_nodeInfo.uiCollection != uiCollection || pNode->m_pDatabase != pDatabase)) { if ((pNode = pNode->m_pNextInBucket) != NULL) { uiNumLooks++; } } // If we found the node, see if we have the right version. if (!pNode) { pNewerNode = pOlderNode = NULL; } else { pNewerNode = NULL; pOlderNode = pNode; for (;;) { // If this one is being read in, we need to wait on it. if (pNode->readingInNode()) { // We need to wait for this record to be read in // in case it coalesces with other versions, resulting // in a version that satisfies our request. m_uiIoWaits++; if (RC_BAD( waitNotify( pDb, &pNode))) { // Don't care what error the other thread had reading // the thing in from disk - we'll bail out and start // our find again. goto Start_Find; } // The thread doing the notify "uses" the record cache // on behalf of this thread to prevent the record // from being replaced after it unlocks the mutex. // At this point, since we have locked the mutex, // we need to release the record - because we // will put a "use" on it below. pNode->decrNodeUseCount(); if (pNode->nodePurged()) { if (!pNode->nodeInUse()) { pNode->freePurged(); } } // Start over with the find because the list // structure has changed. goto Start_Find; } // See if this record version is the one we need. if (ui64VersionNeeded < pNode->m_ui64LowTransId) { pNewerNode = pNode; if ((pOlderNode = pNode = pNode->m_pOlderVersion) == NULL) { break; } uiNumLooks++; } else if (ui64VersionNeeded <= pNode->m_ui64HighTransId) { // Make this the MRU record. if (puiNumLooks) { if (bDontPoisonCache) { m_MRUList.stepUpInGlobal( (F_CachedItem *)pNode); } else if (pNode->m_pPrevInGlobal) { m_MRUList.unlinkGlobal( (F_CachedItem *)pNode); m_MRUList.linkGlobalAsMRU( (F_CachedItem *)pNode); } m_Usage.uiCacheHits++; m_Usage.uiCacheHitLooks += uiNumLooks; } bFound = TRUE; break; } else { pOlderNode = pNode; pNewerNode = pNode->m_pNewerVersion; // Set pNode to NULL as an indicator that we did not // find the version we needed. pNode = NULL; break; } } } *ppNode = pNode; if( ppOlderNode) { *ppOlderNode = pOlderNode; } if( ppNewerNode) { *ppNewerNode = pNewerNode; } if (puiNumLooks) { *puiNumLooks = uiNumLooks; } } /**************************************************************************** Desc: This routine links a new node into the global list and into the correct place in its hash bucket. This routine assumes that the node cache mutex is already locked. ****************************************************************************/ void F_NodeCacheMgr::linkIntoNodeCache( F_CachedNode * pNewerNode, F_CachedNode * pOlderNode, F_CachedNode * pNode, FLMBOOL bLinkAsMRU ) { if( bLinkAsMRU) { m_MRUList.linkGlobalAsMRU( (F_CachedItem *)pNode); } else { m_MRUList.linkGlobalAsLRU( (F_CachedItem *)pNode); } if (pNewerNode) { pNode->linkToVerList( pNewerNode, pOlderNode); } else { if (pOlderNode) { pOlderNode->unlinkFromHashBucket(); } pNode->linkToHashBucket(); pNode->linkToVerList( NULL, pOlderNode); } } /**************************************************************************** Desc: This routine links a new node to its F_Database according to whether or not it is an update transaction or a read transaction. It coalesces out any unnecessary versions. This routine assumes that the node cache mutex is already locked. ****************************************************************************/ void F_CachedNode::linkToDatabase( F_Database * pDatabase, F_Db * pDb, FLMUINT64 ui64LowTransId, FLMBOOL bMostCurrent) { F_CachedNode * pTmpNode; m_ui64LowTransId = ui64LowTransId; // Before coalescing, link to F_Database. // The following test determines if the node is an // uncommitted version generated by the update transaction. // If so, we mark it as such, and link it at the head of the // F_Database list - so we can get rid of it quickly if we abort // the transaction. if (pDb->getTransType() == XFLM_UPDATE_TRANS) { // If we are in an update transaction, there better not // be any newer versions in the list and the high // transaction ID returned better be FLM_MAX_UINT64. flmAssert( m_pNewerVersion == NULL); setTransID( FLM_MAX_UINT64); // If the low transaction ID is the same as the transaction, // we may have modified this node during the transaction. // Unfortunately, there is no sure way to tell, so we are // forced to assume it may have been modified. If the // transaction aborts, we will get rid if this version out // of cache. if (ui64LowTransId == pDb->getTransID()) { setUncommitted(); linkToDatabaseAtHead( pDatabase); } else { unsetUncommitted(); linkToDatabaseAtEnd( pDatabase); } } else { FLMUINT64 ui64HighTransId; // Adjust the high transaction ID to be the same as // the transaction ID - we may have gotten a FLM_MAX_UINT64 // back, but that is possible even if the node is // not the most current version. Besides that, it is // possible that in the mean time one or more update // transactions have come along and created one or // more newer versions of the node. if (bMostCurrent) { // This may be showing up as most current simply because we have // a newer node that was dirty - meaning it would not have been // written to block cache yet - so our read operation would have // read the "most current" version of the block that contains // this node - but it isn't really the most current version of // the node. if (m_pNewerVersion && !m_pNewerVersion->readingInNode()) { ui64HighTransId = m_pNewerVersion->getLowTransId() - 1; } else { ui64HighTransId = FLM_MAX_UINT64; } } else { ui64HighTransId = pDb->getTransID(); } setTransID( ui64HighTransId); // For a read transaction, if there is a newer version, // it better have a higher "low transaction ID" #ifdef FLM_DEBUG if (m_pNewerVersion && !m_pNewerVersion->readingInNode()) { flmAssert( m_ui64HighTransId < m_pNewerVersion->m_ui64LowTransId); if( m_ui64HighTransId >= m_pNewerVersion->m_ui64LowTransId) { checkReadFromDisk( pDb); } } #endif unsetUncommitted(); linkToDatabaseAtEnd( pDatabase); } // Coalesce any versions that overlap - can only // coalesce older versions. For an updater, there // should not be any newer versions. For a reader, it // is impossible to know how high up it can coalesce. // The read operation that read the node may have // gotten back a FLM_MAX_UINT64 for its high transaction // ID - but after that point in time, it is possible // that one or more update transactions may have come // along and created one or more newer versions that // it would be incorrect to coalesce with. // In reality, a read transaction has to ignore the // FLM_MAX_UINT64 in the high transaction ID anyway // because there is no way to know if it is correct. // Coalesce older versions. for (;;) { if ((pTmpNode = m_pOlderVersion) == NULL) { break; } // Stop if we encounter one that is being read in. if (pTmpNode->readingInNode()) { break; } // If there is no overlap between these two, there is // nothing more to coalesce. if (m_ui64LowTransId > pTmpNode->m_ui64HighTransId) { break; } if (m_ui64HighTransId <= pTmpNode->m_ui64HighTransId) { // This assert represents the following case, // which should not be possible to hit: // pOlder->m_ui64HighTransId > m_ui64HighTransId. // This cannot be, because if pOlder has a higher // transaction ID, we would have found it up above and // not tried to have read it in. flmAssert( 0); #ifdef FLM_DEBUG checkReadFromDisk( pDb); #endif } else if (m_ui64LowTransId >= pTmpNode->m_ui64LowTransId) { m_ui64LowTransId = pTmpNode->m_ui64LowTransId; pTmpNode->freeCache( (FLMBOOL)((pTmpNode->nodeInUse() || pTmpNode->readingInNode()) ? TRUE : FALSE)); } else { // This assert represents the following case, // which should not be possible to hit: // m_ui64LowTransId < pOlder->m_ui64LowTransId. // This cannot be, because pOlder has to have been read // in to memory by a transaction whose transaction ID is // less than or equal to our own. That being the case, // it would be impossible for our transaction to have // found a version of the node that is older than pOlder. flmAssert( 0); #ifdef FLM_DEBUG checkReadFromDisk( pDb); #endif } } } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::resizeDataBuffer( FLMUINT uiSize, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldSize; FLMUINT uiNewSize; FLMUINT uiDataBufSize = calcDataBufSize( uiSize); FLMBYTE * pucActualAlloc; FLMBOOL bHeapAlloc = FALSE; void * pvThis = this; FLMBOOL bLockedMutex = FALSE; flmAssert( !m_uiDataBufSize || m_pucData); flmAssert( !m_uiStreamUseCount); if( uiDataBufSize == m_uiDataBufSize) { goto Exit; } if( !bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bLockedMutex = TRUE; } uiOldSize = memSize(); if (!m_pucData) { pucActualAlloc = NULL; if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->allocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_nodeDataRelocator, uiDataBufSize, &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc))) { goto Exit; } } else { pucActualAlloc = getActualPointer( m_pucData); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->reallocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_nodeDataRelocator, m_uiDataBufSize, uiDataBufSize, &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc))) { goto Exit; } } flmAssert( *((F_CachedNode **)pucActualAlloc) == this); setNodeAndDataPtr( pucActualAlloc); m_uiDataBufSize = uiDataBufSize; uiNewSize = memSize(); if (m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize; } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize; if( bHeapAlloc) { linkToHeapList(); } else if( m_uiFlags & FDOM_HEAP_ALLOC) { unlinkFromHeapList(); } Exit: if( bLockedMutex) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } flmAssert( !m_uiDataBufSize || m_pucData); return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::resizeChildElmList( FLMUINT uiChildElmCount, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldSize; FLMBYTE * pucActualAlloc; FLMBOOL bHeapAlloc = FALSE; void * pvThis = this; if( uiChildElmCount == m_nodeInfo.uiChildElmCount) { goto Exit; } if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } uiOldSize = memSize(); if( !uiChildElmCount) { // The only thing we better be doing if we pass in a zero, is // reducing the number of child elements. Hence, the current // child element count better be non-zero. flmAssert( m_nodeInfo.uiChildElmCount); pucActualAlloc = getActualPointer( m_pNodeList); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( calcNodeListBufSize( m_nodeInfo.uiChildElmCount), &pucActualAlloc); } else { if( !m_nodeInfo.uiChildElmCount) { pucActualAlloc = NULL; rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->allocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_nodeListRelocator, calcNodeListBufSize( uiChildElmCount), &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); } else { pucActualAlloc = getActualPointer( m_pNodeList); rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->reallocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_nodeListRelocator, calcNodeListBufSize( m_nodeInfo.uiChildElmCount), calcNodeListBufSize( uiChildElmCount), &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); } flmAssert( *((F_CachedNode **)pucActualAlloc) == this); } if (RC_OK( rc)) { FLMUINT uiNewSize; m_nodeInfo.uiChildElmCount = uiChildElmCount; if (m_nodeInfo.uiChildElmCount) { setNodeListPtr( pucActualAlloc); } else { m_pNodeList = NULL; } uiNewSize = memSize(); if (m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize; } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize; if( bHeapAlloc) { linkToHeapList(); } else if( m_uiFlags & FDOM_HEAP_ALLOC) { unlinkFromHeapList(); } } if (!bMutexAlreadyLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::resizeAttrList( FLMUINT uiAttrCount, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldSize; FLMBYTE * pucActualAlloc; FLMBOOL bHeapAlloc = FALSE; void * pvThis = this; if( uiAttrCount == m_uiAttrCount) { goto Exit; } if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } uiOldSize = memSize(); if( !uiAttrCount) { // The only thing we better be doing if we pass in a zero, is // reducing the number of attributes. Hence, the current // attribute count better be non-zero. flmAssert( m_uiAttrCount); pucActualAlloc = getActualPointer( m_ppAttrList); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( calcAttrListBufSize( m_uiAttrCount), &pucActualAlloc); } else { if( !m_uiAttrCount) { pucActualAlloc = NULL; rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->allocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_attrListRelocator, calcAttrListBufSize( uiAttrCount), &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); } else { pucActualAlloc = getActualPointer( m_ppAttrList); rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->reallocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_attrListRelocator, calcAttrListBufSize( m_uiAttrCount), calcAttrListBufSize( uiAttrCount), &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); } flmAssert( *((F_CachedNode **)pucActualAlloc) == this); } if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } if (RC_OK( rc)) { FLMUINT uiNewSize; m_uiAttrCount = uiAttrCount; if (m_uiAttrCount) { setAttrListPtr( pucActualAlloc); } else { m_ppAttrList = NULL; } uiNewSize = memSize(); if (m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize; } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize; gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize; if( bHeapAlloc) { linkToHeapList(); } else if( m_uiFlags & FDOM_HEAP_ALLOC) { unlinkFromHeapList(); } } if (!bMutexAlreadyLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } Exit: return( rc); } /**************************************************************************** Desc: This routine retrieves a node from disk. ****************************************************************************/ RCODE F_NodeCacheMgr::readNodeFromDisk( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_CachedNode * pNode, FLMUINT64 * pui64LowTransId, FLMBOOL * pbMostCurrent) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; FLMBOOL bCloseIStream = FALSE; F_BTreeIStream btreeIStream; if( RC_BAD( rc = pDb->getCachedBTree( uiCollection, &pBTree))) { goto Exit; } if (RC_BAD( rc = btreeIStream.openStream( pDb, pBTree, XFLM_EXACT, uiCollection, ui64NodeId, 0, 0))) { goto Exit; } bCloseIStream = TRUE; // Read the node from the B-Tree if (RC_BAD( rc = pNode->readNode( pDb, uiCollection, ui64NodeId, &btreeIStream, (FLMUINT)btreeIStream.remainingSize(), NULL))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } pNode->m_uiOffsetIndex = btreeIStream.getOffsetIndex(); pNode->m_ui32BlkAddr = btreeIStream.getBlkAddr(); pBTree->btGetTransInfo( pui64LowTransId, pbMostCurrent); Exit: if( bCloseIStream) { btreeIStream.closeStream(); } if( pBTree) { pBTree->Release(); } return( rc); } /**************************************************************************** Desc: This routine retrieves a node from the node cache. ****************************************************************************/ RCODE F_NodeCacheMgr::retrieveNode( F_Db * pDb, FLMUINT uiCollection, // Collection node is in. FLMUINT64 ui64NodeId, // Node ID F_DOMNode ** ppDOMNode) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; F_Database * pDatabase = pDb->m_pDatabase; F_CachedNode * pNode; F_CachedNode * pNewerNode; F_CachedNode * pOlderNode; FLMUINT64 ui64LowTransId; FLMBOOL bMostCurrent; FLMUINT64 ui64CurrTransId; F_NOTIFY_LIST_ITEM * pNotify; FLMUINT uiNumLooks; FLMBOOL bDontPoisonCache = pDb->m_uiFlags & FDB_DONT_POISON_CACHE ? TRUE : FALSE; if (RC_BAD( rc = pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // Get the current transaction ID flmAssert( pDb->m_eTransType != XFLM_NO_TRANS); ui64CurrTransId = pDb->getTransID(); flmAssert( ui64NodeId != 0); // Lock the node cache mutex f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; // Reset the DB's inactive time pDb->m_uiInactiveTime = 0; Start_Find: findNode( pDb, uiCollection, ui64NodeId, ui64CurrTransId, bDontPoisonCache, &uiNumLooks, &pNode, &pNewerNode, &pOlderNode); if (pNode) { // Have the DOM Node point to the node we found goto Exit1; } // Did not find the node, fetch from disk // Increment the number of faults only if we retrieve the record from disk. m_Usage.uiCacheFaults++; m_Usage.uiCacheFaultLooks += uiNumLooks; // Create a place holder for the object. if (RC_BAD( rc = allocNode( &pNode, TRUE))) { goto Exit; } pNode->m_nodeInfo.ui64NodeId = ui64NodeId; pNode->m_nodeInfo.uiCollection = uiCollection; // Set the F_Database so that other threads looking for this node in // cache will find it and wait until the read has completed. If // the F_Database is not set, other threads will attempt their own read, // because they won't match a NULL F_Database. The result of not setting // the F_Database is that multiple copies of the same version of a particular // node could end up in cache. pNode->m_pDatabase = pDatabase; linkIntoNodeCache( pNewerNode, pOlderNode, pNode, !bDontPoisonCache); pNode->setReadingIn(); pNode->incrNodeUseCount(); pNode->m_pNotifyList = NULL; // Unlock mutex before reading in from disk. f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = FALSE; // Read node from disk. rc = readNodeFromDisk( pDb, uiCollection, ui64NodeId, pNode, &ui64LowTransId, &bMostCurrent); // Relock mutex f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; // If read was successful, link the node to its place in // the F_Database list and coalesce any versions that overlap // this one. if (RC_OK( rc)) { pNode->linkToDatabase( pDb->m_pDatabase, pDb, ui64LowTransId, bMostCurrent); } pNode->unsetReadingIn(); // Notify any threads waiting for the read to complete. pNotify = pNode->m_pNotifyList; pNode->m_pNotifyList = NULL; if (pNotify) { notifyWaiters( pNotify, (F_CachedNode *)((RC_BAD( rc)) ? (F_CachedNode *)NULL : pNode), rc); } pNode->decrNodeUseCount(); // If we did not succeed, free the F_CachedNode structure. if (RC_BAD( rc)) { pNode->freeCache( FALSE); goto Exit; } // If this item was purged while we were reading it in, // start over with the search. if (pNode->nodePurged()) { if (!pNode->nodeInUse()) { pNode->freePurged(); } // Start over with the find - this one has // been marked for purging. goto Start_Find; } Exit1: // Have the DOM Node point to the node we read in from disk if( *ppDOMNode == NULL) { if( RC_BAD( rc = allocDOMNode( ppDOMNode))) { goto Exit; } } if ( (*ppDOMNode)->m_pCachedNode) { (*ppDOMNode)->m_pCachedNode->decrNodeUseCount(); } (*ppDOMNode)->m_pCachedNode = pNode; pNode->incrNodeUseCount(); Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /**************************************************************************** Desc: This routine creates a node into the node cache. This is ONLY called when a new node is being created. ****************************************************************************/ RCODE F_NodeCacheMgr::createNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_DOMNode ** ppDOMNode) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->m_pDatabase; F_COLLECTION * pCollection; F_CachedNode * pNode = NULL; F_CachedNode * pNewerNode = NULL; F_CachedNode * pOlderNode = NULL; FLMBOOL bMutexLocked = FALSE; // A zero ui64NodeId means we are to use the next node ID for the // collection. if( !ui64NodeId) { if (RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } ui64NodeId = pCollection->ui64NextNodeId; // Lock the node cache mutex f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; } else { // Lock the node cache mutex f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; // See if we can find the node in cache findNode( pDb, uiCollection, ui64NodeId, pDb->m_ui64CurrTransID, TRUE, NULL, &pNode, &pNewerNode, &pOlderNode); if (pNode) { // If we found the last committed version, instead of replacing it, // we want to change its high transaction ID, and go create a new // node to put in cache. if (pNode->m_ui64LowTransId < pDb->m_ui64CurrTransID) { // pOlderNode and pNode should be the same at this point if we // found something. Furthermore, the high transaction ID on what // we found better be -1 - most current version. flmAssert( pOlderNode == pNode); flmAssert( pOlderNode->m_ui64HighTransId == FLM_MAX_UINT64); pOlderNode->setTransID( (pDb->m_ui64CurrTransID - 1)); flmAssert( pOlderNode->m_ui64HighTransId >= pOlderNode->m_ui64LowTransId); pOlderNode->setUncommitted(); pOlderNode->setLatestVer(); pOlderNode->unlinkFromDatabase(); pOlderNode->linkToDatabaseAtHead( pDatabase); } else { // Found latest UNCOMMITTED VERSION pNode = NULL; rc = RC_SET_AND_ASSERT( NE_XFLM_EXISTS); goto Exit; } } } // We are positioned to insert the new node. For an update, it // must always be the newest version. flmAssert( !pNewerNode); // Create a new object. if (RC_BAD( rc = allocNode( &pNode, bMutexLocked))) { goto Exit; } pNode->m_nodeInfo.ui64NodeId = ui64NodeId; pNode->m_nodeInfo.uiCollection = uiCollection; pNode->m_uiOffsetIndex = 0; pNode->m_ui32BlkAddr = 0; // NOTE: Not everything is initialized in pNode at this point, but // no other thread should be accessing it anyway. The caller of this // function must ensure that all of the necessary items get set before // releasing the node. // Set the F_Database so that other threads looking for this node in // cache will find it and wait until the read has completed. If // the F_Database is not set, other threads will attempt their own read, // because they won't match a NULL F_Database. The result of not setting // the F_Database is that multiple copies of the same version of a particular // node could end up in cache. pNode->m_pDatabase = pDatabase; linkIntoNodeCache( pNewerNode, pOlderNode, pNode, TRUE); // Link the node to its place in the F_Database list pNode->linkToDatabase( pDatabase, pDb, pDb->m_ui64CurrTransID, TRUE); // Have the DOM node point to the node we created flmAssert( *ppDOMNode == NULL); if( RC_BAD( rc = allocDOMNode( ppDOMNode))) { goto Exit; } if ( (*ppDOMNode)->m_pCachedNode) { (*ppDOMNode)->m_pCachedNode->decrNodeUseCount(); } (*ppDOMNode)->m_pCachedNode = pNode; pNode->incrNodeUseCount(); Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /**************************************************************************** Desc: This routine makes a writeable copy of the node pointed to by F_DOMNode. ****************************************************************************/ RCODE F_NodeCacheMgr::_makeWriteCopy( F_Db * pDb, F_CachedNode ** ppCachedNode) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->m_pDatabase; F_CachedNode * pNewerNode = NULL; F_CachedNode * pOlderNode = *ppCachedNode; FLMBOOL bMutexLocked = FALSE; flmAssert( pOlderNode->m_ui64HighTransId == FLM_MAX_UINT64); flmAssert( !pOlderNode->m_pNewerVersion); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; // Create a new object. if (RC_BAD( rc = allocNode( &pNewerNode, TRUE))) { goto Exit; } // If we found the last committed version, instead of replacing it, // we want to change its high transaction ID, and go create a new // node to put in cache. // Although this routine could be written to not do anything if we // are already on the uncommitted version of the node, for performance // reasons, we would prefer that they make the check on the outside // before calling this routine. flmAssert( pOlderNode->m_ui64LowTransId < pDb->m_ui64CurrTransID); pOlderNode->setTransID( pDb->m_ui64CurrTransID - 1); flmAssert( pOlderNode->m_ui64HighTransId >= pOlderNode->m_ui64LowTransId); pOlderNode->setUncommitted(); pOlderNode->setLatestVer(); pOlderNode->unlinkFromDatabase(); pOlderNode->linkToDatabaseAtHead( pDatabase); pNewerNode->m_pDatabase = pDatabase; pNewerNode->m_uiFlags = pOlderNode->m_uiFlags; pNewerNode->m_uiOffsetIndex = pOlderNode->m_uiOffsetIndex; pNewerNode->m_ui32BlkAddr = pOlderNode->m_ui32BlkAddr; if( pNewerNode->m_uiFlags & FDOM_HEAP_ALLOC) { pNewerNode->m_uiFlags &= ~FDOM_HEAP_ALLOC; } f_memcpy( &pNewerNode->m_nodeInfo, &pOlderNode->m_nodeInfo, sizeof( F_NODE_INFO)); if (pNewerNode->m_uiFlags & (FDOM_SIGNED_QUICK_VAL | FDOM_UNSIGNED_QUICK_VAL)) { pNewerNode->m_numberVal = pOlderNode->m_numberVal; } if( pNewerNode->m_uiFlags & FDOM_HAVE_CELM_LIST) { // Need to set to zero, because we really haven't allocated // space for it yet. pNewerNode->m_nodeInfo.uiChildElmCount = 0; if( pOlderNode->m_nodeInfo.uiChildElmCount) { if( RC_BAD( rc = pNewerNode->resizeChildElmList( pOlderNode->m_nodeInfo.uiChildElmCount, TRUE))) { goto Exit; } f_memcpy( pNewerNode->m_pNodeList, pOlderNode->m_pNodeList, sizeof( NODE_ITEM) * pNewerNode->m_nodeInfo.uiChildElmCount); } } else { flmAssert( !pOlderNode->m_nodeInfo.uiChildElmCount); } if( !(pNewerNode->m_uiFlags & FDOM_VALUE_ON_DISK)) { if( pNewerNode->getDataLength()) { if (RC_BAD( rc = pNewerNode->resizeDataBuffer( pNewerNode->getDataLength(), TRUE))) { goto Exit; } f_memcpy( pNewerNode->getDataPtr(), pOlderNode->getDataPtr(), pNewerNode->getDataLength()); } } else { flmAssert( pNewerNode->getDataLength()); flmAssert( !pNewerNode->m_nodeInfo.uiChildElmCount); } if( pOlderNode->m_uiAttrCount) { if( RC_BAD( rc = pNewerNode->importAttributeList( pDb, pOlderNode, TRUE))) { goto Exit; } } linkIntoNodeCache( NULL, pOlderNode, pNewerNode, TRUE); // Link the node to its place in the F_Database list pNewerNode->linkToDatabase( pDatabase, pDb, pDb->m_ui64CurrTransID, TRUE); // Update the node pointer passed into the routine if( *ppCachedNode) { (*ppCachedNode)->decrNodeUseCount(); } *ppCachedNode = pNewerNode; pNewerNode->incrNodeUseCount(); // Set pNewerNode to NULL so it won't get freed at Exit pNewerNode = NULL; Exit: // A non-NULL pNewerNode means there was an error of some kind where we will // need to free up the cached item. if (pNewerNode) { flmAssert( RC_BAD( rc)); delete pNewerNode; } if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /**************************************************************************** Desc: This routine is called to remove a node from cache. If this is an uncommitted version of the node, it should remove that version from cache. If the last committed version is in cache, it should set the high transaction ID on that version to be one less than the transaction ID of the update transaction. ****************************************************************************/ void F_NodeCacheMgr::removeNode( F_Db * pDb, F_CachedNode * pNode, FLMBOOL bDecrementUseCount, FLMBOOL bMutexLocked) { F_Database * pDatabase = pDb->m_pDatabase; flmAssert( pNode); // Lock the mutex if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } // Decrement the node use count if told to by the caller. if (bDecrementUseCount) { pNode->decrNodeUseCount(); } // Unset the new and dirty flags pNode->unsetNodeDirtyAndNew( pDb, TRUE); // Determine if pNode is the last committed version // or a node that was added by this same transaction. // If it is the last committed version, set its high transaction ID. // Otherwise, remove the node from cache. if (pNode->m_ui64LowTransId < pDb->m_ui64CurrTransID) { // The high transaction ID on pNode better be -1 - most current version. flmAssert( pNode->m_ui64HighTransId == FLM_MAX_UINT64); pNode->setTransID( (pDb->m_ui64CurrTransID - 1)); flmAssert( pNode->m_ui64HighTransId >= pNode->m_ui64LowTransId); pNode->setUncommitted(); pNode->setLatestVer(); pNode->unlinkFromDatabase(); pNode->linkToDatabaseAtHead( pDatabase); } else { pNode->freeCache( pNode->nodeInUse()); } if( !bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } } /**************************************************************************** Desc: This routine is called to remove a node from cache. ****************************************************************************/ void F_NodeCacheMgr::removeNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId) { F_CachedNode * pNode; // Lock the mutex f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); // Find the node in cache findNode( pDb, uiCollection, ui64NodeId, pDb->m_ui64CurrTransID, TRUE, NULL, &pNode, NULL, NULL); if( pNode) { removeNode( pDb, pNode, FALSE, TRUE); } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /**************************************************************************** Desc: This routine is called when an F_Database object is going to be removed from the shared memory area. At that point, we also need to get rid of all nodes that have been cached for that F_Database. ****************************************************************************/ void F_Database::freeNodeCache( void) { FLMUINT uiNumFreed = 0; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); while (m_pFirstNode) { m_pFirstNode->freeCache( m_pFirstNode->nodeInUse()); // Release the CPU every 100 nodes freed. if (++uiNumFreed == 100) { f_yieldCPU(); uiNumFreed = 0; } } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /**************************************************************************** Desc: This routine is called when an update transaction aborts. At that point, we need to get rid of any uncommitted versions of nodes in the node cache. ****************************************************************************/ void F_Database::freeModifiedNodes( F_Db * pDb, FLMUINT64 ui64OlderTransId) { F_CachedNode * pNode; F_CachedNode * pOlderVersion; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pNode = m_pFirstNode; while (pNode) { if (pNode->nodeUncommitted()) { if (pNode->nodeIsLatestVer()) { pNode->setTransID( FLM_MAX_UINT64); pNode->unsetUncommitted(); pNode->unsetLatestVer(); pNode->unlinkFromDatabase(); pNode->linkToDatabaseAtEnd( this); } else { // Save the older version - we may be changing its // high transaction ID back to FLM_MAX_UINT64 pOlderVersion = pNode->m_pOlderVersion; // Clear the dirty and new flags pNode->unsetNodeDirtyAndNew( pDb, TRUE); // Free the uncommitted version. pNode->freeCache( (FLMBOOL)((pNode->nodeInUse() || pNode->readingInNode()) ? TRUE : FALSE)); // If the older version has a high transaction ID that // is exactly one less than our current transaction, // it is the most current version. Hence, we need to // change its high transaction ID back to FLM_MAX_UINT64. if (pOlderVersion && pOlderVersion->m_ui64HighTransId == ui64OlderTransId) { pOlderVersion->setTransID( FLM_MAX_UINT64); } } pNode = m_pFirstNode; } else { // We can stop when we hit a committed version, because // uncommitted versions are always linked in together at // the head of the list. break; } } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /**************************************************************************** Desc: This routine is called when an update transaction commits. At that point, we need to unset the "uncommitted" flag on any nodes currently in node cache for the F_Database object. ****************************************************************************/ void F_Database::commitNodeCache( void) { F_CachedNode * pNode; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pNode = m_pFirstNode; while (pNode) { if (pNode->nodeUncommitted()) { pNode->unsetUncommitted(); pNode->unsetLatestVer(); pNode = pNode->m_pNextInDatabase; } else { // We can stop when we hit a committed version, because // uncommitted versions are always linked in together at // the head of the list. break; } } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /**************************************************************************** Desc: This routine is called when a collection in the database is deleted. All nodes in node cache that are in that collection must be removed from cache. ****************************************************************************/ void F_Db::removeCollectionNodes( FLMUINT uiCollection, FLMUINT64 ui64TransId) { F_CachedNode * pNode; F_CachedNode * pNextNode; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pNode = m_pDatabase->m_pFirstNode; // Stay in the loop until we have freed all nodes in the // collection while (pNode) { // Save the pointer to the previous entry in the list because // we may end up unlinking pNode below, in which case we would // have lost the previous entry. pNextNode = pNode->m_pNextInDatabase; // Only look at nodes in this collection if (pNode->m_nodeInfo.uiCollection == uiCollection) { flmAssert( pNode->m_pDatabase == m_pDatabase); // Only look at the most current versions. if (pNode->m_ui64HighTransId == FLM_MAX_UINT64) { // Better not be a newer version. flmAssert( pNode->m_pNewerVersion == NULL); if (pNode->m_ui64LowTransId < ui64TransId) { // This version was not added or modified by this // transaction so it's high transaction ID should simply // be set to one less than the current transaction ID. pNode->setTransID( ui64TransId - 1); flmAssert( pNode->m_ui64HighTransId >= pNode->m_ui64LowTransId); pNode->setUncommitted(); pNode->setLatestVer(); pNode->unlinkFromDatabase(); pNode->linkToDatabaseAtHead( m_pDatabase); } else { // The node was added or modified in this // transaction. Simply remove it from cache. pNode->freeCache( pNode->nodeInUse()); } } else { // If not most current version, the node's high transaction // ID better already be less than transaction ID. flmAssert( pNode->m_ui64HighTransId < ui64TransId); } } pNode = pNextNode; } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL F_CachedNode::findChildElm( FLMUINT uiChildElmNameId, FLMUINT * puiInsertPos) { FLMBOOL bFound = FALSE; FLMUINT uiLoop; NODE_ITEM * pChildElmNode; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblNameId; // If the child element count is <= 4, do a sequential search through // the array. Otherwise, do a binary search. if ((uiTblSize = m_nodeInfo.uiChildElmCount) <= 4) { for (uiLoop = 0, pChildElmNode = m_pNodeList; uiLoop < m_nodeInfo.uiChildElmCount && pChildElmNode->uiNameId < uiChildElmNameId; uiLoop++, pChildElmNode++) { ; } if (uiLoop < m_nodeInfo.uiChildElmCount) { *puiInsertPos = uiLoop; if (pChildElmNode->uiNameId == uiChildElmNameId) { bFound = TRUE; } } else { *puiInsertPos = uiLoop; } } else { uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblNameId = m_pNodeList [uiMid].uiNameId; if (uiTblNameId == uiChildElmNameId) { // Found Match *puiInsertPos = uiMid; bFound = TRUE; goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found *puiInsertPos = (uiChildElmNameId < uiTblNameId) ? uiMid : uiMid + 1; goto Exit; } if (uiChildElmNameId < uiTblNameId) { if (uiMid == 0) { *puiInsertPos = 0; goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { *puiInsertPos = uiMid + 1; goto Exit; } uiLow = uiMid + 1; } } } Exit: return( bFound); } /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void F_CachedNode::checkReadFromDisk( F_Db * pDb) { FLMUINT64 ui64LowTransId; FLMBOOL bMostCurrent; RCODE rc; // Need to unlock the node cache mutex before doing the read. f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); rc = gv_XFlmSysData.pNodeCacheMgr->readNodeFromDisk( pDb, m_nodeInfo.uiCollection, m_nodeInfo.ui64NodeId, this, &ui64LowTransId, &bMostCurrent); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } #endif /**************************************************************************** Desc: ****************************************************************************/ void F_CachedNode::setNodeDirty( F_Db * pDb, FLMBOOL bNew) { if (!nodeIsDirty()) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); // Should already be the uncommitted version. flmAssert( nodeUncommitted()); // Should NOT be the latest version - those cannot // be set to dirty. - latest ver flag is only set // for nodes that should be returned to being the // latest version of the node if the transaction // aborts. flmAssert( !nodeIsLatestVer()); // Unlink from its database, set the dirty flag, // and relink at the head. unlinkFromDatabase(); if (bNew) { m_uiFlags |= (FDOM_DIRTY | FDOM_NEW); } else { m_uiFlags |= FDOM_DIRTY; } linkToDatabaseAtHead( pDb->m_pDatabase); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); pDb->m_uiDirtyNodeCount++; } else if (bNew) { m_uiFlags |= FDOM_NEW; } } /**************************************************************************** Desc: ****************************************************************************/ void F_CachedNode::unsetNodeDirtyAndNew( F_Db * pDb, FLMBOOL bMutexAlreadyLocked) { // When outputting a binary or text stream, it is possible that the // dirty flag was unset when the last buffer was output if (nodeIsDirty()) { if( !bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } // Unlink from its database, unset the dirty flag, // and relink at the head. unlinkFromDatabase(); if( m_uiFlags & FDOM_DIRTY) { flmAssert( pDb->m_uiDirtyNodeCount); pDb->m_uiDirtyNodeCount--; } m_uiFlags &= ~(FDOM_DIRTY | FDOM_NEW); linkToDatabaseAtHead( pDb->m_pDatabase); if( !bMutexAlreadyLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } } } /***************************************************************************** Desc: Calculate the SEN length of a number and add it to the node info item. ******************************************************************************/ FINLINE void flmAddInfoSenLen( XFLM_NODE_INFO_ITEM * pInfoItem, FLMUINT64 ui64Num, FLMUINT * puiTotalOverhead) { FLMUINT uiSenLen = f_getSENByteCount( ui64Num); pInfoItem->ui64Bytes += (FLMUINT64)uiSenLen; pInfoItem->ui64Count++; (*puiTotalOverhead) += uiSenLen; } /***************************************************************************** Desc: Node header format ******************************************************************************/ // Header size 1 byte // // Node and data type 1 byte (bits = HDDDNNNN) // H = Have data (1 bit) // D = Data type (3 bits) // N = Node type (4 bits) // // Storage Flags 1-5 bytes (typically 1 byte) // NSF_HAVE_BASE_ID_BIT // NSF_HAVE_META_VALUE_BIT // NSF_HAVE_SIBLINGS_BIT // NSF_HAVE_CHILDREN_BIT // NSF_HAVE_ATTR_LIST_BIT // NSF_HAVE_CELM_LIST_BIT // NSF_HAVE_DATA_LEN_BIT // // NSF_EXT_HAVE_DCHILD_COUNT_BIT // NSF_EXT_READ_ONLY_BIT // NSF_EXT_CANNOT_DELETE_BIT // NSF_EXT_PREFIX_BIT // NSF_EXT_ENCRYPTED_BIT // NSF_EXT_ANNOTATION_BIT // NSF_EXT_QUARANTINED_BIT // // Document ID 0-9 byte SEN // Base ID 1-9 byte SEN - if NSF_HAVE_BASE_ID_BIT is set // Parent ID 0-9 byte SEN (offset from base) // Name ID 0-5 byte SEN // Prefix ID 0-5 byte SEN // Meta value 0-9 byte SEN - if NSF_HAVE_META_VALUE_BIT is set // Prev+Next Siblings 0-9 + 0-9 byte SEN (offset from base) - if NSF_HAVE_SIBLINGS_BIT is set // First+Last Child ID 0-9 + 0-9 byte SEN (offset from base) - if NSF_HAVE_CHILDREN_BIT is set // Data node child count 0-5 byte SEN - If NSF_HAVE_CHILDREN_BIT and NSF_EXT_HAVE_DCHILD_COUNT_BIT is set // Child Element Count 0-5 byte SEN - if NSF_HAVE_CELM_LIST_BIT is set // Encryption ID 0-5 byte SEN - if NSF_EXT_ENCRYPTED_BIT set // Annotation node 0-9 byte SEN (offset from base) - if NSF_EXT_ANNOTATION_BIT is set // Data length 0-5 byte SEN - if NSF_HAVE_DATA_LEN_BIT is set // // After the "core" header, one or more of the following may be present: // // If HAVE_CELM_LIST_BIT is set and we actually have a non-zero child element count: // child element list [0-5 SEN for element ID + 0-9 SEN for node id]... // // IV 0 if no encryption, 8 or 16 bytes if encrypting // Value 0+ bytes /***************************************************************************** Desc: Creates a variable-sized header for the node and copies it into the supplied buffer. The buffer must be sized to allow at least MAX_DOM_HEADER_SIZE bytes. ******************************************************************************/ RCODE F_CachedNode::headerToBuf( FLMBOOL bFixedSizeHeader, FLMBYTE * pucBuf, FLMUINT * puiHeaderStorageSize, XFLM_NODE_INFO * pNodeInfo, F_Db * pDb) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucStart = pucBuf; FLMUINT uiLoop; FLMUINT uiFlagsLen; FLMUINT uiStorageFlags = 0; FLMUINT uiDataChildCount = getDataChildCount(); FLMUINT uiEncDefId = 0; FLMUINT uiDataLength = getDataLength(); FLMUINT uiNameId = getNameId(); FLMUINT uiPrefixId = getPrefixId(); FLMUINT uiFlags = m_uiFlags; eDomNodeType eNodeType = getNodeType(); FLMUINT64 ui64NodeId = m_nodeInfo.ui64NodeId; FLMUINT64 ui64BaseId = ui64NodeId; FLMUINT64 ui64DocId = getDocumentId(); FLMUINT64 ui64ParentId = getParentId(); FLMUINT64 ui64FirstChildId = getFirstChildId(); FLMUINT64 ui64LastChildId = getLastChildId(); FLMUINT64 ui64PrevSibId = getPrevSibId(); FLMUINT64 ui64NextSibId = getNextSibId(); FLMUINT64 ui64AnnotationId = getAnnotationId(); FLMUINT64 ui64MetaValue = getMetaValue(); FLMUINT64 ui64Tmp; FLMBYTE ucTmpSEN[ FLM_MAX_SEN_LEN]; FLMBYTE * pucTmpSEN; FLMUINT uiTotalOverhead = 0; if( !ui64NodeId) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Storage flags used by fixed and variable size headers if( uiFlags & FDOM_READ_ONLY) { uiStorageFlags |= NSF_EXT_READ_ONLY_BIT; } if( uiFlags & FDOM_CANNOT_DELETE) { uiStorageFlags |= NSF_EXT_CANNOT_DELETE_BIT; } if( uiFlags & FDOM_QUARANTINED) { uiStorageFlags |= NSF_EXT_QUARANTINED_BIT; } if( uiFlags & FDOM_HAVE_CELM_LIST) { uiStorageFlags |= NSF_HAVE_CELM_LIST_BIT; } if( uiFlags & FDOM_NAMESPACE_DECL) { uiStorageFlags |= NSF_EXT_NAMESPACE_DECL_BIT; } if( m_uiAttrCount) { flmAssert( eNodeType == ELEMENT_NODE); uiStorageFlags |= NSF_HAVE_ATTR_LIST_BIT; } if( uiDataLength) { if( (uiEncDefId = getEncDefId()) != 0) { uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT; } } // Output the header if( bFixedSizeHeader) { if (pucBuf) { flmAssert( !pNodeInfo && !pDb && puiHeaderStorageSize); // Set the header size bytes *pucBuf++ = XFLM_FIXED_SIZE_HEADER_TOKEN; // Encode the node type *pucBuf++ = ((((FLMBYTE)m_nodeInfo.uiDataType) & 0x07) << 4) | (((FLMBYTE)eNodeType) & 0x0F); // Document ID U642FBA( ui64DocId, pucBuf); pucBuf += sizeof( FLMUINT64); // Parent ID U642FBA( ui64ParentId, pucBuf); pucBuf += sizeof( FLMUINT64); // Name ID UD2FBA( (FLMUINT32)uiNameId, pucBuf); pucBuf += sizeof( FLMUINT32); // Prefix ID UD2FBA( (FLMUINT32)uiPrefixId, pucBuf); pucBuf += sizeof( FLMUINT32); // Metavalue U642FBA( ui64MetaValue, pucBuf); pucBuf += sizeof( FLMUINT64); // Previous and next siblings U642FBA( ui64PrevSibId, pucBuf); pucBuf += sizeof( FLMUINT64); U642FBA( ui64NextSibId, pucBuf); pucBuf += sizeof( FLMUINT64); // First and last children U642FBA( ui64FirstChildId, pucBuf); pucBuf += sizeof( FLMUINT64); U642FBA( ui64LastChildId, pucBuf); pucBuf += sizeof( FLMUINT64); // Data child count UD2FBA( (FLMUINT32)uiDataChildCount, pucBuf); pucBuf += sizeof( FLMUINT32); // Child element count UD2FBA( (FLMUINT32)m_nodeInfo.uiChildElmCount, pucBuf); pucBuf += sizeof( FLMUINT32); // Data length UD2FBA( (FLMUINT32)uiDataLength, pucBuf); pucBuf += sizeof( FLMUINT32); // Encryption definition ID UD2FBA( (FLMUINT32)uiEncDefId, pucBuf); pucBuf += sizeof( FLMUINT32); // Annotation ID U642FBA( ui64AnnotationId, pucBuf); pucBuf += sizeof( FLMUINT64); // Storage flags UD2FBA( (FLMUINT32)uiStorageFlags, pucBuf); pucBuf += sizeof( FLMUINT32); flmAssert( (FLMUINT)(pucBuf - pucStart) == FIXED_DOM_HEADER_SIZE); *puiHeaderStorageSize = FIXED_DOM_HEADER_SIZE; } else { flmAssert( pNodeInfo && pDb && !puiHeaderStorageSize); pNodeInfo->headerSize.ui64Bytes++; pNodeInfo->headerSize.ui64Count++; pNodeInfo->nodeAndDataType.ui64Bytes++; pNodeInfo->nodeAndDataType.ui64Count++; pNodeInfo->documentId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->documentId.ui64Count++; pNodeInfo->parentId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->parentId.ui64Count++; pNodeInfo->nameId.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->nameId.ui64Count++; pNodeInfo->prefixId.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->prefixId.ui64Count++; pNodeInfo->metaValue.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->metaValue.ui64Count++; pNodeInfo->prevSibId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->prevSibId.ui64Count++; pNodeInfo->nextSibId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->nextSibId.ui64Count++; pNodeInfo->firstChildId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->firstChildId.ui64Count++; pNodeInfo->lastChildId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->lastChildId.ui64Count++; pNodeInfo->dataChildCount.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->dataChildCount.ui64Count++; pNodeInfo->childElmCount.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->childElmCount.ui64Count++; pNodeInfo->unencDataLen.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->unencDataLen.ui64Count++; pNodeInfo->encDefId.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->encDefId.ui64Count++; pNodeInfo->annotationId.ui64Bytes += sizeof( FLMUINT64); pNodeInfo->annotationId.ui64Count++; pNodeInfo->flags.ui64Bytes += sizeof( FLMUINT32); pNodeInfo->flags.ui64Count++; uiTotalOverhead += FIXED_DOM_HEADER_SIZE; } } else { // Determine the base ID if( ui64DocId < ui64BaseId) { flmAssert( ui64DocId); ui64BaseId = ui64DocId; } if( ui64ParentId && ui64ParentId < ui64BaseId) { ui64BaseId = m_nodeInfo.ui64ParentId; } if( ui64PrevSibId && ui64PrevSibId < ui64BaseId) { ui64BaseId = ui64PrevSibId; } if( ui64NextSibId && ui64NextSibId < ui64BaseId) { ui64BaseId = ui64NextSibId; } if( ui64FirstChildId && ui64FirstChildId < ui64BaseId) { flmAssert( ui64LastChildId); ui64BaseId = ui64FirstChildId; } if( ui64LastChildId && ui64LastChildId < ui64BaseId) { flmAssert( ui64FirstChildId); ui64BaseId = ui64LastChildId; } if( ui64AnnotationId && ui64AnnotationId < ui64BaseId) { ui64BaseId = ui64AnnotationId; } if (pucBuf) { flmAssert( !pNodeInfo && !pDb && puiHeaderStorageSize); // Reserve a byte for the header length pucBuf++; // Encode the node type *pucBuf++ = ((((FLMBYTE)m_nodeInfo.uiDataType) & 0x07) << 4) | (((FLMBYTE)eNodeType) & 0x0F) | (uiDataLength ? 0x80 : 0); // Document ID f_encodeSEN( ui64DocId, &pucBuf); // Encode the base ID if it isn't equal to the document ID if( ui64BaseId != ui64DocId) { uiStorageFlags |= NSF_HAVE_BASE_ID_BIT; f_encodeSEN( ui64BaseId, &pucBuf); } // Parent ID if( (ui64Tmp = ui64ParentId) == 0) { ui64Tmp = ui64NodeId; } f_encodeSEN( ui64Tmp - ui64BaseId, &pucBuf); // Name ID f_encodeSEN( uiNameId, &pucBuf); // Prefix ID if( uiPrefixId) { uiStorageFlags |= NSF_EXT_HAVE_PREFIX_BIT; f_encodeSEN( uiPrefixId, &pucBuf); } // Metavalue if( ui64MetaValue) { uiStorageFlags |= NSF_HAVE_META_VALUE_BIT; f_encodeSEN( ui64MetaValue, &pucBuf); } // Previous and next siblings if( ui64PrevSibId || ui64NextSibId) { if( (ui64Tmp = ui64PrevSibId) == 0) { ui64Tmp = ui64NodeId; } f_encodeSEN( ui64Tmp - ui64BaseId, &pucBuf); if( (ui64Tmp = ui64NextSibId) == 0) { ui64Tmp = ui64NodeId; } f_encodeSEN( ui64Tmp - ui64BaseId, &pucBuf); uiStorageFlags |= NSF_HAVE_SIBLINGS_BIT; } // First, last, and data children if( ui64FirstChildId) { flmAssert( ui64LastChildId); f_encodeSEN( ui64FirstChildId - ui64BaseId, &pucBuf); f_encodeSEN( ui64LastChildId - ui64BaseId, &pucBuf); uiStorageFlags |= NSF_HAVE_CHILDREN_BIT; if( uiDataChildCount) { f_encodeSEN( uiDataChildCount, &pucBuf); uiStorageFlags |= NSF_EXT_HAVE_DCHILD_COUNT_BIT; } } // Child element count if( uiFlags & FDOM_HAVE_CELM_LIST) { // NOTE: It is legal for m_nodeInfo.uiChildElmCount to be zero. // The FDOM_EXT_CHILD_ELM_LIST bit is also used to enforce // the fact that all of the child elements must have unique // name IDs. f_encodeSEN( m_nodeInfo.uiChildElmCount, &pucBuf); } // Encryption ID if( uiEncDefId) { uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT; f_encodeSEN( uiEncDefId, &pucBuf); } // Annotation ID if( ui64AnnotationId) { uiStorageFlags |= NSF_EXT_ANNOTATION_BIT; f_encodeSEN( ui64AnnotationId - ui64BaseId, &pucBuf); } // Output the data length if needed if( uiDataLength && (uiEncDefId || (uiFlags & FDOM_HAVE_CELM_LIST) || m_uiAttrCount)) { uiStorageFlags |= NSF_HAVE_DATA_LEN_BIT; f_encodeSEN( uiDataLength, &pucBuf); } // Output the storage flags (inverted SEN) uiFlagsLen = f_getSENByteCount( uiStorageFlags); if( uiFlagsLen > 1) { pucTmpSEN = ucTmpSEN; f_encodeSEN( uiStorageFlags, &pucTmpSEN); for( uiLoop = uiFlagsLen; uiLoop > 0; uiLoop--) { *pucBuf++ = ucTmpSEN[ uiLoop - 1]; } } else { *pucBuf++ = (FLMBYTE)uiStorageFlags; } flmAssert( (FLMUINT)(pucBuf - pucStart) <= MAX_DOM_HEADER_SIZE); // Set the header size *puiHeaderStorageSize = (FLMUINT)(pucBuf - pucStart); *pucStart = (FLMBYTE)(*puiHeaderStorageSize); } else { flmAssert( pNodeInfo && pDb && !puiHeaderStorageSize); pNodeInfo->headerSize.ui64Bytes++; pNodeInfo->headerSize.ui64Count++; pNodeInfo->nodeAndDataType.ui64Bytes++; pNodeInfo->nodeAndDataType.ui64Count++; pNodeInfo->documentId.ui64Bytes += f_getSENByteCount( ui64DocId); pNodeInfo->documentId.ui64Count++; uiTotalOverhead = 3; // Document ID flmAddInfoSenLen( &pNodeInfo->documentId, ui64DocId, &uiTotalOverhead); // Encode the base ID if it isn't equal to the document ID if (ui64BaseId != ui64DocId) { uiStorageFlags |= NSF_HAVE_BASE_ID_BIT; flmAddInfoSenLen( &pNodeInfo->baseId, ui64BaseId, &uiTotalOverhead); } // Parent ID if ((ui64Tmp = ui64ParentId) == 0) { ui64Tmp = ui64NodeId; } flmAddInfoSenLen( &pNodeInfo->parentId, ui64Tmp - ui64BaseId, &uiTotalOverhead); // Name ID flmAddInfoSenLen( &pNodeInfo->nameId, uiNameId, &uiTotalOverhead); // Prefix ID if (uiPrefixId) { uiStorageFlags |= NSF_EXT_HAVE_PREFIX_BIT; flmAddInfoSenLen( &pNodeInfo->prefixId, uiPrefixId, &uiTotalOverhead); } // Meta Value if (ui64MetaValue) { uiStorageFlags |= NSF_HAVE_META_VALUE_BIT; flmAddInfoSenLen( &pNodeInfo->metaValue, ui64MetaValue, &uiTotalOverhead); } // First/Last sibling if (ui64PrevSibId || ui64NextSibId) { if ((ui64Tmp = ui64PrevSibId) == 0) { ui64Tmp = ui64NodeId; } flmAddInfoSenLen( &pNodeInfo->prevSibId, ui64Tmp, &uiTotalOverhead); if ((ui64Tmp = ui64NextSibId) == 0) { ui64Tmp = ui64NodeId; } flmAddInfoSenLen( &pNodeInfo->nextSibId, ui64Tmp, &uiTotalOverhead); uiStorageFlags |= NSF_HAVE_SIBLINGS_BIT; } // First, last, and data children if (ui64FirstChildId) { flmAddInfoSenLen( &pNodeInfo->firstChildId, ui64FirstChildId - ui64BaseId, &uiTotalOverhead); flmAddInfoSenLen( &pNodeInfo->lastChildId, ui64LastChildId - ui64BaseId, &uiTotalOverhead); uiStorageFlags |= NSF_HAVE_CHILDREN_BIT; if (uiDataChildCount) { flmAddInfoSenLen( &pNodeInfo->dataChildCount, uiDataChildCount, &uiTotalOverhead); uiStorageFlags |= NSF_EXT_HAVE_DCHILD_COUNT_BIT; } } // Child element count - may be zero, so we should test the flag. if (uiFlags & FDOM_HAVE_CELM_LIST) { flmAddInfoSenLen( &pNodeInfo->childElmCount, m_nodeInfo.uiChildElmCount, &uiTotalOverhead); } // Encryption ID if (uiEncDefId) { uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT; flmAddInfoSenLen( &pNodeInfo->encDefId, uiEncDefId, &uiTotalOverhead); } // Annotation ID if( ui64AnnotationId) { uiStorageFlags |= NSF_EXT_ANNOTATION_BIT; flmAddInfoSenLen( &pNodeInfo->annotationId, ui64AnnotationId - ui64BaseId, &uiTotalOverhead); } // Data length if needed if( uiDataLength && (uiEncDefId || (uiFlags & FDOM_HAVE_CELM_LIST) || m_uiAttrCount)) { uiStorageFlags |= NSF_HAVE_DATA_LEN_BIT; flmAddInfoSenLen( &pNodeInfo->unencDataLen, uiDataLength, &uiTotalOverhead); } flmAddInfoSenLen( &pNodeInfo->flags, uiStorageFlags, &uiTotalOverhead); } } // Account for other overhead. if (pNodeInfo) { if (eNodeType == ELEMENT_NODE) { NODE_ITEM * pNodeItem; FLMUINT uiNodeCount = getChildElmCount(); FLMUINT uiPrevNameId = 0; FLMUINT64 ui64ElmNodeId = getNodeId(); // Go through the child element list and calculate the length needed // to store the name id and node id for each one. pNodeItem = m_pNodeList; for( uiLoop = 0; uiLoop < uiNodeCount; pNodeItem++, uiLoop++) { flmAssert( pNodeItem->uiNameId > uiPrevNameId); flmAssert( pNodeItem->ui64NodeId > ui64ElmNodeId); flmAddInfoSenLen( &pNodeInfo->childElmNameId, pNodeItem->uiNameId - uiPrevNameId, &uiTotalOverhead); flmAddInfoSenLen( &pNodeInfo->childElmNodeId, pNodeItem->ui64NodeId - ui64ElmNodeId, &uiTotalOverhead); uiPrevNameId = pNodeItem->uiNameId; } // Determine space taken by attributes. if (m_uiAttrCount) { if( RC_BAD( rc = exportAttributeList( pDb, NULL, pNodeInfo))) { goto Exit; } } } // Determine space needed to store encryption IV and any // encryption padding. if (uiEncDefId) { F_ENCDEF * pEncDef; FLMUINT uiTmp; if (RC_BAD( rc = pDb->m_pDict->getEncDef( uiEncDefId, &pEncDef))) { goto Exit; } uiTmp = pEncDef->pCcs->getIVLen(); flmAssert( uiTmp == 8 || uiTmp == 16); pNodeInfo->encIV.ui64Bytes += (FLMUINT64)uiTmp; pNodeInfo->encIV.ui64Count++; uiTotalOverhead += uiTmp; uiTmp = getEncLen( uiDataLength) - uiDataLength; if (uiTmp) { pNodeInfo->encPadding.ui64Bytes += (FLMUINT64)uiTmp; pNodeInfo->encPadding.ui64Count++; uiTotalOverhead += uiTmp; } } pNodeInfo->totalOverhead.ui64Bytes += (FLMUINT64)uiTotalOverhead; pNodeInfo->totalOverhead.ui64Count++; switch (eNodeType) { case ELEMENT_NODE: pNodeInfo->elementNode.ui64Bytes += (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; pNodeInfo->elementNode.ui64Count++; break; case DATA_NODE: pNodeInfo->dataNode.ui64Bytes += (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; pNodeInfo->dataNode.ui64Count++; break; case COMMENT_NODE: pNodeInfo->commentNode.ui64Bytes += (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; pNodeInfo->commentNode.ui64Count++; break; default: pNodeInfo->otherNode.ui64Bytes += (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; pNodeInfo->otherNode.ui64Count++; break; } switch (m_nodeInfo.uiDataType) { case XFLM_NODATA_TYPE: pNodeInfo->dataNodata.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataNodata.ui64Count++; break; case XFLM_TEXT_TYPE: pNodeInfo->dataString.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataString.ui64Count++; break; case XFLM_NUMBER_TYPE: pNodeInfo->dataNumeric.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataNumeric.ui64Count++; break; case XFLM_BINARY_TYPE: pNodeInfo->dataBinary.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataBinary.ui64Count++; break; default: flmAssert( 0); break; } } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE flmReadNodeInfo( FLMUINT uiCollection, FLMUINT64 ui64NodeId, IF_IStream * pIStream, FLMUINT uiOverallLength, FLMBOOL bAssertOnCorruption, F_NODE_INFO * pNodeInfo, FLMUINT * puiStorageFlags, FLMBOOL * pbFixedSizeHeader) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLMUINT uiStorageFlags = 0; FLMUINT uiStorageFlagsLen; FLMUINT uiHeaderStorageSize; FLMBYTE ucHeader[ MAX_DOM_HEADER_SIZE]; const FLMBYTE * pucHeader; const FLMBYTE * pucHeaderEnd; FLMUINT64 ui64BaseId = 0; FLMBYTE ucTmpSEN[ FLM_MAX_SEN_LEN]; FLMBYTE * pucTmpSEN; FLMBOOL bEOFValid = TRUE; FLMBOOL bHaveData; #ifndef FLM_DEBUG F_UNREFERENCED_PARM( bAssertOnCorruption); #endif // Set the node ID and collection pNodeInfo->uiCollection = uiCollection; pNodeInfo->ui64NodeId = ui64NodeId; // Read the expected length of the header if( RC_BAD( rc = pIStream->read( &ucHeader[ 0], 1))) { goto Exit; } bEOFValid = FALSE; // A length value of XFLM_FIXED_SIZE_HEADER_TOKEN indicates that we have a // non-compressed header on the node. This type of header is used when a // large text or binary value is streamed into the database. if( ucHeader[ 0] == XFLM_FIXED_SIZE_HEADER_TOKEN) { // Read the rest of the header uiHeaderStorageSize = FIXED_DOM_HEADER_SIZE; if( RC_BAD( rc = pIStream->read( &ucHeader[ 1], uiHeaderStorageSize - 1, NULL))) { goto Exit; } pucHeader = ucHeader; pucHeaderEnd = pucHeader + uiHeaderStorageSize; // Skip past the header size byte pucHeader++; // Get the node type, data type, and data flag pNodeInfo->eNodeType = (eDomNodeType)((*pucHeader) & 0x0F); pNodeInfo->uiDataType = ((*pucHeader) >> 4) & 0x07; pucHeader++; // Document ID pNodeInfo->ui64DocumentId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); // Parent ID pNodeInfo->ui64ParentId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); // Name ID pNodeInfo->uiNameId = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); // Prefix ID pNodeInfo->uiPrefixId = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); // Metavalue pNodeInfo->ui64MetaValue = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); // Previous and next siblings pNodeInfo->ui64PrevSibId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); pNodeInfo->ui64NextSibId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); // First and last children pNodeInfo->ui64FirstChildId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); pNodeInfo->ui64LastChildId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); // Data child count pNodeInfo->uiDataChildCount = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); // Child element count pNodeInfo->uiChildElmCount = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); if( pNodeInfo->uiChildElmCount && pNodeInfo->eNodeType != ELEMENT_NODE) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Data length pNodeInfo->uiDataLength = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); // Encryption Id pNodeInfo->uiEncDefId = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); if( pNodeInfo->uiEncDefId && !pNodeInfo->uiDataLength) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Annotation ID pNodeInfo->ui64AnnotationId = FB2U64( pucHeader); pucHeader += sizeof( FLMUINT64); // Storage flags uiStorageFlags = FB2UD( pucHeader); pucHeader += sizeof( FLMUINT32); // Set the fixed size header flag if( pbFixedSizeHeader) { *pbFixedSizeHeader = TRUE; } } else { if( (uiHeaderStorageSize = (FLMUINT)ucHeader[ 0]) > MAX_DOM_HEADER_SIZE) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Read the rest of the header if( RC_BAD( rc = pIStream->read( &ucHeader[ 1], uiHeaderStorageSize - 1, NULL))) { goto Exit; } pucHeader = ucHeader; pucHeaderEnd = pucHeader + uiHeaderStorageSize; // Get the storage flags uiStorageFlags = pucHeader[ uiHeaderStorageSize - 1]; uiStorageFlagsLen = f_getSENLength( (FLMBYTE)uiStorageFlags); if( uiStorageFlagsLen > 1) { pucTmpSEN = ucTmpSEN; for( uiLoop = 1; uiLoop <= uiStorageFlagsLen; uiLoop++) { *pucTmpSEN++ = pucHeader[ uiHeaderStorageSize - uiLoop]; } pucTmpSEN = ucTmpSEN; if( RC_BAD( rc = f_decodeSEN( (const FLMBYTE **)&pucTmpSEN, pucTmpSEN + uiStorageFlagsLen, &uiStorageFlags))) { goto Exit; } } // Skip past the header size byte pucHeader++; // Get the node type, data type, and data flag pNodeInfo->eNodeType = (eDomNodeType)((*pucHeader) & 0x0F); if( pNodeInfo->eNodeType == INVALID_NODE || pNodeInfo->eNodeType > PROCESSING_INSTRUCTION_NODE) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } pNodeInfo->uiDataType = ((*pucHeader) >> 4) & 0x07; bHaveData = *pucHeader & 0x80 ? TRUE : FALSE; pucHeader++; // Document ID if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64DocumentId))) { goto Exit; } // Base ID if( uiStorageFlags & NSF_HAVE_BASE_ID_BIT) { if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &ui64BaseId))) { goto Exit; } } else { ui64BaseId = pNodeInfo->ui64DocumentId; } // Parent ID if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64ParentId))) { goto Exit; } if( (pNodeInfo->ui64ParentId += ui64BaseId) == ui64NodeId) { pNodeInfo->ui64ParentId = 0; } // Name ID if( RC_BAD( rc = f_decodeSEN( &pucHeader, pucHeaderEnd, &pNodeInfo->uiNameId))) { goto Exit; } // Prefix ID if( uiStorageFlags & NSF_EXT_HAVE_PREFIX_BIT) { if( RC_BAD( rc = f_decodeSEN( &pucHeader, pucHeaderEnd, &pNodeInfo->uiPrefixId))) { goto Exit; } } else { pNodeInfo->uiPrefixId = 0; } // Metavalue if( uiStorageFlags & NSF_HAVE_META_VALUE_BIT) { if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64MetaValue))) { goto Exit; } } else { pNodeInfo->ui64MetaValue = 0; } // Previous and next siblings if( uiStorageFlags & NSF_HAVE_SIBLINGS_BIT) { if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64PrevSibId))) { goto Exit; } if( (pNodeInfo->ui64PrevSibId += ui64BaseId) == ui64NodeId) { pNodeInfo->ui64PrevSibId = 0; } if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64NextSibId))) { goto Exit; } if( (pNodeInfo->ui64NextSibId += ui64BaseId) == ui64NodeId) { pNodeInfo->ui64NextSibId = 0; } } else { pNodeInfo->ui64PrevSibId = 0; pNodeInfo->ui64NextSibId = 0; } // First and last children. Also will read the data child count. if( uiStorageFlags & NSF_HAVE_CHILDREN_BIT) { if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64FirstChildId))) { goto Exit; } pNodeInfo->ui64FirstChildId += ui64BaseId; if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64LastChildId))) { goto Exit; } pNodeInfo->ui64LastChildId += ui64BaseId; if( uiStorageFlags & NSF_EXT_HAVE_DCHILD_COUNT_BIT) { if( RC_BAD( rc = f_decodeSEN( &pucHeader, pucHeaderEnd, &pNodeInfo->uiDataChildCount))) { goto Exit; } } else { pNodeInfo->uiDataChildCount = 0; } } else { pNodeInfo->ui64FirstChildId = 0; pNodeInfo->ui64LastChildId = 0; pNodeInfo->uiDataChildCount = 0; } // Child element count if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT) { // This bit should only be set for elements. if( pNodeInfo->eNodeType != ELEMENT_NODE) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // NOTE: This bit may be set even if there are no children. // This bit also serves to indicate that this particular // element is a unique child element, and we should keep // a list of child elements as they are added and removed. // We should also enforce that no children have the same // name id. if( RC_BAD( rc = f_decodeSEN( &pucHeader, pucHeaderEnd, &pNodeInfo->uiChildElmCount))) { goto Exit; } // If the count > 0, the NSF_HAVE_CHILDREN_BIT better also be set. if( pNodeInfo->uiChildElmCount) { if( !(uiStorageFlags & NSF_HAVE_CHILDREN_BIT)) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } } } else { pNodeInfo->uiChildElmCount = 0; } // Encryption ID if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT) { if( RC_BAD( rc = f_decodeSEN( &pucHeader, pucHeaderEnd, &pNodeInfo->uiEncDefId))) { goto Exit; } } else { pNodeInfo->uiEncDefId = 0; } // Annotation ID if( uiStorageFlags & NSF_EXT_ANNOTATION_BIT) { if( RC_BAD( rc = f_decodeSEN64( &pucHeader, pucHeaderEnd, &pNodeInfo->ui64AnnotationId))) { goto Exit; } pNodeInfo->ui64AnnotationId += ui64BaseId; } else { pNodeInfo->ui64AnnotationId = 0; } if( uiStorageFlags & NSF_HAVE_DATA_LEN_BIT) { if( RC_BAD( rc = f_decodeSEN( &pucHeader, pucHeaderEnd, &pNodeInfo->uiDataLength))) { goto Exit; } } else { pNodeInfo->uiDataLength = 0; } // Account for storage flags pucHeader += uiStorageFlagsLen; // Make sure the header was the expected size if( pucHeader != pucHeaderEnd) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Set the data length to whatever is remaining if we didn't // have a data length in the header if( bHaveData && !(uiStorageFlags & NSF_HAVE_DATA_LEN_BIT)) { flmAssert( uiOverallLength >= uiHeaderStorageSize); pNodeInfo->uiDataLength = uiOverallLength - uiHeaderStorageSize; } // Set the fixed size header flag if( pbFixedSizeHeader) { *pbFixedSizeHeader = FALSE; } } if( pNodeInfo->uiEncDefId && !pNodeInfo->uiDataLength) { #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } if( puiStorageFlags) { *puiStorageFlags = uiStorageFlags; } Exit: if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BAD_SEN) { if( !bEOFValid) { // If one of the calls to read from the stream returned an EOF error, // the database is corrupt. #ifdef FLM_DEBUG if( bAssertOnCorruption) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else #endif { rc = RC_SET( NE_XFLM_DATA_ERROR); } } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::readNode( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, IF_IStream * pIStream, FLMUINT uiOverallLength, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; FLMUINT uiStorageLength; FLMUINT uiStorageFlags; FLMUINT uiIVLen; FLMBYTE ucIV[ 16]; FLMBOOL bEOFValid = TRUE; FLMBOOL bFixedSizeHeader; if( RC_BAD( rc = flmReadNodeInfo( uiCollection, ui64NodeId, pIStream, uiOverallLength, FALSE, &m_nodeInfo, &uiStorageFlags, &bFixedSizeHeader))) { goto Exit; } bEOFValid = FALSE; m_uiFlags = 0; if( bFixedSizeHeader) { m_uiFlags |= FDOM_FIXED_SIZE_HEADER; } // Read the child element list, if any if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT) { if( m_nodeInfo.uiChildElmCount) { FLMUINT uiLen; FLMUINT uiLoop; NODE_ITEM * pElmNode; FLMUINT uiPrevNameId = 0; FLMUINT64 ui64ElmNodeId = getNodeId(); FLMUINT uiChildElmCount = m_nodeInfo.uiChildElmCount; flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); // Need to set to zero so the resizeChildElmList will work. This // is the actual size allocated. m_nodeInfo.uiChildElmCount = 0; if( RC_BAD( rc = resizeChildElmList( uiChildElmCount, FALSE))) { goto Exit; } // Read in all of the element name IDs and node IDs. for( uiLoop = 0, pElmNode = m_pNodeList; uiLoop < m_nodeInfo.uiChildElmCount; uiLoop++, pElmNode++) { if( RC_BAD( rc = f_readSEN( pIStream, &pElmNode->uiNameId, &uiLen))) { goto Exit; } pElmNode->uiNameId += uiPrevNameId; uiPrevNameId = pElmNode->uiNameId; if( RC_BAD( rc = f_readSEN64( pIStream, &pElmNode->ui64NodeId, &uiLen))) { goto Exit; } pElmNode->ui64NodeId += ui64ElmNodeId; } } m_uiFlags |= FDOM_HAVE_CELM_LIST; } // Read the attribute list if( uiStorageFlags & NSF_HAVE_ATTR_LIST_BIT) { if( m_nodeInfo.eNodeType != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = importAttributeList( pDb, pIStream, FALSE))) { goto Exit; } } // Read the initialization vector if this is an encrypted node if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT) { F_Dict * pDict; F_ENCDEF * pEncDef; if( RC_BAD( rc = pDb->getDictionary( &pDict))) { goto Exit; } if( RC_BAD( rc = pDict->getEncDef( getEncDefId(), &pEncDef))) { goto Exit; } uiIVLen = pEncDef->pCcs->getIVLen(); if( uiIVLen != 8 && uiIVLen != 16) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } if( RC_BAD( rc = pIStream->read( ucIV, uiIVLen))) { goto Exit; } if( pucIV) { f_memcpy( pucIV, ucIV, uiIVLen); } uiStorageLength = getEncLen( m_nodeInfo.uiDataLength); } else { uiStorageLength = m_nodeInfo.uiDataLength; } // Read the data part of the node, if any if( uiStorageLength) { // Data size must have room for data to point back to // the node. Always align on 8 byte boundaries - just to be safe. // It is the highest alignment we support. if( calcDataBufSize( uiStorageLength) <= gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->getMaxCellSize() || m_nodeInfo.eNodeType == ELEMENT_NODE) { if( RC_BAD( rc = resizeDataBuffer( uiStorageLength, FALSE))) { goto Exit; } if( RC_BAD( rc = pIStream->read( (char *)getDataPtr(), uiStorageLength))) { goto Exit; } // Decrypt the data if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT) { if( RC_BAD( rc = pDb->decryptData( m_nodeInfo.uiEncDefId, ucIV, getDataPtr(), uiStorageLength, getDataPtr(), uiStorageLength))) { goto Exit; } } // If the type is a number, cache a 'quick' number. if( m_nodeInfo.uiDataType == XFLM_NUMBER_TYPE) { FLMUINT64 ui64Num; FLMBOOL bNeg; if( RC_BAD( rc = flmStorageNumberToNumber( getDataPtr(), getDataLength(), &ui64Num, &bNeg))) { goto Exit; } if( !bNeg) { setUINT64( ui64Num); } else { setINT64( -(FLMINT64)ui64Num); } } } else { flmAssert( m_nodeInfo.eNodeType != ELEMENT_NODE); flmAssert( m_nodeInfo.uiDataType == XFLM_TEXT_TYPE || m_nodeInfo.uiDataType == XFLM_BINARY_TYPE); m_uiFlags |= FDOM_VALUE_ON_DISK; } } if( uiStorageFlags & NSF_EXT_READ_ONLY_BIT) { m_uiFlags |= FDOM_READ_ONLY; } if( uiStorageFlags & NSF_EXT_CANNOT_DELETE_BIT) { m_uiFlags |= FDOM_CANNOT_DELETE; } if( uiStorageFlags & NSF_EXT_QUARANTINED_BIT) { m_uiFlags |= FDOM_QUARANTINED; } if( uiStorageFlags & NSF_EXT_NAMESPACE_DECL_BIT) { m_uiFlags |= FDOM_NAMESPACE_DECL; } // Sanity checks switch( m_nodeInfo.eNodeType) { case DOCUMENT_NODE: { if( !isRootNode() || getFirstChildId() != getLastChildId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } break; } case ELEMENT_NODE: { if( isRootNode() && getParentId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } break; } default: { break; } } Exit: if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BAD_SEN) { if( !bEOFValid) { // If one of the calls to read from the stream returned an EOF error, // the database is corrupt. rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::importAttributeList( F_Db * pDb, IF_IStream * pIStream, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; FLMUINT uiAttrCount; FLMUINT uiNameId; FLMUINT uiBaseNameId; FLMUINT uiStorageFlags; FLMUINT uiPayloadLength; FLMUINT uiLoop; FLMUINT uiInsertPos; F_AttrElmInfo defInfo; flmAssert( !m_uiAttrCount); flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); // Determine the number of attributes if( RC_BAD( rc = f_readSEN( pIStream, &uiAttrCount))) { goto Exit; } if( !uiAttrCount) { goto Exit; } // Import the attributes if( RC_BAD( rc = f_readSEN( pIStream, &uiBaseNameId))) { goto Exit; } for( uiLoop = 0; uiLoop < uiAttrCount; uiLoop++) { if( RC_BAD( rc = f_readSEN( pIStream, &uiNameId))) { goto Exit; } uiNameId += uiBaseNameId; if (getAttribute( uiNameId, &uiInsertPos) != NULL) { flmAssert( 0); } if( RC_BAD( rc = allocAttribute( pDb, uiNameId, NULL, uiInsertPos, &pAttrItem, bMutexAlreadyLocked))) { goto Exit; } if( RC_BAD( rc = f_readSEN( pIStream, &uiStorageFlags))) { goto Exit; } if( uiStorageFlags & ASF_READ_ONLY_BIT) { pAttrItem->m_uiFlags |= FDOM_READ_ONLY; } if( uiStorageFlags & ASF_CANNOT_DELETE_BIT) { pAttrItem->m_uiFlags |= FDOM_CANNOT_DELETE; } if( uiStorageFlags & ASF_HAVE_PREFIX_BIT) { if( RC_BAD( rc = f_readSEN( pIStream, &pAttrItem->m_uiPrefixId))) { goto Exit; } } uiPayloadLength = (uiStorageFlags & ASF_PAYLOAD_LEN_MASK); if( uiPayloadLength == ASF_HAVE_PAYLOAD_LEN_SEN) { if( RC_BAD( rc = f_readSEN( pIStream, &uiPayloadLength))) { goto Exit; } } if( RC_BAD( rc = pDb->m_pDict->getAttribute( pDb, uiNameId, &defInfo))) { goto Exit; } pAttrItem->m_uiDataType = defInfo.getDataType(); if( uiStorageFlags & ASF_ENCRYPTED_BIT) { F_ENCDEF * pEncDef; if( RC_BAD( rc = f_readSEN( pIStream, &pAttrItem->m_uiEncDefId))) { goto Exit; } if( RC_BAD( rc = f_readSEN( pIStream, &pAttrItem->m_uiDecryptedDataLen))) { goto Exit; } if( RC_BAD( rc = pDb->m_pDict->getEncDef( pAttrItem->m_uiEncDefId, &pEncDef))) { goto Exit; } pAttrItem->m_uiIVLen = pEncDef->pCcs->getIVLen(); flmAssert( pAttrItem->m_uiIVLen == 8 || pAttrItem->m_uiIVLen == 16); } if( uiPayloadLength) { if( RC_BAD( rc = pAttrItem->resizePayloadBuffer( uiPayloadLength, bMutexAlreadyLocked))) { goto Exit; } if( RC_BAD( rc = pIStream->read( pAttrItem->getAttrPayloadPtr(), uiPayloadLength))) { goto Exit; } } pAttrItem->m_uiPayloadLen = uiPayloadLength; if( pAttrItem->m_uiDataType == XFLM_NUMBER_TYPE && !pAttrItem->m_uiEncDefId) { FLMBOOL bNeg; if( RC_BAD( rc = flmStorageNumberToNumber( pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength(), &pAttrItem->m_ui64QuickVal, &bNeg))) { goto Exit; } if( !bNeg) { pAttrItem->m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL; } else { pAttrItem->m_uiFlags |= FDOM_SIGNED_QUICK_VAL; } } } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::importAttributeList( F_Db * pDb, F_CachedNode * pSourceNode, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; F_AttrItem * pNewItem; F_AttrItem * pSourceItem; FLMUINT uiLoop; flmAssert( !m_uiAttrCount); if( RC_BAD( rc = resizeAttrList( pSourceNode->m_uiAttrCount, bMutexAlreadyLocked))) { goto Exit; } for (uiLoop = 0; uiLoop < pSourceNode->m_uiAttrCount; uiLoop++) { pSourceItem = pSourceNode->m_ppAttrList [uiLoop]; if( RC_BAD( rc = allocAttribute( pDb, pSourceItem->m_uiNameId, pSourceItem, uiLoop, &pNewItem, bMutexAlreadyLocked))) { goto Exit; } if( pSourceItem->m_uiPayloadLen > sizeof( FLMBYTE *)) { if( RC_BAD( rc = pNewItem->setupAttribute( pDb, pSourceItem->m_uiEncDefId, pSourceItem->getAttrDataLength(), FALSE, bMutexAlreadyLocked))) { goto Exit; } flmAssert( pSourceItem->getAttrPayloadSize() == pNewItem->getAttrPayloadSize()); f_memcpy( pNewItem->getAttrPayloadPtr(), pSourceItem->getAttrPayloadPtr(), pSourceItem->m_uiPayloadLen); } } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ void F_AttrItem::getAttrSizeNeeded( FLMUINT uiBaseNameId, XFLM_NODE_INFO * pNodeInfo, FLMUINT * puiSaveStorageFlags, FLMUINT * puiSizeNeeded) { FLMUINT uiNameSize; FLMUINT uiFlagsSize; FLMUINT uiPrefixSize; FLMUINT uiPayloadLength; FLMUINT uiPayloadLenSize; FLMUINT uiEncIdSize; FLMUINT uiUnencLenSize; FLMUINT uiOverhead = 0; FLMUINT uiStorageFlags; uiNameSize = f_getSENByteCount( m_uiNameId - uiBaseNameId); uiOverhead += uiNameSize; uiStorageFlags = getAttrStorageFlags(); if (puiSaveStorageFlags) { *puiSaveStorageFlags = uiStorageFlags; } uiFlagsSize = f_getSENByteCount( uiStorageFlags); uiOverhead += uiFlagsSize; if( m_uiPrefixId) { uiPrefixSize = f_getSENByteCount( m_uiPrefixId); uiOverhead += uiPrefixSize; } else { uiPrefixSize = 0; } uiPayloadLength = m_uiPayloadLen; (*puiSizeNeeded) += uiPayloadLength; if( uiPayloadLength > ASF_MAX_EMBEDDED_PAYLOAD_LEN) { uiPayloadLenSize = f_getSENByteCount( uiPayloadLength); uiOverhead += uiPayloadLenSize; } else { uiPayloadLenSize = 0; } if( m_uiEncDefId) { flmAssert( uiPayloadLength); uiEncIdSize = f_getSENByteCount( m_uiEncDefId); uiOverhead += uiEncIdSize; uiUnencLenSize = f_getSENByteCount( m_uiDecryptedDataLen); uiOverhead += uiUnencLenSize; } else { uiEncIdSize = 0; uiUnencLenSize = 0; } (*puiSizeNeeded) += uiOverhead; if (pNodeInfo) { FLMUINT uiDataLength; pNodeInfo->nameId.ui64Bytes += (FLMUINT64)uiNameSize; pNodeInfo->nameId.ui64Count++; pNodeInfo->attrFlags.ui64Bytes += (FLMUINT64)uiFlagsSize; pNodeInfo->attrFlags.ui64Count++; if (uiPrefixSize) { pNodeInfo->prefixId.ui64Bytes += (FLMUINT64)uiPrefixSize; pNodeInfo->prefixId.ui64Count++; } if (uiPayloadLenSize) { pNodeInfo->attrPayloadLen.ui64Bytes += (FLMUINT64)uiPayloadLenSize; pNodeInfo->attrPayloadLen.ui64Count++; } uiDataLength = getAttrDataLength(); if (m_uiEncDefId) { FLMUINT uiEncPadding; pNodeInfo->encDefId.ui64Bytes += (FLMUINT64)uiEncIdSize; pNodeInfo->encDefId.ui64Count++; pNodeInfo->unencDataLen.ui64Bytes += (FLMUINT64)uiUnencLenSize; pNodeInfo->unencDataLen.ui64Count++; pNodeInfo->encIV.ui64Bytes += (FLMUINT64)m_uiIVLen; pNodeInfo->encIV.ui64Count++; uiOverhead += m_uiIVLen; flmAssert( m_uiPayloadLen >= m_uiIVLen - uiDataLength); uiEncPadding = m_uiPayloadLen - m_uiIVLen - uiDataLength; if (uiEncPadding) { pNodeInfo->encPadding.ui64Bytes += (FLMUINT64)uiEncPadding; pNodeInfo->encPadding.ui64Count++; uiOverhead += uiEncPadding; } } pNodeInfo->totalOverhead.ui64Bytes += (FLMUINT64)uiOverhead; pNodeInfo->totalOverhead.ui64Count++; pNodeInfo->attributeNode.ui64Bytes += (FLMUINT64)(uiOverhead + uiDataLength); pNodeInfo->attributeNode.ui64Count++; switch (m_uiDataType) { case XFLM_NODATA_TYPE: pNodeInfo->dataNodata.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataNodata.ui64Count++; break; case XFLM_TEXT_TYPE: pNodeInfo->dataString.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataString.ui64Count++; break; case XFLM_NUMBER_TYPE: pNodeInfo->dataNumeric.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataNumeric.ui64Count++; break; case XFLM_BINARY_TYPE: pNodeInfo->dataBinary.ui64Bytes += (FLMUINT64)uiDataLength; pNodeInfo->dataBinary.ui64Count++; break; default: flmAssert( 0); break; } } } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::exportAttributeList( F_Db * pDb, F_DynaBuf * pDynaBuf, XFLM_NODE_INFO * pNodeInfo) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; FLMBYTE * pucStart; FLMBYTE * pucBuf; FLMBYTE * pucEnd; FLMUINT uiPayloadLength; FLMUINT uiLoop; FLMUINT uiSizeNeeded; FLMUINT uiStorageFlags; FLMUINT uiBaseNameId = m_ppAttrList [0]->m_uiNameId; #define MAX_STORAGE_FLAGS 32 FLMUINT storageFlagsList[ MAX_STORAGE_FLAGS]; // Logging should be done by the caller #ifdef FLM_DEBUG if (!pNodeInfo) { flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); } #endif // Determine the size of the node buffer uiSizeNeeded = 0; for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) { pAttrItem = m_ppAttrList [uiLoop]; // If uiAttributeCount < MAX_STORAGE_FLAGS, we pass in a pointer // to that slot in the storageFlagsList array to save the // storage flags, so we don't have to recalculate them in the // 2nd pass - up to the first MAX_STORAGE_FLAGS storage flags // will be saved. if (uiLoop < MAX_STORAGE_FLAGS) { pAttrItem->getAttrSizeNeeded( uiBaseNameId, pNodeInfo, &storageFlagsList [uiLoop], &uiSizeNeeded); } else { pAttrItem->getAttrSizeNeeded( uiBaseNameId, pNodeInfo, NULL, &uiSizeNeeded); } } flmAssert( m_uiAttrCount); if (pNodeInfo) { flmAssert( !pDynaBuf); pNodeInfo->attrCount.ui64Bytes += f_getSENByteCount( m_uiAttrCount); pNodeInfo->attrCount.ui64Count++; pNodeInfo->attrBaseId.ui64Bytes += f_getSENByteCount( uiBaseNameId); pNodeInfo->attrBaseId.ui64Count++; } else { uiSizeNeeded += f_getSENByteCount( m_uiAttrCount); uiSizeNeeded += f_getSENByteCount( uiBaseNameId); flmAssert( pDynaBuf); if( RC_BAD( rc = pDynaBuf->allocSpace( uiSizeNeeded, (void **)&pucBuf))) { goto Exit; } pucStart = pucBuf; pucEnd = pucStart + uiSizeNeeded; if( RC_BAD( rc = f_encodeSEN( m_uiAttrCount, &pucBuf, pucEnd))) { goto Exit; } if( RC_BAD( rc = f_encodeSEN( uiBaseNameId, &pucBuf, pucEnd))) { goto Exit; } // Once we have written out attribute count, we can reset it to zero. for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) { pAttrItem = m_ppAttrList [uiLoop]; if( RC_BAD( rc = f_encodeSEN( pAttrItem->m_uiNameId - uiBaseNameId, &pucBuf, pucEnd))) { goto Exit; } // If we saved the storage flags in the first pass, get them // from storageFlagsList, otherwise recalculate them. uiStorageFlags = (uiLoop < MAX_STORAGE_FLAGS) ? storageFlagsList [uiLoop] : pAttrItem->getAttrStorageFlags(); if( RC_BAD( rc = f_encodeSEN( uiStorageFlags, &pucBuf, pucEnd))) { goto Exit; } if( pAttrItem->m_uiPrefixId) { if( RC_BAD( rc = f_encodeSEN( pAttrItem->m_uiPrefixId, &pucBuf, pucEnd))) { goto Exit; } } uiPayloadLength = pAttrItem->m_uiPayloadLen; if( uiPayloadLength > ASF_MAX_EMBEDDED_PAYLOAD_LEN) { if( RC_BAD( rc = f_encodeSEN( uiPayloadLength, &pucBuf, pucEnd))) { goto Exit; } } if( pAttrItem->m_uiEncDefId) { flmAssert( uiPayloadLength); if( RC_BAD( rc = f_encodeSEN( pAttrItem->m_uiEncDefId, &pucBuf, pucEnd))) { goto Exit; } if( RC_BAD( rc = f_encodeSEN( pAttrItem->m_uiDecryptedDataLen, &pucBuf, pucEnd))) { goto Exit; } } f_memcpy( pucBuf, pAttrItem->getAttrPayloadPtr(), uiPayloadLength); pucBuf += uiPayloadLength; } flmAssert( pucBuf == pucEnd); } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ void F_CachedNode::resetNode( void) { FLMBYTE * pucActualAlloc; // Should not count attribute size here, because it will be subtracted // when the attributes themselves are deleted. FLMUINT uiSize = memSize() - m_uiTotalAttrSize; flmAssert( !m_pPrevInBucket); flmAssert( !m_pNextInBucket); flmAssert( !m_pOlderVersion); flmAssert( !m_pNewerVersion); flmAssert( !m_pPrevInOldList); flmAssert( !m_pNextInOldList); flmAssert( !m_pNotifyList); flmAssert( !m_uiStreamUseCount); flmAssert( !nodeInUse()); f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); // If this is an old version, decrement the old version counters. if (m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize && gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize && gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; if( m_uiFlags & FDOM_HEAP_ALLOC) { unlinkFromHeapList(); } if( m_pucData || m_pNodeList || m_ppAttrList) { f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); if( m_pucData) { pucActualAlloc = getActualPointer( m_pucData); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( m_uiDataBufSize, &pucActualAlloc); m_pucData = NULL; m_uiDataBufSize = 0; } if( m_pNodeList) { pucActualAlloc = getActualPointer( m_pNodeList); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( calcNodeListBufSize( m_nodeInfo.uiChildElmCount), &pucActualAlloc); m_pNodeList = NULL; } if( m_ppAttrList) { FLMUINT uiLoop; for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) { delete m_ppAttrList [uiLoop]; } pucActualAlloc = getActualPointer( m_ppAttrList); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( calcAttrListBufSize( m_uiAttrCount), &pucActualAlloc); m_ppAttrList = NULL; m_uiAttrCount = 0; } } m_ui64LowTransId = 0; m_ui64HighTransId = FLM_MAX_UINT64; m_uiCacheFlags = 0; m_pDatabase = NULL; m_uiFlags = 0; m_uiOffsetIndex = 0; m_ui32BlkAddr = 0; f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); uiSize = memSize(); if (m_ui64HighTransId != FLM_MAX_UINT64) { gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; } gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize; } #undef new #undef delete /**************************************************************************** Desc: ****************************************************************************/ void * F_CachedNode::operator new( FLMSIZET uiSize) #ifndef FLM_NLM throw() #endif { #ifndef FLM_DEBUG F_UNREFERENCED_PARM( uiSize); #endif flmAssert( uiSize == sizeof( F_CachedNode)); f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); return( gv_XFlmSysData.pNodeCacheMgr->m_pNodeAllocator->allocCell( &gv_XFlmSysData.pNodeCacheMgr->m_nodeRelocator, NULL, 0)); } /**************************************************************************** Desc: ****************************************************************************/ void * F_CachedNode::operator new[]( FLMSIZET) #ifndef FLM_NLM throw() #endif { flmAssert( 0); return( NULL); } /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void * F_CachedNode::operator new( FLMSIZET, // uiSize, const char *, // pszFileName, int) // iLineNum) #ifndef FLM_NLM throw() #endif { // This new should never be called flmAssert( 0); return( NULL); } #endif /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void * F_CachedNode::operator new[]( FLMSIZET, // uiSize, const char *, // pszFileName, int) // iLine) #ifndef FLM_NLM throw() #endif { flmAssert( 0); return( NULL); } #endif /**************************************************************************** Desc: ****************************************************************************/ void F_CachedNode::operator delete( void * ptr) { if( !ptr) { return; } f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->m_pNodeAllocator->freeCell( (FLMBYTE *)ptr); } /**************************************************************************** Desc: ****************************************************************************/ void F_CachedNode::operator delete[]( void *) // ptr) { flmAssert( 0); } /**************************************************************************** Desc: ****************************************************************************/ F_AttrItem::~F_AttrItem() { FLMUINT uiSize = memSize(); if (m_pCachedNode) { flmAssert( m_pCachedNode->m_uiTotalAttrSize >= uiSize); m_pCachedNode->m_uiTotalAttrSize -= uiSize; if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; } if( m_uiPayloadLen > sizeof( FLMBYTE *)) { f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); m_pucPayload -= sizeof( F_AttrItem *); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( m_uiPayloadLen + sizeof( F_AttrItem *), &m_pucPayload); } } /**************************************************************************** Desc: ****************************************************************************/ void * F_AttrItem::operator new( FLMSIZET uiSize) #ifndef FLM_NLM throw() #endif { #ifndef FLM_DEBUG F_UNREFERENCED_PARM( uiSize); #endif flmAssert( uiSize == sizeof( F_AttrItem)); f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); return( gv_XFlmSysData.pNodeCacheMgr->m_pAttrItemAllocator->allocCell( &gv_XFlmSysData.pNodeCacheMgr->m_attrItemRelocator, NULL, 0)); } /**************************************************************************** Desc: ****************************************************************************/ void * F_AttrItem::operator new[]( FLMSIZET) #ifndef FLM_NLM throw() #endif { flmAssert( 0); return( NULL); } /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void * F_AttrItem::operator new( FLMSIZET, // uiSize, const char *, // pszFileName, int) // iLineNum) #ifndef FLM_NLM throw() #endif { // This new should never be called flmAssert( 0); return( NULL); } #endif /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void * F_AttrItem::operator new[]( FLMSIZET, // uiSize, const char *, // pszFileName, int) // iLine) #ifndef FLM_NLM throw() #endif { flmAssert( 0); return( NULL); } #endif /**************************************************************************** Desc: ****************************************************************************/ void F_AttrItem::operator delete( void * ptr) { if( !ptr) { return; } f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); gv_XFlmSysData.pNodeCacheMgr->m_pAttrItemAllocator->freeCell( (FLMBYTE *)ptr); } /**************************************************************************** Desc: ****************************************************************************/ void F_AttrItem::operator delete[]( void *) // ptr) { flmAssert( 0); } libxflaim-5.1.969/src/kycollat.cpp0000644000175000017500000002431410511001742020356 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Index collation routines // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kycollat.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE KYFormatUTF8Text( IF_PosIStream * pIStream, FLMUINT uiFlags, FLMUINT uiCompareRules, F_DynaBuf * pDynaBuf); /**************************************************************************** Desc: Build a collated key value piece. ****************************************************************************/ RCODE KYCollateValue( FLMBYTE * pucDest, FLMUINT * puiDestLen, IF_PosIStream * pIStream, FLMUINT uiDataType, FLMUINT uiFlags, FLMUINT uiCompareRules, FLMUINT uiLimit, FLMUINT * puiCollationLen, FLMUINT * puiLuLen, FLMUINT uiLanguage, FLMBOOL bFirstSubstring, FLMBOOL bDataTruncated, FLMBOOL * pbDataTruncated, FLMBOOL * pbOriginalCharsLost) { RCODE rc = NE_XFLM_OK; FLMUINT uiDestLen; IF_BufferIStream * pBufferIStream = NULL; FLMUINT uiCharLimit; FLMUINT uiLength; FLMBYTE * pucTmpDest; FLMUINT uiBytesRead; FLMBOOL bHaveData = TRUE; FLMUNICODE uChar; FLMBYTE ucDynaBuf[ 64]; F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); if (puiLuLen) { *puiLuLen = 0; } if ((uiDestLen = *puiDestLen) == 0) { rc = RC_SET( NE_XFLM_KEY_OVERFLOW); goto Exit; } if (uiDataType != XFLM_TEXT_TYPE) { if( !pIStream->remainingSize()) { bHaveData = FALSE; } } else { FLMUINT64 ui64SavePosition = pIStream->getCurrPosition(); if( RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) { if (rc == NE_XFLM_EOF_HIT) { bHaveData = FALSE; rc = NE_XFLM_OK; } else { goto Exit; } } if( RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) { goto Exit; } // The text is expected to be 0-terminated UTF-8 if ((uiFlags & ICD_ESC_CHAR) || (uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | XFLM_COMP_NO_WHITESPACE | XFLM_COMP_NO_UNDERSCORES | XFLM_COMP_NO_DASHES | XFLM_COMP_WHITESPACE_AS_SPACE | XFLM_COMP_IGNORE_LEADING_SPACE | XFLM_COMP_IGNORE_TRAILING_SPACE))) { dynaBuf.truncateData( 0); if (RC_BAD( rc = KYFormatUTF8Text( pIStream, uiFlags, uiCompareRules, &dynaBuf))) { goto Exit; } if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)dynaBuf.getBufferPtr(), dynaBuf.getDataLength()))) { goto Exit; } pIStream = pBufferIStream; } uiCharLimit = uiLimit ? uiLimit : ICD_DEFAULT_LIMIT; if( (uiLanguage >= FLM_FIRST_DBCS_LANG ) && (uiLanguage <= FLM_LAST_DBCS_LANG)) { if( RC_BAD( rc = f_asiaUTF8ToColText( pIStream, pucDest, &uiDestLen, (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) ? TRUE : FALSE, puiCollationLen, puiLuLen, uiCharLimit, bFirstSubstring, bDataTruncated, pbDataTruncated))) { goto Exit; } } else { if( RC_BAD( rc = flmUTF8ToColText( pIStream, pucDest, &uiDestLen, (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) ? TRUE : FALSE, puiCollationLen, puiLuLen, uiLanguage, uiCharLimit, bFirstSubstring, bDataTruncated, pbOriginalCharsLost, pbDataTruncated))) { goto Exit; } } } // TRICKY: uiDestLen could be set to zero if text and no value. if (!bHaveData || !uiDestLen) { uiDestLen = 0; goto Exit; } switch (uiDataType) { case XFLM_TEXT_TYPE: break; case XFLM_NUMBER_TYPE: { FLMBYTE ucTmpBuf [FLM_MAX_NUM_BUF_SIZE]; uiLength = (FLMUINT)pIStream->remainingSize(); flmAssert( uiLength <= sizeof( ucTmpBuf)); if (RC_BAD( rc = pIStream->read( ucTmpBuf, uiLength, &uiBytesRead))) { goto Exit; } flmAssert( uiBytesRead == uiLength); if (RC_BAD( rc = flmStorageNum2CollationNum( ucTmpBuf, uiBytesRead, pucDest, &uiDestLen))) { goto Exit; } break; } case XFLM_BINARY_TYPE: { uiLength = (FLMUINT)pIStream->remainingSize(); pucTmpDest = pucDest; if (uiLength >= uiLimit) { uiLength = uiLimit; bDataTruncated = TRUE; } // We don't want any single key piece to "pig out" more // than 256 bytes of the key if (uiDestLen > 256) { uiDestLen = 256; } if (uiLength > uiDestLen) { // Compute length so will not overflow uiLength = uiDestLen; bDataTruncated = TRUE; } else { uiDestLen = uiLength; } // Store as is. if (RC_BAD( rc = pIStream->read( pucTmpDest, uiDestLen, &uiBytesRead))) { goto Exit; } if (bDataTruncated && pbDataTruncated) { *pbDataTruncated = TRUE; } break; } default: { rc = RC_SET( NE_XFLM_CANNOT_INDEX_DATA_TYPE); break; } } Exit: if( pBufferIStream) { pBufferIStream->Release(); } *puiDestLen = uiDestLen; return( rc); } /**************************************************************************** Desc: Format text removing leading and trailing spaces. Treat underscores as spaces. As options, remove all spaces and dashes. Ret: NE_XFLM_OK always. WIll truncate so text will fill XFLM_MAX_KEY_SIZE. Allocate 8 more than XFLM_MAX_KEY_SIZE for psDestBuf. Visit: Pass in uiLimit and pass back a truncated flag when the string is truncated. This was not done because we will have to get the exact truncated count that is done in f_tocoll.cpp and that could introduce some bugs. ****************************************************************************/ FSTATIC RCODE KYFormatUTF8Text( IF_PosIStream * pIStream, FLMUINT uiFlags, // ICD flags FLMUINT uiCompareRules, // ICD compare rules F_DynaBuf * pDynaBuf) { RCODE rc = NE_XFLM_OK; FLMUINT uiFirstSpaceCharPos = FLM_MAX_UINT; FLMUNICODE uChar; FLMUINT uiSize; FLMUINT uiStrSize = 0; FLMBYTE * pucTmp; if( !pIStream->remainingSize()) { pDynaBuf->truncateData( 0); goto Exit; } for (;;) { if (RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } goto Exit; } if ((uChar = f_convertChar( uChar, uiCompareRules)) == 0) { continue; } if (uChar == ASCII_SPACE) { if (uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | XFLM_COMP_IGNORE_TRAILING_SPACE)) { // Remember the position of the first space. // When we come to the end of the spaces, we may reset // the size to compress out spaces if necessary. Or, // we may opt to get rid of all of them. if (uiFirstSpaceCharPos == FLM_MAX_UINT) { uiFirstSpaceCharPos = uiStrSize; } } } else { // Once we hit a non-space character, we can turn off the // ignore leading spaces flag. uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); // See if we need to compress spaces. if (uiFirstSpaceCharPos != FLM_MAX_UINT) { // Output exactly one ASCII_SPACE character if we are compressing // spaces. If we are not compressing spaces, then the only other // way uiFirstSpaceCharPos would have been set is if we were // ignoring trailing spaces. In that case, since the spaces // were not trailing spaces, we need to leave them as is. if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) { // A space will already have been encoded into the string. // Since we know a space takes exactly one byte in the UTF8 // space, we can simply set our pointer one byte past where // the last non-space character was found. uiStrSize = uiFirstSpaceCharPos + 1; pDynaBuf->truncateData( uiStrSize); } uiFirstSpaceCharPos = FLM_MAX_UINT; } // If we are allowing escaped characters, backslash is treated // always as an escape character. Whatever follows the // backslash is the character we need to process. if (uChar == ASCII_BACKSLASH && (uiFlags & ICD_ESC_CHAR)) { if (RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { goto Exit; } } } } // Output the character - need at most three bytes if (RC_BAD( rc = pDynaBuf->allocSpace( 3, (void **)&pucTmp))) { goto Exit; } uiSize = 3; if (RC_BAD( rc = f_uni2UTF8( uChar, pucTmp, &uiSize))) { goto Exit; } uiStrSize += uiSize; pDynaBuf->truncateData( uiStrSize); } // If uiFirstSpaceCharPos != FLM_MAX_UINT, it means that all of the // characters at the end of the string were spaces. If we // are ignoring trailing spaces, we need to truncate the string so // they will be ignored. Otherwise, we need to compress them into // a single space. if (uiFirstSpaceCharPos != FLM_MAX_UINT) { if (uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE) { uiStrSize = uiFirstSpaceCharPos; } else { flmAssert( uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE); // A space will already have been encoded into the string. // Since we know a space takes exactly one byte in the UTF8 // space, we can simply set our pointer one byte past where // the last non-space character was found. uiStrSize = uiFirstSpaceCharPos + 1; } pDynaBuf->truncateData( uiStrSize); } // Terminate the UTF-8 string if (RC_BAD( rc = pDynaBuf->appendByte( 0))) { goto Exit; } Exit: return( rc); } libxflaim-5.1.969/src/fslfile.cpp0000644000175000017500000003476310511001742020171 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines for reading and writing logical file headers. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fslfile.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC FLMUINT FSLFileFindEmpty( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr); /*************************************************************************** Desc: Searches a block for an empty LFH slot. This is called whenever a new logical file is create so we re-use the slots. Ret: 0-Empty slot not found non-zero - offset in the block of the empty slot ***************************************************************************/ FSTATIC FLMUINT FSLFileFindEmpty( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr) { FLMUINT uiPos = SIZEOF_STD_BLK_HDR; FLMUINT uiEndPos = blkGetEnd( uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR); while (uiPos < uiEndPos) { if (pLfHdr->ui32LfType == XFLM_LF_INVALID) { break; } uiPos += sizeof( F_LF_HDR); pLfHdr++; } return( (uiPos < uiEndPos) ? uiPos : 0); } /*************************************************************************** Desc: Retrieves the logical file header record from disk & updates LFILE. Called when it is discovered that the LFH for a particular logical file is out of date. *****************************************************************************/ RCODE F_Database::lFileRead( F_Db * pDb, LFILE * pLFile, F_COLLECTION * pCollection ) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache; FLMBOOL bReleaseCache = FALSE; // Read in the block containing the logical file header if (RC_BAD( rc = getBlock( pDb, NULL, pLFile->uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; // Copy the LFH from the block to the LFILE FSLFileIn( (FLMBYTE *)(pSCache->m_pBlkHdr) + pLFile->uiOffsetInBlk, pLFile, pCollection, pLFile->uiBlkAddress, pLFile->uiOffsetInBlk); Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Update the LFH data on disk. *****************************************************************************/ RCODE F_Database::lFileWrite( F_Db * pDb, F_COLLECTION * pCollection, LFILE * pLFile) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache; FLMBOOL bReleaseCache = FALSE; F_LF_HDR * pLfHdr; #ifdef DEBUG F_CachedBlock * pTmpSCache = NULL; #endif flmAssert( !pDb->m_pDatabase->m_pRfl || !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); // Read in the block containing the logical file header if (RC_BAD( rc = getBlock( pDb, NULL, pLFile->uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; // Log the block before modifying it if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } // Now modify the block and set its status to dirty pLfHdr = (F_LF_HDR *)((FLMBYTE *)(pSCache->m_pBlkHdr) + pLFile->uiOffsetInBlk); // If deleted, fill with 0, except for type - it is set below if (pLFile->eLfType == XFLM_LF_INVALID) { f_memset( pLfHdr, 0, sizeof( F_LF_HDR)); pLfHdr->ui32LfType = XFLM_LF_INVALID; } else { pLfHdr->ui32LfNumber = (FLMUINT32)pLFile->uiLfNum; pLfHdr->ui32LfType = (FLMUINT32)pLFile->eLfType; pLfHdr->ui32EncId = (FLMUINT32)pLFile->uiEncId; #ifdef DEBUG if (RC_BAD( rc = getBlock( pDb, NULL, pLFile->uiRootBlk, NULL, &pTmpSCache))) { goto Exit; } if (!isRootBlk( (F_BTREE_BLK_HDR *)pTmpSCache->m_pBlkHdr)) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } #endif pLfHdr->ui32RootBlkAddr = (FLMUINT32)pLFile->uiRootBlk; if (pCollection) { flmAssert( pLFile == &pCollection->lfInfo); flmAssert( pLFile->eLfType == XFLM_LF_COLLECTION); pLfHdr->ui64NextNodeId = pCollection->ui64NextNodeId; pLfHdr->ui64FirstDocId = pCollection->ui64FirstDocId; pLfHdr->ui64LastDocId = pCollection->ui64LastDocId; pCollection->bNeedToUpdateNodes = FALSE; } else { flmAssert( pLFile->eLfType == XFLM_LF_INDEX); pLfHdr->ui64NextNodeId = 0; pLfHdr->ui64FirstDocId = 0; pLfHdr->ui64LastDocId = 0; } } // If the LFILE was deleted, we need to set pLFile->uiLfNum to zero. // This should happen only AFTER the LFILE update is logged, because // logLFileUpdate relies on pLFile->uiLfNum being non-zero. if (pLFile->eLfType == XFLM_LF_INVALID) { pLFile->uiLfNum = 0; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } #ifdef DEBUG if (pTmpSCache) { ScaReleaseCache( pTmpSCache, FALSE); } #endif return( rc); } /*************************************************************************** Desc: Creates and initializes a LFILE structure on disk and in memory. *****************************************************************************/ RCODE F_Database::lFileCreate( F_Db * pDb, LFILE * pLFile, F_COLLECTION * pCollection, FLMUINT uiLfNum, eLFileType eLfType, FLMBOOL bCounts, FLMBOOL bHaveData, FLMUINT uiEncId) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pNewSCache = NULL; F_CachedBlock * pSCache = NULL; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiBlkAddress = 0; FLMUINT uiNextBlkAddress; FLMUINT uiEndPos = 0; FLMUINT uiPos = 0; FLMBOOL bReleaseCache2 = FALSE; FLMBOOL bReleaseCache = FALSE; F_Btree * pbTree = NULL; flmAssert( !pDb->m_pDatabase->m_pRfl || !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); if (eLfType == XFLM_LF_COLLECTION) { if( bCounts) { // Force bCounts to be FALSE in this case flmAssert( 0); bCounts = FALSE; } if( !bHaveData) { // Force bHaveData to be TRUE in this case flmAssert( 0); bHaveData = TRUE; } } // Find an available slot to create the LFH -- follow the linked list // of LFH blocks to find one. uiNextBlkAddress = (FLMUINT)m_uncommittedDbHdr.ui32FirstLFBlkAddr; // Better be at least one LFH block. if (uiNextBlkAddress == 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } while (uiNextBlkAddress != 0) { if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } uiBlkAddress = uiNextBlkAddress; if (RC_BAD( rc = getBlock( pDb, NULL, uiBlkAddress, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; pBlkHdr = pSCache->m_pBlkHdr; uiNextBlkAddress = (FLMUINT)pBlkHdr->ui32NextBlkInChain; uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); if ((uiPos = FSLFileFindEmpty( m_uiBlockSize, pBlkHdr)) != 0) { break; } } // If we did not find a deleted slot we can use, see if there // is room for a new logical file in the last block // in the chain. If not, allocate a new block. if (uiPos == 0) { uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); // Allocate new block? if (uiEndPos + sizeof( F_LF_HDR) >= m_uiBlockSize) { if (RC_BAD( rc = createBlock( pDb, &pNewSCache))) { goto Exit; } bReleaseCache2 = TRUE; pBlkHdr = pNewSCache->m_pBlkHdr; uiNextBlkAddress = (FLMUINT)pBlkHdr->ui32BlkAddr; // Modify the new block's next pointer and other fields. pBlkHdr->ui32NextBlkInChain = 0; pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiBlkAddress; pBlkHdr->ui8BlkType = BT_LFH_BLK; pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - SIZEOF_STD_BLK_HDR); if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiNextBlkAddress; // Set everything up so we are pointing to the new block. ScaReleaseCache( pSCache, FALSE); pSCache = pNewSCache; bReleaseCache2 = FALSE; uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pSCache->m_pBlkHdr); uiBlkAddress = uiNextBlkAddress; } // Modify the end of block pointer -- log block before modifying. uiPos = uiEndPos; uiEndPos += sizeof( F_LF_HDR); } // Call memset to ensure unused bytes are zero. // pBlkHdr, uiPos and uiEndPos should ALL be set. if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } pBlkHdr = pSCache->m_pBlkHdr; f_memset( (FLMBYTE *)(pBlkHdr) + uiPos, 0, sizeof( F_LF_HDR)); flmAssert( uiEndPos >= SIZEOF_STD_BLK_HDR && uiEndPos <= m_uiBlockSize); pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - uiEndPos); // Done with block in this routine ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; // Set the variables in the LFILE structure to later save to disk pLFile->uiLfNum = uiLfNum; pLFile->eLfType = eLfType; pLFile->uiBlkAddress = uiBlkAddress; pLFile->uiOffsetInBlk = uiPos; pLFile->uiEncId = uiEncId; if (pCollection) { pCollection->ui64NextNodeId = 1; pCollection->ui64FirstDocId = 0; pCollection->ui64LastDocId = 0; pCollection->bNeedToUpdateNodes = TRUE; } // Get the btree... if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree))) { goto Exit; } if (RC_BAD( rc = pbTree->btCreate( pDb, pLFile, bCounts, bHaveData))) { goto Exit; } if (RC_BAD( rc = lFileWrite( pDb, pCollection, pLFile))) { goto Exit; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } if (bReleaseCache2) { ScaReleaseCache( pNewSCache, FALSE); } if (pbTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree); } return( rc); } /*************************************************************************** Desc: Delete a logical file. *****************************************************************************/ RCODE F_Database::lFileDelete( F_Db * pDb, F_COLLECTION * pCollection, LFILE * pLFile, FLMBOOL bCounts, FLMBOOL bHaveData) { RCODE rc = NE_XFLM_OK; F_Btree * pbTree = NULL; F_DOMNode * pDoc = NULL; F_DOMNode * pChainNode = NULL; F_DOMNode * pAddrNode = NULL; FLMUINT uiLoop; flmAssert( pDb->m_uiFlags & FDB_UPDATED_DICTIONARY); // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree))) { goto Exit; } // Delete the logical file's B-Tree blocks. // If there is no root block, no need to do anything. flmAssert( pLFile->uiRootBlk); if (pLFile->eLfType == XFLM_LF_COLLECTION) { flmAssert( pCollection); flmAssert( !bCounts && bHaveData); bCounts = FALSE; bHaveData = TRUE; if( RC_BAD( rc = pbTree->btOpen( pDb, pLFile, bCounts, bHaveData))) { goto Exit; } // Delete the B-Tree if (RC_BAD( rc = pbTree->btDeleteTree( pDb->m_pDeleteStatus))) { goto Exit; } } else { FLMUINT puiBlkChains[ BH_MAX_LEVELS]; FLMUINT uiChainCount; flmAssert( !pCollection); flmAssert( pLFile->eLfType == XFLM_LF_INDEX); if( RC_BAD( rc = pbTree->btOpen( pDb, pLFile, bCounts, bHaveData))) { goto Exit; } if( RC_BAD( rc = pbTree->btGetBlockChains( puiBlkChains, &uiChainCount))) { goto Exit; } // Indexes are always deleted in the background. // Set up a maintenance document to free the blocks of // the B-Tree. if( RC_BAD( rc = pDb->createRootNode( XFLM_MAINT_COLLECTION, ELM_DELETE_TAG, ELEMENT_NODE, &pDoc))) { goto Exit; } for( uiLoop = 0; uiLoop < uiChainCount; uiLoop++) { if( RC_BAD( rc = pDoc->createNode( pDb, ELEMENT_NODE, ELM_BLOCK_CHAIN_TAG, XFLM_LAST_CHILD, (IF_DOMNode **)&pChainNode))) { goto Exit; } if( RC_BAD( rc = pChainNode->createAttribute( pDb, ATTR_ADDRESS_TAG, (IF_DOMNode **)&pAddrNode))) { goto Exit; } if( RC_BAD( rc = pAddrNode->setUINT( pDb, puiBlkChains[ uiLoop]))) { goto Exit; } if( RC_BAD( rc = pAddrNode->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pChainNode->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } if( RC_BAD( rc = pDoc->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pDb->documentDone( pDoc))) { goto Exit; } // Signal the maintenance thread that it has work to do f_semSignal( m_hMaintSem); } // Delete the LFILE entry. pLFile->uiRootBlk = 0; pLFile->eLfType = XFLM_LF_INVALID; if( RC_BAD( rc = lFileWrite( pDb, pCollection, pLFile))) { goto Exit; } Exit: if( pChainNode) { pChainNode->Release(); } if( pAddrNode) { pAddrNode->Release(); } if( pDoc) { pDoc->Release(); } if( pbTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree); } return( rc); } /*************************************************************************** Desc: Set the next node ID for a collection. Must be inside an update transaction. *****************************************************************************/ RCODE F_Db::setNextNodeId( FLMUINT uiCollection, FLMUINT64 ui64NextNodeId) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Get a pointer to the collection if (RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } // Set the next NODE ID for the collection if (ui64NextNodeId > pCollection->ui64NextNodeId) { pCollection->ui64NextNodeId = ui64NextNodeId; pCollection->bNeedToUpdateNodes = TRUE; } // Log the operation pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logSetNextNodeId( this, uiCollection, ui64NextNodeId))) { goto Exit; } Exit: if( RC_BAD( rc)) { setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } return( rc); } libxflaim-5.1.969/src/fltrbeg.cpp0000644000175000017500000006134710511001742020170 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains routines for starting a transaction. // // Tabs: 3 // // Copyright (c) 1991, 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fltrbeg.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: This routine unlinks an F_Db from a transaction's list of F_Dbs. ****************************************************************************/ void F_Db::unlinkFromTransList( FLMBOOL bCommitting) { flmAssert( m_pIxdFixups == NULL); if( m_eTransType != XFLM_NO_TRANS) { if (m_uiFlags & FDB_HAS_WRITE_LOCK) { // If this is a commit operation and we have a commit callback, // call the callback function before unlocking the DIB. if (bCommitting && m_pCommitClient) { m_pCommitClient->commit( this); } unlockExclusive(); } m_pDatabase->lockMutex(); if (m_pDict) { unlinkFromDict(); } // Unlink the transaction from the F_Database if it is a read transaction. if (m_eTransType == XFLM_READ_TRANS) { if (m_pNextReadTrans) { m_pNextReadTrans->m_pPrevReadTrans = m_pPrevReadTrans; } else if (!m_uiKilledTime) { m_pDatabase->m_pLastReadTrans = m_pPrevReadTrans; } if (m_pPrevReadTrans) { m_pPrevReadTrans->m_pNextReadTrans = m_pNextReadTrans; } else if (m_uiKilledTime) { m_pDatabase->m_pFirstKilledTrans = m_pNextReadTrans; } else { m_pDatabase->m_pFirstReadTrans = m_pNextReadTrans; } // Zero out so it will be zero for next transaction begin. m_uiKilledTime = 0; } else { // Reset to NULL or zero for next update transaction. m_pIxStartList = m_pIxStopList = NULL; flmAssert( !m_pIxdFixups); } m_pDatabase->unlockMutex(); m_eTransType = XFLM_NO_TRANS; m_uiFlags &= (~(FDB_UPDATED_DICTIONARY | FDB_DONT_KILL_TRANS | FDB_DONT_POISON_CACHE | FDB_SWEEP_SCHEDULED)); flmAssert( !m_uiDirtyNodeCount); } } /**************************************************************************** Desc: This routine reads a database's dictionary. This is called only when we did not have a dictionary off of the F_Database object - which will be the first transaction after a database is opened. ****************************************************************************/ RCODE F_Db::readDictionary( void) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = dictOpen())) { goto Exit; } m_pDatabase->lockMutex(); // At this point, we will not yet have opened the database for // general use, so there is no way that any other thread can have // created a dictionary yet. flmAssert( !m_pDatabase->m_pDictList); // Link the new local dictionary to its file structure. m_pDict->linkToDatabase( m_pDatabase); m_pDatabase->unlockMutex(); Exit: return( rc); } /**************************************************************************** Desc: This routine starts a transaction for the specified database. The transaction may be part of an overall larger transaction. ****************************************************************************/ RCODE F_Db::beginTrans( eDbTransType eTransType, FLMUINT uiMaxLockWait, FLMUINT uiFlags, XFLM_DB_HDR * pDbHdr) { RCODE rc = NE_XFLM_OK; XFLM_DB_HDR * pLastCommittedDbHdr; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; FLMBOOL bMutexLocked = FALSE; // Should not be calling on a temporary database flmAssert( !m_pDatabase->m_bTempDb); // Check the state of the database engine if( RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Initialize a few things - as few as is necessary to avoid // unnecessary overhead. m_AbortRc = NE_XFLM_OK; pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr; m_bKrefSetup = FALSE; m_eTransType = eTransType; m_uiThreadId = (FLMUINT)f_threadId(); m_uiTransCount++; // Link the F_Db to the database's most current F_Dict structure, // if there is one. Also, if it is a read transaction, link the F_Db // into the list of read transactions off of the F_Database object. m_pDatabase->lockMutex(); bMutexLocked = TRUE; if (m_pDatabase->m_pDictList) { // Link the F_Db to the right F_Dict object linkToDict( m_pDatabase->m_pDictList); } // If it is a read transaction, link into the list of // read transactions off of the F_Database object. Until we // get the DB header transaction ID below, we set ui64CurrTransID // to zero and link this transaction in at the beginning of the // list. if (eTransType == XFLM_READ_TRANS) { getDbHdrInfo( pLastCommittedDbHdr); // Link in at the end of the transaction list. m_pNextReadTrans = NULL; if ((m_pPrevReadTrans = m_pDatabase->m_pLastReadTrans) != NULL) { // Make sure transaction IDs are always in ascending order. They // should be at this point. flmAssert( m_pDatabase->m_pLastReadTrans->m_ui64CurrTransID <= m_ui64CurrTransID); m_pDatabase->m_pLastReadTrans->m_pNextReadTrans = this; } else { m_pDatabase->m_pFirstReadTrans = this; } m_pDatabase->m_pLastReadTrans = this; m_uiInactiveTime = 0; if (uiFlags & XFLM_DONT_KILL_TRANS) { m_uiFlags |= FDB_DONT_KILL_TRANS; } else { m_uiFlags &= ~FDB_DONT_KILL_TRANS; } if (pDbHdr) { f_memcpy( pDbHdr, &m_pDatabase->m_lastCommittedDbHdr, sizeof( XFLM_DB_HDR)); } } m_pDatabase->unlockMutex(); bMutexLocked = FALSE; if (uiFlags & XFLM_DONT_POISON_CACHE) { m_uiFlags |= FDB_DONT_POISON_CACHE; } else { m_uiFlags &= ~FDB_DONT_POISON_CACHE; } // Put an exclusive lock on the database if we are not in a read // transaction. Read transactions require no lock. if (eTransType != XFLM_READ_TRANS) { // Set the m_bHadUpdOper to TRUE for all transactions to begin with. // Many calls to beginTrans are internal, and we WANT the // normal behavior at the end of the transaction when it is // committed or aborted. The only time this flag will be set // to FALSE is when the application starts the transaction as // opposed to an internal starting of the transaction. m_bHadUpdOper = TRUE; // Initialize the count of blocks changed to be 0 m_uiBlkChangeCnt = 0; if (RC_BAD( rc = lockExclusive( uiMaxLockWait))) { goto Exit; } flmAssert( !m_pDatabase->m_DocumentList.m_uiLastCollection); flmAssert( !m_pDatabase->m_DocumentList.m_ui64LastDocument); // If there was a problem with the RFL volume, we must wait // for a checkpoint to be completed before continuing. // The checkpoint thread looks at this same flag and forces // a checkpoint. If it completes one successfully, it will // reset this flag. // Also, if the last forced checkpoint had a problem // (pFile->CheckpointRc != NE_XFLM_OK), we don't want to // start up a new update transaction until it is resolved. if( !pRfl->seeIfRflVolumeOk() || RC_BAD( m_pDatabase->m_CheckpointRc)) { rc = RC_SET( NE_XFLM_MUST_WAIT_CHECKPOINT); goto Exit; } // Set the first log block address to zero. m_pDatabase->m_uiFirstLogBlkAddress = 0; // Header must be read before opening roll forward log file to make // sure we have the most current log file and log options. f_memcpy( &m_pDatabase->m_uncommittedDbHdr, pLastCommittedDbHdr, sizeof( XFLM_DB_HDR)); getDbHdrInfo( pLastCommittedDbHdr); // Need to increment the current checkpoint for update transactions // so that it will be correct when we go to mark cache blocks. if (m_uiFlags & FDB_REPLAYING_RFL) { // During recovery we need to set the transaction ID to the // transaction ID that was logged. m_ui64CurrTransID = pRfl->getCurrTransID(); } else { m_ui64CurrTransID++; } // Link F_Db to the most current local dictionary, if there // is one. m_pDatabase->lockMutex(); if (m_pDatabase->m_pDictList != m_pDict && m_pDatabase->m_pDictList) { linkToDict( m_pDatabase->m_pDictList); } m_pDatabase->unlockMutex(); // Set the transaction EOF to the current file EOF m_uiTransEOF = m_uiLogicalEOF; // Put the transaction ID into the uncommitted log header. m_pDatabase->m_uncommittedDbHdr.ui64CurrTransID = m_ui64CurrTransID; if (pDbHdr) { f_memcpy( pDbHdr, &m_pDatabase->m_uncommittedDbHdr, sizeof( XFLM_DB_HDR)); } } // Set up to collect statistics. We only do this at transaction // begin and not on any other type of operation. So this is the // only time when an F_Db will sense that statistics have been // turned on or off. if (!gv_XFlmSysData.Stats.bCollectingStats) { m_pStats = NULL; m_pDbStats = NULL; } else { m_pStats = &m_Stats; // Statistics are being collected for the system. Therefore, // if we are not currently collecting statistics in the // session, start. If we were collecting statistics, but the // start time was earlier than the start time in the system // statistics structure, reset the statistics in the session. if (!m_Stats.bCollectingStats) { flmStatStart( &m_Stats); } else if (m_Stats.uiStartTime < gv_XFlmSysData.Stats.uiStartTime) { flmStatReset( &m_Stats, FALSE); } (void)flmStatGetDb( &m_Stats, m_pDatabase, 0, &m_pDbStats, NULL, NULL); m_pLFileStats = NULL; } if (m_pDbStats) { f_timeGetTimeStamp( &m_TransStartTime); } // If we do not have a dictionary, read it in from disk. // NOTE: This should only happen when we are first opening // the database. if (!m_pDict) { if (eTransType != XFLM_READ_TRANS) { pRfl->disableLogging( &uiRflToken); } if (RC_BAD( rc = readDictionary())) { goto Exit; } } Exit: if( bMutexLocked) { m_pDatabase->unlockMutex(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if (eTransType != XFLM_READ_TRANS) { if (RC_OK( rc)) { rc = pRfl->logBeginTransaction( this); } #ifdef FLM_DBG_LOG flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID, 0, 0, rc, "TBeg"); #endif } if (eTransType == XFLM_UPDATE_TRANS && gv_XFlmSysData.EventHdrs [XFLM_EVENT_UPDATES].pEventCBList) { flmTransEventCallback( XFLM_EVENT_BEGIN_TRANS, this, rc, (FLMUINT)(RC_OK( rc) ? m_ui64CurrTransID : (FLMUINT64)0)); } if (RC_BAD( rc)) { // If there was an error, unlink the database from the transaction // structure as well as from the FDICT structure. Also dump any nodes // that are already in the cache. unlinkFromTransList( FALSE); if (m_pStats) { (void)flmStatUpdate( &m_Stats); } } return( rc); } /**************************************************************************** Desc: This routine starts a transaction for the specified database. It uses the transaction information in the passed in pDb. ****************************************************************************/ RCODE F_Db::beginTrans( F_Db * pDb) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; // Should not be calling on a temporary database flmAssert( !m_pDatabase->m_bTempDb); // pDb better be running a read transaction. flmAssert( pDb->m_eTransType == XFLM_READ_TRANS); if( RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Initialize a few things - as few as is necessary to avoid // unnecessary overhead. m_AbortRc = NE_XFLM_OK; m_bKrefSetup = FALSE; m_eTransType = XFLM_READ_TRANS; m_uiThreadId = (FLMUINT)f_threadId(); m_uiTransCount++; // Link the F_Db to the database's most current F_Dict structure, // if there is one. Also, if it is a read transaction, link the F_Db // into the list of read transactions off of the F_Database object. m_pDatabase->lockMutex(); bMutexLocked = TRUE; // Link to the same dictionary as pDb. linkToDict( pDb->m_pDict); // If it is a read transaction, link into the list of // read transactions off of the F_Database object. Until we // get the DB header transaction ID below, we set ui64CurrTransID // to zero and link this transaction in at the beginning of the // list. getDbHdrInfo( pDb); // Link into the transaction list right after the point where // pDb is linked in. We need to keep transaction IDs in ascending // order. m_pPrevReadTrans = pDb; if ((m_pNextReadTrans = pDb->m_pNextReadTrans) != NULL) { m_pNextReadTrans->m_pPrevReadTrans = this; } else { m_pDatabase->m_pLastReadTrans = this; } pDb->m_pNextReadTrans = this; m_uiInactiveTime = 0; if (pDb->m_uiFlags & FDB_DONT_KILL_TRANS) { m_uiFlags |= FDB_DONT_KILL_TRANS; } else { m_uiFlags &= ~FDB_DONT_KILL_TRANS; } if (pDb->m_uiFlags & FDB_DONT_POISON_CACHE) { m_uiFlags |= FDB_DONT_POISON_CACHE; } else { m_uiFlags &= ~FDB_DONT_POISON_CACHE; } m_pDatabase->unlockMutex(); bMutexLocked = FALSE; // Set up to collect statistics. We only do this at transaction // begin and not on any other type of operation. So this is the // only time when an F_Db will sense that statistics have been // turned on or off. if (!gv_XFlmSysData.Stats.bCollectingStats) { m_pStats = NULL; m_pDbStats = NULL; } else { m_pStats = &m_Stats; // Statistics are being collected for the system. Therefore, // if we are not currently collecting statistics in the // session, start. If we were collecting statistics, but the // start time was earlier than the start time in the system // statistics structure, reset the statistics in the session. if (!m_Stats.bCollectingStats) { flmStatStart( &m_Stats); } else if (m_Stats.uiStartTime < gv_XFlmSysData.Stats.uiStartTime) { flmStatReset( &m_Stats, FALSE); } (void)flmStatGetDb( &m_Stats, m_pDatabase, 0, &m_pDbStats, NULL, NULL); m_pLFileStats = NULL; } if (m_pDbStats) { f_timeGetTimeStamp( &m_TransStartTime); } Exit: if( bMutexLocked) { m_pDatabase->unlockMutex(); } if (RC_BAD( rc)) { // If there was an error, unlink the database from the transaction // structure as well as from the FDICT structure. Also dump any nodes // that are already in the cache. unlinkFromTransList( FALSE); if (m_pStats) { (void)flmStatUpdate( &m_Stats); } } return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Starts a transaction. *END************************************************************************/ RCODE FLMAPI F_Db::transBegin( eDbTransType eTransType, // [IN] Specifies the type of transaction to begin. // Possible values are: // // XFLM_READ_TRANS: Begins a read transaction. // XFLM_UPDATE_TRANS: Begins an update transaction. FLMUINT uiMaxLockWait, // [IN] Maximum lock wait time. Specifies the amount of time // to wait for lock requests occuring during the transaction // to be granted. Valid values are 0 through 255 seconds. Zero // is used to specify no-wait locks. FLMUINT uiFlags, // Transaction flags. XFLM_DB_HDR * pDbHdr // [IN] 2K buffer // [OUT] Returns the DB header for the file. ) { RCODE rc = NE_XFLM_OK; // Verify the transaction type. if (eTransType != XFLM_UPDATE_TRANS && eTransType != XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_TYPE); goto Exit; } // Verify the transaction flags if ((uiFlags & XFLM_DONT_KILL_TRANS) && eTransType != XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_TYPE); goto Exit; } // Can't start an update transaction on a database that // is locked in shared mode. if (eTransType == XFLM_UPDATE_TRANS && (m_uiFlags & FDB_FILE_LOCK_SHARED)) { rc = RC_SET( NE_XFLM_SHARED_LOCK); goto Exit; } // If the database is not running a transaction, start one. if (m_eTransType != XFLM_NO_TRANS) { // Cannot nest transactions. rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if (RC_BAD( rc = beginTrans( eTransType, uiMaxLockWait, uiFlags, pDbHdr))) { goto Exit; } m_bHadUpdOper = FALSE; Exit: return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Starts a transaction. *END************************************************************************/ RCODE FLMAPI F_Db::transBegin( IF_Db * pDb // [IN] Start a transaction that has the same view as whatever // transaction is running on this database. NOTE: If pDb is // running an update transaction, it is illegal for another pDb // to also run an update transaction, so such a request would fail. ) { RCODE rc = NE_XFLM_OK; // Database cannot already be running a transaction. if (m_eTransType != XFLM_NO_TRANS) { // Cannot nest transactions. rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // Verify the transaction type. if (((F_Db *)pDb)->m_eTransType != XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_TYPE); goto Exit; } if (RC_BAD( rc = beginTrans( (F_Db *)pDb))) { goto Exit; } m_bHadUpdOper = FALSE; Exit: return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Obtains a a lock on the database. *END************************************************************************/ RCODE FLMAPI F_Db::dbLock( eLockType lockType, // [IN] Type of lock request - must be FLM_LOCK_EXCLUSIVE or // FLM_LOCK_SHARED FLMINT iPriority, // [IN] Priority to be assigned to lock. FLMUINT uiTimeout // [IN] Seconds to wait for lock to be granted. FLM_NO_TIMEOUT // means that it will wait forever for the lock to be granted. ) { RCODE rc = NE_XFLM_OK; // lockType better be exclusive or shared if (lockType != FLM_LOCK_EXCLUSIVE && lockType != FLM_LOCK_SHARED) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // Nesting of locks is not allowed - this test also keeps this call from // being executed inside an update transaction that implicitly acquired // the lock. if (m_uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED | FDB_FILE_LOCK_IMPLICIT)) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Attempt to acquire the lock. if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->lock( m_hWaitSem, (FLMBOOL)((lockType == FLM_LOCK_EXCLUSIVE) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE), uiTimeout, iPriority, m_pDbStats ? &m_pDbStats->LockStats : NULL))) { goto Exit; } m_uiFlags |= FDB_HAS_FILE_LOCK; if (lockType == FLM_LOCK_SHARED) { m_uiFlags |= FDB_FILE_LOCK_SHARED; } Exit: return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Releases a lock on the database *END************************************************************************/ RCODE FLMAPI F_Db::dbUnlock( void) { RCODE rc = NE_XFLM_OK; // If we don't have an explicit lock, can't do the unlock. It is // also illegal to do the unlock during an update transaction. if (!(m_uiFlags & FDB_HAS_FILE_LOCK) || (m_uiFlags & FDB_FILE_LOCK_IMPLICIT) || (m_eTransType == XFLM_UPDATE_TRANS)) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // Unlock the file. if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->unlock())) { goto Exit; } // Unset the flags that indicated the file was explicitly locked. m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED)); Exit: if (RC_OK( rc)) { rc = checkState( __FILE__, __LINE__); } return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Returns information about current and pending locks on the database. *END************************************************************************/ RCODE FLMAPI F_Db::getLockInfo( FLMINT iPriority, // [IN] A count of all locks with a priority >= to this priority // level will be returned in pLockInfo. eLockType * pCurrLockType, FLMUINT * puiThreadId, FLMUINT * puiNumExclQueued, FLMUINT * puiNumSharedQueued, FLMUINT * puiPriorityCount ) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } m_pDatabase->m_pDatabaseLockObj->getLockInfo( iPriority, pCurrLockType, puiThreadId, puiNumExclQueued, puiNumSharedQueued, puiPriorityCount); Exit: return( rc); } /*API~*********************************************************************** Desc : Returns information about the lock held by the specified database handle. *END************************************************************************/ RCODE FLMAPI F_Db::getLockType( eLockType * pLockType, FLMBOOL * pbImplicit) { RCODE rc = NE_XFLM_OK; if (pLockType) { *pLockType = FLM_LOCK_NONE; } if (pbImplicit) { *pbImplicit = FALSE; } if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if (m_uiFlags & FDB_HAS_FILE_LOCK) { if (pLockType) { if (m_uiFlags & FDB_FILE_LOCK_SHARED) { *pLockType = FLM_LOCK_SHARED; } else { *pLockType = FLM_LOCK_EXCLUSIVE; } } if (pbImplicit) { *pbImplicit = (m_uiFlags & FDB_FILE_LOCK_IMPLICIT) ? TRUE : FALSE; } } Exit: return( rc); } /*API~*********************************************************************** Area : TRANSACTION Desc : Forces a checkpoint on the database. *END************************************************************************/ RCODE FLMAPI F_Db::doCheckpoint( FLMUINT uiTimeout // [IN] Seconds to wait to obtain lock on the database. // FLM_NO_TIMEOUT means that it will wait forever for // the lock to be granted. ) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Start an update transaction. Must not already be one going. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // If we get to this point, we need to start a transaction on the // database. if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS, uiTimeout))) { goto Exit; } // Commit the transaction, forcing it to be checkpointed. m_bHadUpdOper = FALSE; if (RC_BAD( rc = commitTrans( 0, TRUE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine locks a database for exclusive access. ****************************************************************************/ RCODE F_Db::lockExclusive( FLMUINT uiMaxLockWait) { RCODE rc = NE_XFLM_OK; FLMBOOL bGotFileLock = FALSE; flmAssert( !m_pDatabase->m_bTempDb); // There must NOT be a shared lock on the file. if (m_uiFlags & FDB_FILE_LOCK_SHARED) { rc = RC_SET( NE_XFLM_SHARED_LOCK); goto Exit; } // Must acquire an exclusive file lock first, if it hasn't been // acquired. if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) { if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->lock( m_hWaitSem, TRUE, uiMaxLockWait, 0, m_pDbStats ? &m_pDbStats->LockStats : NULL))) { goto Exit; } bGotFileLock = TRUE; m_uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); } if (RC_OK( rc = m_pDatabase->dbWriteLock( m_hWaitSem, m_pDbStats))) { m_uiFlags |= FDB_HAS_WRITE_LOCK; } Exit: if (rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) { if (bGotFileLock) { (void)m_pDatabase->m_pDatabaseLockObj->unlock(); m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK)); } if (m_eTransType != XFLM_NO_TRANS) { // Unlink the DB from the transaction. unlinkFromTransList( FALSE); } } else if (RC_BAD( rc)) { if (bGotFileLock) { (void)m_pDatabase->m_pDatabaseLockObj->unlock(); m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK)); } } return( rc); } /**************************************************************************** Desc: This routine unlocks a database that was previously locked using the lockExclusive routine. ****************************************************************************/ void F_Db::unlockExclusive( void) { flmAssert( !m_pDatabase->m_bTempDb); // If we have the write lock, unlock it first. flmAssert( m_uiFlags & FDB_HAS_WRITE_LOCK); m_pDatabase->dbWriteUnlock(); m_uiFlags &= ~FDB_HAS_WRITE_LOCK; // Give up the file lock, if it was acquired implicitly. if (m_uiFlags & FDB_FILE_LOCK_IMPLICIT) { (void)m_pDatabase->m_pDatabaseLockObj->unlock(); m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT)); } } libxflaim-5.1.969/src/fdom.cpp0000644000175000017500000124253310511001742017467 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: DOM node implementation // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdom.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" // Local constants #define SEN_RESERVE_BYTES 5 /**************************************************************************** Desc: ****************************************************************************/ FINLINE RCODE F_Db::attrIsInIndexDef( FLMUINT uiAttrNameId, FLMBOOL * pbIsInIndexDef) { RCODE rc = NE_XFLM_OK; F_AttrElmInfo defInfo; if( RC_BAD( rc = m_pDict->getAttribute( this, uiAttrNameId, &defInfo))) { return( rc); } *pbIsInIndexDef = defInfo.m_pFirstIcd ? TRUE : FALSE; return( NE_XFLM_OK); } /***************************************************************************** Desc: This class converts a text stream of Unicode or UTF8 to an ASCII text stream. ******************************************************************************/ class F_AsciiIStream : public IF_IStream { public: F_AsciiIStream( FLMBYTE * pucText, FLMUINT uiNumBytesInBuffer, eXFlmTextType eTextType) { m_pucText = pucText; m_pucCurrPtr = pucText; m_uiCurrChar = 0; m_eTextType = eTextType; if( uiNumBytesInBuffer) { m_pucEnd = &pucText[ uiNumBytesInBuffer]; } else { m_pucEnd = NULL; } } virtual ~F_AsciiIStream() { } RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FINLINE RCODE FLMAPI closeStream( void) { return( NE_XFLM_OK); } private: const FLMBYTE * m_pucText; const FLMBYTE * m_pucCurrPtr; const FLMBYTE * m_pucEnd; FLMUINT m_uiCurrChar; eXFlmTextType m_eTextType; }; /***************************************************************************** Desc: ******************************************************************************/ FLMINT FLMAPI F_BTreeIStream::Release( void) { FLMATOMIC refCnt = --m_refCnt; if (m_refCnt == 0) { closeStream(); if( gv_XFlmSysData.pNodePool) { m_refCnt = 1; gv_XFlmSysData.pNodePool->insertBTreeIStream( this); return( 0); } else { delete this; } } return( refCnt); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::canSetValue( F_Db * pDb, FLMUINT uiDataType) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->m_pDatabase; IF_DOMNode * pNode = NULL; eDomNodeType eNodeType = getNodeType(); if( eNodeType < ELEMENT_NODE || eNodeType > ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Cannot set a value without a data type if( uiDataType == XFLM_NODATA_TYPE) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); goto Exit; } // If the node is read-only, don't allow it to be changed if( getModeFlags() & FDOM_READ_ONLY) { rc = RC_SET( NE_XFLM_READ_ONLY); goto Exit; } // If this is a comment or CDATA node, only allow text values if (uiDataType != XFLM_TEXT_TYPE && (eNodeType == COMMENT_NODE || eNodeType == CDATA_SECTION_NODE)) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // If the node is a data node and it has already been linked // into the document, its data type cannot be changed. if( getParentId() && eNodeType == DATA_NODE && uiDataType != getDataType()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Cannot allow a value to be set on this node if a pending input stream // is still open. if( pDatabase->m_pPendingInput && pDatabase->m_pPendingInput != m_pCachedNode) { rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); goto Exit; } Exit: if( pNode) { pNode->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMINT FLMAPI F_DOMNode::Release( void) { FLMINT iRefCnt = --m_refCnt; if (iRefCnt == 0) { if( gv_XFlmSysData.pNodeCacheMgr) { m_refCnt = 1; gv_XFlmSysData.pNodeCacheMgr->insertDOMNode( this); return( 0); } else { delete this; } } return( iRefCnt); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::isChildTypeValid( eDomNodeType eChildNodeType) { RCODE rc = NE_XFLM_OK; FLMBOOL bTypeValid = FALSE; if( !m_pCachedNode) { rc = RC_SET( NE_XFLM_DOM_INVALID_CHILD_TYPE); goto Exit; } switch( getNodeType()) { case ELEMENT_NODE: { if (eChildNodeType == ELEMENT_NODE || (eChildNodeType == DATA_NODE && getDataType() != XFLM_NODATA_TYPE && getDataLength() == 0) || eChildNodeType == COMMENT_NODE || eChildNodeType == PROCESSING_INSTRUCTION_NODE || eChildNodeType == CDATA_SECTION_NODE) { bTypeValid = TRUE; } break; } case DOCUMENT_NODE: { if (eChildNodeType == ELEMENT_NODE || eChildNodeType == PROCESSING_INSTRUCTION_NODE || eChildNodeType == COMMENT_NODE) { bTypeValid = TRUE; } break; } case ATTRIBUTE_NODE: case DATA_NODE: case CDATA_SECTION_NODE: case PROCESSING_INSTRUCTION_NODE: case COMMENT_NODE: { break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } if (!bTypeValid) { rc = RC_SET( NE_XFLM_DOM_INVALID_CHILD_TYPE); goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::isDescendantOf( F_Db * pDb, F_DOMNode * pAncestor, FLMBOOL * pbDescendant) { RCODE rc = NE_XFLM_OK; F_DOMNode * pParent = NULL; FLMUINT64 ui64AncestorId; FLMUINT64 ui64ThisParentId; *pbDescendant = FALSE; if( !m_pCachedNode) { goto Exit; } ui64AncestorId = pAncestor->getNodeId(); ui64ThisParentId = getParentId(); if( ui64ThisParentId == ui64AncestorId) { *pbDescendant = TRUE; goto Exit; } if( !ui64ThisParentId || (ui64AncestorId != ui64ThisParentId && ui64ThisParentId == getDocumentId()) || !pAncestor->getFirstChildId()) { goto Exit; } if( RC_BAD( rc = getParentNode( pDb, (IF_DOMNode **)&pParent))) { goto Exit; } while( pParent) { if( pParent->getNodeId() == ui64AncestorId) { *pbDescendant = TRUE; goto Exit; } if( RC_BAD( rc = pParent->getParentNode( pDb, (IF_DOMNode **)&pParent))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } } Exit: if( pParent) { pParent->Release(); } return( rc); } /***************************************************************************** Notes: When an node is unlinked, its document or root node ID is not changed. Once unlinked, the node can be deleted or re-linked elsewhere within the same document. This routine assumes that the caller has checked the cannot delete bits and read-only bits if necessary. ******************************************************************************/ RCODE F_DOMNode::unlinkNode( F_Db * pDb, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; F_DOMNode * pTmpNode = NULL; F_COLLECTION * pCollection = NULL; FLMUINT64 ui64OldPrevSib = 0; FLMUINT64 ui64OldNextSib = 0; FLMBOOL bChangedThisHeader = FALSE; eDomNodeType eNodeType; // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Unlink the node from its siblings and parent if( (ui64OldPrevSib = getPrevSibId()) != 0) { if( RC_BAD( rc = pDb->getNode( getCollection(), getPrevSibId(), &pTmpNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } pTmpNode->setNextSibId( getNextSibId()); if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, uiFlags))) { goto Exit; } if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } setPrevSibId( 0); bChangedThisHeader = TRUE; } if( (ui64OldNextSib = getNextSibId()) != 0) { if( RC_BAD( rc = pDb->getNode( getCollection(), getNextSibId(), &pTmpNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } pTmpNode->setPrevSibId( ui64OldPrevSib); if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, uiFlags))) { goto Exit; } if( !bChangedThisHeader) { if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } } setNextSibId( 0); bChangedThisHeader = TRUE; } if( getParentId()) { FLMBOOL bChangedTmpHeader = FALSE; if( RC_BAD( rc = pDb->getNode( getCollection(), getParentId(), &pTmpNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND || rc == NE_XFLM_DOM_NODE_DELETED) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if( eNodeType == ANNOTATION_NODE) { if (!bChangedTmpHeader) { if (RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } bChangedTmpHeader = TRUE; } pTmpNode->setAnnotationId( 0); } else { // If the parent node is one whose child elements must all // be unique, we must remove the node from the node list of the // parent. if( pTmpNode->getModeFlags() & FDOM_HAVE_CELM_LIST) { FLMUINT uiElmOffset; if( !bChangedTmpHeader) { if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } bChangedTmpHeader = TRUE; } // Only element nodes should be child nodes of this parent. flmAssert( eNodeType == ELEMENT_NODE); if( !pTmpNode->findChildElm( getNameId(), &uiElmOffset)) { // Child node should have been found. rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = pTmpNode->removeChildElm( uiElmOffset))) { goto Exit; } } // If this is a data node, we need to update the parent element's // data node count if( eNodeType == DATA_NODE) { if( !bChangedTmpHeader) { if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } bChangedTmpHeader = TRUE; } flmAssert( pTmpNode->getDataChildCount()); pTmpNode->setDataChildCount( pTmpNode->getDataChildCount() - 1); } if( !ui64OldPrevSib) { if( !bChangedTmpHeader) { if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } bChangedTmpHeader = TRUE; } flmAssert( pTmpNode->canHaveChildren()); pTmpNode->setFirstChildId( ui64OldNextSib); } if( !ui64OldNextSib) { if( !bChangedTmpHeader) { if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } bChangedTmpHeader = TRUE; } flmAssert( pTmpNode->canHaveChildren()); pTmpNode->setLastChildId( ui64OldPrevSib); } } if( bChangedTmpHeader) { if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, uiFlags))) { goto Exit; } } if( !bChangedThisHeader) { if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } } setParentId( 0); bChangedThisHeader = TRUE; } // If this is a root node, the document list pointers may need // to be updated if( isRootNode()) { if( eNodeType != DOCUMENT_NODE && eNodeType != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Get a pointer to the collection if( RC_BAD( rc = pDb->m_pDict->getCollection( getCollection(), &pCollection))) { goto Exit; } if( pCollection->ui64FirstDocId == getNodeId() || pCollection->ui64LastDocId == getNodeId()) { // Clone the dictionary since the first/last document ID // values of the collection will be changed if( !(pDb->m_uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = pDb->dictClone())) { goto Exit; } } // Get a pointer to the new collection if( RC_BAD( rc = pDb->m_pDict->getCollection( getCollection(), &pCollection))) { goto Exit; } // Change the first and/or last document IDs if( pCollection->ui64FirstDocId == getNodeId()) { pCollection->ui64FirstDocId = ui64OldNextSib; pCollection->bNeedToUpdateNodes = TRUE; } if( pCollection->ui64LastDocId == getNodeId()) { pCollection->ui64LastDocId = ui64OldPrevSib; pCollection->bNeedToUpdateNodes = TRUE; } } } // If this is a data node, clear its name tag if( eNodeType == DATA_NODE) { if( !bChangedThisHeader) { if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } } setNameId( 0); bChangedThisHeader = TRUE; } if( bChangedThisHeader) { if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, uiFlags))) { goto Exit; } } Exit: if( pTmpNode) { pTmpNode->Release(); } if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::addModeFlags( F_Db * pDb, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // Only need to set flags if they are not all currently set. if( (getModeFlags() & uiFlags) != uiFlags) { pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->addModeFlags( pDb, m_uiAttrNameId, uiFlags))) { goto Exit; } } else { m_pCachedNode->setFlags( uiFlags); } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } pRfl->enableLogging( &uiRflToken); if( RC_BAD( pRfl->logNodeFlagsUpdate( pDb, getCollection(), m_pCachedNode->getNodeId(), m_uiAttrNameId, uiFlags, TRUE))) { goto Exit; } } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::removeModeFlags( F_Db * pDb, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // Only need to remove the flags if any of them are currently set. if( getModeFlags() & uiFlags) { pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->removeModeFlags( pDb, m_uiAttrNameId, uiFlags))) { goto Exit; } } else { m_pCachedNode->unsetFlags( uiFlags); } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } pRfl->enableLogging( &uiRflToken); if( RC_BAD( pRfl->logNodeFlagsUpdate( pDb, getCollection(), m_pCachedNode->getNodeId(), m_uiAttrNameId, uiFlags, FALSE))) { goto Exit; } } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getData( F_Db * pDb, FLMBYTE * pucBuffer, FLMUINT * puiLength) { RCODE rc = NE_XFLM_OK; IF_PosIStream * pIStream = NULL; FLMBOOL bStartedTrans = FALSE; F_NodeBufferIStream bufferIStream; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // If a NULL buffer is passed in, just return the // data length if( !pucBuffer) { rc = getDataLength( pDb, puiLength); goto Exit; } if( RC_BAD( rc = getIStream( pDb, &bufferIStream, &pIStream))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsBinary( pIStream, pucBuffer, *puiLength, 0, puiLength))) { goto Exit; } Exit: if( pIStream) { pIStream->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getDataLength( IF_Db * ifpDb, FLMUINT * puiLength) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; eDomNodeType eNodeType; F_Db * pDb = (F_Db *)ifpDb; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getDataLength( m_uiAttrNameId, puiLength))) { goto Exit; } } else if( (*puiLength = getDataLength()) == 0) { // If this is an element node, we will automatically search // for the first data node (if any) if( eNodeType == ELEMENT_NODE && getDataChildCount()) { F_DOMNode * pNode = NULL; if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } *puiLength = pNode->getDataLength(); pNode->Release(); } } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::insertBefore( IF_Db * ifpDb, IF_DOMNode * ifpNewChild, IF_DOMNode * ifpRefChild) { RCODE rc = NE_XFLM_OK; F_DOMNode * pFirstNewChild = NULL; F_DOMNode * pLastNewChild = NULL; F_DOMNode * pCurNewNode = NULL; F_DOMNode * pNextSib = NULL; F_DOMNode * pInsertBefore = NULL; F_DOMNode * pTmpNode = NULL; FLMBOOL bDescendant; FLMBOOL bDone = FALSE; FLMUINT64 ui64Tmp; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bStartOfUpdate; F_DOMNode * pDataElementNode = NULL; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; F_DOMNode * pNewChild = (F_DOMNode *)ifpNewChild; F_DOMNode * pRefChild = (F_DOMNode *)ifpRefChild; FLMBOOL bStartedTrans = FALSE; FLMBOOL bUpdatedNode = FALSE; FLMUINT uiRflToken = 0; FLMUINT64 ui64RefChildId = 0; eDomNodeType eThisNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eThisNodeType = getNodeType(); if( eThisNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } if( !pNewChild) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = pNewChild->syncFromDb( pDb))) { goto Exit; } if( pRefChild) { if( RC_BAD( rc = pRefChild->syncFromDb( pDb))) { goto Exit; } if( pRefChild->getNodeType() == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } ui64RefChildId = pRefChild->getNodeId(); } if( pNewChild->getDatabase() != getDatabase() || pNewChild->getCollection() != getCollection()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( pNewChild->getNodeType() == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // If this is a node whose children are all supposed to be unique, // make sure that is the case, and find out where to insert the node. if( getModeFlags() & FDOM_HAVE_CELM_LIST) { FLMUINT uiInsertPos; // Only element child nodes are allowed. if( pNewChild->getNodeType() != ELEMENT_NODE) { rc = RC_SET( NE_XFLM_DOM_INVALID_CHILD_TYPE); goto Exit; } // All of the element names must be unique. if( m_pCachedNode->findChildElm( pNewChild->getNameId(), &uiInsertPos)) { rc = RC_SET( NE_XFLM_DOM_DUPLICATE_ELEMENT); goto Exit; } // Element was not found, insert into the list of elements. if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bUpdatedNode = TRUE; if( RC_BAD( rc = m_pCachedNode->insertChildElm( uiInsertPos, pNewChild->getNameId(), pNewChild->getNodeId()))) { goto Exit; } } // If a non-NULL reference child was passed in, // do some basic sanity checks if( pRefChild) { if( pRefChild->getDatabase() != getDatabase() || pRefChild->getCollection() != getCollection()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( pNewChild->getNodeId() == pRefChild->getNodeId()) { rc = RC_SET( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); goto Exit; } if( pRefChild->getParentId() != getNodeId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } } pFirstNewChild = pNewChild; pFirstNewChild->AddRef(); pLastNewChild = pNewChild; pLastNewChild->AddRef(); if( pRefChild) { pInsertBefore = pRefChild; pInsertBefore->AddRef(); } pCurNewNode = pFirstNewChild; pCurNewNode->AddRef(); bStartOfUpdate = TRUE; for( ;;) { // Make sure it is legal for the new child node to be // linked to this node if( RC_BAD( rc = isChildTypeValid( pCurNewNode->getNodeType()))) { goto Exit; } // If the node being inserted is not from the same document, // we cannot perform the operation if( pCurNewNode->getDocumentId() != getDocumentId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_WRONG_DOCUMENT_ERR); goto Exit; } // A document node can only have one element node and one document type // node. if( eThisNodeType == DOCUMENT_NODE) { if( pCurNewNode->getNodeType() == ELEMENT_NODE && getFirstChildId()) { if( RC_BAD( rc = getChild( ifpDb, ELEMENT_NODE, (IF_DOMNode **)&pTmpNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); goto Exit; } } } // Get the next sibling node (if any) before we // change the tree if( pNextSib) { pNextSib->Release(); pNextSib = NULL; } if( RC_BAD( rc = pCurNewNode->getNextSibling( pDb, (IF_DOMNode **)&pNextSib))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; bDone = TRUE; } else { goto Exit; } } bMustAbortOnError = TRUE; if( pDataElementNode) { pDataElementNode->Release(); pDataElementNode = NULL; } // Do indexing work before making changes if( pCurNewNode->getNameId()) { if( pCurNewNode->getNodeType() == DATA_NODE) { if( pCurNewNode->getParentId()) { if( RC_BAD( rc = pCurNewNode->getParentNode( pDb, (IF_DOMNode **)&pDataElementNode))) { goto Exit; } if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), pDataElementNode, IX_DEL_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } if( !getFirstChildId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } } else { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), pCurNewNode, IX_UNLINK_NODE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } } // Remove pCurNewNode from the tree if( pCurNewNode->getModeFlags() & (FDOM_CANNOT_DELETE | FDOM_READ_ONLY)) { rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); goto Exit; } if( RC_BAD( rc = pCurNewNode->unlinkNode( pDb, 0))) { goto Exit; } if( pDataElementNode) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), pDataElementNode, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate))) { goto Exit; } } // Make sure that "this" node is not a child of the node // being inserted if( RC_BAD( rc = isDescendantOf( pDb, pCurNewNode, &bDescendant))) { goto Exit; } if( bDescendant) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); goto Exit; } // If the node being inserted isn't from the same document, // we cannot perform the operation if( pCurNewNode->getDocumentId() != getDocumentId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_WRONG_DOCUMENT_ERR); goto Exit; } if( RC_BAD( rc = pCurNewNode->makeWriteCopy( pDb))) { goto Exit; } if( pInsertBefore) { if( RC_BAD( rc = pInsertBefore->makeWriteCopy( pDb))) { goto Exit; } // Insert the new child before the ref child if( !pInsertBefore->getPrevSibId()) { if( !bUpdatedNode) { if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bUpdatedNode = TRUE; } // Change the parent's first child pointer setFirstChildId( pCurNewNode->getNodeId()); } else { pCurNewNode->setPrevSibId( pInsertBefore->getPrevSibId()); // Get the prev sib and set its next sib value if( RC_BAD( rc = pDb->getNode( getCollection(), pInsertBefore->getPrevSibId(), &pTmpNode))) { goto Exit; } if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } pTmpNode->setNextSibId( pCurNewNode->getNodeId()); if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } pTmpNode->Release(); pTmpNode = NULL; } pInsertBefore->setPrevSibId( pCurNewNode->getNodeId()); pCurNewNode->setNextSibId( pInsertBefore->getNodeId()); if( RC_BAD( rc = pDb->updateNode( pInsertBefore->m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } } else { if( (ui64Tmp = getLastChildId()) != 0) { // Get the prev sib and set its next sib value if( RC_BAD( rc = pDb->getNode( getCollection(), getLastChildId(), &pTmpNode))) { goto Exit; } if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } pTmpNode->setNextSibId( pCurNewNode->getNodeId()); pCurNewNode->setPrevSibId( getLastChildId()); if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } pTmpNode->Release(); pTmpNode = NULL; } pCurNewNode->setPrevSibId( getLastChildId()); if( !bUpdatedNode) { if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bUpdatedNode = TRUE; } setLastChildId( pCurNewNode->getNodeId()); if( !ui64Tmp) { setFirstChildId( pCurNewNode->getNodeId()); } } // Need to increment the data child node count if( pCurNewNode->getNodeType() == DATA_NODE && eThisNodeType == ELEMENT_NODE) { setDataChildCount( getDataChildCount() + 1); bUpdatedNode = TRUE; } if( bUpdatedNode) { if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } // Reset to FALSE for next time around in loop. bUpdatedNode = FALSE; } pCurNewNode->setParentId( getNodeId()); // Need to set the naming tag and data type of the node. if( pCurNewNode->getNodeType() == DATA_NODE && eThisNodeType == ELEMENT_NODE) { if( pCurNewNode->getDataType() == XFLM_NODATA_TYPE) { pCurNewNode->setDataType( getDataType()); } else if( getDataType() != pCurNewNode->getDataType()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } pCurNewNode->setNameId( getNameId()); } // An additional restriction on unique child elements is that they // must have a node ID greater than the parent element. This allows // them to be stored using a very compact representation. if( getModeFlags() & FDOM_HAVE_CELM_LIST) { if( pCurNewNode->getNodeId() < getNodeId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_CHILD_ELM_NODE_ID); goto Exit; } } if( RC_BAD( rc = pDb->updateNode( pCurNewNode->m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } if( pCurNewNode->getNodeId() == pLastNewChild->getNodeId()) { bDone = TRUE; } // Do post-link indexing work if( pCurNewNode->getNameId()) { if( pCurNewNode->getNodeType() == DATA_NODE) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } } else { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), pCurNewNode, IX_LINK_NODE, bStartOfUpdate))) { goto Exit; } } bStartOfUpdate = FALSE; } pCurNewNode->Release(); pCurNewNode = pNextSib; pNextSib = NULL; if( bDone) { break; } } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logInsertBefore( pDb, getCollection(), getNodeId(), pNewChild->getNodeId(), ui64RefChildId))) { goto Exit; } Exit: // Release any nodes we are still holding if( pFirstNewChild) { pFirstNewChild->Release(); } if( pLastNewChild) { pLastNewChild->Release(); } if( pCurNewNode) { pCurNewNode->Release(); } if( pDataElementNode) { pDataElementNode->Release(); } if( pNextSib) { pNextSib->Release(); } if( pInsertBefore) { pInsertBefore->Release(); } if( pTmpNode) { pTmpNode->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setNumber64( IF_Db * ifpDb, FLMINT64 i64Value, FLMUINT64 ui64Value, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; FLMBOOL bNeg = FALSE; F_Db * pDb = (F_Db*)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; IF_DOMNode * pNode = NULL; FLMUINT uiCollection; FLMUINT uiRflToken = 0; FLMUINT uiNodeDataType; FLMUINT uiValLen; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBOOL bIsIndexed = TRUE; FLMBOOL bStartOfUpdate = TRUE; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = canSetValue( pDb, XFLM_NUMBER_TYPE))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Grab some information about the node uiCollection = getCollection(); eNodeType = getNodeType(); // Special case for element nodes if( eNodeType == ELEMENT_NODE && getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } bMustAbortOnError = TRUE; rc = ((F_DOMNode *)pNode)->setNumber64( pDb, i64Value, ui64Value, uiEncDefId); goto Exit; } // If the number is less than zero, invert the sign if( i64Value < 0) { bNeg = TRUE; ui64Value = (FLMUINT64)(-i64Value); } else if( i64Value) { ui64Value = (FLMUINT64)i64Value; } if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; if( RC_BAD( rc = m_pCachedNode->setNumber64( pDb, m_uiAttrNameId, ui64Value, bNeg, uiEncDefId))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, m_pCachedNode, m_uiAttrNameId))) { goto Exit; } goto Exit; } // Generate the storage value uiNodeDataType = getDataType(); if( uiNodeDataType == XFLM_NUMBER_TYPE || uiNodeDataType == XFLM_NODATA_TYPE) { if( bNeg) { if( (getModeFlags() & FDOM_SIGNED_QUICK_VAL) && i64Value == getQuickINT64()) { goto Exit; } } else if( (getModeFlags() & FDOM_UNSIGNED_QUICK_VAL) && ui64Value == getQuickUINT64()) { goto Exit; } bMustAbortOnError = TRUE; if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; } else { bIsIndexed = FALSE; } if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) { goto Exit; } if( getDataBufSize() < FLM_MAX_NUM_BUF_SIZE) { if( RC_BAD( rc = resizeDataBuffer( FLM_MAX_NUM_BUF_SIZE, FALSE))) { goto Exit; } } uiValLen = FLM_MAX_NUM_BUF_SIZE; if( RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiValLen, getDataPtr(), bNeg, FALSE))) { goto Exit; } if( uiNodeDataType == XFLM_NODATA_TYPE) { uiNodeDataType = XFLM_NUMBER_TYPE; setDataType( uiNodeDataType); } } else if( uiNodeDataType == XFLM_TEXT_TYPE) { FLMBYTE ucNumBuf[ 64]; FLMBYTE * pucSen; FLMUINT uiSenLen; if( !bNeg) { f_ui64toa( ui64Value, (char *)&ucNumBuf[ 1]); } else { f_i64toa( i64Value, (char *)&ucNumBuf[ 1]); } uiValLen = f_strlen( (const char *)ucNumBuf); pucSen = &ucNumBuf[ 0]; uiSenLen = f_encodeSEN( uiValLen, &pucSen, (FLMUINT)0); flmAssert( uiSenLen == 1); uiValLen += uiSenLen + 1; // If the value isn't being changed, there is no need to continue if( getDataLength() == uiValLen && f_memcmp( getDataPtr(), ucNumBuf, uiValLen) == 0) { goto Exit; } bMustAbortOnError = TRUE; if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; } else { bIsIndexed = FALSE; } if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) { goto Exit; } // Allocate or re-allocate the buffer if( calcDataBufSize( uiValLen) > getDataBufSize()) { if( RC_BAD( rc = resizeDataBuffer( uiValLen, FALSE))) { goto Exit; } } if( uiValLen) { f_memcpy( getDataPtr(), ucNumBuf, uiValLen); } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); goto Exit; } setEncDefId( uiEncDefId); setDataLength( uiValLen); // Clear the "on disk" flag (if set), as well as the quick nums unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER | FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } if( !bNeg) { setUINT64( ui64Value); } else { setINT64( i64Value); } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( !uiEncDefId) { if( RC_BAD( rc = pRfl->logNodeSetNumberValue( pDb, uiCollection, getNodeId(), ui64Value, bNeg))) { goto Exit; } } else { if( RC_BAD( rc = pRfl->logEncryptedNodeUpdate( pDb, m_pCachedNode))) { goto Exit; } } Exit: if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( pNode) { pNode->Release(); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setStorageValue( F_Db * pDb, void * pvValue, FLMUINT uiValueLen, FLMUINT uiEncDefId, FLMBOOL bLast) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; eDomNodeType eNodeType; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBOOL bIsIndexed = TRUE; F_Database * pDatabase = pDb->m_pDatabase; FLMBOOL bFirst = pDatabase->m_pPendingInput ? FALSE : TRUE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getDataType() == XFLM_UNKNOWN_TYPE || (bStartedTrans && !bLast)) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } bMustAbortOnError = TRUE; uiCollection = getCollection(); eNodeType = getNodeType(); if( eNodeType == ELEMENT_NODE || eNodeType == DATA_NODE) { if( !getNameId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } if( bFirst) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_DEL_NODE_VALUE, TRUE, &bIsIndexed))) { goto Exit; } if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } } if( eNodeType == ELEMENT_NODE || eNodeType == DATA_NODE || eNodeType == COMMENT_NODE) { FLMUINT uiBytesToCopy; FLMBYTE * pucValue = (FLMBYTE *)pvValue; if( bFirst) { setEncDefId( uiEncDefId); } if( bFirst && bLast) { if( calcDataBufSize( uiValueLen) != getDataBufSize()) { if( RC_BAD( rc = resizeDataBuffer( uiValueLen, FALSE))) { goto Exit; } } setDataLength( uiValueLen); if( uiValueLen) { f_memcpy( getDataPtr(), pvValue, uiValueLen); } } else { if( bFirst) { if( RC_BAD( rc = m_pCachedNode->openPendingInput( pDb, getDataType()))) { goto Exit; } } while( uiValueLen) { uiBytesToCopy = pDatabase->m_uiUpdBufferSize - pDatabase->m_uiUpdByteCount; uiBytesToCopy = uiBytesToCopy > uiValueLen ? uiValueLen : uiBytesToCopy; if( !uiBytesToCopy) { if( RC_BAD( rc = m_pCachedNode->flushPendingInput( pDb, FALSE))) { goto Exit; } continue; } f_memcpy( &pDatabase->m_pucUpdBuffer[ pDatabase->m_uiUpdByteCount], pucValue, uiBytesToCopy); pucValue += uiBytesToCopy; uiValueLen -= uiBytesToCopy; pDatabase->m_uiUpdByteCount += uiBytesToCopy; } if( bLast) { if( pDatabase->m_bUpdFirstBuf) { if( RC_BAD( rc = m_pCachedNode->flushPendingInput( pDb, FALSE))) { goto Exit; } } if( RC_BAD( rc = m_pCachedNode->flushPendingInput( pDb, TRUE))) { goto Exit; } pDatabase->endPendingInput(); } } } else if( eNodeType == ATTRIBUTE_NODE) { if( !bLast) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = m_pCachedNode->setStorageValue( pDb, m_uiAttrNameId, pvValue, uiValueLen, uiEncDefId))) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( bLast) { if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, this, IX_ADD_NODE_VALUE, FALSE))) { goto Exit; } } } Exit: if( RC_BAD( rc)) { if( bMustAbortOnError) { pDb->setMustAbortTrans( rc); } pDatabase->endPendingInput(); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::openPendingInput( F_Db * pDb, FLMUINT uiNewDataType) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->m_pDatabase; eDomNodeType eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Set up the database to point to this node. Only one node // is allowed to stream data into the B-Tree at a time. if( RC_BAD( rc = pDatabase->startPendingInput( uiNewDataType, this))) { goto Exit; } // If the naming tag has already been set, make sure the // new data type is compatible with the defined type for // the tag. if( getNameId()) { switch( eNodeType) { case ELEMENT_NODE: case DATA_NODE: { F_AttrElmInfo elmInfo; if( RC_BAD( rc = pDb->m_pDict->getElement( pDb, m_nodeInfo.uiNameId, &elmInfo))) { goto Exit; } if( elmInfo.getDataType() != uiNewDataType) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } break; } case ANNOTATION_NODE: { break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } } // Better be the latest version for update. flmAssert( m_ui64LowTransId == pDb->m_ui64CurrTransID); m_nodeInfo.uiDataLength = 0; m_nodeInfo.uiDataType = uiNewDataType; unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); setFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); flmAssert( !pDatabase->m_uiUpdByteCount); flmAssert( !pDatabase->m_uiUpdCharCount); Exit: if( RC_BAD( rc)) { pDatabase->endPendingInput(); pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::flushPendingInput( F_Db * pDb, FLMBOOL bLast) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection = NULL; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMBYTE ucHeader[ MAX_DOM_HEADER_SIZE + 16]; FLMUINT uiKeyLen; FLMUINT uiHeaderStorageSize; F_Database * pDatabase = pDb->m_pDatabase; FLMUINT uiOutputLength; FLMUINT uiLeftoverLength; uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( m_nodeInfo.ui64NodeId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } // Open the B-Tree if( !pDatabase->m_pPendingBTree) { if( !pDatabase->m_bUpdFirstBuf) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pDatabase->m_pPendingBTree))) { goto Exit; } if( RC_BAD( rc = pDb->m_pDict->getCollection( m_nodeInfo.uiCollection, &pCollection))) { goto Exit; } if( RC_BAD( rc = pDatabase->m_pPendingBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } } // Better be the latest version for update. flmAssert( m_ui64LowTransId == pDb->m_ui64CurrTransID); uiOutputLength = pDatabase->m_uiUpdByteCount; uiLeftoverLength = 0; // Output the node header if( pDatabase->m_bUpdFirstBuf) { FLMUINT uiIVLen = 0; // This routine is designed to handle multi-block data streams. // It shouldn't be called if everything fits within the a single // update buffer. flmAssert( !bLast); // There shouldn't be any attributes at this point, because // this shouldn't be an element node flmAssert( !hasAttributes()); flmAssert( m_nodeInfo.eNodeType != ELEMENT_NODE); // Build the node header if( RC_BAD( rc = headerToBuf( TRUE, ucHeader, &uiHeaderStorageSize, NULL, NULL))) { goto Exit; } if( getEncDefId()) { F_ENCDEF * pEncDef; if( RC_BAD( rc = pDb->m_pDict->getEncDef( getEncDefId(), &pEncDef))) { goto Exit; } uiIVLen = pEncDef->pCcs->getIVLen(); flmAssert( uiIVLen == 8 || uiIVLen == 16); if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, pDatabase->m_ucIV))) { goto Exit; } f_memcpy( &ucHeader[ uiHeaderStorageSize], pDatabase->m_ucIV, uiIVLen); } // Output the header if( nodeIsNew()) { // If this is a new entry, we need to insert it into the // b-tree. if( RC_BAD( rc = pDatabase->m_pPendingBTree->btInsertEntry( ucKey, uiKeyLen, ucHeader, uiHeaderStorageSize + uiIVLen, TRUE, FALSE, &m_ui32BlkAddr, &m_uiOffsetIndex))) { if( rc == NE_XFLM_NOT_UNIQUE) { rc = RC_SET( NE_XFLM_EXISTS); } goto Exit; } } else { if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( ucKey, uiKeyLen, ucHeader, uiHeaderStorageSize + uiIVLen, TRUE, FALSE, TRUE, &m_ui32BlkAddr, &m_uiOffsetIndex))) { goto Exit; } } pDatabase->m_bUpdFirstBuf = FALSE; } // Output the data buffer if( pDatabase->m_uiUpdByteCount || bLast) { // Encrypt the buffer if this node is encrypted. If encrypted, // uiOutputLength will hold the length of the encrypted data. // If not encrypted, it will hold the length of the non-encrypted data, // i.e. pDatabase->m_uiUpdByteCount. the encrypted data will be returned // in the input buffer. if( getEncDefId()) { // If this is not the last buffer, only encrypt to the nearest // FLM_ENCRYPT_CHUNK_SIZE byte boundary. Move whatever we don't // encrypt down to the beginning of the buffer after writing the // encrypted part out to the B-tree (see below). This is necessary // because the decryption algorithm assumes that all of the // encrypted data is in FLM_ENRYPT_CHUNK_SIZE byte chunks - except // for the last chunk, which may be encrypted to the nearest 16 byte // boundary. if( !bLast && (uiOutputLength & (FLM_ENCRYPT_CHUNK_SIZE - 1))) { uiLeftoverLength = uiOutputLength & (FLM_ENCRYPT_CHUNK_SIZE - 1); uiOutputLength -= uiLeftoverLength; } if( RC_BAD( rc = pDb->encryptData( getEncDefId(), pDatabase->m_ucIV, pDatabase->m_pucUpdBuffer, pDatabase->m_uiUpdBufferSize, uiOutputLength, &uiOutputLength))) { goto Exit; } } if( nodeIsNew()) { // If this is a new entry, we need to continue calling the // insert method to stream its data into the b-tree if( RC_BAD( rc = pDatabase->m_pPendingBTree->btInsertEntry( ucKey, uiKeyLen, pDatabase->m_pucUpdBuffer, uiOutputLength, FALSE, bLast, &m_ui32BlkAddr, &m_uiOffsetIndex))) { if( rc == NE_XFLM_NOT_UNIQUE) { rc = RC_SET( NE_XFLM_EXISTS); } goto Exit; } } else { if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( ucKey, uiKeyLen, pDatabase->m_pucUpdBuffer, uiOutputLength, FALSE, bLast, TRUE, &m_ui32BlkAddr, &m_uiOffsetIndex))) { goto Exit; } } } m_nodeInfo.uiDataLength += uiOutputLength; if( (pDatabase->m_uiUpdByteCount = uiLeftoverLength) != 0) { f_memmove( pDatabase->m_pucUpdBuffer, pDatabase->m_pucUpdBuffer + uiOutputLength, uiLeftoverLength); } if( bLast) { // Clear the dirty flag and the new flag. unsetNodeDirtyAndNew( pDb); } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::setMetaValue( IF_Db * ifpDb, FLMUINT64 ui64Value) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db*)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bStartedTrans = FALSE; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); // Only allow meta values on element nodes. if( eNodeType != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If the value isn't changing, don't do anything if( ui64Value == getMetaValue()) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Make sure the node can be updated if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } // Set the value setMetaValue( ui64Value); // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeSetMetaValue( pDb, getCollection(), getNodeId(), ui64Value))) { goto Exit; } Exit: if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getMetaValue( IF_Db * ifpDb, FLMUINT64 * pui64Value) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db*)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } *pui64Value = getMetaValue(); Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::isDataLocalToNode( IF_Db * ifpDb, FLMBOOL * pbDataIsLocal) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db*)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (getNodeType() == ATTRIBUTE_NODE) { *pbDataIsLocal = TRUE; } else { *pbDataIsLocal = getDataLength() ? TRUE : FALSE; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: Read from a text stream, outputting ASCII. If we encounter a character that cannot be output as ASCII, we will return an error. ******************************************************************************/ RCODE F_AsciiIStream::read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; FLMUNICODE uzChar; *puiBytesRead = 0; while( *puiBytesRead < uiBytesToRead) { if( m_eTextType == XFLM_UNICODE_TEXT) { if( (m_pucEnd && ((FLMUINT)(m_pucEnd - m_pucCurrPtr) < sizeof( FLMUNICODE))) || (uzChar = *((FLMUNICODE *)m_pucCurrPtr)) == 0) { break; } if( uzChar > 127) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } *pucBuffer++ = (FLMBYTE)uzChar; m_pucCurrPtr += sizeof( FLMUNICODE); } else // UTF8 { if( RC_BAD( rc = f_getCharFromUTF8Buf( &m_pucCurrPtr, m_pucEnd, &uzChar))) { goto Exit; } if( !uzChar) { break; } if( uzChar > 127) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } *pucBuffer++ = (FLMBYTE)uzChar; } m_uiCurrChar++; (*puiBytesRead)++; } if( *puiBytesRead < uiBytesToRead) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: This class converts a storage text stream to an ASCII text stream. ******************************************************************************/ class F_AsciiStorageStream : public IF_IStream { public: F_AsciiStorageStream() { m_pIStream = NULL; } ~F_AsciiStorageStream() { closeStream(); } RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FINLINE RCODE FLMAPI closeStream( void) { if( m_pIStream) { m_pIStream->Release(); m_pIStream = NULL; } return( NE_XFLM_OK); } RCODE openStream( IF_IStream * pIStream); private: IF_IStream * m_pIStream; }; /***************************************************************************** Desc: Open an Ascii storage stream. ******************************************************************************/ RCODE F_AsciiStorageStream::openStream( IF_IStream * pIStream) { RCODE rc = NE_XFLM_OK; FLMBYTE ucSENBuf [16]; FLMUINT uiLen; FLMUINT uiSENLen; closeStream(); m_pIStream = pIStream; m_pIStream->AddRef(); // Skip over the SEN uiLen = 1; if( RC_BAD( rc = m_pIStream->read( (char *)&ucSENBuf [0], uiLen, &uiLen))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } if( (uiSENLen = f_getSENLength( ucSENBuf[ 0])) > 1) { uiLen = uiSENLen - 1; if( RC_BAD( rc = m_pIStream->read( (char *)&ucSENBuf [1], uiLen, &uiLen))) { goto Exit; } } // We are now positioned to read UTF8 Exit: if( RC_BAD( rc)) { closeStream(); } return( rc); } /***************************************************************************** Desc: Read from a storage text stream, outputting ASCII. If we encounter a character that cannot be output as ASCII, we will return an error. ******************************************************************************/ RCODE F_AsciiStorageStream::read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { RCODE rc = NE_XFLM_OK; FLMUNICODE uzChar; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; FLMUINT uiBytesRead = 0; // Better have a stream that has been positioned by a call to open(). flmAssert( m_pIStream); while( uiBytesRead < uiBytesToRead) { if( RC_BAD( rc = f_readUTF8CharAsUnicode( m_pIStream, &uzChar))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } else { goto Exit; } } if( uzChar <= 127) { *pucBuffer++ = (FLMBYTE)uzChar; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } uiBytesRead++; } if( uiBytesRead < uiBytesToRead) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } Exit: if( puiBytesRead) { *puiBytesRead = uiBytesRead; } return( rc); } /***************************************************************************** Desc: This class converts a binary stream to an ASCII text storage stream. ******************************************************************************/ class F_BinaryToTextStream : public IF_IStream { public: F_BinaryToTextStream() { m_pEncoderStream = NULL; } ~F_BinaryToTextStream() { closeStream(); } RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FINLINE RCODE FLMAPI closeStream( void) { if( m_pEncoderStream) { m_pEncoderStream->Release(); m_pEncoderStream = NULL; } return( NE_XFLM_OK); } RCODE openStream( IF_IStream * pIStream, FLMUINT uiDataLen, FLMUINT * puiTextLength); private: FLMBYTE m_ucSENBuf [16]; FLMUINT m_uiSENLen; FLMUINT m_uiCurrOffset; IF_IStream * m_pEncoderStream; }; /***************************************************************************** Desc: Open a binary-to-text stream. ******************************************************************************/ RCODE F_BinaryToTextStream::openStream( IF_IStream * pIStream, FLMUINT uiDataLen, FLMUINT * puiTextLength) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucSen; FLMUINT uiOutputLen; closeStream(); // Set up the SEN buffer. Calculate length to be 4 bytes for every 3 // binary bytes. uiOutputLen = (uiDataLen / 3) * 4; // If number of bytes is not an exact multiple of 3, we will need 4 // more bytes. if( uiDataLen % 3) { uiOutputLen += 4; } pucSen = &m_ucSENBuf [0]; m_uiSENLen = f_encodeSEN( (FLMUINT64)uiOutputLen, &pucSen, (FLMUINT)0); m_uiCurrOffset = 0; // Need to include the data length, SEN length, and the terminating null // character. *puiTextLength = uiOutputLen + m_uiSENLen + 1; // Set up the encoder stream if( RC_BAD( rc = FlmOpenBase64EncoderIStream( pIStream, FALSE, &m_pEncoderStream))) { goto Exit; } Exit: if( RC_BAD( rc)) { closeStream(); } return( rc); } /***************************************************************************** Desc: Read from a binary stream, outputting base 64 encoded ASCII. ******************************************************************************/ RCODE F_BinaryToTextStream::read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; FLMUINT uiBytesRead; // Better have a stream that has been positioned by a call to open(). flmAssert( m_pEncoderStream); *puiBytesRead = 0; if( m_uiCurrOffset < m_uiSENLen) { if( uiBytesToRead >= m_uiSENLen - m_uiCurrOffset) { f_memcpy( pucBuffer, &m_ucSENBuf [m_uiCurrOffset], m_uiSENLen - m_uiCurrOffset); (*puiBytesRead) += (m_uiSENLen - m_uiCurrOffset); pucBuffer += (m_uiSENLen - m_uiCurrOffset); m_uiCurrOffset = m_uiSENLen; } else { f_memcpy( pucBuffer, &m_ucSENBuf [m_uiCurrOffset], uiBytesToRead); (*puiBytesRead) += uiBytesToRead; m_uiCurrOffset += uiBytesToRead; pucBuffer += uiBytesToRead; } } // If we didn't get everything from the SEN buffer, read from the // decoding stream. if( *puiBytesRead < uiBytesToRead) { if( RC_BAD( rc = m_pEncoderStream->read( pucBuffer, uiBytesToRead - *puiBytesRead, &uiBytesRead))) { if( rc == NE_XFLM_EOF_HIT) { (*puiBytesRead) += uiBytesRead; } goto Exit; } else { (*puiBytesRead) += uiBytesRead; } } if( *puiBytesRead < uiBytesToRead) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: Store text as a number. ******************************************************************************/ RCODE F_DOMNode::storeTextAsNumber( F_Db * pDb, void * pvValue, FLMUINT uiNumBytesInBuffer, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; FLMBYTE ucChar; FLMUINT uiIncrAmount = 0; FLMBOOL bNeg = FALSE; FLMBOOL bHex = FALSE; FLMUINT64 ui64Num = 0; FLMUINT uiBytesRead; FLMBOOL bFirstChar = TRUE; F_AsciiIStream asciiStream( (FLMBYTE *)pvValue, uiNumBytesInBuffer, XFLM_UTF8_TEXT); // Convert the text to a number. for (;;) { if( RC_BAD( rc = asciiStream.read( &ucChar, 1, &uiBytesRead))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } goto Exit; } // See if we can convert to a number. if( ucChar >= ASCII_ZERO && ucChar <= ASCII_NINE) { uiIncrAmount = (FLMUINT)(ucChar - '0'); } else if( ucChar >= ASCII_UPPER_A && ucChar <= ASCII_UPPER_F) { if( bHex) { uiIncrAmount = (FLMUINT)(ucChar - ASCII_UPPER_A + 10); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } else if( ucChar >= ASCII_LOWER_A && ucChar <= ASCII_LOWER_F) { if( bHex) { uiIncrAmount = (FLMUINT)(ucChar - ASCII_LOWER_A + 10); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } else if( ucChar == ASCII_LOWER_X || ucChar == ASCII_UPPER_X) { if( !ui64Num && !bHex) { bHex = TRUE; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } else if( ucChar == ASCII_DASH && bFirstChar) { bNeg = TRUE; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } if( !bHex) { if( ui64Num > (~(FLMUINT64)0) / 10) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num *= (FLMUINT64)10; } else { if( ui64Num > (~(FLMUINT64)0) / 16) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num *= (FLMUINT64)16; } if( ui64Num > (~(FLMUINT64)0) - (FLMUINT64)uiIncrAmount) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num += (FLMUINT64)uiIncrAmount; bFirstChar = FALSE; } // If the number is negative, make sure it doesn't // overflow the maximum negative number. if( bNeg) { if( ui64Num > gv_ui64MaxSignedIntVal + 1) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_UNDERFLOW); goto Exit; } if( RC_BAD( rc = setINT64( (IF_Db *)pDb, -((FLMINT64)ui64Num), uiEncDefId))) { goto Exit; } } else { if( RC_BAD( rc = setUINT64( (IF_Db *)pDb, ui64Num, uiEncDefId))) { goto Exit; } } Exit: return( rc); } /***************************************************************************** Desc: Store text as a binary value. ******************************************************************************/ RCODE F_DOMNode::storeTextAsBinary( F_Db * pDb, const void * pvValue, FLMUINT uiNumBytesInBuffer, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; FLMBYTE ucBuf[ 64]; F_AsciiIStream asciiStream( (FLMBYTE *)pvValue, uiNumBytesInBuffer, XFLM_UTF8_TEXT); IF_IStream * pDecoderStream = NULL; FLMBYTE ucDynaBuf[ 64]; F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); FLMUINT uiBytesRead; if( RC_BAD( rc = FlmOpenBase64DecoderIStream( &asciiStream, &pDecoderStream))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pDecoderStream->read( ucBuf, sizeof( ucBuf), &uiBytesRead))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } if( RC_BAD( rc = dynaBuf.appendData( ucBuf, uiBytesRead))) { goto Exit; } } if( RC_BAD( rc = setBinary( (IF_Db *)pDb, dynaBuf.getBufferPtr(), dynaBuf.getDataLength(), uiEncDefId))) { goto Exit; } Exit: if( pDecoderStream) { pDecoderStream->Release(); } return( rc); } /***************************************************************************** Desc: Store binary as a text value (base 64 encoded) ******************************************************************************/ RCODE F_DOMNode::storeBinaryAsText( F_Db * pDb, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; FLMBYTE ucBuf[ 64]; IF_PosIStream * pIStream = NULL; IF_IStream * pEncoderStream = NULL; FLMBYTE ucDynaBuf[ 64]; F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); FLMUINT uiBytesRead; if( RC_BAD( rc = FlmOpenBufferIStream( (const char *)pvValue, uiLength, &pIStream))) { goto Exit; } if( RC_BAD( rc = FlmOpenBase64EncoderIStream( pIStream, FALSE, &pEncoderStream))) { goto Exit; } for( ;;) { if( RC_BAD( rc = pEncoderStream->read( ucBuf, sizeof( ucBuf), &uiBytesRead))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } if( RC_BAD( rc = dynaBuf.appendData( ucBuf, uiBytesRead))) { goto Exit; } } if( RC_BAD( rc = setTextFastPath( pDb, dynaBuf.getBufferPtr(), dynaBuf.getDataLength(), XFLM_UTF8_TEXT, uiEncDefId))) { goto Exit; } Exit: if( pEncoderStream) { pEncoderStream->Release(); } if( pIStream) { pIStream->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setTextStreaming( F_Db * pDb, const void * pvValue, FLMUINT uiNumBytesInBuffer, eXFlmTextType eTextType, FLMBOOL bLast, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen; FLMBYTE * pucUpdBuffer; FLMUINT uiUpdBufferSize; FLMBYTE * pucTmp; FLMUINT uiSenLen; FLMBYTE ucTmpSen[ FLM_MAX_NUM_BUF_SIZE]; F_Database * pDatabase = pDb->m_pDatabase; F_Rfl * pRfl = pDatabase->m_pRfl; F_DOMNode * pNode = NULL; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bFirst = pDatabase->m_pPendingInput ? FALSE : TRUE; FLMUINT uiRflToken = 0; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); // Not supported on attributes if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If this is an element node, we need to find or create // the child data node if( eNodeType == ELEMENT_NODE) { // The streaming interface is not directly supported on element nodes. // If the node already has a value and does not already have a child data // node, we can clear the value on the element, create a data node, and // allow the operation to continue. if( getDataLength()) { if( getDataChildCount()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = clearNodeValue( pDb))) { goto Exit; } } else if( getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } if( !pNode) { if( RC_BAD( rc = createNode( (IF_Db *)pDb, DATA_NODE, getNameId(), XFLM_LAST_CHILD, (IF_DOMNode **)&pNode))) { goto Exit; } bMustAbortOnError = TRUE; } if( RC_BAD( rc = pNode->setTextStreaming( pDb, pvValue, uiNumBytesInBuffer, eTextType, bLast, uiEncDefId))) { goto Exit; } goto Exit; } // Make sure the state of the node and database // allow a value to be set. if( RC_BAD( rc = canSetValue( pDb, XFLM_TEXT_TYPE))) { goto Exit; } pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); bMustAbortOnError = TRUE; // If we are in the middle of streaming data into a node, // any error will probably leave the database in a bad // state. Because of this, we want to force the // transaction to abort. if( pDatabase->m_pPendingInput) { if( pDatabase->m_pPendingInput != m_pCachedNode) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } pucUpdBuffer = pDatabase->m_pucUpdBuffer; // NOTE: So that flushNode would not have to allocate a buffer for // writing out data with a non-padded header, we reserve enough // room in pDatabase->m_pucUpdBuffer so that it could hold a maximum // header plus all of the data if it needed to. // Also, make sure there is room to encrypt the data. That is why // we subtract another 16 bytes. uiUpdBufferSize = pDatabase->m_uiUpdBufferSize - MAX_DOM_HEADER_SIZE - ENCRYPT_MIN_CHUNK_SIZE; // Update the index keys if( bFirst) { if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, TRUE))) { goto Exit; } } } // Open the pending input stream if( bFirst) { if( RC_BAD( rc = openPendingInput( pDb, XFLM_TEXT_TYPE))) { goto Exit; } // Reserve 5 bytes for a SEN indicating the number of characters // in the string. The SEN (representing a 32-bit number) will // never require more than 5 bytes and will typically only // require 1 or 2 bytes. pDatabase->m_uiUpdByteCount += SEN_RESERVE_BYTES; *pucUpdBuffer = 0; // Save the encryption scheme setEncDefId( uiEncDefId); } // Set the value if( pvValue) { // If a zero was passed for the character count, change it to // the UINT high value. We will output characters until a terminating // null is found. if( !uiNumBytesInBuffer) { uiNumBytesInBuffer = FLM_MAX_UINT; } switch( eTextType) { case XFLM_UNICODE_TEXT: { FLMUNICODE * puCurChar = (FLMUNICODE *)pvValue; while( *puCurChar && uiNumBytesInBuffer >= sizeof( FLMUNICODE)) { uiLen = uiUpdBufferSize - pDatabase->m_uiUpdByteCount; if( RC_BAD( rc = f_uni2UTF8( *puCurChar, &pucUpdBuffer[ pDatabase->m_uiUpdByteCount], &uiLen))) { if( rc == NE_XFLM_CONV_DEST_OVERFLOW) { if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) { goto Exit; } continue; } goto Exit; } pDatabase->m_uiUpdByteCount += uiLen; pDatabase->m_uiUpdCharCount++; uiNumBytesInBuffer -= sizeof( FLMUNICODE); puCurChar++; } break; } case XFLM_UTF8_TEXT: { FLMBYTE * pucCurByte = (FLMBYTE *)pvValue; FLMBYTE * pucEnd = (FLMBYTE *)pvValue + uiNumBytesInBuffer; for( ;;) { if( (uiUpdBufferSize - pDatabase->m_uiUpdByteCount) < 3) { if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) { goto Exit; } } if( RC_BAD( rc = f_getUTF8CharFromUTF8Buf( &pucCurByte, pucEnd, &pucUpdBuffer[ pDatabase->m_uiUpdByteCount], &uiLen))) { goto Exit; } if( !uiLen) { break; } pDatabase->m_uiUpdByteCount += uiLen; pDatabase->m_uiUpdCharCount++; uiNumBytesInBuffer -= uiLen; } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } } if( bLast) { // Terminate the buffer with a null byte if( pDatabase->m_uiUpdCharCount) { // See if there is room for a single zero byte if( pDatabase->m_uiUpdByteCount == uiUpdBufferSize) { if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) { goto Exit; } } pucUpdBuffer[ pDatabase->m_uiUpdByteCount++] = 0; } if( pDatabase->m_bUpdFirstBuf) { if( pDatabase->m_uiUpdCharCount) { FLMUINT uiValLen; // Five bytes were reserved to encode the number of characters // in the string. Since we didn't have to use multiple buffers // to write the string to the database, we won't have to // output a padded SEN. pucTmp = ucTmpSen; f_encodeSEN( pDatabase->m_uiUpdCharCount, &pucTmp); uiSenLen = (FLMUINT)(pucTmp - ucTmpSen); // Copy the value into the node uiValLen = (pDatabase->m_uiUpdByteCount - SEN_RESERVE_BYTES) + uiSenLen; if( calcDataBufSize( uiValLen) > getDataBufSize()) { if( RC_BAD( rc = resizeDataBuffer( uiValLen, FALSE))) { goto Exit; } } setDataLength( uiValLen); f_memcpy( getDataPtr(), ucTmpSen, uiSenLen); f_memcpy( getDataPtr() + uiSenLen, &pucUpdBuffer[ SEN_RESERVE_BYTES], pDatabase->m_uiUpdByteCount - SEN_RESERVE_BYTES); } else { setDataLength( 0); } // Clear flags unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { setDataLength( 0); goto Exit; } } else { FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMUINT uiHeaderStorageSize; FLMUINT32 ui32BlkAddr; FLMUINT uiOffsetIndex; // Flush anything that is in the current buffer and end the // pending input stream. if( RC_BAD( rc = flushPendingInput( pDb, TRUE))) { goto Exit; } pucTmp = ucTmpSen; f_encodeSEN( pDatabase->m_uiUpdCharCount, &pucTmp, SEN_RESERVE_BYTES); flmAssert( (FLMUINT)(pucTmp - ucTmpSen) == SEN_RESERVE_BYTES); // Output the header if( RC_BAD( rc = headerToBuf( TRUE, pDatabase->m_pucUpdBuffer, &uiHeaderStorageSize, NULL, NULL))) { goto Exit; } // Copy the SEN into the update buffer f_memcpy( &pDatabase->m_pucUpdBuffer[ uiHeaderStorageSize], ucTmpSen, SEN_RESERVE_BYTES); // Replace the header uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( getNodeId(), &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } ui32BlkAddr = getBlkAddr(); uiOffsetIndex = getOffsetIndex(); if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( ucKey, uiKeyLen, pDatabase->m_pucUpdBuffer, uiHeaderStorageSize + SEN_RESERVE_BYTES, TRUE, TRUE, FALSE, &ui32BlkAddr, &uiOffsetIndex))) { goto Exit; } setBlkAddr( ui32BlkAddr); setOffsetIndex( uiOffsetIndex); // Clear the dirty flag and the new flag. unsetNodeDirtyAndNew( pDb); } pDatabase->endPendingInput(); if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, FALSE))) { goto Exit; } } // Log the node to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, RFL_NODE_SET_TEXT_VALUE_PACKET, m_pCachedNode))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } if( RC_BAD( rc)) { pDatabase->endPendingInput(); if( bMustAbortOnError) { pDb->setMustAbortTrans( rc); } } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setTextFastPath( F_Db * pDb, const void * pvValue, FLMUINT uiNumBytesInBuffer, eXFlmTextType eTextType, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumCharsInBuffer = 0; FLMBYTE ucUTFCharBuf[ 3]; F_Database * pDatabase = pDb->m_pDatabase; F_Rfl * pRfl = pDatabase->m_pRfl; F_DOMNode * pNode = NULL; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; FLMUINT uiValLen; FLMUINT uiReadLen; FLMBOOL bIsIndexed = TRUE; FLMBYTE ucDynaBuf[ 64]; F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); FLMBOOL bStartOfUpdate = TRUE; eDomNodeType eNodeType; // Make sure a transaction is active if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Cannot set a value if input is still pending if( pDatabase->m_pPendingInput) { rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); // Convert the text to UTF-8 if needed if( eTextType == XFLM_UNICODE_TEXT) { FLMUNICODE * puCurChar = (FLMUNICODE *)pvValue; if( puCurChar) { if( !uiNumBytesInBuffer) { uiNumBytesInBuffer = FLM_MAX_UINT; } while( *puCurChar && uiNumBytesInBuffer >= sizeof( FLMUNICODE)) { if( *puCurChar <= 0x007F) { if( RC_BAD( rc = dynaBuf.appendByte( (FLMBYTE)*puCurChar))) { goto Exit; } } else { uiReadLen = sizeof( ucUTFCharBuf); if( RC_BAD( rc = f_uni2UTF8( *puCurChar, ucUTFCharBuf, &uiReadLen))) { goto Exit; } if( RC_BAD( rc = dynaBuf.appendData( ucUTFCharBuf, uiReadLen))) { goto Exit; } } puCurChar++; uiNumBytesInBuffer -= sizeof( FLMUNICODE); uiNumCharsInBuffer++; } } if( RC_BAD( rc = dynaBuf.appendByte( 0))) { goto Exit; } eTextType = XFLM_UTF8_TEXT; pvValue = dynaBuf.getBufferPtr(); uiNumBytesInBuffer = dynaBuf.getDataLength(); } else if( eTextType == XFLM_UTF8_TEXT) { if( RC_BAD( rc = f_getUTF8Length( (FLMBYTE *)pvValue, uiNumBytesInBuffer, &uiNumBytesInBuffer, &uiNumCharsInBuffer))) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If this is an element node, we need to see if there is a // child data node if( eNodeType == ELEMENT_NODE && getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } bMustAbortOnError = TRUE; if( getDataChildCount() == 1) { // If this node only has one data child, delete the child and // store the value directly on the element if( RC_BAD( rc = pNode->deleteNode( pDb))) { goto Exit; } pNode->Release(); pNode = NULL; } else { rc = pNode->setTextFastPath( pDb, pvValue, uiNumBytesInBuffer, eTextType, uiEncDefId); goto Exit; } } // Make sure the states of the node and database // allow a value to be set. if( RC_BAD( rc = canSetValue( pDb, XFLM_TEXT_TYPE))) { goto Exit; } // If this is an attribute node, need to use the // attribute list object to set the value if( eNodeType == ATTRIBUTE_NODE) { pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; if( RC_BAD( rc = m_pCachedNode->setUTF8( pDb, m_uiAttrNameId, pvValue, uiNumBytesInBuffer, uiNumCharsInBuffer, uiEncDefId))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } } // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { setDataLength( 0); goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, m_pCachedNode, m_uiAttrNameId))) { goto Exit; } goto Exit; } pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); bMustAbortOnError = TRUE; // Update the index keys if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; } else { bIsIndexed = FALSE; } // Verify and save the encryption scheme if( uiEncDefId) { if( RC_BAD( rc = pDb->m_pDict->getEncDef( uiEncDefId, NULL))) { flmAssert( 0); goto Exit; } } setEncDefId( uiEncDefId); // Set the value uiValLen = 0; if( pvValue && uiNumBytesInBuffer) { FLMUINT uiSenLen; FLMBYTE * pucTmp; FLMBYTE * pucValue = (FLMBYTE *)pvValue; FLMBOOL bNullTerminate = FALSE; if( pucValue[ uiNumBytesInBuffer - 1] != 0) { bNullTerminate = TRUE; } uiSenLen = f_getSENByteCount( uiNumCharsInBuffer); uiValLen = uiNumBytesInBuffer + uiSenLen + (bNullTerminate ? 1 : 0); if( calcDataBufSize( uiValLen) > getDataBufSize()) { if( RC_BAD( rc = resizeDataBuffer( uiValLen, FALSE))) { goto Exit; } } pucTmp = getDataPtr(); f_encodeSENKnownLength( uiNumCharsInBuffer, uiSenLen, &pucTmp); f_memcpy( pucTmp, pucValue, uiNumBytesInBuffer); if( bNullTerminate) { pucTmp[ uiNumBytesInBuffer] = 0; } } setDataLength( uiValLen); // Clear flags unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { setDataLength( 0); goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } // Log the node to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, RFL_NODE_SET_TEXT_VALUE_PACKET, m_pCachedNode))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } if( RC_BAD( rc)) { if( bMustAbortOnError) { pDb->setMustAbortTrans( rc); } } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setBinaryStreaming( IF_Db * ifpDb, const void * pvValue, FLMUINT uiLength, FLMBOOL bLast, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_Database * pDatabase = pDb->m_pDatabase; F_Rfl * pRfl = pDatabase->m_pRfl; FLMBYTE * pucValue = (FLMBYTE *)pvValue; FLMBYTE * pucUpdBuffer; FLMUINT uiUpdBufferSize; FLMUINT uiTmp; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pNode = NULL; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); // Not supported on attribute nodes if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If this is an element node, we need to find or create // the child data node if( eNodeType == ELEMENT_NODE) { // The streaming interface is not directly supported on element nodes. // If the node already has a value and does not already have a child data // node, we can clear the value on the element, create a data node, and // allow the operation to continue. if( getDataLength()) { if( getDataChildCount()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = clearNodeValue( pDb))) { goto Exit; } } else if( getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } if( !pNode) { if( RC_BAD( rc = createNode( (IF_Db *)pDb, DATA_NODE, getNameId(), XFLM_LAST_CHILD, (IF_DOMNode **)&pNode))) { goto Exit; } bMustAbortOnError = TRUE; } if( RC_BAD( rc = pNode->setBinary( ifpDb, pucValue, uiLength, bLast, uiEncDefId))) { goto Exit; } goto Exit; } // Make sure the state of the node and database // allow a value to be set. if( RC_BAD( rc = canSetValue( pDb, XFLM_BINARY_TYPE))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); bMustAbortOnError = TRUE; // If we are in the middle of streaming data into a node, // any error will probably leave the database in a bad // state. Because of this, we want to force the // transaction to abort. if( pDatabase->m_pPendingInput) { if( pDatabase->m_pPendingInput != m_pCachedNode) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } pucUpdBuffer = pDatabase->m_pucUpdBuffer; // NOTE: So that flushNode would not have to allocate a buffer for // writing out data with a non-padded header, we reserve enough // room in pDatabase->m_pucUpdBuffer so that it could hold a maximum // header plus all of the data if it needed to. // // Also, make sure there is room to encrypt the data. That is why // we subtract another 16 bytes. uiUpdBufferSize = pDatabase->m_uiUpdBufferSize - MAX_DOM_HEADER_SIZE - ENCRYPT_MIN_CHUNK_SIZE; // Open the pending input stream if( !pDatabase->m_pPendingInput) { if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, TRUE))) { goto Exit; } } if( RC_BAD( rc = openPendingInput( pDb, XFLM_BINARY_TYPE))) { goto Exit; } } // Save the encryption scheme setEncDefId( uiEncDefId); while( uiLength) { if( (uiTmp = f_min( uiLength, uiUpdBufferSize - pDatabase->m_uiUpdByteCount)) == 0) { if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) { goto Exit; } continue; } f_memcpy( &pucUpdBuffer[ pDatabase->m_uiUpdByteCount], pucValue, uiTmp); pDatabase->m_uiUpdByteCount += uiTmp; pucValue += uiTmp; uiLength -= uiTmp; } if( bLast) { if( pDatabase->m_bUpdFirstBuf) { if( pDatabase->m_uiUpdByteCount) { if( calcDataBufSize( pDatabase->m_uiUpdByteCount) > getDataBufSize()) { if( RC_BAD( rc = resizeDataBuffer( pDatabase->m_uiUpdByteCount, FALSE))) { goto Exit; } } } setDataLength( pDatabase->m_uiUpdByteCount); if( pDatabase->m_uiUpdByteCount) { f_memcpy( getDataPtr(), pucUpdBuffer, pDatabase->m_uiUpdByteCount); } // Clear unwanted flags unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } } else { FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMUINT uiHeaderStorageSize; FLMUINT32 ui32BlkAddr; FLMUINT uiOffsetIndex; // Flush anything that is in the current buffer if( RC_BAD( rc = flushPendingInput( pDb, TRUE))) { goto Exit; } // Output the header if( RC_BAD( rc = headerToBuf( TRUE, pDatabase->m_pucUpdBuffer, &uiHeaderStorageSize, NULL, NULL))) { goto Exit; } // Replace the header uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( getNodeId(), &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } ui32BlkAddr = getBlkAddr(); uiOffsetIndex = getOffsetIndex(); if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( ucKey, uiKeyLen, pDatabase->m_pucUpdBuffer, uiHeaderStorageSize, TRUE, TRUE, FALSE, &ui32BlkAddr, &uiOffsetIndex))) { goto Exit; } setBlkAddr( ui32BlkAddr); setOffsetIndex( uiOffsetIndex); // Clear the dirty flag and the new flag. unsetNodeDirtyAndNew( pDb); } pDatabase->endPendingInput(); if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, FALSE))) { goto Exit; } } // Log the node to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, RFL_NODE_SET_BINARY_VALUE_PACKET, m_pCachedNode))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } if( RC_BAD( rc) && bMustAbortOnError) { pDatabase->endPendingInput(); } if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setBinaryFastPath( IF_Db * ifpDb, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_Database * pDatabase = pDb->m_pDatabase; F_Rfl * pRfl = pDatabase->m_pRfl; FLMBYTE * pucValue = (FLMBYTE *)pvValue; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pNode = NULL; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; eDomNodeType eNodeType; FLMBOOL bIsIndexed = TRUE; FLMBOOL bStartOfUpdate = TRUE; // Make sure a transaction is active if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Cannot set a value if input is still pending if( pDatabase->m_pPendingInput) { rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); // If this is an element node, we need to find or create // the child data node if( eNodeType == ELEMENT_NODE && getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } bMustAbortOnError = TRUE; if( getDataChildCount() == 1) { // If this node only has one data child, delete the child and // store the value directly on the element if( RC_BAD( rc = pNode->deleteNode( ifpDb))) { goto Exit; } pNode->Release(); pNode = NULL; } else { rc = pNode->setBinaryFastPath( ifpDb, pucValue, uiLength, uiEncDefId); goto Exit; } } else if( eNodeType == ATTRIBUTE_NODE) { // Make sure the state of the node and database // allow a value to be set. if( RC_BAD( rc = canSetValue( pDb, XFLM_BINARY_TYPE))) { goto Exit; } pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; if( RC_BAD( rc = m_pCachedNode->setBinary( pDb, m_uiAttrNameId, pvValue, uiLength, uiEncDefId))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, m_pCachedNode, m_uiAttrNameId))) { goto Exit; } goto Exit; } // If node is a text data type, convert the binary to base 64 encoding // and store as text. if( getDataType() == XFLM_TEXT_TYPE) { // Convert to base64 text and call the routine to set as native. rc = storeBinaryAsText( (F_Db *)ifpDb, pvValue, uiLength, uiEncDefId); goto Exit; } // Make sure the state of the node and database // allow a value to be set. if( RC_BAD( rc = canSetValue( pDb, XFLM_BINARY_TYPE))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); if( getNameId()) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; } else { bIsIndexed = FALSE; } setEncDefId( uiEncDefId); if( calcDataBufSize( uiLength) > getDataBufSize()) { if( RC_BAD( rc = resizeDataBuffer( uiLength, FALSE))) { goto Exit; } } setDataLength( uiLength); if( uiLength) { f_memcpy( getDataPtr(), pvValue, uiLength); } // Clear unwanted flags unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } // Log the node to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, RFL_NODE_SET_BINARY_VALUE_PACKET, m_pCachedNode))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::clearNodeValue( F_Db * pDb) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->m_pDatabase; F_Rfl * pRfl = pDatabase->m_pRfl; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; // Make sure a transaction is active if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Cannot clear a value if input is still pending if( pDatabase->m_pPendingInput) { rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); bMustAbortOnError = TRUE; if( RC_BAD( rc = setStorageValue( pDb, NULL, 0, 0, TRUE))) { goto Exit; } // Log the update to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeClearValue( pDb, getCollection(), getNodeId(), m_uiAttrNameId))) { goto Exit; } Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::getIStream( F_Db * pDb, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiDataType, FLMUINT * puiDataLength) { RCODE rc = NE_XFLM_OK; IF_PosIStream * pIStream = NULL; F_DOMNode * pNode = NULL; F_CachedNode * pCachedNode = this; eDomNodeType eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } if( m_uiFlags & FDOM_VALUE_ON_DISK) { F_BTreeIStream * pBTreeIStream; F_ENCDEF * pEncDef; FLMUINT uiIVLen; FLMUINT uiLen; F_NODE_INFO nodeInfo; FLMUINT uiTmpFlags; if( eNodeType == ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = pDb->flushDirtyNode( this))) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pNodePool->allocBTreeIStream( &pBTreeIStream))) { goto Exit; } pIStream = pBTreeIStream; if( RC_BAD( rc = pBTreeIStream->openStream( pDb, getCollection(), getNodeId(), getBlkAddr(), getOffsetIndex()))) { goto Exit; } // Skip the node header if( RC_BAD( rc = flmReadNodeInfo( getCollection(), getNodeId(), pBTreeIStream, (FLMUINT)pBTreeIStream->remainingSize(), TRUE, &nodeInfo, &uiTmpFlags))) { goto Exit; } // Read the IV if data is encrypted if( getEncDefId()) { if( RC_BAD( rc = pDb->m_pDict->getEncDef( getEncDefId(), &pEncDef))) { goto Exit; } uiIVLen = pEncDef->pCcs->getIVLen(); flmAssert( uiIVLen == 8 || uiIVLen == 16); if( RC_BAD( rc = pBTreeIStream->read( (char *)pBTreeIStream->m_ucIV, uiIVLen, &uiLen))) { goto Exit; } flmAssert( uiLen == uiIVLen); pBTreeIStream->m_bDataEncrypted = TRUE; } pBTreeIStream->m_uiEncDefId = getEncDefId(); pBTreeIStream->m_uiDataLength = getDataLength(); } else { F_NodeBufferIStream * pNodeBufferIStream; FLMUINT64 ui64TmpNodeId; if( eNodeType == ELEMENT_NODE && getDataChildCount()) { ui64TmpNodeId = getFirstChildId(); for( ;;) { if( !ui64TmpNodeId) { break; } if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( getCollection(), ui64TmpNodeId, (IF_DOMNode **)&pNode))) { goto Exit; } if( pNode->getNodeType() == DATA_NODE) { pCachedNode = pNode->m_pCachedNode; break; } ui64TmpNodeId = pNode->getNextSibId(); } } if( pStackStream) { pNodeBufferIStream = pStackStream; pStackStream->AddRef(); flmAssert( !pStackStream->m_pCachedNode); } else { if( (pNodeBufferIStream = f_new F_NodeBufferIStream) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } pIStream = pNodeBufferIStream; if( RC_BAD( rc = pNodeBufferIStream->openStream( (const char *)pCachedNode->getDataPtr(), pCachedNode->getDataLength()))) { goto Exit; } if( !pStackStream) { pNodeBufferIStream->m_pCachedNode = pCachedNode; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pCachedNode->incrNodeUseCount(); pCachedNode->incrStreamUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } } if( puiDataType) { *puiDataType = pCachedNode->getDataType(); } if( puiDataLength) { *puiDataLength = pCachedNode->getDataLength(); } *ppIStream = pIStream; pIStream = NULL; Exit: if( pIStream) { pIStream->Release(); } if( pNode) { pNode->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::getRawIStream( F_Db * pDb, IF_PosIStream ** ppIStream) { RCODE rc = NE_XFLM_OK; IF_PosIStream * pIStream = NULL; F_BTreeIStream * pBTreeIStream; eDomNodeType eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Flush all dirty nodes out to the B-Tree if( RC_BAD( rc = pDb->flushDirtyNode( this))) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pNodePool->allocBTreeIStream( &pBTreeIStream))) { goto Exit; } pIStream = pBTreeIStream; if( RC_BAD( rc = pBTreeIStream->openStream( pDb, getCollection(), getNodeId(), getBlkAddr(), getOffsetIndex()))) { goto Exit; } *ppIStream = pIStream; pIStream = NULL; Exit: if( pIStream) { pIStream->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getIStream( F_Db * pDb, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiDataType, FLMUINT * puiDataLength) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, NULL))) { goto Exit; } // Sync the node to make sure it is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } switch( getNodeType()) { case DATA_NODE: case COMMENT_NODE: case ANNOTATION_NODE: case CDATA_SECTION_NODE: { if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, pStackStream, ppIStream, puiDataType, puiDataLength))) { goto Exit; } break; } case ELEMENT_NODE: { if( getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if( RC_BAD( rc = pNode->m_pCachedNode->getIStream( pDb, pStackStream, ppIStream, puiDataType, puiDataLength))) { goto Exit; } } else { if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, pStackStream, ppIStream, puiDataType, puiDataLength))) { goto Exit; } } break; } case ATTRIBUTE_NODE: { if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, m_uiAttrNameId, pStackStream, ppIStream, puiDataType, puiDataLength))) { goto Exit; } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } Exit: if( pNode) { pNode->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getTextIStream( F_Db * pDb, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiNumChars) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataType; *ppIStream = NULL; *puiNumChars = 0; if( RC_BAD( rc = getIStream( pDb, pStackStream, ppIStream, &uiDataType))) { goto Exit; } if( uiDataType != XFLM_TEXT_TYPE) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); goto Exit; } // Skip the leading SEN so that the stream is positioned to // read raw utf8. if( (*ppIStream)->remainingSize()) { if( RC_BAD( rc = f_readSEN( *ppIStream, puiNumChars))) { goto Exit; } } Exit: if( RC_BAD( rc) && *ppIStream) { (*ppIStream)->Release(); *ppIStream = NULL; *puiNumChars = 0; } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getNumber64( F_Db * pDb, FLMUINT64 * pui64Num, FLMBOOL * pbNeg) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataType; FLMUINT64 ui64Num; FLMBOOL bNeg; F_DOMNode * pNode = NULL; eDomNodeType eNodeType; IF_PosIStream * pIStream = NULL; F_NodeBufferIStream bufferIStream; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getNumber64( pDb, m_uiAttrNameId, &ui64Num, &bNeg))) { goto Exit; } } else if ( !getQuickNumber64( &ui64Num, &bNeg)) { if( eNodeType == ELEMENT_NODE && getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } rc = pNode->getNumber64( pDb, pui64Num, pbNeg); goto Exit; } else { if( RC_BAD( rc = getIStream( pDb, &bufferIStream, &pIStream, &uiDataType))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsNumber( pIStream, uiDataType, &ui64Num, &bNeg))) { goto Exit; } } } if( pui64Num) { *pui64Num = ui64Num; } if( pbNeg) { *pbNeg = bNeg; } Exit: if( pIStream) { pIStream->Release(); } if( pNode) { pNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: Allocate data for a unicode element and retrieve it. *****************************************************************************/ RCODE FLMAPI F_DOMNode::getUnicode( IF_Db * ifpDb, FLMUNICODE ** ppuzUnicode) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // Get the unicode length (does not include NULL terminator) if( RC_BAD( rc = getUnicodeChars( pDb, &uiLen))) { goto Exit; } if( uiLen) { FLMUINT uiBufSize = (uiLen + 1) * sizeof( FLMUNICODE); if( RC_BAD( rc = f_alloc( uiBufSize, ppuzUnicode))) { goto Exit; } if( RC_BAD( rc = getUnicode( pDb, *ppuzUnicode, uiBufSize, 0, uiLen, &uiLen))) { goto Exit; } } else { *ppuzUnicode = NULL; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getUnicode( IF_Db * ifpDb, FLMUNICODE * puzBuffer, FLMUINT uiBufSize, FLMUINT uiCharOffset, FLMUINT uiMaxCharsRequested, FLMUINT * puiCharsReturned, FLMUINT * puiBufferBytesUsed) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataType; FLMUINT uiDataLength; F_NodeBufferIStream bufferStream; IF_PosIStream * pIStream = NULL; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = getIStream( pDb, &bufferStream, &pIStream, &uiDataType, &uiDataLength))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsText( pIStream, NULL, uiDataLength, uiDataType, puzBuffer, uiBufSize, XFLM_UNICODE_TEXT, uiMaxCharsRequested, uiCharOffset, puiCharsReturned, puiBufferBytesUsed))) { goto Exit; } Exit: if( pIStream) { pIStream->Release(); } if( bStartedTrans) { pDb->abortTrans(); } return( rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getUnicode( IF_Db * ifpDb, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiBufSize; FLMUINT uiChars; void * pvBuffer = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } pBuffer->truncateData( 0); if( RC_BAD( rc = getUnicode( ifpDb, NULL, 0, 0, ~((FLMUINT)0), &uiChars))) { goto Exit; } uiBufSize = (uiChars + 1) * sizeof( FLMUNICODE); if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) { goto Exit; } if( RC_BAD( rc = getUnicode( ifpDb, (FLMUNICODE *)pvBuffer, uiBufSize, 0, ~((FLMUINT)0), NULL))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getUTF8( IF_Db * ifpDb, FLMBYTE * pszValue, FLMUINT uiBufferSize, FLMUINT uiCharOffset, FLMUINT uiMaxCharsRequested, FLMUINT * puiCharsReturned, FLMUINT * puiBufferBytesUsed) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataType; FLMUINT uiDataLength; F_DOMNode * pNode = NULL; IF_PosIStream * pIStream = NULL; F_NodeBufferIStream bufferIStream; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } switch( getNodeType()) { case DATA_NODE: case COMMENT_NODE: case ANNOTATION_NODE: case CDATA_SECTION_NODE: { pNode = this; pNode->AddRef(); break; } case ATTRIBUTE_NODE: { pNode = this; pNode->AddRef(); goto SlowDecode; } case ELEMENT_NODE: { if( getDataChildCount()) { if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } else { pNode = this; pNode->AddRef(); } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } if( (pNode->getModeFlags() & FDOM_VALUE_ON_DISK) || pNode->getDataType() != XFLM_TEXT_TYPE || uiCharOffset) { SlowDecode: if( RC_BAD( rc = pNode->getIStream( pDb, &bufferIStream, &pIStream, &uiDataType, &uiDataLength))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsText( pIStream, NULL, uiDataLength, uiDataType, pszValue, uiBufferSize, XFLM_UTF8_TEXT, uiMaxCharsRequested, uiCharOffset, puiCharsReturned, puiBufferBytesUsed))) { goto Exit; } } else { const FLMBYTE * pucBuffer = pNode->getDataPtr(); const FLMBYTE * pucEnd = pucBuffer + pNode->getDataLength(); FLMUINT uiCharCount = 0; FLMUINT uiStrByteLen = 0; if( pucBuffer) { if( RC_BAD( rc = f_decodeSEN( &pucBuffer, pucEnd, &uiCharCount))) { goto Exit; } uiStrByteLen = (FLMUINT)(pucEnd - pucBuffer); } if( uiCharCount > uiMaxCharsRequested || (pszValue && uiBufferSize < uiStrByteLen)) { goto SlowDecode; } if( pszValue) { if( uiStrByteLen) { f_memcpy( pszValue, pucBuffer, uiStrByteLen); } else if( uiBufferSize > 0) { *pszValue = 0; } } if( puiCharsReturned) { *puiCharsReturned = uiCharCount; } if( puiBufferBytesUsed) { *puiBufferBytesUsed = uiStrByteLen; } } Exit: if( pIStream) { pIStream->Release(); } if( pNode) { pNode->Release(); } if( bStartedTrans) { pDb->abortTrans(); } return( rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getUTF8( IF_Db * ifpDb, FLMBYTE ** ppszUTF8) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiBufSize; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getUTF8( ifpDb, NULL, 0, 0, FLM_MAX_UINT, NULL, &uiBufSize))) { goto Exit; } if( uiBufSize) { if( RC_BAD( rc = f_alloc( uiBufSize, ppszUTF8))) { goto Exit; } if( RC_BAD( rc = getUTF8( ifpDb, *ppszUTF8, uiBufSize, 0, FLM_MAX_UINT, NULL, NULL))) { goto Exit; } } else { *ppszUTF8 = NULL; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getUTF8( IF_Db * ifpDb, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiBufSize; void * pvBuffer = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } pBuffer->truncateData( 0); if( RC_BAD( rc = getUTF8( ifpDb, NULL, 0, 0, FLM_MAX_UINT, NULL, &uiBufSize))) { goto Exit; } if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) { goto Exit; } if( RC_BAD( rc = getUTF8( ifpDb, (FLMBYTE *)pvBuffer, uiBufSize, 0, FLM_MAX_UINT, NULL, NULL))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getBinary( IF_Db * ifpDb, void * pvValue, FLMUINT uiByteOffset, FLMUINT uiBytesRequested, FLMUINT * puiBytesReturned) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucValue = (FLMBYTE *)pvValue; IF_PosIStream * pIStream = NULL; IF_IStream * pDecoderStream = NULL; F_NodeBufferIStream bufferIStream; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiTmp; FLMUINT uiDataType; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // If a NULL buffer is passed in, just return the // data length if( !pucValue) { if( RC_BAD( rc = getDataLength( pDb, &uiTmp))) { goto Exit; } if( uiByteOffset <= uiTmp) { *puiBytesReturned = uiTmp - uiByteOffset; } else { *puiBytesReturned = 0; } goto Exit; } if( RC_BAD( rc = getIStream( pDb, &bufferIStream, &pIStream, &uiDataType))) { goto Exit; } if( uiDataType == XFLM_TEXT_TYPE) { F_AsciiStorageStream asciiStream; if( RC_BAD( rc = asciiStream.openStream( pIStream))) { goto Exit; } else { if( RC_BAD( rc = FlmOpenBase64DecoderIStream( &asciiStream, &pDecoderStream))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsBinary( pDecoderStream, (FLMBYTE *)pucValue, uiBytesRequested, uiByteOffset, puiBytesReturned))) { goto Exit; } } } else { if( RC_BAD( rc = flmReadStorageAsBinary( pIStream, (FLMBYTE *)pucValue, uiBytesRequested, uiByteOffset, puiBytesReturned))) { goto Exit; } } Exit: if( pDecoderStream) { pDecoderStream->Release(); } if( pIStream) { pIStream->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getBinary( IF_Db * ifpDb, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiBufSize; void * pvBuffer = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } pBuffer->truncateData( 0); if( RC_BAD( rc = getBinary( ifpDb, NULL, 0, FLM_MAX_UINT, &uiBufSize))) { goto Exit; } if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) { goto Exit; } if( RC_BAD( rc = getBinary( ifpDb, pvBuffer, 0, uiBufSize, NULL))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getAttributeValueNumber( F_Db * pDb, FLMUINT uiAttrName, FLMUINT64 * pui64Num, FLMBOOL * pbNeg) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (RC_BAD( rc = checkAttrList())) { goto Exit; } if( RC_BAD( rc = m_pCachedNode->getNumber64( pDb, uiAttrName, pui64Num, pbNeg))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getAttributeValueText( IF_Db * ifpDb, FLMUINT uiAttrName, eXFlmTextType eTextType, void * pvBuffer, FLMUINT uiBufSize, FLMUINT * puiCharsReturned, FLMUINT * puiBufferBytesUsed) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucStorageData = NULL; FLMUINT uiDataType; FLMUINT uiDataLength; F_AttrItem * pAttrItem; IF_PosIStream * pIStream = NULL; F_NodeBufferIStream bufferIStream; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (RC_BAD( rc = checkAttrList())) { goto Exit; } if( (pAttrItem = m_pCachedNode->getAttribute( uiAttrName, NULL)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( !pAttrItem->m_uiEncDefId) { pucStorageData = pAttrItem->getAttrDataPtr(); uiDataLength = pAttrItem->getAttrDataLength(); uiDataType = pAttrItem->m_uiDataType; if( uiDataType == XFLM_TEXT_TYPE && eTextType == XFLM_UTF8_TEXT) { const FLMBYTE * pucStart = pucStorageData; const FLMBYTE * pucEnd = pucStart + uiDataLength; FLMUINT uiCharCount = 0; FLMUINT uiStrByteLen = 0; if( pucStart) { if( RC_BAD( rc = f_decodeSEN( &pucStart, pucEnd, &uiCharCount))) { goto Exit; } uiStrByteLen = (FLMUINT)(pucEnd - pucStart); if( uiBufSize < uiStrByteLen) { goto SlowDecode; } f_memcpy( pvBuffer, pucStart, uiStrByteLen); } if( puiCharsReturned) { *puiCharsReturned = uiCharCount; } if( puiBufferBytesUsed) { *puiBufferBytesUsed = uiStrByteLen; } goto Exit; } } else { if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, uiAttrName, &bufferIStream, &pIStream, &uiDataType, &uiDataLength))) { goto Exit; } } SlowDecode: if( RC_BAD( rc = flmReadStorageAsText( pIStream, pucStorageData, uiDataLength, uiDataType, pvBuffer, uiBufSize, eTextType, FLM_MAX_UINT, 0, puiCharsReturned, puiBufferBytesUsed))) { goto Exit; } Exit: if( pIStream) { pIStream->Release(); } if( bStartedTrans) { pDb->abortTrans(); } return( rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getAttributeValueUnicode( IF_Db * ifpDb, FLMUINT uiAttrName, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; FLMUINT uiBufSize; void * pvBuffer = NULL; pBuffer->truncateData( 0); if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, NULL, 0, NULL, &uiBufSize))) { goto Exit; } if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) { goto Exit; } if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, (FLMUNICODE *)pvBuffer, uiBufSize, NULL))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getAttributeValueUnicode( IF_Db * ifpDb, FLMUINT uiAttrName, FLMUNICODE ** ppuzUnicode) { RCODE rc = NE_XFLM_OK; FLMUINT uiBufSize; if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, NULL, 0, NULL, &uiBufSize))) { goto Exit; } if( uiBufSize) { if( RC_BAD( rc = f_alloc( uiBufSize, ppuzUnicode))) { goto Exit; } if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, *ppuzUnicode, uiBufSize))) { goto Exit; } } else { *ppuzUnicode = NULL; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getAttributeValueUTF8( IF_Db * ifpDb, FLMUINT uiAttrName, FLMBYTE ** ppszValue) { RCODE rc = NE_XFLM_OK; FLMUINT uiBufSize; if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, NULL, 0, NULL, &uiBufSize))) { goto Exit; } if( uiBufSize) { if( RC_BAD( rc = f_alloc( uiBufSize, ppszValue))) { goto Exit; } if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, *ppszValue, uiBufSize))) { goto Exit; } } else { *ppszValue = NULL; } Exit: return( rc); } /***************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getAttributeValueUTF8( IF_Db * ifpDb, FLMUINT uiAttrName, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; FLMUINT uiBufSize; void * pvBuffer = NULL; pBuffer->truncateData( 0); if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, NULL, 0, NULL, &uiBufSize))) { goto Exit; } if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) { goto Exit; } if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, (FLMBYTE *)pvBuffer, uiBufSize))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getAttributeValueBinary( IF_Db * ifpDb, FLMUINT uiAttrName, void * pvValue, FLMUINT uiBufferSize, FLMUINT * puiLength) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (RC_BAD( rc = checkAttrList())) { goto Exit; } if( RC_BAD( rc = m_pCachedNode->getBinary( pDb, uiAttrName, pvValue, uiBufferSize, puiLength))) { goto Exit; } Exit: if( bStartedTrans) { pDb->abortTrans(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getAttributeValueBinary( IF_Db * ifpDb, FLMUINT uiAttrName, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiBufSize; void * pvBuffer = NULL; pBuffer->truncateData( 0); if( RC_BAD( rc = getAttributeValueBinary( ifpDb, uiAttrName, NULL, 0, &uiBufSize))) { goto Exit; } if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) { goto Exit; } if( RC_BAD( rc = getAttributeValueBinary( ifpDb, uiAttrName, pvBuffer, uiBufSize, &uiBufSize))) { goto Exit; } Exit: if( bStartedTrans) { ifpDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::setAttributeValueNumber( IF_Db * ifpDb, FLMUINT uiAttrName, FLMINT64 i64Value, FLMUINT64 ui64Value, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_DOMNode * pAttribute = NULL; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; FLMBOOL bNeg = FALSE; FLMBOOL bIsInIndexDef = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = pDb->attrIsInIndexDef( uiAttrName, &bIsInIndexDef))) { goto Exit; } if( bIsInIndexDef) { if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, (IF_DOMNode **)&pAttribute))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = pAttribute->setNumber64( pDb, i64Value, ui64Value, uiEncDefId))) { goto Exit; } } else { pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; if( !ui64Value) { if( i64Value < 0) { bNeg = TRUE; ui64Value = (FLMUINT64)-i64Value; } else { ui64Value = (FLMUINT64)i64Value; } } if( RC_BAD( rc = m_pCachedNode->setNumber64( pDb, uiAttrName, ui64Value, bNeg, uiEncDefId))) { goto Exit; } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, m_pCachedNode, uiAttrName))) { goto Exit; } } if( bStartedTrans) { bStartedTrans = FALSE; if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } } Exit: if( pAttribute) { pAttribute->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::setAttributeValueUnicode( IF_Db * ifpDb, FLMUINT uiAttrName, const FLMUNICODE * puzValue, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_DOMNode * pAttribute = NULL; FLMBOOL bStartedTrans = FALSE; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bMustAbortOnError = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, (IF_DOMNode **)&pAttribute))) { goto Exit; } if( RC_BAD( rc = pAttribute->setUnicode( (IF_Db *)pDb, puzValue, 0, TRUE, uiEncDefId))) { goto Exit; } if( bStartedTrans) { bStartedTrans = FALSE; if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } } Exit: if( pAttribute) { pAttribute->Release(); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::setAttributeValueBinary( IF_Db * ifpDb, FLMUINT uiAttrName, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_DOMNode * pAttribute = NULL; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; FLMBOOL bIsInIndexDef = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = pDb->attrIsInIndexDef( uiAttrName, &bIsInIndexDef))) { goto Exit; } if( bIsInIndexDef) { if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, (IF_DOMNode **)&pAttribute))) { goto Exit; } if( RC_BAD( rc = pAttribute->setBinary( (IF_Db *)pDb, (FLMBYTE *)pvValue, uiLength, TRUE, uiEncDefId))) { goto Exit; } } else { pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = m_pCachedNode->setBinary( pDb, uiAttrName, pvValue, uiLength, uiEncDefId))) { goto Exit; } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, m_pCachedNode, uiAttrName))) { goto Exit; } } if( bStartedTrans) { bStartedTrans = FALSE; if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } } Exit: if( pAttribute) { pAttribute->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::setAttributeValueUTF8( IF_Db * ifpDb, FLMUINT uiAttrName, const FLMBYTE * pucValue, FLMUINT uiLength, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_DOMNode * pAttribute = NULL; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiNumCharsInBuffer; FLMUINT uiRflToken = 0; FLMBOOL bIsInIndexDef = FALSE; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = pDb->attrIsInIndexDef( uiAttrName, &bIsInIndexDef))) { goto Exit; } if( bIsInIndexDef) { if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, (IF_DOMNode **)&pAttribute))) { goto Exit; } if( RC_BAD( rc = pAttribute->setUTF8( (IF_Db *)pDb, pucValue, uiLength, TRUE, uiEncDefId))) { goto Exit; } } else { pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = f_getUTF8Length( pucValue, uiLength, &uiLength, &uiNumCharsInBuffer))) { goto Exit; } if( RC_BAD( m_pCachedNode->setUTF8( pDb, uiAttrName, pucValue, uiLength, uiNumCharsInBuffer, uiEncDefId))) { goto Exit; } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } // Log the value to the RFL pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, m_pCachedNode, uiAttrName))) { goto Exit; } } if( bStartedTrans) { bStartedTrans = FALSE; if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } } Exit: if( pAttribute) { pAttribute->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: Delete a node and all of its child/descendant-nodes. NOTE: If the cannot-delete bit or read-only bit is set on the node, the delete is not allowed. However, the child/descendant-nodes cannot-delete and read-only bits will NOT be checked. If a parent node can be deleted, then by definition all of its child/descendant nodes can also be deleted. ******************************************************************************/ RCODE F_DOMNode::deleteNode( IF_Db * ifpDb) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64CurNode; F_DOMNode * pCurNode = NULL; F_DOMNode * pParentNode = NULL; F_DOMNode * pTmpNode = NULL; FLMBOOL bMustAbortOnError = FALSE; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMBOOL bStartOfUpdate; FLMBOOL bStartedTrans = FALSE; FLMUINT uiCollection; FLMUINT uiFlags = 0; FLMUINT uiRflToken = 0; FLMUINT64 ui64MyNodeId; FLMBOOL bIsIndexed; eDomNodeType eNodeType; // Start a transaction if necessary if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } uiCollection = getCollection(); eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = pDb->getNode( uiCollection, getParentId(), &pCurNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } rc = pCurNode->deleteAttribute( pDb, m_uiAttrNameId); goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // See if the node can be deleted if( getModeFlags() & (FDOM_READ_ONLY | FDOM_CANNOT_DELETE)) { rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); goto Exit; } if( isRootNode()) { // Set flags to FLM_UPD_INTERNAL_CHANGE to prevent the node // from being added to the document list or constraint // checking list - no need since we are deleting the root node. uiFlags = FLM_UPD_INTERNAL_CHANGE; // If we are deleting the root node of a document in the dictionary // collection, before deleting it, we must allow the dictionary // to be updated. if( uiCollection == XFLM_DICT_COLLECTION) { // Call dictDocumentDone with bDeleting flag set to TRUE. if( RC_BAD( rc = pDb->dictDocumentDone( getNodeId(), TRUE, NULL))) { goto Exit; } pDb->m_pDatabase->m_DocumentList.removeNode( uiCollection, getNodeId(), 0); } } bMustAbortOnError = TRUE; // Traverse the tree and delete all nodes below and including the // node we are starting on. ui64MyNodeId = ui64CurNode = getNodeId(); bStartOfUpdate = TRUE; for (;;) { if (RC_BAD( rc = pDb->getNode( uiCollection, ui64CurNode, &pCurNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } // If the current node has children, go to those children if( pCurNode->getLastChildId()) { ui64CurNode = pCurNode->getLastChildId(); } else if( pCurNode->hasAttributes()) { if( pCurNode->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } flmAssert( pCurNode->m_pCachedNode->m_uiAttrCount); if( RC_BAD( rc = pCurNode->deleteAttributes( pDb, 0, uiFlags))) { goto Exit; } } else if (pCurNode->getAnnotationId()) { ui64CurNode = pCurNode->getAnnotationId(); } else { // Node has no children, no attributes, and no annotations. It is // therefore a leaf node that can be purged. FLMUINT64 ui64ParentId; FLMBOOL bWasDataNode = FALSE; // Save the node's parent node before purging it. That is the // node we want to return to. if( RC_BAD( rc = pCurNode->getParentId( pDb, &ui64ParentId))) { goto Exit; } // Update the index if( pCurNode->getNodeType() == DATA_NODE) { bWasDataNode = TRUE; // Data nodes MUST be children to an element node. flmAssert( ui64ParentId); if (RC_BAD( rc = pDb->getNode( uiCollection, ui64ParentId, (IF_DOMNode **)&pParentNode))) { goto Exit; } if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pParentNode, IX_DEL_NODE_VALUE, bStartOfUpdate))) { goto Exit; } } else { if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pCurNode, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) { goto Exit; } bStartOfUpdate = FALSE; if( bIsIndexed) { if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pCurNode, IX_UNLINK_NODE, bStartOfUpdate))) { goto Exit; } } } bStartOfUpdate = FALSE; flmAssert( pCurNode->getNodeType() != ATTRIBUTE_NODE); if (RC_BAD( rc = pCurNode->unlinkNode( pDb, uiFlags))) { goto Exit; } if( RC_BAD( rc = pDb->purgeNode( uiCollection, ui64CurNode))) { goto Exit; } pCurNode->Release(); pCurNode = NULL; if( bWasDataNode) { flmAssert( pParentNode); if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pParentNode, IX_ADD_NODE_VALUE, bStartOfUpdate))) { goto Exit; } bStartOfUpdate = FALSE; } // Did we just delete the primary target or root node? // Do NOT access m_pCachedNode after this point, because it // may have been set to NULL by the call to purgeNode. if (ui64CurNode == ui64MyNodeId || !ui64ParentId) { break; } // Go back to the parent node. ui64CurNode = ui64ParentId; } } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeDelete( pDb, uiCollection, ui64MyNodeId))) { goto Exit; } Exit: if( pTmpNode) { pTmpNode->Release(); } if( pCurNode) { pCurNode->Release(); } if( pParentNode) { pParentNode->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::deleteChildren( IF_Db * ifpDb, FLMUINT uiNameId) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NextNode; F_DOMNode * pCurNode = NULL; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiCollection; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; eDomNodeType eNodeType; // Start a transaction if necessary if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } uiCollection = getCollection(); eNodeType = getNodeType(); // Not supported on attribute nodes if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // See if the node can be deleted if( getModeFlags() & (FDOM_READ_ONLY | FDOM_CANNOT_DELETE)) { rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); goto Exit; } // Turn of RFL logging pRfl->disableLogging( &uiRflToken); // Iterate over the children bMustAbortOnError = TRUE; ui64NextNode = getFirstChildId(); while( ui64NextNode) { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NextNode, XFLM_EXACT, &pCurNode))) { goto Exit; } ui64NextNode = pCurNode->getNextSibId(); if( !uiNameId || uiNameId == pCurNode->getNameId()) { if( RC_BAD( rc = pCurNode->deleteNode( pDb))) { goto Exit; } } } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeChildrenDelete( pDb, uiCollection, getNodeId(), uiNameId))) { goto Exit; } Exit: if( pCurNode) { pCurNode->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::_syncFromDb( F_Db * pDb) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDOMNode = this; // If we get to this point, we are going to read the node // from the database. This instance of the node should // not be dirty. flmAssert( !nodeIsDirty()); // Should not have any input streams open on the cached node if( getStreamUseCount()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->retrieveNode( pDb, getCollection(), m_pCachedNode->getNodeId(), &pDOMNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DOM_NODE_DELETED); } goto Exit; } if( m_uiAttrNameId) { if( !m_pCachedNode->m_uiAttrCount || !m_pCachedNode->getAttribute( m_uiAttrNameId, NULL)) { rc = RC_SET( NE_XFLM_DOM_NODE_DELETED); goto Exit; } } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getFirstAttribute( IF_Db * ifpDb, IF_DOMNode ** ifppAttr) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pAttrNode = NULL; F_AttrItem * pAttrItem; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (RC_BAD( rc = checkAttrList())) { goto Exit; } if( (pAttrItem = m_pCachedNode->getFirstAttribute()) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttrNode->m_pCachedNode = m_pCachedNode; m_pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; if( ifppAttr) { if( *ifppAttr) { (*ifppAttr)->Release(); } *ifppAttr = (IF_DOMNode *)pAttrNode; pAttrNode = NULL; } Exit: if( pAttrNode) { pAttrNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getLastAttribute( IF_Db * ifpDb, IF_DOMNode ** ifppAttr) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pAttrNode = NULL; F_AttrItem * pAttrItem; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (RC_BAD( rc = checkAttrList())) { goto Exit; } if( (pAttrItem = m_pCachedNode->getLastAttribute()) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttrNode->m_pCachedNode = m_pCachedNode; m_pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; if( ifppAttr) { if( *ifppAttr) { (*ifppAttr)->Release(); } *ifppAttr = (IF_DOMNode *)pAttrNode; pAttrNode = NULL; } Exit: if( pAttrNode) { pAttrNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::deleteAttribute( IF_Db * ifpDb, FLMUINT uiAttrName) { RCODE rc = NE_XFLM_OK; if( !uiAttrName) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = deleteAttributes( (F_Db *)ifpDb, uiAttrName, 0))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::deleteAttributes( F_Db * pDb, FLMUINT uiAttrToDelete, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiCollection; FLMUINT uiAttrName; F_DOMNode * pAttrNode = NULL; F_AttrItem * pAttrItem; FLMUINT uiPos; FLMBOOL bIsIndexed; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Disable logging pRfl->disableLogging( &uiRflToken); // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( !m_pCachedNode->m_uiAttrCount) { goto Exit; } if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; uiCollection = getCollection(); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttrNode->m_pCachedNode = m_pCachedNode; m_pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); for( ;;) { if( !uiAttrToDelete) { pAttrItem = m_pCachedNode->getFirstAttribute(); uiPos = 0; } else { if( (pAttrItem = m_pCachedNode->getAttribute( uiAttrToDelete, &uiPos)) == NULL) { break; } } if( uiAttrToDelete && (pAttrItem->m_uiFlags & (FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); goto Exit; } uiAttrName = pAttrItem->m_uiNameId; pAttrNode->m_uiAttrNameId = uiAttrName; if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pAttrNode, IX_DEL_NODE_VALUE, TRUE, &bIsIndexed))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pAttrNode, IX_UNLINK_NODE, FALSE))) { goto Exit; } } // Free the attribute if (RC_BAD( rc = m_pCachedNode->freeAttribute( pAttrItem, uiPos))) { goto Exit; } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttributeDelete( pDb, uiCollection, getNodeId(), uiAttrName))) { goto Exit; } pRfl->disableLogging( &uiRflToken); if( !m_pCachedNode->m_uiAttrCount) { break; } } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, uiFlags))) { goto Exit; } if( bStartedTrans) { bStartedTrans = FALSE; if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } } Exit: if( pAttrNode) { pAttrNode->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::hasAttribute( IF_Db * ifpDb, FLMUINT uiNameId, IF_DOMNode ** ifppAttr) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_DOMNode * pAttrNode = NULL; F_AttrItem * pAttrItem; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if (RC_BAD( rc = checkAttrList())) { goto Exit; } if( (pAttrItem = m_pCachedNode->getAttribute( uiNameId, NULL)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( ifppAttr) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttrNode->m_pCachedNode = m_pCachedNode; m_pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; if( *ifppAttr) { (*ifppAttr)->Release(); } *ifppAttr = (IF_DOMNode *)pAttrNode; pAttrNode = NULL; } Exit: if( pAttrNode) { pAttrNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::insertChildElm( FLMUINT uiChildElmOffset, FLMUINT uiChildElmNameId, FLMUINT64 ui64ChildElmNodeId) { RCODE rc = NE_XFLM_OK; NODE_ITEM * pChildElmNode; if( RC_BAD( rc = resizeChildElmList( m_nodeInfo.uiChildElmCount + 1, FALSE))) { goto Exit; } // Remember, m_nodeInfo.uiChildElmCount has been incremented by // resizeChildElmList, so there really isn't anything in the // m_nodeInfo.uiChildElmCount - 1 slot. pChildElmNode = &m_pNodeList [ uiChildElmOffset]; if( m_nodeInfo.uiChildElmCount > 1 && uiChildElmOffset < m_nodeInfo.uiChildElmCount - 1) { f_memmove( &m_pNodeList [ uiChildElmOffset + 1], pChildElmNode, sizeof( NODE_ITEM) * (m_nodeInfo.uiChildElmCount - uiChildElmOffset - 1)); } pChildElmNode->uiNameId = uiChildElmNameId; pChildElmNode->ui64NodeId = ui64ChildElmNodeId; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::createAttribute( IF_Db * ifpDb, FLMUINT uiNameId, IF_DOMNode ** ifppAttr) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bStartedTrans = FALSE; F_AttrElmInfo attrInfo; F_DOMNode * pAttr = NULL; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; F_AttrItem * pAttrItem = NULL; FLMBOOL bCreatedNewAttr = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Disable logging pRfl->disableLogging( &uiRflToken); // Make sure our copy of the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // If this isn't an element node, return an error if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Force the transaction to abort on error beyond this point bMustAbortOnError = TRUE; // Check the attribute state if( RC_BAD( rc = pDb->checkAndUpdateState( ATTRIBUTE_NODE, uiNameId))) { goto Exit; } // Retrieve or create the attribute list node if( !m_pCachedNode->m_uiAttrCount || (pAttrItem = m_pCachedNode->getAttribute( uiNameId, NULL)) == NULL) { if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } if( RC_BAD( rc = m_pCachedNode->createAttribute( pDb, uiNameId, &pAttrItem))) { goto Exit; } bCreatedNewAttr = TRUE; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttr))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttr->m_pCachedNode = m_pCachedNode; m_pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); pAttr->m_uiAttrNameId = uiNameId; if( bCreatedNewAttr) { // Update the element. if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } // Update the indexes if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), pAttr, IX_LINK_AND_ADD_NODE, TRUE))) { goto Exit; } // Log the attribute create pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logAttributeCreate( pDb, getCollection(), getNodeId(), uiNameId, 0))) { goto Exit; } } if( ifppAttr) { if( *ifppAttr) { (*ifppAttr)->Release(); } *ifppAttr = (IF_DOMNode *)pAttr; pAttr = NULL; } Exit: if( pAttr) { pAttr->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc) && bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::createNode( IF_Db * ifpDb, eDomNodeType eNodeType, FLMUINT uiNameId, eNodeInsertLoc eLocation, IF_DOMNode ** ifppNewNode, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; F_DOMNode * pNewNode = NULL; F_CachedNode * pNewCachedNode; F_DOMNode * pRefNode = NULL; F_DOMNode * pNewParent = NULL; FLMUINT uiDataType = XFLM_NODATA_TYPE; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; // Not supported for attributes if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Make sure an update transaction is active if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of this node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Make sure the node type is valid if( eLocation == XFLM_FIRST_CHILD || eLocation == XFLM_LAST_CHILD) { if( RC_BAD( rc = isChildTypeValid( eNodeType))) { goto Exit; } } else if( eLocation == XFLM_PREV_SIB || eLocation == XFLM_NEXT_SIB) { if( eNodeType != ELEMENT_NODE && eNodeType != DATA_NODE && eNodeType != COMMENT_NODE && eNodeType != CDATA_SECTION_NODE && eNodeType != PROCESSING_INSTRUCTION_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If the user is requesting a specific nodeId, then make sure // the node is not already in use. if( pui64NodeId) { if( *pui64NodeId && (pDb->m_uiFlags & FDB_REBUILDING_DATABASE)) { if( RC_BAD( rc = pDb->getNode( getCollection(), *pui64NodeId, &pNewNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { // Already in use rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } else if( *pui64NodeId) { // Set to zero so we don't use it. We will return the // new nodeId. *pui64NodeId = 0; } } // Look at the node's state (checking, etc.) and verify that // the node's name ID is valid // // IMPORTANT NOTE: checkAndUpdateState may change m_pDict if it ends // up calling changeItemState if( RC_BAD( rc = pDb->checkAndUpdateState( eNodeType, uiNameId))) { goto Exit; } // Create the new node. if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( pDb, getCollection(), (FLMUINT64)(pui64NodeId ? *pui64NodeId : (FLMUINT64)0), &pNewNode))) { goto Exit; } pNewCachedNode = pNewNode->m_pCachedNode; if( eNodeType == DATA_NODE) { flmAssert( getNodeType() == ELEMENT_NODE); uiNameId = getNameId(); } if( eNodeType == ELEMENT_NODE || eNodeType == DATA_NODE) { F_AttrElmInfo elmInfo; if( RC_BAD( rc = pDb->m_pDict->getElement( pDb, uiNameId, &elmInfo))) { goto Exit; } uiDataType = elmInfo.m_uiDataType; // Is this a node whose child elements must all be unique? if( eNodeType == ELEMENT_NODE && elmInfo.m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) { flmAssert( uiDataType == XFLM_NODATA_TYPE); pNewCachedNode->setFlags( FDOM_HAVE_CELM_LIST); } } else { uiDataType = XFLM_NODATA_TYPE; uiNameId = 0; } pNewCachedNode->setNodeType( eNodeType); pNewCachedNode->setDocumentId( getDocumentId()); pNewCachedNode->setDataType( uiDataType); if( uiNameId) { pNewCachedNode->setNameId( uiNameId); } if( RC_BAD( rc = pDb->updateNode( pNewCachedNode, FLM_UPD_ADD))) { goto Exit; } if( eNodeType == ELEMENT_NODE) { if( RC_BAD( rc = pDb->updateIndexKeys( pNewCachedNode->getCollection(), pNewNode, IX_ADD_NODE_VALUE, TRUE))) { goto Exit; } } switch( eLocation) { case XFLM_FIRST_CHILD: { pNewParent = this; pNewParent->AddRef(); if( getFirstChildId()) { if( RC_BAD( rc = pDb->getNode( getCollection(), getFirstChildId(), &pRefNode))) { goto Exit; } } break; } case XFLM_LAST_CHILD: { pNewParent = this; pNewParent->AddRef(); break; } case XFLM_PREV_SIB: { if( !getParentId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); goto Exit; } if( RC_BAD( rc = pDb->getNode( getCollection(), getParentId(), &pNewParent))) { goto Exit; } pRefNode = this; pRefNode->AddRef(); break; } case XFLM_NEXT_SIB: { if( !getParentId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); goto Exit; } if( RC_BAD( rc = pDb->getNode( getCollection(), getParentId(), &pNewParent))) { goto Exit; } if( getNextSibId()) { if( RC_BAD( rc = pDb->getNode( getCollection(), getNextSibId(), &pRefNode))) { goto Exit; } } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } if( RC_BAD( rc = pNewParent->insertBefore( pDb, pNewNode, pRefNode))) { goto Exit; } if( pui64NodeId) { *pui64NodeId = pNewCachedNode->getNodeId(); } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeCreate( pDb, pNewNode->getCollection(), getNodeId(), eNodeType, uiNameId, eLocation, pNewNode->getNodeId()))) { goto Exit; } if( ifppNewNode) { if( *ifppNewNode) { (*ifppNewNode)->Release(); } *ifppNewNode = (IF_DOMNode *)pNewNode; pNewNode = NULL; } Exit: if( pNewNode) { pNewNode->Release(); } if( pRefNode) { pRefNode->Release(); } if( pNewParent) { pNewParent->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::createChildElement( IF_Db * ifpDb, FLMUINT uiNameId, eNodeInsertLoc eLocation, IF_DOMNode ** ifppNewNode, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; F_DOMNode * pTmpNode = NULL; F_DOMNode * pNewNode = NULL; F_CachedNode * pNewCachedNode; F_AttrElmInfo elmInfo; eDomNodeType eThisNodeType; FLMUINT uiCollection; FLMUINT uiDataType = XFLM_NODATA_TYPE; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; FLMBOOL bIsIndexed; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of this node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // Make sure the insert location is supported if( eLocation != XFLM_FIRST_CHILD && eLocation != XFLM_LAST_CHILD) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Make sure the node type is valid eThisNodeType = getNodeType(); if( eThisNodeType != ELEMENT_NODE && eThisNodeType != DOCUMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // A document node can only have one element node if( eThisNodeType == DOCUMENT_NODE && getFirstChildId()) { if( RC_BAD( rc = getChild( ifpDb, ELEMENT_NODE, (IF_DOMNode **)&pTmpNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); goto Exit; } } // Setup misc. variables uiCollection = getCollection(); // If the user is requesting a specific nodeId, then make sure // the node is not already in use. if( pui64NodeId) { if( *pui64NodeId && (pDb->m_uiFlags & FDB_REBUILDING_DATABASE)) { if( RC_OK( rc = pDb->getNode( uiCollection, *pui64NodeId, &pNewNode))) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } else if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } else { goto Exit; } } else if( *pui64NodeId) { *pui64NodeId = 0; } } // Check the element's state if( RC_BAD( rc = pDb->checkAndUpdateState( ELEMENT_NODE, uiNameId))) { goto Exit; } // Create the new node. if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( pDb, uiCollection, (FLMUINT64)(pui64NodeId ? *pui64NodeId : (FLMUINT64)0), &pNewNode))) { goto Exit; } pNewCachedNode = pNewNode->m_pCachedNode; // Make sure the parent node (this) can be updated if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } // Does the parent expect all children to be unique? if( getModeFlags() & FDOM_HAVE_CELM_LIST) { FLMUINT uiInsertPos; if( m_pCachedNode->findChildElm( uiNameId, &uiInsertPos)) { rc = RC_SET( NE_XFLM_DOM_DUPLICATE_ELEMENT); goto Exit; } if( RC_BAD( rc = m_pCachedNode->insertChildElm( uiInsertPos, uiNameId, pNewCachedNode->getNodeId()))) { goto Exit; } } // Update the element's state if( RC_BAD( rc = pDb->m_pDict->getElement( pDb, uiNameId, &elmInfo))) { goto Exit; } uiDataType = elmInfo.m_uiDataType; // Is this a node whose children must all be unique? if( elmInfo.m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) { flmAssert( uiDataType == XFLM_NODATA_TYPE); pNewCachedNode->setFlags( FDOM_HAVE_CELM_LIST); } pNewCachedNode->setNodeType( ELEMENT_NODE); pNewCachedNode->setParentId( getNodeId()); pNewCachedNode->setDocumentId( getDocumentId()); pNewCachedNode->setDataType( uiDataType); pNewCachedNode->setNameId( uiNameId); // Set the sibling pointers if( eLocation == XFLM_LAST_CHILD) { if( getLastChildId()) { if( RC_BAD( rc = pDb->getNode( uiCollection, getLastChildId(), &pTmpNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } flmAssert( pTmpNode->getNextSibId() == 0); if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } pTmpNode->setNextSibId( pNewCachedNode->getNodeId()); pNewCachedNode->setPrevSibId( getLastChildId()); if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, 0))) { goto Exit; } } else { setFirstChildId( pNewCachedNode->getNodeId()); } setLastChildId( pNewCachedNode->getNodeId()); } else { flmAssert( eLocation == XFLM_FIRST_CHILD); if( getFirstChildId()) { if( RC_BAD( rc = pDb->getNode( uiCollection, getFirstChildId(), &pTmpNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } flmAssert( pTmpNode->getPrevSibId() == 0); if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) { goto Exit; } pTmpNode->setPrevSibId( pNewCachedNode->getNodeId()); pNewCachedNode->setNextSibId( getFirstChildId()); if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, 0))) { goto Exit; } } else { setLastChildId( pNewCachedNode->getNodeId()); } setFirstChildId( pNewCachedNode->getNodeId()); } if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } if( RC_BAD( rc = pDb->updateNode( pNewCachedNode, FLM_UPD_ADD))) { goto Exit; } if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pNewNode, IX_ADD_NODE_VALUE, TRUE, &bIsIndexed))) { goto Exit; } if( bIsIndexed) { if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pNewNode, IX_LINK_NODE, FALSE, &bIsIndexed))) { goto Exit; } } if( pui64NodeId) { *pui64NodeId = pNewCachedNode->getNodeId(); } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeCreate( pDb, uiCollection, pNewCachedNode->getParentId(), ELEMENT_NODE, uiNameId, XFLM_LAST_CHILD, pNewCachedNode->getNodeId()))) { goto Exit; } if( ifppNewNode) { if( *ifppNewNode) { (*ifppNewNode)->Release(); } *ifppNewNode = (IF_DOMNode *)pNewNode; pNewNode = NULL; } Exit: if( pNewNode) { pNewNode->Release(); } if( pTmpNode) { pTmpNode->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::createAnnotation( IF_Db * ifpDb, IF_DOMNode ** ifppAnnotation, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_CachedNode * pCachedNode; FLMBOOL bMustAbortOnError = FALSE; F_Db * pDb = (F_Db *)ifpDb; F_DOMNode ** ppAnnotation = (F_DOMNode **)ifppAnnotation; FLMBOOL bStartedTrans = FALSE; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure our copy of this node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); // Not supported on attribute nodes if( eNodeType == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // If the node already has an annotation, return an error if( getAnnotationId()) { rc = RC_SET( NE_XFLM_EXISTS); goto Exit; } // If the user is requesting a specific nodeId, then make sure // the node is not already in use. if( pui64NodeId) { if( *pui64NodeId && (pDb->m_uiFlags & FDB_REBUILDING_DATABASE)) { if( RC_BAD( rc = pDb->getNode( getCollection(), *pui64NodeId, &pNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { // Already in use rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } else if( *pui64NodeId) { // Set to zero so we don't use it. We will return the // new nodeId. *pui64NodeId = 0; } } // Create the new node. if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( pDb, getCollection(), (FLMUINT64)(pui64NodeId ? *pui64NodeId : (FLMUINT64)0), &pNode))) { goto Exit; } pCachedNode = pNode->m_pCachedNode; pCachedNode->setNodeType( ANNOTATION_NODE); pCachedNode->setDocumentId( getDocumentId()); pCachedNode->setParentId( getNodeId()); pCachedNode->setDataType( XFLM_NODATA_TYPE); bMustAbortOnError = TRUE; if( RC_BAD( rc = pDb->updateNode( pCachedNode, FLM_UPD_ADD))) { goto Exit; } // Link the annotation to this node if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } setAnnotationId( pCachedNode->getNodeId()); if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } if( bStartedTrans) { if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } bStartedTrans = FALSE; } if( pui64NodeId) { *pui64NodeId = pCachedNode->getNodeId(); } // Release any node that the passed-in parameter may be // pointing at if( *ppAnnotation) { (*ppAnnotation)->Release(); } *ppAnnotation = pNode; pNode = NULL; Exit: if( pNode) { pNode->Release(); } if( RC_BAD( rc)) { if( bMustAbortOnError) { pDb->setMustAbortTrans( rc); } if( bStartedTrans) { pDb->transAbort(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::hasAnnotation( IF_Db * ifpDb, FLMBOOL * pbHasAnnotation) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; *pbHasAnnotation = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( (F_Db *)ifpDb))) { goto Exit; } if( getAnnotationId()) { *pbHasAnnotation = TRUE; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getAnnotation( IF_Db * ifpDb, IF_DOMNode ** ifppAnnotation) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( (F_Db *)ifpDb))) { goto Exit; } if( !getAnnotationId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getAnnotationId(), ifppAnnotation))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getDocumentId( IF_Db * ifpDb, FLMUINT64 * pui64DocId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } *pui64DocId = m_pCachedNode->getDocumentId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getNodeId( IF_Db * ifpDb, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pui64NodeId = m_pCachedNode->getNodeId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: puiAttrNameId returns 0 if the node isn't an attribute. ******************************************************************************/ RCODE F_DOMNode::getNodeId( F_Db * pDb, FLMUINT64 * pui64NodeId, FLMUINT * puiAttrNameId) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } *pui64NodeId = m_pCachedNode->getNodeId(); if( getNodeType() == ATTRIBUTE_NODE) { *puiAttrNameId = m_uiAttrNameId; } else { *puiAttrNameId = 0; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getParentId( IF_Db * ifpDb, FLMUINT64 * pui64ParentId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } *pui64ParentId = getParentId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: COM version of the getPrevSibId method. This method ensures that the DOM node is up-to-date. ******************************************************************************/ RCODE FLMAPI F_DOMNode::getPrevSibId( IF_Db * ifpDb, FLMUINT64 * pui64PrevSibId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pui64PrevSibId = getPrevSibId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return rc; } /***************************************************************************** Desc: COM version of the getNextSibId method. This method ensures that the DOM node is up-to-date. ******************************************************************************/ RCODE FLMAPI F_DOMNode::getNextSibId( IF_Db * ifpDb, FLMUINT64 * pui64NextSibId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pui64NextSibId = getNextSibId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return rc; } /***************************************************************************** Desc: COM version of the getFirstChildId method. This method ensures that the DOM node is up-to-date. ******************************************************************************/ RCODE FLMAPI F_DOMNode::getFirstChildId( IF_Db * ifpDb, FLMUINT64 * pui64FirstChildId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pui64FirstChildId = getFirstChildId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return rc; } /***************************************************************************** Desc: COM version of the getLastChildId method. This method ensures that the DOM node is up-to-date. ******************************************************************************/ RCODE FLMAPI F_DOMNode::getLastChildId( IF_Db * ifpDb, FLMUINT64 * pui64LastChildId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pui64LastChildId = getLastChildId(); Exit: if( bStartedTrans) { pDb->transAbort(); } return rc; } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::isNamespaceDecl( IF_Db * ifpDb, FLMBOOL * pbIsNamespaceDecl) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } *pbIsNamespaceDecl = isNamespaceDecl(); Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::hasChildren( IF_Db * ifpDb, FLMBOOL * pbHasChildren) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( (F_Db *)ifpDb))) { goto Exit; } if (getNodeType() == ATTRIBUTE_NODE) { *pbHasChildren = FALSE; } else { *pbHasChildren = getFirstChildId() ? TRUE : FALSE; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getNameId( IF_Db * ifpDb, FLMUINT * puiNameId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); if( eNodeType == ATTRIBUTE_NODE) { *puiNameId = m_uiAttrNameId; } else if( m_pCachedNode) { *puiNameId = getNameId(); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getEncDefId( IF_Db * ifpDb, FLMUINT * puiEncDefNumber) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getEncDefId( m_uiAttrNameId, puiEncDefNumber))) { goto Exit; } } else if( m_pCachedNode) { *puiEncDefNumber = getEncDefId(); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getAnnotationId( IF_Db * ifpDb, FLMUINT64 * pui64AnnotationId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { *pui64AnnotationId = 0; } else if( m_pCachedNode) { *pui64AnnotationId = getAnnotationId(); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::getDataType( IF_Db * ifpDb, FLMUINT * puiDataType) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getDataType( m_uiAttrNameId, puiDataType))) { goto Exit; } } else { *puiDataType = m_pCachedNode->getDataType(); } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getPrefixId( IF_Db * ifpDb, FLMUINT * puiPrefixId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiPrefix = 0; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getPrefixId( m_uiAttrNameId, &uiPrefix))) { goto Exit; } } else { if( (uiPrefix = m_pCachedNode->getPrefixId()) != 0) { if( RC_BAD( rc = pDb->m_pDict->getPrefix( uiPrefix, NULL))) { if( rc != NE_XFLM_BAD_PREFIX) { goto Exit; } rc = NE_XFLM_OK; uiPrefix = 0; } } } *puiPrefixId = uiPrefix; Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::hasAttributes( IF_Db * ifpDb, FLMBOOL * pbHasAttrs) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { *pbHasAttrs = FALSE; goto Exit; } *pbHasAttrs = m_pCachedNode->m_uiAttrCount ? TRUE : FALSE; Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::hasNextSibling( IF_Db * ifpDb, FLMBOOL * pbHasNextSibling) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pbHasNextSibling = (m_pCachedNode->getNextSibId() && getParentId()) ? TRUE : FALSE; Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_DOMNode::hasPreviousSibling( IF_Db * ifpDb, FLMBOOL * pbHasPreviousSibling) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } *pbHasPreviousSibling = (m_pCachedNode->getPrevSibId() && getParentId()) ? TRUE : FALSE; Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getAncestorElement( IF_Db * ifpDb, FLMUINT uiNameId, IF_DOMNode ** ifppAncestor) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_DOMNode * pTmpNode = NULL; FLMBOOL bStartedTrans = FALSE; FLMUINT uiCollection; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } pTmpNode = this; pTmpNode->AddRef(); uiCollection = getCollection(); while( pTmpNode) { if( !pTmpNode->getParentId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = pDb->getNode( uiCollection, pTmpNode->getParentId(), &pTmpNode))) { goto Exit; } if( pTmpNode->getNameId() == uiNameId) { break; } } if( !pTmpNode) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( *ifppAncestor) { (*ifppAncestor)->Release(); } *ifppAncestor = pTmpNode; pTmpNode = NULL; Exit: if( pTmpNode) { pTmpNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getDescendantElement( IF_Db * ifpDb, FLMUINT uiNameId, IF_DOMNode ** ifppDescendant) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_DOMNode * pContextNode = NULL; F_DOMNode * pFoundNode = NULL; FLMBOOL bStartedTrans = FALSE; FLMUINT uiCollection; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } pContextNode = this; pContextNode->AddRef(); uiCollection = getCollection(); while( pContextNode) { if( pContextNode->getFirstChildId()) { if( RC_BAD( rc = pDb->getNode( uiCollection, pContextNode->getFirstChildId(), &pContextNode))) { goto Exit; } } else if( pContextNode->getNextSibId()) { Get_Next_Sib: if( pContextNode->getNodeId() == getNodeId()) { break; } if( RC_BAD( rc = pDb->getNode( uiCollection, pContextNode->getNextSibId(), &pContextNode))) { goto Exit; } } else { if( pContextNode->getNodeId() == getNodeId()) { break; } if( RC_BAD( rc = pDb->getNode( uiCollection, pContextNode->getParentId(), &pContextNode))) { goto Exit; } goto Get_Next_Sib; } if( pContextNode->getNodeType() != ELEMENT_NODE) { continue; } if( pContextNode->getNameId() == uiNameId) { pFoundNode = pContextNode; pFoundNode->AddRef(); break; } } if( *ifppDescendant) { (*ifppDescendant)->Release(); } *ifppDescendant = pFoundNode; pFoundNode = NULL; Exit: if( pContextNode) { pContextNode->Release(); } if( pFoundNode) { pFoundNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getDocumentNode( IF_Db * ifpDb, IF_DOMNode ** ifppDoc) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( isRootNode()) { IF_DOMNode * pTmpNode = *ifppDoc; *ifppDoc = this; (*ifppDoc)->AddRef(); if( pTmpNode) { pTmpNode->Release(); } goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getDocumentId(), ifppDoc))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getParentNode( IF_Db * ifpDb, IF_DOMNode ** ifppParent) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( !getParentId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getParentId(), ifppParent))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getFirstChild( IF_Db * ifpDb, IF_DOMNode ** ifppChild) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( !getFirstChildId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getFirstChildId(), ifppChild))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getLastChild( IF_Db * ifpDb, IF_DOMNode ** ifppChild) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( !getLastChildId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getLastChildId(), ifppChild))) { goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getChild( IF_Db * ifpDb, eDomNodeType eNodeType, IF_DOMNode ** ppChild) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pCurNode = NULL; FLMUINT64 ui64NodeId; F_Db * pDb = (F_Db *)ifpDb; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // We can do a quick lookup if this node is an element where we are // maintaining a child element list. if( eNodeType == ELEMENT_NODE && (getModeFlags() & FDOM_HAVE_CELM_LIST)) { if( getChildElmCount()) { if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( getCollection(), getChildElmNodeId( 0), ppChild))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } } } else { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } ui64NodeId = getFirstChildId(); for( ;;) { if( !ui64NodeId) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( getCollection(), ui64NodeId, (IF_DOMNode **)&pCurNode))) { goto Exit; } if( pCurNode->getNodeType() == eNodeType) { if( *ppChild) { (*ppChild)->Release(); } *ppChild = pCurNode; pCurNode = NULL; break; } ui64NodeId = pCurNode->getNextSibId(); } Exit: if( pCurNode) { pCurNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getChildElement( IF_Db * ifpDb, FLMUINT uiNameId, IF_DOMNode ** ppChild, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pCurNode = NULL; FLMUINT64 ui64NodeId; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiCollection; FLMUINT uiElmPos; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // We can do a quick lookup if this node is an element where we are // maintaining a child element list. if( getModeFlags() & FDOM_HAVE_CELM_LIST) { if( !getChildElmCount()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( !findChildElm( uiNameId, &uiElmPos) && (!uiFlags || (uiFlags & XFLM_EXACT) || uiElmPos >= getChildElmCount())) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } // At this point, if we did an exact match, we will be on the node. // If we did an inclusive match, we will be either on the node or // past it. If we did an exclusive match, we need to determine if // we are on the node or past it. If we are on the node, we need // to try to move past it, unless we are at the end of the list, in // which case we cannot go exclusive. if( uiFlags & XFLM_EXCL) { // If we found the node, we need to go one past it, if there // is one past it to go to. If not, we must return // not found. if( getChildElmNameId( uiElmPos) == uiNameId) { if( uiElmPos == getChildElmCount() - 1) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } else { uiElmPos++; } } } if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( getCollection(), getChildElmNodeId( uiElmPos), ppChild))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } else { // Cannot set uiFlags for nodes that are not unique-child nodes. if( uiFlags) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_FLAG); goto Exit; } ui64NodeId = getFirstChildId(); uiCollection = getCollection(); for( ;;) { if( !ui64NodeId) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( uiCollection, ui64NodeId, (IF_DOMNode **)&pCurNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if( pCurNode->getNodeType() == ELEMENT_NODE && pCurNode->getNameId() == uiNameId) { if( *ppChild) { (*ppChild)->Release(); } *ppChild = pCurNode; pCurNode = NULL; break; } ui64NodeId = pCurNode->getNextSibId(); } } Exit: if( pCurNode) { pCurNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getSiblingElement( IF_Db * ifpDb, FLMUINT uiNameId, FLMBOOL bNext, IF_DOMNode ** ppSibling) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pCurNode = NULL; FLMUINT64 ui64NodeId; F_Db * pDb = (F_Db *)ifpDb; FLMUINT uiCollection; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( bNext) { ui64NodeId = getNextSibId(); } else { ui64NodeId = getPrevSibId(); } uiCollection = getCollection(); for( ;;) { if( !ui64NodeId) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( uiCollection, ui64NodeId, (IF_DOMNode **)&pCurNode))) { goto Exit; } if( pCurNode->getNodeType() == ELEMENT_NODE && pCurNode->getNameId() == uiNameId) { if( *ppSibling) { (*ppSibling)->Release(); } *ppSibling = pCurNode; pCurNode = NULL; break; } if( bNext) { ui64NodeId = pCurNode->getNextSibId(); } else { ui64NodeId = pCurNode->getPrevSibId(); } } Exit: if( pCurNode) { pCurNode->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getPreviousSibling( IF_Db * ifpDb, IF_DOMNode ** ifppSib) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( !(*ifppSib)) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = m_pCachedNode->getPrevSiblingNode( m_uiAttrNameId, ifppSib))) { goto Exit; } } else { if( !getPrevSibId() || !getParentId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getPrevSibId(), ifppSib))) { goto Exit; } } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getNextSibling( IF_Db * ifpDb, IF_DOMNode ** ifppSib) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( getNodeType() == ATTRIBUTE_NODE) { if( !(*ifppSib)) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = m_pCachedNode->getNextSiblingNode( m_uiAttrNameId, ifppSib))) { goto Exit; } } else { if( !getNextSibId() || !getParentId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( getCollection(), getNextSibId(), ifppSib))) { goto Exit; } } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getPreviousDocument( IF_Db * ifpDb, IF_DOMNode ** ifppDoc) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pNode = NULL; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64StartDocId; FLMUINT64 ui64DocumentId; FLMBOOL bNeg; FLMUINT uiBytesProcessed; F_Btree * pBTree = NULL; FLMUINT uiCollection; F_COLLECTION * pCollection; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } uiCollection = getCollection(); if( RC_BAD( rc = syncFromDb( pDb))) { if( rc != NE_XFLM_DOM_NODE_DELETED) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) { goto Exit; } if( RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } ui64DocumentId = ui64StartDocId = getDocumentId(); uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( ui64StartDocId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pBTree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) { if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } for (;;) { // Need to go to the previous node. if( RC_BAD( rc = pBTree->btPrevEntry( ucKey, uiKeyLen, &uiKeyLen))) { if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } if( RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64DocumentId, &bNeg, &uiBytesProcessed))) { goto Exit; } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64DocumentId, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Better be able to find the node at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } // If the node is a root node, we have a document we can // process. if( pNode->isRootNode() && pNode->getNodeId() < ui64StartDocId) { if( *ifppDoc) { (*ifppDoc)->Release(); } // Just use the reference on pNode for *ifppDoc *ifppDoc = pNode; pNode = NULL; goto Exit; } } } else { // If we are not at the root node of the document, // jump to the root. if( !isRootNode()) { if( RC_BAD( rc = pDb->getNode( uiCollection, getDocumentId(), &pNode))) { goto Exit; } } else { pNode = this; pNode->AddRef(); } if( !pNode->getPrevSibId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( uiCollection, pNode->getPrevSibId(), ifppDoc))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } if( pBTree) { pBTree->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_DOMNode::getNextDocument( IF_Db * ifpDb, IF_DOMNode ** ifppDoc) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; F_DOMNode * pNode = NULL; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocumentId; FLMBOOL bNeg; FLMUINT uiBytesProcessed; F_Btree * pBTree = NULL; FLMUINT uiCollection; F_COLLECTION * pCollection; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } uiCollection = getCollection(); if( RC_BAD( rc = syncFromDb( pDb))) { if( rc != NE_XFLM_DOM_NODE_DELETED) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) { goto Exit; } if( RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } ui64DocumentId = getDocumentId(); uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( ui64DocumentId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pBTree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_EXCL))) { if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } for (;;) { if( RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64DocumentId, &bNeg, &uiBytesProcessed))) { goto Exit; } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64DocumentId, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Better be able to find the node at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } // If the node is a root node, we have a document we can // process. if( pNode->isRootNode()) { if( *ifppDoc) { (*ifppDoc)->Release(); } // Just use the reference on pNode for *ifppDoc *ifppDoc = pNode; pNode = NULL; goto Exit; } // Need to go to the next node. if( RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } } } else { // If we are not at the root node of the document, // jump to the root. if( !isRootNode()) { if( RC_BAD( rc = pDb->getNode( uiCollection, getDocumentId(), &pNode))) { goto Exit; } } else { pNode = this; pNode->AddRef(); } if( !pNode->getNextSibId()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = ifpDb->getNode( uiCollection, getNextSibId(), ifppDoc))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } if( pBTree) { pBTree->Release(); } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE flmReadStorageAsText( IF_IStream * pIStream, FLMBYTE * pucStorageData, FLMUINT uiDataLen, FLMUINT uiDataType, void * pvBuffer, FLMUINT uiBufLen, eXFlmTextType eTextType, FLMUINT uiMaxCharsToRead, FLMUINT uiCharOffset, FLMUINT * puiCharsRead, FLMUINT * puiBufferBytesUsed) { RCODE rc = NE_XFLM_OK; FLMBYTE ucByte; FLMUINT uiCharsDecoded = 0; FLMUINT uiSENLen; FLMUINT uiNumChars; FLMUINT uiCharsOutput = 0; FLMUNICODE * puzOutBuf = NULL; FLMBYTE * pszOutBuf = NULL; void * pvEnd = ((char *)pvBuffer) + uiBufLen; const FLMBYTE * pucTmp; FLMUINT uiLen; FLMBYTE ucSENBuf[ 16]; FLMBYTE ucConvBuf[ 64]; FLMUINT uiLastUTFLen = 0; IF_IStream * pStream = pIStream; IF_BufferIStream * pConvStream = NULL; F_BinaryToTextStream binaryToTextStream; // If the value is a number, convert to text if( uiDataType == XFLM_NUMBER_TYPE) { FLMBYTE ucNumBuf[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiNumBufLen; // Read the entire number into the temporary buffer. // NOTE: Numbers are not encoded with a length. It // is expected that the number of bytes remaining // in the stream will be equal to the exact number of // bytes representing the number. If this is not the case, // either a corruption error or an incorrect value will // be returned. uiNumBufLen = sizeof( ucNumBuf); if( pStream) { if( RC_BAD( rc = pStream->read( (char *)ucNumBuf, uiNumBufLen, &uiNumBufLen))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; } } else { f_memcpy( ucNumBuf, pucStorageData, f_max( uiNumBufLen, uiDataLen)); } // Convert the storage number to storage text uiDataLen = sizeof( ucConvBuf); if( RC_BAD( rc = flmStorageNum2StorageText( ucNumBuf, uiNumBufLen, ucConvBuf, &uiDataLen))) { goto Exit; } if( RC_BAD( rc = FlmAllocBufferIStream( &pConvStream))) { goto Exit; } if( RC_BAD( rc = pConvStream->openStream( (const char *)ucConvBuf, uiDataLen))) { goto Exit; } pStream = pConvStream; pucStorageData = NULL; } else if( uiDataType == XFLM_BINARY_TYPE) { if( !pStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pConvStream))) { goto Exit; } if( RC_BAD( rc = pConvStream->openStream( (const char *)pucStorageData, uiDataLen))) { goto Exit; } pStream = pConvStream; } if( RC_BAD( rc = binaryToTextStream.openStream( pStream, uiDataLen, &uiDataLen))) { goto Exit; } pStream = &binaryToTextStream; pucStorageData = NULL; } else if( uiDataType == XFLM_NODATA_TYPE) { goto Empty_String; } else if( uiDataType != XFLM_TEXT_TYPE) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } // Determine the SEN length if( pStream) { uiLen = 1; if( RC_BAD( rc = pStream->read( (char *)&ucSENBuf[ 0], uiLen, &uiLen))) { if( rc == NE_XFLM_EOF_HIT) { Empty_String: rc = NE_XFLM_OK; if( eTextType == XFLM_UTF8_TEXT) { if( pvBuffer) { *((FLMBYTE *)pvBuffer) = 0; } if( puiBufferBytesUsed) { *puiBufferBytesUsed = 1; } } else { flmAssert( eTextType == XFLM_UNICODE_TEXT); if( pvBuffer) { *((FLMUNICODE *)pvBuffer) = 0; } if( puiBufferBytesUsed) { *puiBufferBytesUsed = sizeof( FLMUNICODE); } } } goto Exit; } uiDataLen -= uiLen; } else { if( !uiDataLen) { goto Empty_String; } ucSENBuf[ 0] = *pucStorageData++; uiDataLen--; } if( (uiSENLen = f_getSENLength( ucSENBuf[ 0])) > 1) { uiLen = uiSENLen - 1; if( pStream) { if( RC_BAD( rc = pStream->read( (char *)&ucSENBuf[ 1], uiLen, &uiLen))) { goto Exit; } } else { if( uiDataLen < uiLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_EOF_HIT); goto Exit; } f_memcpy( &ucSENBuf[ 1], pucStorageData, uiLen); pucStorageData += uiLen; } uiDataLen -= uiLen; } pucTmp = &ucSENBuf[ 0]; if( RC_BAD( rc = f_decodeSEN( &pucTmp, &ucSENBuf[ uiSENLen], &uiNumChars))) { goto Exit; } // If only a length is needed (number of bytes), we can // return that without parsing the string if( !pvBuffer) { uiCharsOutput = uiCharOffset >= uiNumChars ? 0 : uiNumChars - uiCharOffset; if( puiBufferBytesUsed) { if( eTextType == XFLM_UNICODE_TEXT) { *puiBufferBytesUsed = (uiCharsOutput + 1) * sizeof( FLMUNICODE); } else // UTF-8 { *puiBufferBytesUsed = uiDataLen; } } goto Exit; } if( eTextType == XFLM_UTF8_TEXT) { if( !uiBufLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } pszOutBuf = (FLMBYTE *)pvBuffer; } else { flmAssert( eTextType == XFLM_UNICODE_TEXT); if( uiBufLen < sizeof( FLMUNICODE)) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } puzOutBuf = (FLMUNICODE *)pvBuffer; } // If we have a zero-length string, jump to exit. if( !uiNumChars) { // Read the null terminator if( pStream) { uiLen = 1; if( RC_BAD( rc = pStream->read( (char *)&ucByte, uiLen, &uiLen))) { goto Exit; } } else { if( !uiDataLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } ucByte = *pucStorageData++; uiDataLen--; } if( ucByte != 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } goto Empty_String; } // Parse through the string, outputting data to the buffer as we go. uiCharsDecoded = 0; if( eTextType == XFLM_UNICODE_TEXT) { FLMUNICODE uChar; while( uiCharsOutput < uiMaxCharsToRead) { if( pStream) { if( RC_BAD( rc = f_readUTF8CharAsUnicode( pStream, &uChar))) { if( rc == NE_XFLM_EOF_HIT) { Unicode_EOF_Hit: if( uiCharsDecoded != uiNumChars) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } rc = NE_XFLM_OK; break; } goto Exit; } } else { FLMBYTE ucTmpUni[ 3]; if( !uiDataLen) { goto Unicode_EOF_Hit; } ucTmpUni[ 0] = *pucStorageData++; uiDataLen--; if( ucTmpUni[ 0] <= 0x7F) { if( !ucTmpUni[ 0]) { goto Unicode_EOF_Hit; } uChar = (FLMUNICODE)ucTmpUni[ 0]; } else { if( !uiDataLen) { goto Unicode_EOF_Hit; } ucTmpUni[ 1] = *pucStorageData++; uiDataLen--; if( (ucTmpUni[ 1] >> 6) != 0x02) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); goto Exit; } if( (ucTmpUni[ 0] >> 5) == 0x06) { uChar = ((FLMUNICODE)( ucTmpUni[ 0] - 0xC0) << 6) + (FLMUNICODE)(ucTmpUni[ 1] - 0x80); } else { if( !uiDataLen) { goto Unicode_EOF_Hit; } ucTmpUni[ 2] = *pucStorageData++; uiDataLen--; if( (ucTmpUni[ 0] >> 4) != 0x0E || (ucTmpUni[ 2] >> 6) != 0x02) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); goto Exit; } uChar = ((FLMUNICODE)(ucTmpUni[ 0] - 0xE0) << 12) + ((FLMUNICODE)(ucTmpUni[ 1] - 0x80) << 6) + (FLMUNICODE)(ucTmpUni[ 2] - 0x80); } } } if( ++uiCharsDecoded > uiNumChars) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( uiCharOffset) { uiCharOffset--; continue; } if( puzOutBuf) { if( puzOutBuf + 1 >= pvEnd) { goto Overflow_Error; } *puzOutBuf++ = uChar; uiCharsOutput++; } else { if( uChar <= 0xFF) { if( pszOutBuf + 1 >= pvEnd) { goto Overflow_Error; } *pszOutBuf++ = f_tonative( (FLMBYTE)uChar); uiCharsOutput++; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); goto Exit; } } } } else // UTF-8 { flmAssert( eTextType == XFLM_UTF8_TEXT); while( uiCharsOutput < uiMaxCharsToRead) { if( (uiLen = ((FLMBYTE *)pvEnd) - pszOutBuf) == 0) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Overflow_Error; } if( pStream) { if( RC_BAD( rc = f_readUTF8CharAsUTF8( pStream, pszOutBuf, &uiLen))) { if( rc == NE_XFLM_CONV_DEST_OVERFLOW) { goto Overflow_Error; } if( rc == NE_XFLM_EOF_HIT) { UTF8_EOF_Hit: if( uiCharsDecoded != uiNumChars) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } rc = NE_XFLM_OK; break; } goto Exit; } } else { if( !uiDataLen) { goto UTF8_EOF_Hit; } pszOutBuf[ 0] = *pucStorageData++; uiDataLen--; if( pszOutBuf[ 0] <= 0x7F) { if( !pszOutBuf[ 0]) { goto UTF8_EOF_Hit; } uiLen = 1; } else { if( uiLen < 2) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Overflow_Error; } if( !uiDataLen) { goto UTF8_EOF_Hit; } pszOutBuf[ 1] = *pucStorageData++; uiDataLen--; if( (pszOutBuf[ 1] >> 6) != 0x02) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); goto Exit; } if( (pszOutBuf[ 0] >> 5) == 0x06) { uiLen = 2; } else { if( uiLen < 3) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Overflow_Error; } if( !uiDataLen) { goto UTF8_EOF_Hit; } pszOutBuf[ 2] = *pucStorageData++; uiDataLen--; if( (pszOutBuf[ 0] >> 4) != 0x0E || (pszOutBuf[ 2] >> 6) != 0x02) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); goto Exit; } uiLen = 3; } } } if( ++uiCharsDecoded > uiNumChars) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( uiCharOffset) { uiCharOffset--; continue; } if( pszOutBuf + uiLen >= pvEnd) { goto Overflow_Error; } pszOutBuf += uiLen; uiLastUTFLen = uiLen; uiCharsOutput++; } } // There is room for the 0 terminating character, but we // will not increment the return length, but we will output the // number of buffer bytes used if( eTextType == XFLM_UTF8_TEXT && pszOutBuf < pvEnd) { *pszOutBuf = 0; if( puiBufferBytesUsed) { *puiBufferBytesUsed = (FLMUINT)(pszOutBuf - (FLMBYTE *)pvBuffer) + 1; } } else if( eTextType == XFLM_UNICODE_TEXT && &puzOutBuf[ 1] <= pvEnd) { *puzOutBuf = 0; if( puiBufferBytesUsed) { *puiBufferBytesUsed = (FLMUINT)((FLMBYTE *)puzOutBuf - (FLMBYTE *)pvBuffer) + sizeof( FLMUNICODE); } } else { Overflow_Error: if( uiCharsOutput) { uiCharsOutput--; if( puzOutBuf) { *(puzOutBuf - 1) = 0; if( puiBufferBytesUsed) { *puiBufferBytesUsed = (FLMUINT)((FLMBYTE *)puzOutBuf - (FLMBYTE *)pvBuffer); } } else { pszOutBuf -= uiLastUTFLen; *pszOutBuf = 0; if( puiBufferBytesUsed) { *puiBufferBytesUsed = (FLMUINT)(pszOutBuf - (FLMBYTE *)pvBuffer) + 1; } } } else if( puiBufferBytesUsed) { *puiBufferBytesUsed = 0; } rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } Exit: if( pConvStream) { pConvStream->Release(); } if( puiCharsRead) { *puiCharsRead = uiCharsOutput; } if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE flmReadStorageAsBinary( IF_IStream * pIStream, void * pvBuffer, FLMUINT uiBufLen, FLMUINT uiByteOffset, FLMUINT * puiBytesRead) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; // Position to the requested offset if( uiByteOffset) { if( RC_BAD( rc = pIStream->read( NULL, uiByteOffset, &uiByteOffset))) { goto Exit; } } // Read the requested bytes rc = pIStream->read( pucBuffer, uiBufLen, &uiBufLen); if( puiBytesRead) { *puiBytesRead = uiBufLen; } if( RC_BAD( rc)) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE flmReadStorageAsNumber( IF_IStream * pIStream, FLMUINT uiDataType, FLMUINT64 * pui64Number, FLMBOOL * pbNeg) { RCODE rc = NE_XFLM_OK; FLMBOOL bNeg = FALSE; FLMUINT64 ui64Num = 0; switch( uiDataType) { case XFLM_NUMBER_TYPE : { FLMBYTE ucNumBuf[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiNumBufLen; // Read the entire number into the temporary buffer. // NOTE: Numbers are not encoded with a length. It // is expected that the number of bytes remaining // in the stream will be equal to the exact number of // bytes representing the number. If this is not the case, // either a corruption error or an incorrect value will // be returned. uiNumBufLen = sizeof( ucNumBuf); if( RC_BAD( rc = pIStream->read( (char *)ucNumBuf, uiNumBufLen, &uiNumBufLen))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; } if( RC_BAD( rc = flmStorageNumberToNumber( ucNumBuf, uiNumBufLen, &ui64Num, &bNeg))) { goto Exit; } break; } case XFLM_TEXT_TYPE : { FLMUNICODE uChar; FLMUINT uiLoop; FLMBOOL bHex = FALSE; FLMUINT uiIncrAmount = 0; // Skip the character count if( RC_BAD( rc = f_readSEN64( pIStream, NULL, NULL))) { if( rc == NE_XFLM_EOF_HIT) { // Empty string rc = NE_XFLM_OK; } goto Exit; } for( uiLoop = 0;; uiLoop++) { if( RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } else { goto Exit; } } if( uChar >= FLM_UNICODE_0 && uChar <= FLM_UNICODE_9) { uiIncrAmount = (FLMUINT)(uChar - FLM_UNICODE_0); } else if( uChar >= FLM_UNICODE_A && uChar <= FLM_UNICODE_F) { if( bHex) { uiIncrAmount = (FLMUINT)(uChar - FLM_UNICODE_A + 10); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } else if( uChar >= FLM_UNICODE_a && uChar <= FLM_UNICODE_f) { if( bHex) { uiIncrAmount = (FLMUINT)(uChar - FLM_UNICODE_a + 10); } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } else if( uChar == FLM_UNICODE_X || uChar == FLM_UNICODE_x) { if( !ui64Num && !bHex) { bHex = TRUE; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } else if( uChar == FLM_UNICODE_HYPHEN && !uiLoop) { bNeg = TRUE; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } if( !bHex) { if( ui64Num > (~(FLMUINT64)0) / 10) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num *= (FLMUINT64)10; } else { if( ui64Num > (~(FLMUINT64)0) / 16) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num *= (FLMUINT64)16; } if( ui64Num > (~(FLMUINT64)0) - (FLMUINT64)uiIncrAmount) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num += (FLMUINT64)uiIncrAmount; } break; } default : { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); goto Exit; } } *pui64Number = ui64Num; *pbNeg = bNeg; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE flmReadLine( IF_IStream * pIStream, FLMBYTE * pszBuffer, FLMUINT * puiSize) { RCODE rc = NE_XFLM_OK; FLMUINT uiMaxBytes = *puiSize; FLMUINT uiOffset = 0; FLMBYTE ucByte; *puiSize = 0; for( ;;) { if( RC_BAD( rc = pIStream->read( (char *)&ucByte, 1, NULL))) { if( rc == NE_FLM_IO_END_OF_FILE) { rc = NE_XFLM_OK; break; } goto Exit; } if( ucByte == 0x0A || ucByte == 0x0D) { if( uiOffset) { break; } continue; } else { if( (uiOffset + 1) == uiMaxBytes) { rc = RC_SET_AND_ASSERT( NE_XFLM_BUFFER_OVERFLOW); goto Exit; } pszBuffer[ uiOffset++] = (char)ucByte; } } pszBuffer[ uiOffset] = 0; *puiSize = uiOffset; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_BTreeIStream::openStream( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT32 ui32BlkAddr, FLMUINT uiOffsetIndex) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; F_Dict * pDict = pDb->m_pDict; F_Btree * pBTree = NULL; if( RC_BAD( rc = pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) { goto Exit; } // Set up the btree object if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = openStream( pDb, pBTree, XFLM_EXACT, uiCollection, ui64NodeId, ui32BlkAddr, uiOffsetIndex))) { goto Exit; } pBTree = NULL; m_bReleaseBTree = TRUE; Exit: if( pBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pBTree); } if( RC_BAD( rc)) { closeStream(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_BTreeIStream::openStream( F_Db * pDb, F_Btree * pBTree, FLMUINT uiFlags, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT32 ui32BlkAddr, FLMUINT uiOffsetIndex) { RCODE rc = NE_XFLM_OK; flmAssert( !m_pBTree); m_pDb = pDb; m_uiCollection = uiCollection; m_pBTree = pBTree; // Save the key and key length m_uiKeyLength = sizeof( m_ucKey); if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &m_uiKeyLength, m_ucKey, FALSE, TRUE))) { goto Exit; } m_ui32BlkAddr = ui32BlkAddr; m_uiOffsetIndex = uiOffsetIndex; if( RC_BAD( rc = m_pBTree->btLocateEntry( m_ucKey, sizeof( m_ucKey), &m_uiKeyLength, uiFlags, NULL, &m_uiStreamSize, &m_ui32BlkAddr, &m_uiOffsetIndex))) { if( rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } if( uiFlags == XFLM_EXACT) { m_ui64NodeId = ui64NodeId; } else { if( RC_BAD( rc = flmCollation2Number( m_uiKeyLength, m_ucKey, &m_ui64NodeId, NULL, NULL))) { goto Exit; } } Exit: if( RC_BAD( rc)) { closeStream(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_BTreeIStream::positionTo( FLMUINT64 ui64Position) { RCODE rc = NE_XFLM_OK; if( ui64Position >= m_uiBufferStartOffset && ui64Position <= m_uiBufferStartOffset + m_uiBufferBytes) { m_uiBufferOffset = (FLMUINT)(ui64Position - m_uiBufferStartOffset); } else { if( !m_bDataEncrypted) { if( RC_BAD( rc = m_pBTree->btSetReadPosition( m_ucKey, m_uiKeyLength, (FLMUINT)ui64Position))) { goto Exit; } m_uiBufferStartOffset = (FLMUINT)ui64Position; m_uiBufferOffset = 0; m_uiBufferBytes = 0; } else { // When the data is encrypted, we can't just position the btree to a // new read position. We must read the data chunk by chunk until we // get to the buffer that holds the specified position. Then we can // decrypt the buffer and set the correct position. m_bBufferDecrypted = FALSE; if( ui64Position > m_uiBufferStartOffset + m_uiBufferBytes) { while( ui64Position > m_uiBufferStartOffset + m_uiBufferBytes) { m_uiBufferStartOffset += m_uiBufferBytes; } } else { while( ui64Position < m_uiBufferStartOffset) { m_uiBufferStartOffset -= f_min( m_uiBufferStartOffset, FLM_ENCRYPT_CHUNK_SIZE); } } flmAssert( ui64Position >= m_uiBufferStartOffset && ui64Position <= m_uiBufferStartOffset + m_uiBufferBytes); // If the new position uis out of range, we will get an error returned. if( RC_BAD( rc = m_pBTree->btSetReadPosition( m_ucKey, m_uiKeyLength, m_uiBufferStartOffset))) { goto Exit; } if( RC_BAD( rc = m_pBTree->btGetEntry( m_ucKey, m_uiKeyLength, m_uiKeyLength, m_pucBuffer, m_uiBufferSize, &m_uiBufferBytes))) { if( rc == NE_XFLM_EOF_HIT) { if( !m_uiBufferBytes) { goto Exit; } rc = NE_XFLM_OK; } else { goto Exit; } } flmAssert( m_uiBufferBytes <= FLM_ENCRYPT_CHUNK_SIZE); if( RC_BAD( rc = m_pDb->decryptData( m_uiEncDefId, m_ucIV, m_pucBuffer, m_uiBufferBytes, m_pucBuffer, m_uiBufferSize))) { goto Exit; } // Check to see if we are at the end of the encrypted data. if( m_uiBufferStartOffset + m_uiBufferBytes >= m_uiDataLength) { // Trim back to the valid decrypted data. m_uiBufferBytes -= (ENCRYPT_MIN_CHUNK_SIZE - extraEncBytes( m_uiDataLength)); } m_bBufferDecrypted = TRUE; m_uiBufferOffset = (FLMUINT)(ui64Position - m_uiBufferStartOffset); } } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_BTreeIStream::read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; FLMUINT uiBufBytesAvail; FLMUINT uiTmp; FLMUINT uiOffset = 0; flmAssert( m_pBTree); if( m_bDataEncrypted && !m_bBufferDecrypted) { if( (m_uiBufferBytes - m_uiBufferOffset) > 0) { // Since we are now looking at encrypted data that was not // decrypted, we will need to move the data to the front of the // buffer, then read in enough data to fill the buffer (if there // is any there) before we can decrypt it. f_memmove( &m_ucBuffer[0], &m_ucBuffer[ m_uiBufferOffset], (m_uiBufferBytes - m_uiBufferOffset)); m_uiBufferBytes -= m_uiBufferOffset; m_uiBufferOffset = 0; // Fill the remainder of the buffer if( RC_BAD( rc = m_pBTree->btGetEntry( m_ucKey, m_uiKeyLength, m_uiKeyLength, &m_ucBuffer[ m_uiBufferBytes], m_uiBufferSize - m_uiBufferBytes, &uiTmp))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { goto Exit; } } m_uiBufferBytes += uiTmp; flmAssert( extraEncBytes( m_uiBufferBytes) == 0); flmAssert( m_uiBufferBytes <= FLM_ENCRYPT_CHUNK_SIZE); // Now decrypt what we have. uiTmp = m_uiBufferBytes; if( RC_BAD( rc = m_pDb->decryptData( m_uiEncDefId, m_ucIV, m_ucBuffer, m_uiBufferBytes, m_ucBuffer, m_uiBufferSize))) { goto Exit; } // Check for the end of the data. if( m_uiBufferStartOffset + m_uiBufferBytes > m_uiDataLength) { // Trim back the buffer to valid data only. m_uiBufferBytes -= (ENCRYPT_MIN_CHUNK_SIZE - extraEncBytes( m_uiDataLength)); } m_bBufferDecrypted = TRUE; } } while( uiBytesToRead) { if( (uiBufBytesAvail = m_uiBufferBytes - m_uiBufferOffset) != 0) { uiTmp = f_min( uiBufBytesAvail, uiBytesToRead); if( pucBuffer) { f_memcpy( &pucBuffer[ uiOffset], &m_pucBuffer[ m_uiBufferOffset], uiTmp); } m_uiBufferOffset += uiTmp; uiOffset += uiTmp; if( (uiBytesToRead -= uiTmp) == 0) { break; } } else { m_uiBufferStartOffset += m_uiBufferBytes; m_uiBufferOffset = 0; if( RC_BAD( rc = m_pBTree->btGetEntry( m_ucKey, m_uiKeyLength, m_uiKeyLength, m_pucBuffer, m_uiBufferSize, &m_uiBufferBytes))) { if( rc == NE_XFLM_EOF_HIT) { if( !m_uiBufferBytes) { goto Exit; } rc = NE_XFLM_OK; continue; } goto Exit; } // Check for encryption. if( m_bDataEncrypted) { flmAssert( m_uiBufferBytes <= FLM_ENCRYPT_CHUNK_SIZE); if( RC_BAD( rc = m_pDb->decryptData( m_uiEncDefId, m_ucIV, m_pucBuffer, m_uiBufferBytes, m_pucBuffer, m_uiBufferSize))) { goto Exit; } // Check to see if we are at the end of the encrypted data. if( m_uiBufferStartOffset + m_uiBufferBytes > m_uiDataLength) { // Trim back to the valid decrypted data. m_uiBufferBytes -= (ENCRYPT_MIN_CHUNK_SIZE - extraEncBytes( m_uiDataLength)); } m_bBufferDecrypted = TRUE; } } } Exit: if( puiBytesRead) { *puiBytesRead = uiOffset; } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::decryptData( FLMUINT uiEncDefId, FLMBYTE * pucIV, void * pvInBuf, FLMUINT uiInLen, void * pvOutBuf, FLMUINT uiOutBufSize) { RCODE rc = NE_XFLM_OK; F_Dict * pDict; F_ENCDEF * pEncDef = NULL; FLMBYTE * pucInBuf = (FLMBYTE *)pvInBuf; FLMBYTE * pucOutBuf = (FLMBYTE *)pvOutBuf; FLMUINT uiEncLen; FLMUINT uiOutLen; if( m_pDatabase->m_bInLimitedMode) { rc = RC_SET( m_pDatabase->m_rcLimitedCode); goto Exit; } flmAssert( extraEncBytes( uiInLen) == 0); flmAssert( uiEncDefId); // Need the dictionary and encryption definition. if( RC_BAD( rc = getDictionary( &pDict))) { goto Exit; } if( RC_BAD( rc = pDict->getEncDef( uiEncDefId, &pEncDef))) { goto Exit; } flmAssert( pEncDef); flmAssert( pEncDef->pCcs); flmAssert( pEncDef->uiEncKeySize); while( uiInLen) { uiEncLen = f_min( uiInLen, FLM_ENCRYPT_CHUNK_SIZE); uiOutLen = uiOutBufSize; if( RC_BAD( rc = pEncDef->pCcs->decryptFromStore( pucInBuf, uiEncLen, pucOutBuf, &uiOutLen, pucIV))) { goto Exit; } flmAssert( uiOutLen == uiEncLen); pucInBuf += uiEncLen; uiInLen -= uiEncLen; pucOutBuf += uiEncLen; uiOutBufSize -= uiEncLen; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ F_NodePool::~F_NodePool() { F_BTreeIStream * pTmpBTreeIStream; while( (pTmpBTreeIStream = m_pFirstBTreeIStream) != NULL) { m_pFirstBTreeIStream = m_pFirstBTreeIStream->m_pNextInPool; pTmpBTreeIStream->m_refCnt = 0; pTmpBTreeIStream->m_pNextInPool = NULL; delete pTmpBTreeIStream; } if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_NodePool::setup( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ void F_NodeCacheMgr::insertDOMNode( F_DOMNode * pNode) { flmAssert( pNode->m_refCnt == 1); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pNode->resetDOMNode( TRUE); pNode->m_pNextInPool = m_pFirstNode; m_pFirstNode = pNode; f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_NodePool::allocBTreeIStream( F_BTreeIStream ** ppBTreeIStream) { RCODE rc = NE_XFLM_OK; if( m_pFirstBTreeIStream) { f_mutexLock( m_hMutex); if( !m_pFirstBTreeIStream) { f_mutexUnlock( m_hMutex); } else { f_resetStackInfo( m_pFirstBTreeIStream); *ppBTreeIStream = m_pFirstBTreeIStream; m_pFirstBTreeIStream = m_pFirstBTreeIStream->m_pNextInPool; (*ppBTreeIStream)->m_pNextInPool = NULL; f_mutexUnlock( m_hMutex); goto Exit; } } if( (*ppBTreeIStream = f_new F_BTreeIStream) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ void F_NodePool::insertBTreeIStream( F_BTreeIStream * pBTreeIStream) { flmAssert( pBTreeIStream->m_refCnt == 1); pBTreeIStream->reset(); f_mutexLock( m_hMutex); pBTreeIStream->m_pNextInPool = m_pFirstBTreeIStream; m_pFirstBTreeIStream = pBTreeIStream; f_mutexUnlock( m_hMutex); } /**************************************************************************** Desc: *****************************************************************************/ RCODE F_DOMNode::getNamespaceURI( FLMBOOL bUnicode, IF_Db * ifpDb, void * pvNamespaceURI, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; FLMUINT uiTag; FLMUNICODE * puzNamespaceURI; char * pszNamespaceURI; FLMUINT uiChars = 0; F_NameTable * pNameTable = NULL; FLMUINT uiNameId; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } pNameTable = pDb->m_pDict->getNameTable(); eNodeType = getNodeType(); if( eNodeType == ELEMENT_NODE) { uiTag = ELM_ELEMENT_TAG; uiNameId = getNameId(); } else if( eNodeType == ATTRIBUTE_NODE) { uiTag = ELM_ATTRIBUTE_TAG; uiNameId = m_uiAttrNameId; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } uiChars = uiBufSize; if( bUnicode) { puzNamespaceURI = (FLMUNICODE *)pvNamespaceURI; pszNamespaceURI = NULL; } else { puzNamespaceURI = NULL; pszNamespaceURI = (char *)pvNamespaceURI; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, uiTag, uiNameId, NULL, NULL, NULL, NULL, puzNamespaceURI, pszNamespaceURI, &uiChars, FALSE))) { goto Exit; } Exit: if( puiCharsReturned) { *puiCharsReturned = uiChars; } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE F_DOMNode::getLocalName( FLMBOOL bUnicode, IF_Db * ifpDb, void * pvLocalName, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; FLMUINT uiTag; F_NameTable * pNameTable = NULL; FLMUINT uiChars = 0; FLMUINT uiNameId; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } pNameTable = pDb->m_pDict->getNameTable(); eNodeType = getNodeType(); if( eNodeType == ELEMENT_NODE) { uiTag = ELM_ELEMENT_TAG; uiNameId = getNameId(); } else if( eNodeType == ATTRIBUTE_NODE) { uiTag = ELM_ATTRIBUTE_TAG; uiNameId = m_uiAttrNameId; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } uiChars = uiBufSize; if( bUnicode) { if( RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, uiTag, uiNameId, (FLMUNICODE *)pvLocalName, NULL, &uiChars))) { goto Exit; } } else { if( RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, uiTag, uiNameId, NULL, (char *)pvLocalName, &uiChars))) { goto Exit; } } Exit: if( puiCharsReturned) { *puiCharsReturned = uiChars; } if( bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: Return the prefix name, either as Unicode or as Native. *****************************************************************************/ RCODE F_DOMNode::getPrefix( FLMBOOL bUnicode, IF_Db * ifpDb, void * pvPrefix, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; FLMUINT uiPrefix; FLMUNICODE * puzPrefix = (FLMUNICODE *)pvPrefix; char * pszPrefix = (char *)pvPrefix; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } eNodeType = getNodeType(); if( eNodeType == ELEMENT_NODE) { uiPrefix = getPrefixId(); } else if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getPrefixId( m_uiAttrNameId, &uiPrefix))) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( uiPrefix) { if( bUnicode) { if( RC_BAD( rc = pDb->m_pDict->getPrefix( uiPrefix, puzPrefix, uiBufSize, puiCharsReturned))) { goto Exit; } } else { if( RC_BAD( rc = pDb->m_pDict->getPrefix( uiPrefix, pszPrefix, uiBufSize, puiCharsReturned))) { goto Exit; } } } else { if( uiBufSize) { if( puzPrefix) { *puzPrefix = 0; } } if( puiCharsReturned) { *puiCharsReturned = 0; } goto Exit; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getQualifiedName( IF_Db * ifpDb, FLMUNICODE * puzQualifiedName, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; FLMUINT uiCharsReturned; FLMUINT uiTmp; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = getPrefix( ifpDb, puzQualifiedName, uiBufSize, &uiCharsReturned))) { goto Exit; } if( uiCharsReturned) { if( puzQualifiedName) { uiBufSize -= (sizeof( FLMUNICODE) * uiCharsReturned); if( uiBufSize < sizeof( FLMUNICODE) * 2) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } puzQualifiedName[ uiCharsReturned] = FLM_UNICODE_COLON; uiCharsReturned++; puzQualifiedName += uiCharsReturned; uiBufSize -= sizeof( FLMUNICODE); } else { uiCharsReturned++; } } if( RC_BAD( rc = getLocalName( ifpDb, puzQualifiedName, uiBufSize, &uiTmp))) { goto Exit; } uiCharsReturned += uiTmp; if( puiCharsReturned) { *puiCharsReturned = uiCharsReturned; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::getQualifiedName( IF_Db * ifpDb, char * pszQualifiedName, FLMUINT uiBufSize, FLMUINT * puiCharsReturned) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; FLMUINT uiCharsReturned; FLMUINT uiTmp; if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } if( RC_BAD( rc = getPrefix( ifpDb, pszQualifiedName, uiBufSize, &uiCharsReturned))) { goto Exit; } if( uiCharsReturned) { if( pszQualifiedName) { uiBufSize -= uiCharsReturned; if( uiBufSize < 2) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } pszQualifiedName[ uiCharsReturned] = ASCII_COLON; uiCharsReturned++; pszQualifiedName += uiCharsReturned; uiBufSize--; } else { uiCharsReturned++; } } if( RC_BAD( rc = getLocalName( ifpDb, pszQualifiedName, uiBufSize, &uiTmp))) { goto Exit; } uiCharsReturned += uiTmp; if( puiCharsReturned) { *puiCharsReturned = uiCharsReturned; } Exit: if( bStartedTrans) { pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: Method to set the prefix on a DOM node. Either a unicode or native buffer may be used. ** private ** *****************************************************************************/ RCODE F_DOMNode::setPrefix( FLMBOOL bUnicode, IF_Db * ifpDb, void * pvPrefix) { RCODE rc = NE_XFLM_OK; FLMUINT uiPrefixId; F_Db * pDb = (F_Db *)ifpDb; FLMBOOL bStartedTrans = FALSE; char * pszPrefix = (char *)pvPrefix; FLMUNICODE * puzPrefix = (FLMUNICODE *)pvPrefix; eDomNodeType eNodeType; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // If the node is read-only, don't allow it to be changed if( getModeFlags() & FDOM_READ_ONLY) { rc = RC_SET( NE_XFLM_READ_ONLY); goto Exit; } eNodeType = getNodeType(); // Make sure this is an element or attribute node if( eNodeType != ELEMENT_NODE && eNodeType != ATTRIBUTE_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } uiPrefixId = 0; if( puzPrefix && bUnicode) { // Find the prefix ID if( RC_BAD( rc = pDb->m_pDict->getPrefixId( pDb, puzPrefix, &uiPrefixId))) { if( rc == NE_XFLM_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PREFIX); } goto Exit; } } else if( pszPrefix) { // Find the prefix ID if( RC_BAD( rc = pDb->m_pDict->getPrefixId( pDb, pszPrefix, &uiPrefixId))) { if( rc == NE_XFLM_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PREFIX); } goto Exit; } } // Set the prefix if( RC_BAD( rc = setPrefixId( ifpDb, uiPrefixId))) { goto Exit; } Exit: if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_DOMNode::setPrefixId( IF_Db * ifpDb, FLMUINT uiPrefixId) { RCODE rc = NE_XFLM_OK; F_Db * pDb = (F_Db *)ifpDb; F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; eDomNodeType eNodeType; FLMUINT uiTmp; if( RC_BAD( rc = pDb->checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Make sure the node is current if( RC_BAD( rc = syncFromDb( pDb))) { goto Exit; } // If the node is read-only, don't allow it to be changed if( getModeFlags() & FDOM_READ_ONLY) { rc = RC_SET( NE_XFLM_READ_ONLY); goto Exit; } eNodeType = getNodeType(); // If the prefix isn't changing, don't do anything if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->getPrefixId( m_uiAttrNameId, &uiTmp))) { goto Exit; } if( uiPrefixId == uiTmp) { goto Exit; } } else if( eNodeType == ELEMENT_NODE) { if( uiPrefixId == getPrefixId()) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Verify that the prefix is valid if( RC_BAD( rc = pDb->m_pDict->getPrefix( uiPrefixId, (char *)NULL, 0, NULL))) { goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Set the new prefix if( RC_BAD( rc = makeWriteCopy( pDb))) { goto Exit; } bMustAbortOnError = TRUE; if( eNodeType == ATTRIBUTE_NODE) { if( RC_BAD( rc = m_pCachedNode->setPrefixId( pDb, m_uiAttrNameId, uiPrefixId))) { goto Exit; } } else { setPrefixId( uiPrefixId); } // Update the node if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) { goto Exit; } // Log the update pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeSetPrefixId( pDb, getCollection(), getIxNodeId(), m_uiAttrNameId, uiPrefixId))) { goto Exit; } Exit: if( RC_BAD( rc) && bMustAbortOnError) { if( bMustAbortOnError) { pDb->setMustAbortTrans( rc); } } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { pDb->transAbort(); } else { rc = pDb->transCommit(); } } return( rc); } /**************************************************************************** Desc: Method to compare two dom nodes. *****************************************************************************/ FLMUINT FLMAPI F_DOMNode::compareNode( IF_DOMNode * pNode, IF_Db * pDb1, IF_Db * pDb2, char * pszErrBuff, FLMUINT uiErrBuffLen) { RCODE rc = NE_XFLM_OK; FLMUINT uiEqual = 0; F_DOMNode * pLeftNode = (F_DOMNode *)this; F_DOMNode * pRightNode = (F_DOMNode *)pNode; char szBuffer[ 100]; FLMUNICODE * puzVal1 = NULL; FLMUNICODE * puzVal2 = NULL; char * pucBinary1 = NULL; char * pucBinary2 = NULL; FLMUINT uiBytesReturned1; FLMUINT uiBytesReturned2; FLMUINT uiDataLen1; FLMUINT uiDataLen2; FLMUINT uiTmp1; FLMUINT uiTmp2; FLMUINT64 ui64Tmp1; FLMUINT64 ui64Tmp2; #define NODE_NOT_EQUAL 1 szBuffer[0] = '\0'; if( pLeftNode->getNodeType() != pRightNode->getNodeType()) { f_sprintf( szBuffer, "Node Type mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getDataType( pDb1, &uiTmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getDataType( pDb2, &uiTmp2))) { goto Exit; } if( uiTmp1 != uiTmp2) { f_sprintf( szBuffer, "Data Type mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( pLeftNode->getCollection() != pRightNode->getCollection()) { f_sprintf( szBuffer, "Collection mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getPrefixId( pDb1, &uiTmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getPrefixId( pDb2, &uiTmp2))) { goto Exit; } if( uiTmp1 != uiTmp2) { f_sprintf( szBuffer, "Prefix mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getNameId( pDb1, &uiTmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getNameId( pDb2, &uiTmp2))) { goto Exit; } if( uiTmp1 != uiTmp2) { f_sprintf( szBuffer, "Name Id mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getEncDefId( pDb1, &uiTmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getEncDefId( pDb2, &uiTmp2))) { goto Exit; } if( uiTmp1 != uiTmp2) { f_sprintf( szBuffer, "Encryption Id mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( (pLeftNode->getModeFlags() & FDOM_PERSISTENT_FLAGS) != (pRightNode->getModeFlags() & FDOM_PERSISTENT_FLAGS)) { f_sprintf( szBuffer, "Flags mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getNodeId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getNodeId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Node Id mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getDocumentId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getDocumentId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Root Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getParentId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getParentId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Parent Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getFirstChildId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getFirstChildId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "First Child Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getLastChildId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getLastChildId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Last Child Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getPrevSibId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getPrevSibId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Previous Sibling Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getNextSibId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getNextSibId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Next Sibling Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getAnnotationId( pDb1, &ui64Tmp1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getAnnotationId( pDb2, &ui64Tmp2))) { goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Annotation Node mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pLeftNode->getDataLength( pDb1, &uiDataLen1))) { goto Exit; } if( RC_BAD( rc = pRightNode->getDataLength( pDb2, &uiDataLen2))) { goto Exit; } if( uiDataLen1 != uiDataLen2) { f_sprintf( szBuffer, "Data Length mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( uiDataLen1) { switch( pLeftNode->getDataType()) { case XFLM_NODATA_TYPE: { goto Exit; } case XFLM_TEXT_TYPE: { if( RC_BAD( rc = pLeftNode->getUnicode( pDb1, &puzVal1))) { f_sprintf( szBuffer, "getUnicode failed with rc==0x%04X.", (unsigned)rc); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pRightNode->getUnicode( pDb2, &puzVal2))) { f_sprintf( szBuffer, "getUnicode failed with rc==0x%04X.", (unsigned)rc); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( f_unicmp( puzVal1, puzVal2) != 0) { f_sprintf( szBuffer, "Data Value mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } break; } case XFLM_NUMBER_TYPE: { if( RC_BAD( rc = pLeftNode->getUINT64( pDb1, &ui64Tmp1))) { f_sprintf( szBuffer, "getUINT64 failed with rc==0x%04X.", (unsigned)rc); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pRightNode->getUINT64( pDb2, &ui64Tmp2))) { f_sprintf( szBuffer, "getUINT64 failed with rc==0x%04X.", (unsigned)rc); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( ui64Tmp1 != ui64Tmp2) { f_sprintf( szBuffer, "Data Value mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } break; } case XFLM_BINARY_TYPE: { if( RC_BAD( rc = f_alloc( uiDataLen1 + 1, &pucBinary1))) { goto Exit; } if( RC_BAD( rc = f_alloc( uiDataLen2 + 1, &pucBinary2))) { goto Exit; } if( RC_BAD( rc = pLeftNode->getBinary( pDb1, pucBinary1, 0, uiDataLen1, &uiBytesReturned1))) { f_sprintf( szBuffer, "getBinary failed with rc==0x%04X.", (unsigned)rc); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( RC_BAD( rc = pRightNode->getBinary( pDb2, pucBinary2, 0, uiDataLen2, &uiBytesReturned2))) { f_sprintf( szBuffer, "getBinary failed with rc==0x%04X.", (unsigned)rc); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( uiBytesReturned1 != uiBytesReturned2) { f_sprintf( szBuffer, "Return data length mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } if( f_memcmp( pucBinary1, pucBinary2, uiBytesReturned1) != 0) { f_strcpy( szBuffer, "Data Value mismatch"); uiEqual = NODE_NOT_EQUAL; goto Exit; } break; } default: { f_strcpy( szBuffer, "Invalid Data Type"); uiEqual = NODE_NOT_EQUAL; goto Exit; } } } Exit: f_memcpy( pszErrBuff, szBuffer, f_min( uiErrBuffLen, f_strlen( szBuffer))); pszErrBuff[ f_min( uiErrBuffLen, f_strlen( szBuffer))] = '\0'; if( puzVal1) { f_free( &puzVal1); } if( puzVal2) { f_free( &puzVal2); } if( pucBinary1) { f_free( &pucBinary1); } if( pucBinary2) { f_free( &pucBinary2); } return( uiEqual); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_Db::getDictionaryDef( FLMUINT uiDictType, FLMUINT uiDictNumber, IF_DOMNode ** ppDocumentNode) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_DataVector searchKey; F_DataVector foundKey; searchKey.reset(); foundKey.reset(); if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = searchKey.setUINT( 0, uiDictType))) { goto Exit; } if( RC_BAD( rc = searchKey.setUINT( 1, uiDictNumber))) { goto Exit; } if( RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, foundKey.getDocumentID(), ppDocumentNode))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getElementNameId( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT * puiElementNameId) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_ELEMENT_TAG, puzElementName, NULL, TRUE, puzNamespaceURI, puiElementNameId))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getElementNameId( const char * pszNamespaceURI, const char * pszElementName, FLMUINT * puiElementNameId) { RCODE rc = NE_XFLM_OK; FLMUNICODE * puzNamespaceURI = NULL; FLMUNICODE * puzTmp; const char * pszTmp; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( pszNamespaceURI && *pszNamespaceURI) { if( RC_BAD( rc = f_alloc( (f_strlen( pszNamespaceURI) + 1) * sizeof( FLMUNICODE), &puzNamespaceURI))) { goto Exit; } pszTmp = pszNamespaceURI; puzTmp = puzNamespaceURI; while (*pszTmp) { *puzTmp = (FLMUNICODE)(*pszTmp); pszTmp++; puzTmp++; } *puzTmp = 0; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_ELEMENT_TAG, NULL, pszElementName, TRUE, puzNamespaceURI, puiElementNameId))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } if( puzNamespaceURI) { f_free( &puzNamespaceURI); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getAttributeNameId( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzAttributeName, FLMUINT * puiAttributeNameId) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_ATTRIBUTE_TAG, puzAttributeName, NULL, TRUE, puzNamespaceURI, puiAttributeNameId))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getAttributeNameId( const char * pszNamespaceURI, const char * pszAttributeName, FLMUINT * puiAttributeNameId) { RCODE rc = NE_XFLM_OK; FLMUNICODE * puzNamespaceURI = NULL; FLMUNICODE * puzTmp; const char * pszTmp; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( pszNamespaceURI && *pszNamespaceURI) { if( RC_BAD( rc = f_alloc( (f_strlen( pszNamespaceURI) + 1) * sizeof( FLMUNICODE), &puzNamespaceURI))) { goto Exit; } pszTmp = pszNamespaceURI; puzTmp = puzNamespaceURI; while (*pszTmp) { *puzTmp = (FLMUNICODE)(*pszTmp); pszTmp++; puzTmp++; } *puzTmp = 0; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_ATTRIBUTE_TAG, NULL, pszAttributeName, TRUE, puzNamespaceURI, puiAttributeNameId))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } if( puzNamespaceURI) { f_free( &puzNamespaceURI); } return( rc); } /***************************************************************************** Desc: Get a collection number from collection name. ******************************************************************************/ RCODE FLMAPI F_Db::getCollectionNumber( const char * pszCollectionName, FLMUINT * puiCollectionNumber) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_COLLECTION_TAG, NULL, pszCollectionName, FALSE, NULL, puiCollectionNumber))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: Get a collection number from collection name. ******************************************************************************/ RCODE FLMAPI F_Db::getCollectionNumber( const FLMUNICODE * puzCollectionName, FLMUINT * puiCollectionNumber) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_COLLECTION_TAG, puzCollectionName, NULL, FALSE, NULL, puiCollectionNumber))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: Get an index number from index name. ******************************************************************************/ RCODE FLMAPI F_Db::getIndexNumber( const char * pszIndexName, FLMUINT * puiIndexNumber) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_INDEX_TAG, NULL, pszIndexName, FALSE, NULL, puiIndexNumber))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: Get an index number from index name. ******************************************************************************/ RCODE FLMAPI F_Db::getIndexNumber( const FLMUNICODE * puzIndexName, FLMUINT * puiIndexNumber) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( this, ELM_INDEX_TAG, puzIndexName, NULL, FALSE, NULL, puiIndexNumber))) { goto Exit; } Exit: if( pNameTable) { pNameTable->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::createDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocument, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; if( uiCollection == XFLM_MAINT_COLLECTION) { // Users are not allowed to create documents in the // maintenance collection rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = createRootNode( uiCollection, 0, DOCUMENT_NODE, (F_DOMNode **)ppDocument, pui64NodeId))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::createRootElement( FLMUINT uiCollection, FLMUINT uiNameId, IF_DOMNode ** ppElement, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; if( uiCollection == XFLM_MAINT_COLLECTION) { // Users are not allowed to create documents in the // maintenance collection rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = createRootNode( uiCollection, uiNameId, ELEMENT_NODE, (F_DOMNode **)ppElement, pui64NodeId))) { goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::checkAndUpdateState( eDomNodeType eNodeType, FLMUINT uiNameId) { RCODE rc = NE_XFLM_OK; FLMBOOL bUserDefined = FALSE; FLMUINT uiDictType = 0; F_AttrElmInfo defInfo; // The F_AttrElmInfo constructor should have set the state // to active. flmAssert( defInfo.m_uiState == ATTR_ELM_STATE_ACTIVE); if( eNodeType == ATTRIBUTE_NODE) { uiDictType = ELM_ATTRIBUTE_TAG; if( RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo))) { goto Exit; } bUserDefined = attributeIsUserDefined( uiNameId); } else if( eNodeType == ELEMENT_NODE) { uiDictType = ELM_ELEMENT_TAG; if( RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) { goto Exit; } bUserDefined = elementIsUserDefined( uiNameId); } else if( eNodeType == DATA_NODE) { if( uiNameId) { uiDictType = ELM_ELEMENT_TAG; if( RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) { goto Exit; } bUserDefined = elementIsUserDefined( uiNameId); } } else if( eNodeType != COMMENT_NODE && eNodeType != DOCUMENT_NODE && eNodeType != ANNOTATION_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } if( bUserDefined && defInfo.m_uiState != ATTR_ELM_STATE_ACTIVE) { if( defInfo.m_uiState == ATTR_ELM_STATE_PURGE ) { // Marked as 'purged'. So, user is not allowed to add // new instances of this field. rc = (RCODE)(eNodeType == ELEMENT_NODE ? RC_SET( NE_XFLM_ELEMENT_PURGED) : RC_SET( NE_XFLM_ATTRIBUTE_PURGED)); goto Exit; } else if( defInfo.m_uiState == ATTR_ELM_STATE_CHECKING) { // Because an occurance is being added, update the // state to be 'active' if( RC_BAD( rc = changeItemState( uiDictType, uiNameId, XFLM_ACTIVE_OPTION_STR))) { goto Exit; } } } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::_updateNode( F_CachedNode * pCachedNode, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection = pCachedNode->getCollection(); F_COLLECTION * pCollection; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bAdd = (uiFlags & FLM_UPD_ADD) ? TRUE : FALSE; F_AttrElmInfo defInfo; // Logging should be done at a higher level flmAssert( !m_pDatabase->m_pRfl->isLoggingEnabled()); flmAssert( uiCollection); // Mark the node as being dirty pCachedNode->setNodeDirty( this, bAdd); if( bAdd) { // Get a pointer to the collection if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } // If the nodeId is greater than or equal to the next nodeId, we // will set the next nodeId to 1 greater than the new nodeId to avoid // running into the same nodeId later. // // Node ID should already be set at this point. flmAssert( pCachedNode->getNodeId()); if( pCachedNode->getNodeId() >= pCollection->ui64NextNodeId) { pCollection->ui64NextNodeId = pCachedNode->getNodeId() + 1; pCollection->bNeedToUpdateNodes = TRUE; } bMustAbortOnError = TRUE; } else if( !pCachedNode->getNodeId()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Add the document to the list of documents that need to have // documentDone called at commit time. if( !(uiFlags & FLM_UPD_INTERNAL_CHANGE) && uiCollection == XFLM_DICT_COLLECTION) { if( RC_BAD( rc = m_pDatabase->m_DocumentList.addNode( pCachedNode->getCollection(), pCachedNode->getDocumentId(), 0))) { goto Exit; } } Exit: if( RC_BAD( rc) && bMustAbortOnError) { setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::getCachedBTree( FLMUINT uiCollection, F_Btree ** ppBTree) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( m_pCachedBTree) { flmAssert( m_pCachedBTree->getRefCount() == 1); m_pCachedBTree->btClose(); } else { // Reserve a B-Tree from the pool if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &m_pCachedBTree))) { goto Exit; } } // Set up the btree object if( RC_BAD( rc = m_pCachedBTree->btOpen( this, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } m_pCachedBTree->AddRef(); *ppBTree = m_pCachedBTree; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::flushNode( F_Btree * pBTree, F_CachedNode * pCachedNode) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; FLMUINT64 ui64NodeId = pCachedNode->getNodeId(); FLMBYTE ucKeyBuf[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMUINT uiHeaderStorageSize; F_DynaBuf dynaBuf( m_pDatabase->m_pucUpdBuffer, m_pDatabase->m_uiUpdBufferSize); FLMBOOL bMustAbortOnError = FALSE; IF_PosIStream * pIStream = NULL; FLMBYTE * pucSrc = NULL; FLMBYTE * pucTmp; FLMBOOL bOutputNodeData; FLMBOOL bTruncateOnReplace; FLMUINT32 ui32BlkAddr; FLMUINT uiOffsetIndex; FLMBYTE * pucIV = NULL; FLMUINT uiIVLen = 0; FLMUINT uiEncDefId = pCachedNode->getEncDefId(); eDomNodeType eNodeType = pCachedNode->getNodeType(); FLMUINT uiNodeDataLength; // Node should be dirty flmAssert( pCachedNode->nodeIsDirty()); // Transaction IDs should match if( pCachedNode->getLowTransId() != getTransID()) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } uiNodeDataLength = pCachedNode->m_nodeInfo.uiDataLength; // Output the header if( (pCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER) == 0) { if( RC_BAD( rc = dynaBuf.allocSpace( MAX_DOM_HEADER_SIZE, (void **)&pucTmp))) { goto Exit; } if( RC_BAD( rc = pCachedNode->headerToBuf( FALSE, pucTmp, &uiHeaderStorageSize, NULL, NULL))) { goto Exit; } dynaBuf.truncateData( uiHeaderStorageSize); } else { if( RC_BAD( rc = dynaBuf.allocSpace( FIXED_DOM_HEADER_SIZE, (void **)&pucTmp))) { goto Exit; } if( RC_BAD( rc = pCachedNode->headerToBuf( TRUE, pucTmp, &uiHeaderStorageSize, NULL, NULL))) { goto Exit; } } // Get a pointer to the collection if( RC_BAD( rc = m_pDict->getCollection( pCachedNode->getCollection(), &pCollection))) { goto Exit; } uiKeyLen = sizeof( ucKeyBuf); if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen, ucKeyBuf, FALSE, TRUE))) { goto Exit; } if( eNodeType == ELEMENT_NODE) { FLMUINT uiLoop; NODE_ITEM * pNodeItem; FLMUINT uiNodeCount = pCachedNode->getChildElmCount(); FLMUINT uiTmpOffset; FLMUINT uiPrevNameId = 0; FLMUINT64 ui64ElmNodeId = pCachedNode->getNodeId(); // Go through the child element list and output them to the buffer // Note that the child element node count has already been output // as part of the node header. pNodeItem = pCachedNode->m_pNodeList; for( uiLoop = 0; uiLoop < uiNodeCount; pNodeItem++, uiLoop++) { uiTmpOffset = dynaBuf.getDataLength(); if( RC_BAD( rc = dynaBuf.allocSpace( FLM_MAX_SEN_LEN * 2, (void **)&pucTmp))) { goto Exit; } flmAssert( pNodeItem->uiNameId > uiPrevNameId); flmAssert( pNodeItem->ui64NodeId > ui64ElmNodeId); uiTmpOffset += f_encodeSEN( pNodeItem->uiNameId - uiPrevNameId, &pucTmp); uiTmpOffset += f_encodeSEN( pNodeItem->ui64NodeId - ui64ElmNodeId, &pucTmp); uiPrevNameId = pNodeItem->uiNameId; dynaBuf.truncateData( uiTmpOffset); } // Export any attributes on the element if( pCachedNode->m_uiAttrCount) { if( RC_BAD( rc = pCachedNode->exportAttributeList( this, &dynaBuf, NULL))) { goto Exit; } } } // Set up to output data if( uiNodeDataLength) { if( pCachedNode->getModeFlags() & FDOM_VALUE_ON_DISK) { if( pCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER) { bOutputNodeData = FALSE; bTruncateOnReplace = FALSE; } else { // If the value is on disk and we don't have a fixed-size header, // we'll have to read and output the entire node value. flmAssert( eNodeType != ELEMENT_NODE); if( RC_BAD( rc = pCachedNode->getIStream( this, NULL, &pIStream))) { goto Exit; } bOutputNodeData = TRUE; bTruncateOnReplace = TRUE; } } else { pucSrc = pCachedNode->getDataPtr(); bOutputNodeData = TRUE; bTruncateOnReplace = TRUE; } } else { bOutputNodeData = FALSE; bTruncateOnReplace = TRUE; } if( bOutputNodeData) { FLMUINT uiDataOutputSize = uiNodeDataLength; if( uiEncDefId) { F_ENCDEF * pEncDef; if( RC_BAD( rc = m_pDict->getEncDef( uiEncDefId, &pEncDef))) { goto Exit; } uiIVLen = pEncDef->pCcs->getIVLen(); flmAssert( uiIVLen == 8 || uiIVLen == 16); if( RC_BAD( rc = dynaBuf.allocSpace( uiIVLen, (void **)&pucIV))) { goto Exit; } if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, pucIV))) { goto Exit; } uiDataOutputSize = getEncLen( uiNodeDataLength); } if( RC_BAD( rc = dynaBuf.allocSpace( uiDataOutputSize, (void **)&pucTmp))) { goto Exit; } if( pIStream) { if( RC_BAD( rc = pIStream->read( pucTmp, uiNodeDataLength, NULL))) { goto Exit; } } else { f_memcpy( pucTmp, pucSrc, uiNodeDataLength); } if( uiEncDefId) { if( RC_BAD( rc = encryptData( uiEncDefId, pucIV, pucTmp, uiDataOutputSize, uiNodeDataLength, &uiDataOutputSize))) { goto Exit; } } } ui32BlkAddr = pCachedNode->getBlkAddr(); uiOffsetIndex = pCachedNode->getOffsetIndex(); if( pCachedNode->nodeIsNew()) { // If this is a new node, the value will not be on disk. // This routine is only called for values that we are // keeping in memory. If we stream a value out through // multiple buffers, it is written right away and the // node's dirty and new flags will be unset as soon as // the writing is done. if( pCachedNode->getModeFlags() & FDOM_VALUE_ON_DISK) { // This shouldn't happen rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } if( RC_BAD( rc = pBTree->btInsertEntry( ucKeyBuf, uiKeyLen, dynaBuf.getBufferPtr(), dynaBuf.getDataLength(), TRUE, TRUE, &ui32BlkAddr, &uiOffsetIndex))) { if( rc == NE_XFLM_NOT_UNIQUE) { rc = RC_SET_AND_ASSERT( NE_XFLM_EXISTS); } goto Exit; } } else { // Replace the node on disk. if( RC_BAD( rc = pBTree->btReplaceEntry( ucKeyBuf, uiKeyLen, dynaBuf.getBufferPtr(), dynaBuf.getDataLength(), TRUE, TRUE, bTruncateOnReplace, &ui32BlkAddr, &uiOffsetIndex))) { goto Exit; } } pCachedNode->setBlkAddr( ui32BlkAddr); pCachedNode->setOffsetIndex( uiOffsetIndex); // Clear the dirty flag and the new flag. pCachedNode->unsetNodeDirtyAndNew( this); Exit: if( pIStream) { pIStream->Release(); } if( RC_BAD( rc) && bMustAbortOnError) { setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::encryptData( FLMUINT uiEncDefId, FLMBYTE * pucIV, FLMBYTE * pucBuffer, FLMUINT uiBufferSize, FLMUINT uiDataLen, FLMUINT * puiOutputLength) { RCODE rc = NE_XFLM_OK; F_Dict * pDict; F_ENCDEF * pEncDef; FLMUINT uiEncLen; FLMUINT uiTmpLen; FLMUINT uiEncBuffLen; FLMUINT uiDataToEncrypt = uiDataLen; FLMUINT uiChunkLen; FLMBYTE * pucEncTmp; FLMBYTE ucEncryptBuffer[ FLM_ENCRYPT_CHUNK_SIZE]; if( m_pDatabase->m_bInLimitedMode) { *puiOutputLength = uiDataLen; rc = RC_SET( m_pDatabase->m_rcLimitedCode); goto Exit; } // Need to retrieve the encryption key. if( RC_BAD( rc = getDictionary( &pDict))) { goto Exit; } if( RC_BAD( rc = pDict->getEncDef( uiEncDefId, &pEncDef))) { goto Exit; } flmAssert( pEncDef); flmAssert( pEncDef->pCcs); // Check first to make sure we wil be able to encrypt the entire buffer // since we must return the encrypted data in the source buffer. uiEncLen = getEncLen( uiDataLen); if( uiEncLen > uiBufferSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Encrypt the buffer in chunks uiEncBuffLen = 0; pucEncTmp = &ucEncryptBuffer[0]; while( uiDataToEncrypt) { FLMUINT uiDataEncrypted; uiDataEncrypted = uiChunkLen = ((uiDataToEncrypt > FLM_ENCRYPT_CHUNK_SIZE) ? FLM_ENCRYPT_CHUNK_SIZE : uiDataToEncrypt); if( extraEncBytes( uiChunkLen) != 0) { // If we are padding, we *MUST* be on the last piece to encrypt! flmAssert( uiChunkLen == uiDataToEncrypt); uiChunkLen += (ENCRYPT_MIN_CHUNK_SIZE - extraEncBytes( uiChunkLen)); } flmAssert( uiChunkLen <= FLM_ENCRYPT_CHUNK_SIZE); uiTmpLen = uiChunkLen; if( RC_BAD( rc = pEncDef->pCcs->encryptToStore( pucBuffer, uiChunkLen, pucEncTmp, &uiTmpLen, pucIV))) { goto Exit; } flmAssert( uiTmpLen == uiChunkLen); f_memcpy( pucBuffer, pucEncTmp, uiChunkLen); pucBuffer += uiChunkLen; uiDataToEncrypt -= uiDataEncrypted; uiEncBuffLen += uiChunkLen; flmAssert( uiEncBuffLen <= uiEncLen); } flmAssert( uiEncBuffLen == uiEncLen); *puiOutputLength = uiEncLen; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::createRootNode( FLMUINT uiCollection, FLMUINT uiElementNameId, eDomNodeType eNodeType, F_DOMNode ** ppNewNode, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; F_Rfl * pRfl = m_pDatabase->m_pRfl; F_DOMNode * pPrevSib = NULL; F_DOMNode * pNewNode = NULL; F_CachedNode * pCachedNode; F_COLLECTION * pCollection; FLMBOOL bMustAbortOnError = FALSE; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( eNodeType != ELEMENT_NODE && eNodeType != DOCUMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // If a specific node Id is requested, check to make sure it is not // already in use. if( pui64NodeId) { if( *pui64NodeId && (m_uiFlags & FDB_REBUILDING_DATABASE)) { if( RC_OK( rc = getNode( uiCollection, *pui64NodeId, &pNewNode))) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } else if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } else { goto Exit; } } else { // Set the value to zero so we don't use it. We will // return the new nodeId. *pui64NodeId = 0; } } bMustAbortOnError = TRUE; // Check the state if this is a root element if( eNodeType == ELEMENT_NODE) { if( RC_BAD( rc = checkAndUpdateState( ELEMENT_NODE, uiElementNameId))) { goto Exit; } } // Disable RFL logging pRfl->disableLogging( &uiRflToken); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( this, uiCollection, (FLMUINT64)(pui64NodeId ? *pui64NodeId : (FLMUINT64)0), &pNewNode))) { goto Exit; } // Clone the dictionary since the first/last document ID // values of the collection will be changed if( !(m_uiFlags & FDB_UPDATED_DICTIONARY)) { if( RC_BAD( rc = dictClone())) { goto Exit; } } // Get a pointer to the collection if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } pCachedNode = pNewNode->m_pCachedNode; if( !pCollection->ui64FirstDocId) { pCollection->ui64FirstDocId = pCollection->ui64NextNodeId; pCollection->ui64LastDocId = pCollection->ui64NextNodeId; } else { pCachedNode->setPrevSibId( pCollection->ui64LastDocId); pCollection->ui64LastDocId = pCollection->ui64NextNodeId; } pCollection->bNeedToUpdateNodes = TRUE; if( eNodeType == ELEMENT_NODE) { F_AttrElmInfo elmInfo; if( RC_BAD( rc = m_pDict->getElement( this, uiElementNameId, &elmInfo))) { goto Exit; } pCachedNode->setNameId( uiElementNameId); pCachedNode->setDataType( elmInfo.m_uiDataType); // Is this a node whose child elements must all be unique? if( elmInfo.m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) { flmAssert( elmInfo.m_uiDataType == XFLM_NODATA_TYPE); pCachedNode->setFlags( FDOM_HAVE_CELM_LIST); } } else { pCachedNode->setDataType( XFLM_NODATA_TYPE); } pCachedNode->setNodeType( eNodeType); pCachedNode->setDocumentId( pCachedNode->getNodeId()); // Link the document into the document list bMustAbortOnError = TRUE; if( pui64NodeId && *pui64NodeId) { pCollection->ui64LastDocId = *pui64NodeId; pCollection->bNeedToUpdateNodes = TRUE; } // Output the node if( RC_BAD( rc = updateNode( pCachedNode, FLM_UPD_ADD))) { goto Exit; } // Retrieve the previous sibling and set its next sibling // value if( pCachedNode->getPrevSibId()) { if( RC_BAD( rc = getNode( uiCollection, pCachedNode->getPrevSibId(), &pPrevSib))) { goto Exit; } if( RC_BAD( rc = pPrevSib->makeWriteCopy( this))) { goto Exit; } pPrevSib->setNextSibId( pCachedNode->getNodeId()); if( RC_BAD( rc = updateNode( pPrevSib->m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) { goto Exit; } } // Check indexing if it is an element if( eNodeType == ELEMENT_NODE) { if( RC_BAD( rc = updateIndexKeys( uiCollection, pNewNode, IX_ADD_NODE_VALUE, TRUE))) { goto Exit; } } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logNodeCreate( this, pCachedNode->getCollection(), pCachedNode->getNodeId(), eNodeType, uiElementNameId, XFLM_ROOT, pNewNode->getNodeId()))) { goto Exit; } if( pui64NodeId) { *pui64NodeId = pCachedNode->getNodeId(); } if( ppNewNode) { if( *ppNewNode) { (*ppNewNode)->Release(); } *ppNewNode = pNewNode; pNewNode = NULL; } Exit: if( pNewNode) { pNewNode->Release(); } if( pPrevSib) { pPrevSib->Release(); } if( RC_BAD( rc) && bMustAbortOnError) { setMustAbortTrans( rc); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::findNode( FLMUINT uiCollection, FLMUINT64 * pui64NodeId, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; F_BTreeIStream btreeIStream; // Determine the node's B-Tree address if( RC_BAD( rc = getCachedBTree( uiCollection, &pBTree))) { goto Exit; } // At this point, we know that uiFlags is NOT XFLM_EXACT, but is // XFLM_INCL, XFLM_EXCL, XFLM_FIRST, or XFLM_LAST. So we will // need to reassign ui64NodeId once we have located the node. if( RC_BAD( rc = btreeIStream.openStream( this, pBTree, uiFlags, uiCollection, *pui64NodeId, 0, 0))) { goto Exit; } *pui64NodeId = btreeIStream.m_ui64NodeId; // Close the input stream btreeIStream.closeStream(); Exit: if( pBTree) { pBTree->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::getNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiFlags, F_DOMNode ** ppNode) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } // Quick check to see if the user is re-retrieving the same // node that is currently pointed to by pNode if( uiFlags == XFLM_EXACT) { F_DOMNode * pTmpNode; if( ((pTmpNode = *ppNode) != NULL) && pTmpNode->m_pCachedNode && pTmpNode->m_uiAttrNameId == 0) { if( pTmpNode->getNodeId() == ui64NodeId && pTmpNode->getCollection() == uiCollection && pTmpNode->getDatabase() == m_pDatabase) { if( RC_BAD( rc = pTmpNode->syncFromDb( this))) { if( rc == NE_XFLM_DOM_NODE_DELETED) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } } goto Exit; } } } else { if( getTransType() == XFLM_UPDATE_TRANS) { // Need to flush dirty nodes through to the B-Tree so that // look-ups will work correctly if( RC_BAD( rc = flushDirtyNodes())) { goto Exit; } } if( RC_BAD( rc = findNode( uiCollection, &ui64NodeId, uiFlags))) { goto Exit; } } // Retrieve the node into cache. if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->retrieveNode( this, uiCollection, ui64NodeId, &pNode))) { goto Exit; } if( *ppNode) { (*ppNode)->Release(); } *ppNode = pNode; pNode = NULL; Exit: if( pNode) { pNode->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getAttribute( FLMUINT uiCollection, FLMUINT64 ui64ElementId, FLMUINT uiAttrName, IF_DOMNode ** ifppAttr) { RCODE rc = NE_XFLM_OK; F_DOMNode * pElementNode = NULL; F_DOMNode * pAttrNode = NULL; F_AttrItem * pAttrItem; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = getNode( uiCollection, ui64ElementId, XFLM_EXACT, &pElementNode))) { goto Exit; } if( pElementNode->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( (pAttrItem = pElementNode->m_pCachedNode->getAttribute( uiAttrName, NULL)) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttrNode->m_pCachedNode = pElementNode->m_pCachedNode; pAttrNode->m_pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; if( ifppAttr) { if( *ifppAttr) { (*ifppAttr)->Release(); } *ifppAttr = (IF_DOMNode *)pAttrNode; pAttrNode = NULL; } Exit: if( pAttrNode) { pAttrNode->Release(); } if( pElementNode) { pElementNode->Release(); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getFirstDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( !pCollection->ui64FirstDocId) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64FirstDocId, ppDocumentNode))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getLastDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( !pCollection->ui64LastDocId) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64LastDocId, ppDocumentNode))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE FLMAPI F_Db::getDocument( FLMUINT uiCollection, FLMUINT uiFlags, FLMUINT64 ui64DocumentId, IF_DOMNode ** ppDocumentNode) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; FLMBOOL bStartedTrans = FALSE; F_Btree * pBTree = NULL; F_DOMNode * pNode = NULL; FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMBOOL bNeg; FLMUINT uiBytesProcessed; FLMUINT64 ui64NodeId; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } switch (uiFlags) { case XFLM_FIRST: { if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64FirstDocId, ppDocumentNode))) { goto Exit; } break; } case XFLM_LAST: { if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64LastDocId, ppDocumentNode))) { goto Exit; } break; } case XFLM_EXACT: { if( RC_BAD( rc = getNode( uiCollection, ui64DocumentId, &pNode))) { goto Exit; } if( !pNode->isRootNode()) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( *ppDocumentNode) { (*ppDocumentNode)->Release(); } // No need to do an AddRef on ppDocumentNode - just use the reference // from pNode and set pNode to NULL so it won't be released *ppDocumentNode = pNode; pNode = NULL; break; } case XFLM_INCL: case XFLM_EXCL: { if( getTransType() == XFLM_UPDATE_TRANS) { // Need to flush dirty nodes through to the B-Tree so that // look-ups will work correctly if( RC_BAD( rc = flushDirtyNodes())) { goto Exit; } } // Get a btree if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) { goto Exit; } if( RC_BAD( rc = pBTree->btOpen( this, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( ui64DocumentId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pBTree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } // Make sure we hit a root node. If not, continue reading until we do // or until we hit the end. Root nodes are always linked together in // ascending order, so if there is another document, we will find it // simply by searching forward from where we are. Then we can follow // document links. for (;;) { if( RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64NodeId, &bNeg, &uiBytesProcessed))) { goto Exit; } if( RC_BAD( rc = getNode( uiCollection, ui64NodeId, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Better be able to find the node at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } // If the node is a root node, we have a document we can // process. if( pNode->isRootNode()) { if( uiFlags == XFLM_EXCL && ui64NodeId == ui64DocumentId) { if( RC_BAD( rc = pNode->getNextDocument( this, ppDocumentNode))) { goto Exit; } } else { if( *ppDocumentNode) { (*ppDocumentNode)->Release(); } // No need to do an AddRef on ppDocumentNode - just use the reference // from pNode and set pNode to NULL so it won't be released *ppDocumentNode = pNode; pNode = NULL; } break; } // Need to go to the next node. if( RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); } goto Exit; } } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } } Exit: if( pNode) { pNode->Release(); } if( pBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pBTree); } if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: Notes: This routine assumes that the node has been unlinked. It doesn't perform any checks to guarantee that removing the node won't cause referential integrity problems. ******************************************************************************/ RCODE F_Db::purgeNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; FLMUINT uiKeyLen; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; // Make sure we're in an update transaction if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // Remove the node from the collection if( RC_BAD( rc = getCachedBTree( uiCollection, &pBTree))) { goto Exit; } uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } bMustAbortOnError = TRUE; if( RC_BAD( rc = pBTree->btRemoveEntry( ucKey, uiKeyLen))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } // Item may not have been flushed from cache yet rc = NE_XFLM_OK; } // Remove the node from the cache gv_XFlmSysData.pNodeCacheMgr->removeNode( this, uiCollection, ui64NodeId); Exit: if( pBTree) { pBTree->Release(); } if( RC_BAD( rc)) { if( bMustAbortOnError) { setMustAbortTrans( rc); } } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::documentDone( FLMUINT uiCollection, FLMUINT64 ui64DocId) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; F_Rfl * pRfl = m_pDatabase->m_pRfl; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( !m_pDatabase->m_DocumentList.isNodeInList( uiCollection, ui64DocId, 0)) { goto Exit; } pRfl->disableLogging( &uiRflToken); if( uiCollection == XFLM_DICT_COLLECTION) { FLMUINT uiDictDefType; if( RC_BAD( rc = dictDocumentDone( ui64DocId, FALSE, &uiDictDefType))) { goto Exit; } // If this is an encryption definition, we need to log the encryption // key if( uiDictDefType == ELM_ENCDEF_TAG && !(m_uiFlags & FDB_REPLAYING_RFL)) { FLMBYTE ucDynaBuf[ 64]; F_DynaBuf keyBuffer( ucDynaBuf, sizeof( ucDynaBuf)); FLMUINT uiKeySize; if( RC_BAD( rc = getNode( uiCollection, ui64DocId, XFLM_EXACT, &pNode))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } if( RC_BAD( rc = pNode->getAttributeValueBinary( this, ATTR_ENCRYPTION_KEY_TAG, &keyBuffer))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } if( RC_BAD( rc = pNode->getAttributeValueUINT( this, ATTR_ENCRYPTION_KEY_SIZE_TAG, &uiKeySize))) { goto Exit; } pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logEncDefKey( this, (FLMUINT)ui64DocId, keyBuffer.getBufferPtr(), keyBuffer.getDataLength(), uiKeySize))) { goto Exit; } pRfl->disableLogging( &uiRflToken); } } m_pDatabase->m_DocumentList.removeNode( uiCollection, ui64DocId, 0); pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = pRfl->logDocumentDone( this, uiCollection, ui64DocId))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::documentDone( IF_DOMNode * pDocNode) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiCollection; FLMUINT64 ui64DocId; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = pDocNode->getCollection( this, &uiCollection))) { goto Exit; } if( RC_BAD( rc = pDocNode->getDocumentId( this, &ui64DocId))) { goto Exit; } if( RC_BAD( rc = documentDone( uiCollection, ui64DocId))) { goto Exit; } Exit: if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::import( IF_IStream * ifpStream, FLMUINT uiCollection, IF_DOMNode * pNodeToLinkTo, eNodeInsertLoc eInsertLoc, XFLM_IMPORT_STATS * pImportStats) { RCODE rc = NE_XFLM_OK; F_XMLImport xmlImport; F_DOMNode * pNode = NULL; F_DOMNode * pNewNode = NULL; if( RC_BAD( rc = xmlImport.setup())) { goto Exit; } if( (pNode = (F_DOMNode *)pNodeToLinkTo) != NULL) { pNode->AddRef(); } for( ;;) { if( RC_BAD( rc = xmlImport.import( ifpStream, this, uiCollection, FLM_XML_COMPRESS_WHITESPACE_FLAG | FLM_XML_TRANSLATE_ESC_FLAG | FLM_XML_EXTEND_DICT_FLAG, pNode, eInsertLoc, &pNewNode, pImportStats))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } goto Exit; } if( RC_BAD( rc = documentDone( pNewNode))) { goto Exit; } // If pNode is NULL, we are creating separate documents // and pNode should remain NULL. Otherwise, it should be // set to the newly created node, and all subsequent trees // should be linked as next siblings after this one. if( pNode) { pNode->Release(); pNode = pNewNode; // No need to do pNode->AddRef() and pNewNode->Release() since // there is already a reference on pNewNode. pNewNode = NULL; eInsertLoc = XFLM_NEXT_SIB; } else { pNewNode->Release(); pNewNode = NULL; } xmlImport.reset(); } Exit: if( pNode) { pNode->Release(); } if( pNewNode) { pNewNode->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::importDocument( IF_IStream * ifpStream, FLMUINT uiCollection, IF_DOMNode ** ppDoc, XFLM_IMPORT_STATS * pImportStats) { RCODE rc = NE_XFLM_OK; F_XMLImport xmlImport; F_DOMNode * pNode = NULL; if( pNode) { pNode->AddRef(); } if( RC_BAD( rc = xmlImport.setup())) { goto Exit; } if( RC_BAD( rc = xmlImport.import( ifpStream, this, uiCollection, FLM_XML_COMPRESS_WHITESPACE_FLAG | FLM_XML_TRANSLATE_ESC_FLAG | FLM_XML_EXTEND_DICT_FLAG, NULL, XFLM_LAST_CHILD, &pNode, pImportStats))) { goto Exit; } if( RC_BAD( rc = documentDone( pNode))) { goto Exit; } if( ppDoc) { if( *ppDoc) { (*ppDoc)->Release(); } *ppDoc = pNode; pNode = NULL; } Exit: if( pNode) { pNode->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::createElemOrAttrDef( FLMBOOL bElement, FLMBOOL bUnicode, const void * pvNamespaceURI, const void * pvLocalName, FLMUINT uiDataType, FLMBOOL bUniqueChildElms, FLMUINT * puiNameId, F_DOMNode ** ppDocumentNode) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDoc = NULL; F_DOMNode * pAttr = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, bElement ? ELM_ELEMENT_TAG : ELM_ATTRIBUTE_TAG, ELEMENT_NODE, &pDoc))) { goto Exit; } if( pvNamespaceURI) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_TARGET_NAMESPACE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( bUnicode) { if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvNamespaceURI))) { goto Exit; } } else { if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvNamespaceURI))) { goto Exit; } } } if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( bUnicode) { if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvLocalName))) { goto Exit; } } else { if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvLocalName))) { goto Exit; } } if( puiNameId && *puiNameId) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( this, *puiNameId))) { goto Exit; } } if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_TYPE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUTF8( this, (const FLMBYTE *)fdictGetDataTypeStr( uiDataType)))) { goto Exit; } if( bUniqueChildElms && bElement) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_UNIQUE_SUB_ELEMENTS_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)"true"))) { goto Exit; } } if( RC_BAD( rc = documentDone( pDoc))) { goto Exit; } if( puiNameId) { if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->getUINT( this, puiNameId))) { goto Exit; } } if( ppDocumentNode) { if( (*ppDocumentNode)) { (*ppDocumentNode)->Release(); } *ppDocumentNode = pDoc; pDoc = NULL; } Exit: if( pDoc) { pDoc->Release(); } if( pAttr) { flmAssert( pAttr->m_refCnt <= 2); pAttr->Release(); } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } else if( RC_BAD( rc)) { setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::createPrefixDef( FLMBOOL bUnicode, const void * pvPrefixName, FLMUINT * puiPrefixNumber) { F_DOMNode * pDoc = NULL; F_DOMNode * pAttr = NULL; F_DOMNode * pNumAttr = NULL; RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, ELM_PREFIX_TAG, ELEMENT_NODE, &pDoc))) { goto Exit; } if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( bUnicode) { if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvPrefixName))) { goto Exit; } } else { if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvPrefixName))) { goto Exit; } } // Create the prefix number attribute if passed in. if( puiPrefixNumber && *puiPrefixNumber) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pNumAttr))) { goto Exit; } if( RC_BAD( rc = pNumAttr->setUINT( this, *puiPrefixNumber))) { goto Exit; } } if( RC_BAD( rc = documentDone( pDoc))) { goto Exit; } // Change the modes on the definition so that the name cannot be modified // and the definition cannot be deleted. This needs to be done after // calling documentDone() because it sets the flags on the nodes of the // definition according to a set of default rules that do not correspond // to what we need to have in this case. if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( puiPrefixNumber) { if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pNumAttr))) { goto Exit; } if( RC_BAD( rc = pNumAttr->getUINT( this, puiPrefixNumber))) { goto Exit; } } Exit: if( pDoc) { pDoc->Release(); } if( pAttr) { pAttr->Release(); } if( pNumAttr) { pNumAttr->Release(); } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } else if( RC_BAD( rc)) { setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::createEncDef( FLMBOOL bUnicode, const void * pvEncType, const void * pvEncName, FLMUINT uiKeySize, FLMUINT * puiEncDefNumber) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDoc = NULL; F_DOMNode * pAttr = NULL; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, ELM_ENCDEF_TAG, ELEMENT_NODE, &pDoc))) { goto Exit; } if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( bUnicode) { if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvEncName))) { goto Exit; } } else { if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvEncName))) { goto Exit; } } // Create the encdef id attribute if passed in. if( puiEncDefNumber && *puiEncDefNumber) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( this, *puiEncDefNumber))) { goto Exit; } } // Create the algorithm attribute if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_TYPE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( bUnicode) { if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvEncType))) { goto Exit; } } else { if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvEncType))) { goto Exit; } } // Create the key size attribute if( uiKeySize) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_ENCRYPTION_KEY_SIZE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( this, uiKeySize))) { goto Exit; } } // Call documentDone to complete it all. if( RC_BAD( rc = documentDone( pDoc))) { goto Exit; } if( puiEncDefNumber) { if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->getUINT( this, puiEncDefNumber))) { goto Exit; } } Exit: if( pDoc) { pDoc->Release(); } if( pAttr) { pAttr->Release(); } if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } else if( RC_BAD( rc)) { setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_Db::getPrefixId( const FLMUNICODE * puzPrefixName, FLMUINT * puiPrefixNumber) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getPrefixId( this, puzPrefixName, puiPrefixNumber))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_Db::getPrefixId( const char * pszPrefixName, FLMUINT * puiPrefixNumber) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getPrefixId( this, pszPrefixName, puiPrefixNumber))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_Db::getEncDefId( const char * pszEncDefName, FLMUINT * puiEncDefNumber) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getEncDefId( this, pszEncDefName, puiEncDefNumber))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI F_Db::getEncDefId( const FLMUNICODE * puzEncDefName, FLMUINT * puiEncDefNumber) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) { goto Exit; } if( RC_BAD( rc = m_pDict->getEncDefId( this, puzEncDefName, puiEncDefNumber))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } return( rc); } /***************************************************************************** Desc: Create a new c ollection definition in the dictionary. ******************************************************************************/ RCODE F_Db::createCollectionDef( FLMBOOL bUnicode, const void * pvCollectionName, FLMUINT * puiCollectionNumber, FLMUINT uiEncNumber) { F_DOMNode * pDoc = NULL; F_DOMNode * pAttr = NULL; RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) { goto Exit; } // First, create the root element to hold the collection definition if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, ELM_COLLECTION_TAG, ELEMENT_NODE, &pDoc))) { goto Exit; } // Create the collection name attruibute if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( bUnicode) { if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvCollectionName))) { goto Exit; } } else { if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvCollectionName))) { goto Exit; } } // Create the collection number attribute if passed in. if( puiCollectionNumber && *puiCollectionNumber) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( this, *puiCollectionNumber))) { goto Exit; } } if( uiEncNumber) { if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_ENCRYPTION_ID_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( this, uiEncNumber))) { goto Exit; } } if( RC_BAD( rc = documentDone( pDoc))) { goto Exit; } if( puiCollectionNumber) { if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->getUINT( this, puiCollectionNumber))) { goto Exit; } } Exit: if( bStartedTrans) { if( RC_BAD( rc)) { transAbort(); } else { rc = transCommit(); } } if( pDoc) { pDoc->Release(); } if( pAttr) { pAttr->Release(); } if( RC_BAD( rc)) { setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::flushDirtyNodes( void) { RCODE rc = NE_XFLM_OK; F_CachedNode * pNode; F_Btree * pBtree = NULL; FLMUINT uiRflToken = 0; FLMUINT uiCollection = 0; if( !m_uiDirtyNodeCount) { goto Exit; } // Disable RFL logging m_pDatabase->m_pRfl->disableLogging( &uiRflToken); // All of the dirty nodes should be at the front of the list. f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); while ((pNode = m_pDatabase->m_pFirstNode) != NULL) { if( !pNode->nodeIsDirty()) { break; } // Flushing the node should remove it from the front of the list. // Need to increment the use count on the node to prevent it from // being moved while we are flushing it to disk. pNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); if( uiCollection != pNode->getCollection()) { if( pBtree) { pBtree->Release(); } uiCollection = pNode->getCollection(); if( RC_BAD( rc = getCachedBTree( uiCollection, &pBtree))) { goto Exit; } } rc = flushNode( pBtree, pNode); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pNode->decrNodeUseCount(); if( rc == NE_XFLM_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } if( RC_BAD( rc)) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); flmAssert( !m_uiDirtyNodeCount); Exit: if( uiRflToken) { m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } if( pBtree) { pBtree->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Db::flushDirtyNode( F_CachedNode * pNode) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; FLMUINT uiRflToken = 0; F_Btree * pBTree = NULL; m_pDatabase->m_pRfl->disableLogging( &uiRflToken); if( !pNode->nodeIsDirty()) { goto Exit; } // Get a B-Tree object if( RC_BAD( rc = getCachedBTree( pNode->getCollection(), &pBTree))) { goto Exit; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; pNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = FALSE; rc = flushNode( pBTree, pNode); f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; pNode->decrNodeUseCount(); if( RC_BAD( rc)) { goto Exit; } Exit: if( uiRflToken) { m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } if( pBTree) { pBTree->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_Database::startPendingInput( FLMUINT uiPendingType, F_CachedNode * pPendingNode) { RCODE rc = NE_XFLM_OK; if( m_pPendingInput) { rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); goto Exit; } // Not supported on element nodes if( pPendingNode->getNodeType() == ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } m_uiPendingType = uiPendingType; m_pPendingInput = pPendingNode; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); m_pPendingInput->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); m_uiUpdCharCount = 0; m_bUpdFirstBuf = TRUE; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ void F_Database::endPendingInput( void) { if( m_pPendingInput) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); m_pPendingInput->decrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); m_pPendingInput = NULL; } if( m_pPendingBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pPendingBTree); m_pPendingBTree = NULL; } m_uiPendingType = XFLM_NODATA_TYPE; m_bUpdFirstBuf = TRUE; m_uiUpdByteCount = 0; m_uiUpdCharCount = 0; } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::createAttribute( F_Db * pDb, FLMUINT uiAttrNameId, F_AttrItem ** ppAttrItem) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem = NULL; FLMUINT uiInsertPos; // Attribute should not exist if (getAttribute( uiAttrNameId, &uiInsertPos) != NULL) { flmAssert( 0); } else { // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); // Allocate the new attribute if( RC_BAD( rc = allocAttribute( pDb, uiAttrNameId, NULL, uiInsertPos, &pAttrItem, FALSE))) { goto Exit; } } Exit: if( ppAttrItem) { *ppAttrItem = pAttrItem; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ F_AttrItem * F_CachedNode::getAttribute( FLMUINT uiAttrNameId, FLMUINT * puiInsertPos) { FLMUINT uiLoop; F_AttrItem * pAttrItem = NULL; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblNameId; if (!m_uiAttrCount) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } // If the attribute count is <= 4, do a sequential search through // the array. Otherwise, do a binary search. if ((uiTblSize = m_uiAttrCount) <= 4) { for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) { pAttrItem = m_ppAttrList [uiLoop]; if (pAttrItem->m_uiNameId == uiAttrNameId) { break; } else if (pAttrItem->m_uiNameId > uiAttrNameId) { pAttrItem = NULL; break; } } if (uiLoop == m_uiAttrCount) { pAttrItem = NULL; } if (puiInsertPos) { *puiInsertPos = uiLoop; } } else { uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblNameId = m_ppAttrList [uiMid]->m_uiNameId; if (uiTblNameId == uiAttrNameId) { // Found Match if (puiInsertPos) { *puiInsertPos = uiMid; } pAttrItem = m_ppAttrList [uiMid]; goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (uiAttrNameId < uiTblNameId) ? uiMid : uiMid + 1; } goto Exit; } if (uiAttrNameId < uiTblNameId) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } } Exit: return( pAttrItem); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::getPrevSiblingNode( FLMUINT uiCurrentNameId, IF_DOMNode ** ppSib) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; F_DOMNode * pAttr = NULL; if( (pAttrItem = getPrevSibling( uiCurrentNameId)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttr))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttr->m_uiAttrNameId = pAttrItem->m_uiNameId; pAttr->m_pCachedNode = this; incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); if( ppSib) { if( *ppSib) { (*ppSib)->Release(); } *ppSib = (IF_DOMNode *)pAttr; pAttr = NULL; } Exit: if( pAttr) { pAttr->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::getNextSiblingNode( FLMUINT uiCurrentNameId, IF_DOMNode ** ppSib) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; F_DOMNode * pAttr = NULL; if( (pAttrItem = getNextSibling( uiCurrentNameId)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttr))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pAttr->m_uiAttrNameId = pAttrItem->m_uiNameId; pAttr->m_pCachedNode = this; incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); if( ppSib) { if( *ppSib) { (*ppSib)->Release(); } *ppSib = (IF_DOMNode *)pAttr; pAttr = NULL; } Exit: if( pAttr) { pAttr->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::allocAttribute( F_Db * pDb, FLMUINT uiAttrNameId, F_AttrItem * pCopyFromItem, FLMUINT uiInsertPos, F_AttrItem ** ppAttrItem, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; F_AttrElmInfo defInfo; F_AttrItem * pAttrItem = NULL; FLMUINT uiSize; FLMBOOL bMutexLocked = FALSE; if( RC_BAD( rc = pDb->m_pDict->getAttribute( pDb, uiAttrNameId, &defInfo))) { flmAssert( 0); goto Exit; } if( !bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; } if( (pAttrItem = new F_AttrItem) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( pCopyFromItem) { pAttrItem->copyFrom( pCopyFromItem); } else { if( defInfo.getFlags() & ATTR_ELM_NS_DECL) { pAttrItem->m_uiFlags |= FDOM_NAMESPACE_DECL; } } // Find where this thing goes in the attribute list // If we are copying, we don't need to resize the list as it will // already be the correct size. if (!pCopyFromItem) { if (RC_BAD( rc = resizeAttrList( m_uiAttrCount + 1, bMutexLocked))) { goto Exit; } // NOTE: m_uiAttrCount will have been incremented to accommodate // the new attribute. // Move everything above the [uiInsertPos] slot up. Remember, no need to // preserve the item at [m_uiAttrCount - 1], because we just increased // the size of the array, and there is nothing there right now. if (uiInsertPos < m_uiAttrCount - 1) { f_memmove( &m_ppAttrList [uiInsertPos + 1], &m_ppAttrList [uiInsertPos], sizeof( F_AttrItem *) * (m_uiAttrCount - uiInsertPos - 1)); } } m_ppAttrList [uiInsertPos] = pAttrItem; pAttrItem->m_pCachedNode = this; pAttrItem->m_uiDataType = defInfo.getDataType(); pAttrItem->m_uiNameId = uiAttrNameId; *ppAttrItem = pAttrItem; uiSize = pAttrItem->memSize(); m_uiTotalAttrSize += uiSize; if (m_ui64HighTransId != FLM_MAX_UINT64) { gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; } gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize; // Set to NULL so it won't get deleted below. pAttrItem = NULL; Exit: if (pAttrItem) { delete pAttrItem; } if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::freeAttribute( F_AttrItem * pAttrItem, FLMUINT uiPos) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; // Move everything above the [uiPos] slot down. if (uiPos < m_uiAttrCount - 1) { f_memmove( &m_ppAttrList [uiPos], &m_ppAttrList [uiPos + 1], sizeof( F_AttrItem *) * (m_uiAttrCount - uiPos - 1)); } f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; if (RC_BAD( rc = resizeAttrList( m_uiAttrCount - 1, bMutexLocked))) { goto Exit; } delete pAttrItem; Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::setNumber64( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 ui64Value, FLMBOOL bNeg, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; FLMUINT uiValLen = 0; FLMUINT uiEncryptedLen; FLMBYTE ucNumBuf[ 32]; // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); // Get a pointer to the attribute list item if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) { goto Exit; } } else { if( pAttrItem->m_uiFlags & FDOM_READ_ONLY) { rc = RC_SET( NE_XFLM_READ_ONLY); goto Exit; } pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); } pAttrItem->m_ui64QuickVal = ui64Value; if( bNeg) { pAttrItem->m_uiFlags |= FDOM_SIGNED_QUICK_VAL; } else { pAttrItem->m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL; } switch( pAttrItem->m_uiDataType) { case XFLM_NUMBER_TYPE: { if( ui64Value <= 0x7F && !uiEncDefId) { if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, 0, 1, FALSE, FALSE))) { goto Exit; } *(pAttrItem->getAttrDataPtr()) = (FLMBYTE)ui64Value; goto Exit; } else { uiValLen = sizeof( ucNumBuf); if( RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiValLen, ucNumBuf, bNeg, FALSE))) { goto Exit; } } break; } case XFLM_TEXT_TYPE: { FLMBYTE * pucSen; FLMUINT uiSenLen; if( bNeg) { ucNumBuf[ 1] = '-'; f_ui64toa( ui64Value, (char *)&ucNumBuf[ 2]); } else { f_ui64toa( ui64Value, (char *)&ucNumBuf[ 1]); } uiValLen = f_strlen( (const char *)&ucNumBuf[ 1]); pucSen = &ucNumBuf [0]; uiSenLen = f_encodeSEN( uiValLen, &pucSen, (FLMUINT)0); flmAssert( uiSenLen == 1); uiValLen += uiSenLen + 1; break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); goto Exit; } } if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, uiValLen, TRUE, FALSE))) { goto Exit; } if( uiValLen) { f_memcpy( pAttrItem->getAttrDataPtr(), ucNumBuf, uiValLen); if( uiEncDefId) { if( RC_BAD( rc = pDb->encryptData( uiEncDefId, pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataBufferSize(), uiValLen, &uiEncryptedLen))) { goto Exit; } } } else { pAttrItem->m_uiPayloadLen = 0; } pAttrItem->m_uiDecryptedDataLen = uiValLen; Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::getNumber64( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 * pui64Value, FLMBOOL * pbNeg) { RCODE rc = NE_XFLM_OK; FLMBOOL bNeg; FLMUINT64 ui64Value; F_AttrItem * pAttrItem; IF_PosIStream * pIStream = NULL; F_NodeBufferIStream bufferIStream; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } else if( pAttrItem->m_uiFlags & FDOM_UNSIGNED_QUICK_VAL) { *pbNeg = FALSE; *pui64Value = pAttrItem->m_ui64QuickVal; } else if( pAttrItem->m_uiFlags & FDOM_SIGNED_QUICK_VAL) { *pbNeg = TRUE; *pui64Value = pAttrItem->m_ui64QuickVal; } else { if( RC_BAD( rc = getIStream( pDb, uiAttrNameId, &bufferIStream, &pIStream))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsNumber( pIStream, pAttrItem->m_uiDataType, &ui64Value, &bNeg))) { goto Exit; } *pui64Value = ui64Value; *pbNeg = bNeg; } Exit: if( pIStream) { pIStream->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::setBinary( F_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiValueLen, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; FLMUINT uiDecryptedValLen = 0; FLMUINT uiEncryptedLen; // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); // Get a pointer to the attribute list item if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) { goto Exit; } } else { if( pAttrItem->m_uiFlags & FDOM_READ_ONLY) { rc = RC_SET( NE_XFLM_READ_ONLY); goto Exit; } pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); } switch( pAttrItem->m_uiDataType) { case XFLM_BINARY_TYPE: { break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, uiValueLen, TRUE, FALSE))) { goto Exit; } if( uiValueLen) { f_memcpy( pAttrItem->getAttrDataPtr(), pvValue, uiValueLen); if( uiEncDefId) { uiDecryptedValLen = uiValueLen; if( RC_BAD( rc = pDb->encryptData( uiEncDefId, pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataBufferSize(), uiValueLen, &uiEncryptedLen))) { goto Exit; } flmAssert( uiEncryptedLen == pAttrItem->getAttrDataBufferSize()); } } pAttrItem->m_uiDecryptedDataLen = uiValueLen; Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::getBinary( F_Db * pDb, FLMUINT uiAttrNameId, void * pvBuffer, FLMUINT uiBufferLen, FLMUINT * puiDataLen) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; IF_PosIStream * pIStream = NULL; F_NodeBufferIStream bufferIStream; // If a NULL buffer is passed in, just return the // data length if( !pvBuffer) { rc = getDataLength( uiAttrNameId, puiDataLen); goto Exit; } if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( !pAttrItem->m_uiEncDefId) { FLMUINT uiCopySize = f_min( uiBufferLen, pAttrItem->getAttrDataLength()); if( uiCopySize) { f_memcpy( pvBuffer, pAttrItem->getAttrDataPtr(), uiCopySize); } if( puiDataLen) { *puiDataLen = uiCopySize; } } else { if( RC_BAD( rc = getIStream( pDb, uiAttrNameId, &bufferIStream, &pIStream))) { goto Exit; } if( RC_BAD( rc = flmReadStorageAsBinary( pIStream, pvBuffer, uiBufferLen, 0, puiDataLen))) { goto Exit; } } Exit: if( pIStream) { pIStream->Release(); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::setUTF8( F_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiNumBytesInValue, FLMUINT uiNumCharsInValue, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; FLMUINT uiValLen = 0; FLMUINT uiDecryptedValLen = 0; FLMUINT uiEncryptedLen; FLMUINT uiSenLen; FLMBYTE * pucValue = (FLMBYTE *)pvValue; FLMBOOL bNullTerminate = FALSE; // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); // Get a pointer to the attribute list item if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) { goto Exit; } } else { if( pAttrItem->m_uiFlags & FDOM_READ_ONLY) { rc = RC_SET( NE_XFLM_READ_ONLY); goto Exit; } pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); } switch( pAttrItem->m_uiDataType) { case XFLM_TEXT_TYPE: { break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } if( pvValue && uiNumBytesInValue) { if( pucValue[ uiNumBytesInValue - 1] != 0) { bNullTerminate = TRUE; } uiSenLen = f_getSENByteCount( uiNumCharsInValue); uiValLen = uiNumBytesInValue + uiSenLen + (bNullTerminate ? 1 : 0); } else { uiSenLen = 0; uiValLen = 0; } if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, uiValLen, TRUE, FALSE))) { goto Exit; } if( uiValLen) { FLMBYTE * pucTmp = pAttrItem->getAttrDataPtr(); f_encodeSENKnownLength( uiNumCharsInValue, uiSenLen, &pucTmp); f_memcpy( pucTmp, pucValue, uiNumBytesInValue); if( bNullTerminate) { pucTmp[ uiNumBytesInValue] = 0; } if( uiEncDefId) { uiDecryptedValLen = uiValLen; if( RC_BAD( rc = pDb->encryptData( uiEncDefId, pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataBufferSize(), uiValLen, &uiEncryptedLen))) { goto Exit; } } } pAttrItem->m_uiDecryptedDataLen = uiValLen; Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::setStorageValue( F_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiValueLen, FLMUINT uiEncDefId) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; FLMUINT uiDecryptedValLen = 0; FLMUINT uiEncryptedLen; // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); // Get a pointer to the attribute list item if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) { goto Exit; } } else { pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); } if( pAttrItem->m_uiDataType == XFLM_UNKNOWN_TYPE) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, uiValueLen, TRUE, FALSE))) { goto Exit; } if( uiValueLen) { f_memcpy( pAttrItem->getAttrDataPtr(), pvValue, uiValueLen); if( uiEncDefId) { uiDecryptedValLen = uiValueLen; if( RC_BAD( rc = pDb->encryptData( uiEncDefId, pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataBufferSize(), uiValueLen, &uiEncryptedLen))) { goto Exit; } flmAssert( uiEncryptedLen == pAttrItem->getAttrDataBufferSize()); } } pAttrItem->m_uiDecryptedDataLen = uiValueLen; Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::addModeFlags( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; F_UNREFERENCED_PARM( pDb); // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } pAttrItem->m_uiFlags |= uiFlags; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::removeModeFlags( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiFlags) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; F_UNREFERENCED_PARM( pDb); // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } pAttrItem->m_uiFlags &= ~uiFlags; Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ RCODE F_CachedNode::setPrefixId( F_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiPrefixId) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; F_UNREFERENCED_PARM( pDb); // Logging should be done by the caller flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } pAttrItem->m_uiPrefixId = uiPrefixId; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_CachedNode::getIStream( F_Db * pDb, FLMUINT uiAttrNameId, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiDataType, FLMUINT * puiDataLength) { RCODE rc = NE_XFLM_OK; F_AttrItem * pAttrItem; F_NodeBufferIStream * pNodeBufferIStream = NULL; FLMBYTE * pucAllocatedBuffer = NULL; if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) { rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } if( pStackStream) { pNodeBufferIStream = pStackStream; pStackStream->AddRef(); flmAssert( !pStackStream->m_pCachedNode); } else { if( (pNodeBufferIStream = f_new F_NodeBufferIStream) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } if( pAttrItem->m_uiEncDefId) { flmAssert( pAttrItem->m_uiIVLen); if( RC_BAD( rc = pNodeBufferIStream->openStream( NULL, pAttrItem->getAttrDataBufferSize(), (char **)&pucAllocatedBuffer))) { goto Exit; } if( RC_BAD( rc = pDb->decryptData( pAttrItem->m_uiEncDefId, pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataBufferSize(), pucAllocatedBuffer, (FLMUINT)pNodeBufferIStream->totalSize()))) { goto Exit; } pNodeBufferIStream->truncate( pAttrItem->getAttrDataLength()); } else { if( RC_BAD( rc = pNodeBufferIStream->openStream( (const char *)pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength()))) { goto Exit; } } if( !pStackStream) { pNodeBufferIStream->m_pCachedNode = this; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); incrNodeUseCount(); incrStreamUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } if( puiDataType) { *puiDataType = pAttrItem->m_uiDataType; } if( puiDataLength) { *puiDataLength = (FLMUINT)pNodeBufferIStream->remainingSize(); } if( *ppIStream) { (*ppIStream)->Release(); } *ppIStream = pNodeBufferIStream; pNodeBufferIStream = NULL; Exit: if( pNodeBufferIStream) { pNodeBufferIStream->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_AttrItem::resizePayloadBuffer( FLMUINT uiTotalNeeded, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; FLMUINT uiCurrentSize = m_uiPayloadLen; FLMBOOL bMutexLocked = FALSE; if( uiCurrentSize != uiTotalNeeded) { FLMUINT uiNewSize; FLMUINT uiSize; FLMUINT uiOldSize; if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); bMutexLocked = TRUE; } uiOldSize = memSize(); if( uiTotalNeeded <= sizeof( FLMBYTE *)) { if( uiCurrentSize && uiCurrentSize > sizeof( FLMBYTE *)) { m_pucPayload -= sizeof( F_AttrItem *); gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->freeBuf( m_uiPayloadLen + sizeof( F_AttrItem *), &m_pucPayload); } else { // NOTE: Mutex is NOT locked here, because // nothing will be changed that requires the // mutex to be locked. Nor will the size // change. If the size were to change, we // would want to lock the mutex because we // would be incrementing/decrementing size // counts below. m_pucPayload = NULL; } } else { F_AttrItem * pAttrItem = this; if( uiCurrentSize && uiCurrentSize > sizeof( FLMBYTE *)) { m_pucPayload -= sizeof( F_AttrItem *); if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->reallocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_attrBufferRelocator, m_uiPayloadLen + sizeof( F_AttrItem *), uiTotalNeeded + sizeof( F_AttrItem *), &pAttrItem, sizeof( F_AttrItem *), &m_pucPayload))) { goto Exit; } } else { if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_pBufAllocator->allocBuf( &gv_XFlmSysData.pNodeCacheMgr->m_attrBufferRelocator, uiTotalNeeded + sizeof( F_AttrItem *), &pAttrItem, sizeof( F_AttrItem *), &m_pucPayload))) { goto Exit; } } flmAssert( *((F_AttrItem **)m_pucPayload) == this); m_pucPayload += sizeof( F_AttrItem *); } m_uiPayloadLen = uiTotalNeeded; uiNewSize = memSize(); if( uiNewSize > uiOldSize) { uiSize = uiNewSize - uiOldSize; m_pCachedNode->m_uiTotalAttrSize += uiSize; if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64) { gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; } gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize; } else if (uiNewSize < uiOldSize) { uiSize = uiOldSize - uiNewSize; flmAssert( m_pCachedNode->m_uiTotalAttrSize >= uiSize); m_pCachedNode->m_uiTotalAttrSize -= uiSize; if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64) { flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; } flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize); gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; } } Exit: if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_AttrItem::setupAttribute( F_Db * pDb, FLMUINT uiEncDefId, FLMUINT uiDataSizeNeeded, FLMBOOL bOkToGenerateIV, FLMBOOL bMutexAlreadyLocked) { RCODE rc = NE_XFLM_OK; FLMUINT uiTotalNeeded = uiDataSizeNeeded; FLMUINT uiIVLen = 0; FLMBOOL bGenerateIV = FALSE; F_ENCDEF * pEncDef = NULL; if( uiEncDefId) { if( RC_BAD( rc = pDb->m_pDict->getEncDef( uiEncDefId, &pEncDef))) { goto Exit; } uiIVLen = pEncDef->pCcs->getIVLen(); flmAssert( uiIVLen == 8 || uiIVLen == 16); m_uiEncDefId = uiEncDefId; m_uiIVLen = uiIVLen; if( bOkToGenerateIV) { bGenerateIV = TRUE; } uiTotalNeeded += m_uiIVLen + (getEncLen( uiDataSizeNeeded) - uiDataSizeNeeded); } else { m_uiEncDefId = 0; m_uiIVLen = 0; } #ifdef FLM_DEBUG if( uiEncDefId) { flmAssert( uiTotalNeeded >= 8 + uiDataSizeNeeded); } #endif if( RC_BAD( rc = resizePayloadBuffer( uiTotalNeeded, bMutexAlreadyLocked))) { goto Exit; } if( bGenerateIV) { if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, getAttrIVPtr()))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_NodeBufferIStream::openStream( const char * pucBuffer, FLMUINT uiLength, char ** ppucAllocatedBuffer) { RCODE rc = NE_XFLM_OK; IF_BufferIStream * pBufferIStream = NULL; if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } if( RC_BAD( rc = pBufferIStream->openStream( pucBuffer, uiLength, ppucAllocatedBuffer))) { goto Exit; } m_pBufferIStream = pBufferIStream; pBufferIStream = NULL; Exit: if( pBufferIStream) { pBufferIStream->Release(); } return( rc); } libxflaim-5.1.969/src/fdbcopy.cpp0000644000175000017500000005203410511001742020162 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the F_DbSystem::dbCopy method. // // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbcopy.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" typedef struct COPIED_NAME { char szPath[ F_PATH_MAX_SIZE]; COPIED_NAME * pNext; } COPIED_NAME; typedef struct { FLMUINT64 ui64BytesToCopy; FLMUINT64 ui64BytesCopied; FLMBOOL bNewSrcFile; char szSrcFileName[ F_PATH_MAX_SIZE]; char szDestFileName[ F_PATH_MAX_SIZE]; } DB_COPY_INFO, * DB_COPY_INFO_p; FSTATIC RCODE flmCopyFile( DB_COPY_INFO * pDbCopyInfo, COPIED_NAME ** ppCopiedListRV, FLMBOOL bOkToTruncate, IF_DbCopyStatus * ifpStatus); /**************************************************************************** Desc: Copies a database, including roll-forward log files. ****************************************************************************/ RCODE F_DbSystem::dbCopy( const char * pszSrcDbName, // [IN] Name of source database to be copied. const char * pszSrcDataDir, // [IN] Name of source data directory. const char * pszSrcRflDir, // [IN] RFL directory of source database. NULL can be // passed to indicate that the log files are located // in the same directory as the other database files. const char * pszDestDbName, // [IN] Destination name of database - will be overwritten if it // already exists. const char * pszDestDataDir, // [IN] Name of destination data directory. const char * pszDestRflDir, // [IN] RFL directory of destination database. NULL can be // passed to indicate that the log files are to be located // in the same directory as the other database files. IF_DbCopyStatus * ifpStatus) // [IN] Status callback interface. { RCODE rc = NE_XFLM_OK; IF_Db * pDb = NULL; FLMBOOL bDbLocked = FALSE; // Make sure the destination database is closed if (RC_BAD( rc = checkDatabaseClosed( pszDestDbName, pszDestDataDir))) { goto Exit; } // Open the source database so we can force a checkpoint. if (RC_BAD( rc = openDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, NULL, 0, &pDb))) { goto Exit; } // Need to lock the database, because we want to do a checkpoint // and then the copy immediately after without letting other // threads have the opportunity to get in and update the // database. if (RC_BAD( rc = pDb->dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; // Force a checkpoint if (RC_BAD( rc = pDb->doCheckpoint( FLM_NO_TIMEOUT))) { goto Exit; } // Once we get this far, we have exclusive access to the database // and we have forced a checkpoint. The database's contents are // guaranteed to be on disk at this point, and they will not // change. rc = copyDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, pszDestDbName, pszDestDataDir, pszDestRflDir, ifpStatus); Exit: // Unlock and close the database if (bDbLocked) { pDb->dbUnlock(); } if (pDb) { pDb->Release(); } return( rc); } /**************************************************************************** Desc: Copy a database's files, including roll-forward log files. *****************************************************************************/ RCODE F_DbSystem::copyDb( const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, IF_DbCopyStatus * ifpStatus) { RCODE rc = NE_XFLM_OK; DB_COPY_INFO DbCopyInfo; F_SuperFileHdl * pSrcSFileHdl = NULL; F_SuperFileHdl * pDestSFileHdl = NULL; F_SuperFileClient * pSrcSFileClient = NULL; F_SuperFileClient * pDestSFileClient = NULL; FLMUINT uiFileNumber; FLMUINT uiHighFileNumber; FLMUINT uiHighLogFileNumber; FLMUINT64 ui64FileSize; F_Database * pDatabase = NULL; FLMBOOL bMutexLocked = FALSE; IF_FileHdl * pLockFileHdl = NULL; IF_FileHdl * pTmpFileHdl = NULL; IF_DirHdl * pDirHdl = NULL; FLMBOOL bDatabaseLocked = FALSE; FLMBOOL bWriteLocked = FALSE; IF_LockObject * pWriteLockObj = NULL; IF_LockObject * pDatabaseLockObj = NULL; COPIED_NAME * pCopiedList = NULL; FLMBOOL bUsedDatabase = FALSE; eLockType currLockType; FLMUINT uiThreadId; FLMUINT uiNumExclQueued; FLMUINT uiNumSharedQueued; FLMUINT uiPriorityCount; char * pszActualSrcRflPath = NULL; char * pszActualDestRflPath = NULL; FLMBOOL bCreatedDestRflDir = FALSE; FLMBOOL bWaited; F_SEM hWaitSem = F_SEM_NULL; f_memset( &DbCopyInfo, 0, sizeof( DbCopyInfo)); // Should not do anything if the source and destination names // are the same. if (f_stricmp( pszSrcDbName, pszDestDbName) == 0) { goto Exit; } // Create a "wait" semaphore if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } // Allocate memory for paths we don't want to push onto the stack. if (RC_BAD( rc = f_calloc( F_PATH_MAX_SIZE * 2, &pszActualSrcRflPath))) { goto Exit; } pszActualDestRflPath = &pszActualSrcRflPath[ F_PATH_MAX_SIZE]; // Set up the super file object for the source database. // Must at least open the control file. if( (pSrcSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileClient->setup( pszSrcDbName, pszSrcDataDir, 0))) { goto Exit; } if( (pSrcSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pSrcSFileHdl->setup( pSrcSFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { goto Exit; } // Close all unused file handles if( gv_XFlmSysData.pFileHdlCache) { gv_XFlmSysData.pFileHdlCache->closeUnusedFiles(); } // Lock the destination database, if not already locked. // This is so we can overwrite it without necessarily // deleting it. May unlock and re-lock the global mutex. f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; retry: if (RC_BAD( rc = F_DbSystem::findDatabase( pszDestDbName, pszDestDataDir, &pDatabase))) { goto Exit; } // If we didn't find an FFILE structure, get an // exclusive lock on the file. if (!pDatabase) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // Attempt to get an exclusive lock on the file. if (RC_BAD( rc = flmCreateLckFile( pszDestDbName, &pLockFileHdl))) { goto Exit; } } else { // The call to verifyOkToUse will wait if the database is in // the process of being opened by another thread. if (RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited))) { goto Exit; } if (bWaited) { goto retry; } // Increment the open count on the F_Database object so it will not // disappear while we are copying the database. pDatabase->incrOpenCount(); bUsedDatabase = TRUE; f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // Lock the destination file object and transaction // object, if not already locked. pDatabase->m_pDatabaseLockObj->getLockInfo( (FLMINT)0, &currLockType, &uiThreadId, &uiNumExclQueued, &uiNumSharedQueued, &uiPriorityCount); if (currLockType != FLM_LOCK_EXCLUSIVE || uiThreadId != f_threadId()) { pDatabaseLockObj = pDatabase->m_pDatabaseLockObj; pDatabaseLockObj->AddRef(); if (RC_BAD( rc = pDatabaseLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bDatabaseLocked = TRUE; } // Lock the write object, if not already locked pDatabase->m_pWriteLockObj->getLockInfo( (FLMINT)0, &currLockType, &uiThreadId, &uiNumExclQueued, &uiNumSharedQueued, &uiPriorityCount); if (currLockType != FLM_LOCK_EXCLUSIVE || uiThreadId != (FLMUINT)f_threadId()) { pWriteLockObj = pDatabase->m_pWriteLockObj; pWriteLockObj->AddRef(); // Only contention here is with the checkpoint thread - wait // forever until the checkpoint thread gives it up. if (RC_BAD( rc = pDatabase->dbWriteLock( hWaitSem))) { goto Exit; } bWriteLocked = TRUE; } } // Set up the super file object for the destination database. if( (pDestSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileClient->setup( pszDestDbName, pszDestDataDir, 0))) { goto Exit; } if( (pDestSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pDestSFileHdl->setup( pDestSFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { goto Exit; } // See how many files we have and calculate the total size. uiHighFileNumber = 0; for (;;) { if ((RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighFileNumber, &ui64FileSize))) || !ui64FileSize ) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME || !ui64FileSize) { // If the control file doesn't exist, we will return // path not found. if (!uiHighFileNumber) { goto Exit; } uiHighFileNumber--; rc = NE_XFLM_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) { break; } uiHighFileNumber++; } // See how many rollback log files we have, and calculate // their total size. uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; for (;;) { if ((RC_BAD( rc = pSrcSFileHdl->getFileSize( uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME || !ui64FileSize) { if (uiHighLogFileNumber == FIRST_LOG_BLOCK_FILE_NUMBER) { uiHighLogFileNumber = 0; } else { uiHighLogFileNumber--; } rc = NE_XFLM_OK; break; } goto Exit; } DbCopyInfo.ui64BytesToCopy += ui64FileSize; if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) { break; } uiHighLogFileNumber++; } // Get the sizes of the roll-forward log files if (RC_BAD( rc = rflGetDirAndPrefix( pszSrcDbName, pszSrcRflDir, pszActualSrcRflPath))) { goto Exit; } if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if (RC_BAD( rc = pDirHdl->next())) { if (rc == NE_FLM_IO_NO_MORE_FILES) { rc = NE_XFLM_OK; break; } else { goto Exit; } } // If the current file is an RFL file, increment ui64BytesToCopy if (rflGetFileNum( pDirHdl->currentItemName(), &uiFileNumber)) { DbCopyInfo.ui64BytesToCopy += (FLMUINT64)pDirHdl->currentItemSize(); } } pDirHdl->Release(); pDirHdl = NULL; // Close all file handles in the source and destination pSrcSFileHdl->releaseFiles(); pDestSFileHdl->releaseFiles(); // Copy the database files. for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if( RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if( RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( &DbCopyInfo, &pCopiedList, TRUE, ifpStatus))) { goto Exit; } } // Copy the additional rollback log files, if any. for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; uiFileNumber <= uiHighLogFileNumber; uiFileNumber++) { // Get the source file path and destination file path. if (RC_BAD( rc = pSrcSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = pDestSFileHdl->getFilePath( uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( &DbCopyInfo, &pCopiedList, TRUE, ifpStatus))) { goto Exit; } } // Copy the RFL files // Create the destination RFL directory, if needed. The purpose of this // code is two-fold: 1) We want to keep track of the fact that we tried // to create the destination RFL directory so we can try to remove it // if the copy fails; 2) If the destination RFL directory path specifies // a directory with existing files, we want to remove them. if( RC_BAD( rc = rflGetDirAndPrefix( pszDestDbName, pszDestRflDir, pszActualDestRflPath))) { goto Exit; } if( RC_OK( gv_XFlmSysData.pFileSystem->doesFileExist( pszActualDestRflPath))) { if( gv_XFlmSysData.pFileSystem->isDir( pszActualDestRflPath)) { // Remove the existing directory and all files, etc. (void)gv_XFlmSysData.pFileSystem->removeDir( pszActualDestRflPath, TRUE); } else { (void)gv_XFlmSysData.pFileSystem->deleteFile( pszActualDestRflPath); } } // Try to create the destination RFL directory. This might fail if // another process was accessing the directory for some reason // (i.e., from a command prompt), when we tried to remove it above. // We really don't care if the call to CreateDir is sucessful, because // when we try to create the destination files (below), the FLAIM file // file system code will try to create any necessary directories. (void)gv_XFlmSysData.pFileSystem->createDir( pszActualDestRflPath); bCreatedDestRflDir = TRUE; // Copy the RFL files. NOTE: We need to copy all of the RFL files // in the source RFL directory so that they will be available // when performing a database restore operation. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openDir( pszActualSrcRflPath, (char *)"*", &pDirHdl))) { goto Exit; } for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if (rc == NE_FLM_IO_NO_MORE_FILES) { rc = NE_XFLM_OK; break; } else { goto Exit; } } // If the current file is an RFL file, copy it to the destination if( rflGetFileNum( pDirHdl->currentItemName(), &uiFileNumber)) { // Get the source file path and the destination file path. if (RC_BAD( rc = rflGetFileName( pszSrcDbName, pszSrcRflDir, uiFileNumber, DbCopyInfo.szSrcFileName))) { goto Exit; } if (RC_BAD( rc = rflGetFileName( pszDestDbName, pszDestRflDir, uiFileNumber, DbCopyInfo.szDestFileName))) { goto Exit; } DbCopyInfo.bNewSrcFile = TRUE; if (RC_BAD( rc = flmCopyFile( &DbCopyInfo, &pCopiedList, TRUE, ifpStatus))) { goto Exit; } } } pDirHdl->Release(); pDirHdl = NULL; Exit: if (bUsedDatabase) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } pDatabase->decrOpenCount(); } if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; } // Unlock the database, if it is locked. if (bWriteLocked) { pDatabase->dbWriteUnlock(); bWriteLocked = FALSE; } if (bDatabaseLocked) { RCODE rc3; if (RC_BAD( rc3 = pDatabaseLockObj->unlock())) { if (RC_OK( rc)) rc = rc3; } bDatabaseLocked = FALSE; } if (pWriteLockObj) { pWriteLockObj->Release(); pWriteLockObj = NULL; } if (pDatabaseLockObj) { pDatabaseLockObj->Release(); pDatabaseLockObj = NULL; } if (pLockFileHdl) { (void)pLockFileHdl->closeFile(); pLockFileHdl->Release(); pLockFileHdl = NULL; } if (pTmpFileHdl) { pTmpFileHdl->Release(); } if (pDirHdl) { pDirHdl->Release(); } // Free all the names of files that were copied. // If the copy didn't finish, try to delete any files // that were copied. while (pCopiedList) { COPIED_NAME * pNext = pCopiedList->pNext; // If the overall copy failed, delete the copied file. if (RC_BAD( rc)) { (void)gv_XFlmSysData.pFileSystem->deleteFile( pCopiedList->szPath); } f_free( &pCopiedList); pCopiedList = pNext; } if (RC_BAD( rc) && bCreatedDestRflDir) { (void)gv_XFlmSysData.pFileSystem->removeDir( pszActualDestRflPath); } if (pszActualSrcRflPath) { f_free( &pszActualSrcRflPath); } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } if( pSrcSFileClient) { pSrcSFileClient->Release(); } if( pSrcSFileHdl) { pSrcSFileHdl->Release(); } if( pDestSFileClient) { pDestSFileClient->Release(); } if( pDestSFileHdl) { pDestSFileHdl->Release(); } return( rc); } /**************************************************************************** Desc: Copy a file that is one of the files of the database. *****************************************************************************/ FSTATIC RCODE flmCopyFile( DB_COPY_INFO * pDbCopyInfo, COPIED_NAME ** ppCopiedListRV, FLMBOOL bOkToTruncate, IF_DbCopyStatus * ifpStatus) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucBuffer = NULL; IF_FileHdl * pSrcFileHdl = NULL; IF_FileHdl * pDestFileHdl = NULL; FLMUINT uiBufferSize = 32768; FLMUINT uiBytesToRead; FLMUINT uiBytesRead; FLMUINT uiBytesWritten; FLMUINT uiOffset; FLMBOOL bCreatedDestFile = FALSE; // Open the source file. if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( pDbCopyInfo->szSrcFileName, gv_XFlmSysData.uiFileOpenFlags, &pSrcFileHdl))) { goto Exit; } // Get a file handle for the destination file. // First attempt to open the destination file. If it does // not exist, attempt to create it. if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( pDbCopyInfo->szDestFileName, gv_XFlmSysData.uiFileOpenFlags, &pDestFileHdl))) { if (rc != NE_FLM_IO_PATH_NOT_FOUND && rc != NE_FLM_IO_INVALID_FILENAME) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->createFile( pDbCopyInfo->szDestFileName, gv_XFlmSysData.uiFileCreateFlags, &pDestFileHdl))) { goto Exit; } bCreatedDestFile = TRUE; } // Allocate a buffer for reading and writing. if (RC_BAD( rc = f_alloc( uiBufferSize, &pucBuffer))) { goto Exit; } // Read from source file until we hit EOF in the file or // we hit the end offset. uiOffset = 0; for (;;) { uiBytesToRead = (FLMUINT)((0xFFFFFFFF - uiOffset >= uiBufferSize) ? uiBufferSize : (FLMUINT)(0xFFFFFFFF - uiOffset)); // Read data from source file. if (RC_BAD( rc = pSrcFileHdl->read( uiOffset, uiBytesToRead, pucBuffer, &uiBytesRead))) { if (rc == NE_FLM_IO_END_OF_FILE) { rc = NE_XFLM_OK; if (!uiBytesRead) { break; } } else { goto Exit; } } // Write data to destination file. if (RC_BAD( rc = pDestFileHdl->write( uiOffset, uiBytesRead, pucBuffer, &uiBytesWritten))) { goto Exit; } // Do callback to report progress. if (ifpStatus) { pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten; if (RC_BAD( rc = ifpStatus->dbCopyStatus( pDbCopyInfo->ui64BytesToCopy, pDbCopyInfo->ui64BytesCopied, pDbCopyInfo->bNewSrcFile, pDbCopyInfo->szSrcFileName, pDbCopyInfo->szDestFileName))) { goto Exit; } pDbCopyInfo->bNewSrcFile = FALSE; } if (0xFFFFFFFF - uiBytesWritten < uiOffset) { uiOffset = 0xFFFFFFFF; break; } uiOffset += uiBytesWritten; // Quit once we reach the end offset or we read fewer bytes // than we asked for. if (uiBytesRead < uiBytesToRead) { break; } } // If we overwrote the destination file, as opposed to creating // it, truncate it in case it was larger than the number of // bytes we actually copied. if (!bCreatedDestFile && bOkToTruncate) { if (RC_BAD( rc = pDestFileHdl->truncateFile( uiOffset))) { goto Exit; } } // If the copy succeeded, add the destination name to a list // of destination files. This is done so we can clean up // copied files if we fail somewhere in the overall database // copy. if (ppCopiedListRV) { COPIED_NAME * pCopyName; if( RC_BAD( rc = f_alloc( (FLMUINT)sizeof( COPIED_NAME), &pCopyName))) { goto Exit; } f_strcpy( pCopyName->szPath, pDbCopyInfo->szDestFileName); pCopyName->pNext = *ppCopiedListRV; *ppCopiedListRV = pCopyName; } Exit: if (pucBuffer) { f_free( &pucBuffer); } if (pSrcFileHdl) { pSrcFileHdl->Release(); } if (pDestFileHdl) { pDestFileHdl->flush(); pDestFileHdl->Release(); } // Attempt to delete the destination file if // we didn't successfully copy it. if (RC_BAD( rc)) { (void)gv_XFlmSysData.pFileSystem->deleteFile( pDbCopyInfo->szDestFileName); } return( rc); } libxflaim-5.1.969/src/fdllmain.cpp0000644000175000017500000000706310511001742020324 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This is the standard functionality that all com servers must export // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdllmain.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" static FLMATOMIC gv_lockCount = 0; FLMEXTC RCODE FLMAPI DllCanUnloadNow( void); FLMEXTC RCODE FLMAPI DllStart( void); FLMEXTC RCODE FLMAPI DllStop( void); #if defined( FLM_UNIX) #elif defined( FLM_WIN) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #pragma comment(linker, "/export:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE") #pragma comment(linker, "/export:DllStart=_DllStart@0,PRIVATE") #pragma comment(linker, "/export:DllStop=_DllStop@0,PRIVATE") #elif !defined( FLM_NLM) #error platform not supported. #endif /****************************************************************************** Desc: ******************************************************************************/ void LockModule(void) { f_atomicInc( &gv_lockCount); } /****************************************************************************** Desc: ******************************************************************************/ void UnlockModule(void) { f_atomicDec( &gv_lockCount); } /****************************************************************************** Desc: Returns 0 if it's okay to unload, or a non-zero status code if not. ******************************************************************************/ FLMEXTC RCODE FLMAPI DllCanUnloadNow( void) { if( gv_lockCount > 1) { return( RC_SET( NE_XFLM_FAILURE)); } return( NE_XFLM_OK); } /****************************************************************************** Desc: Called by PSA when it loads the library. Must return 0 for success, or a non-zero error code. ******************************************************************************/ FLMEXTC RCODE FLMAPI DllStart( void) { return( NE_XFLM_OK); } /****************************************************************************** Desc: Called by PSA when it unloads the library. The return value is ignored. ******************************************************************************/ FLMEXTC RCODE FLMAPI DllStop( void) { return( NE_XFLM_OK); } /****************************************************************************** Desc: ******************************************************************************/ FLMEXTC RCODE FLMAPI DllRegisterServer( const char *) { return( NE_XFLM_OK); } /****************************************************************************** Desc: ******************************************************************************/ FLMEXTC RCODE FLMAPI DllUnregisterServer( void) { return( NE_XFLM_OK); } libxflaim-5.1.969/src/flog.cpp0000644000175000017500000000576210511001742017471 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains routines for logging messages from within FLAIM. // // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flog.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Returns an IF_LogMessageClient object if logging is enabled for the specified message type ****************************************************************************/ IF_LogMessageClient * flmBeginLogMessage( eLogMessageType eMsgType) { IF_LogMessageClient * pNewMsg = NULL; f_mutexLock( gv_XFlmSysData.hLoggerMutex); if( !gv_XFlmSysData.pLogger) { goto Exit; } if( (pNewMsg = gv_XFlmSysData.pLogger->beginMessage( eMsgType)) != NULL) { gv_XFlmSysData.uiPendingLogMessages++; } Exit: f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); return( pNewMsg); } /**************************************************************************** Desc: Logs information about an error ****************************************************************************/ void flmLogError( RCODE rc, const char * pszDoing, const char * pszFileName, FLMINT iLineNumber) { IF_LogMessageClient * pLogMsg = NULL; if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) { pLogMsg->changeColor( FLM_YELLOW, FLM_BLACK); if( pszFileName) { f_logPrintf( pLogMsg, "Error %s: %e, File=%s, Line=%d.", pszDoing, rc, pszFileName, (int)iLineNumber); } else { f_logPrintf( pLogMsg, "Error %s: %e.", pszDoing, rc); } flmEndLogMessage( &pLogMsg); } } /**************************************************************************** Desc: Ends a logging message ****************************************************************************/ void flmEndLogMessage( IF_LogMessageClient ** ppLogMessage) { if( *ppLogMessage) { f_mutexLock( gv_XFlmSysData.hLoggerMutex); flmAssert( gv_XFlmSysData.uiPendingLogMessages); (*ppLogMessage)->endMessage(); (*ppLogMessage)->Release(); *ppLogMessage = NULL; gv_XFlmSysData.uiPendingLogMessages--; f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); } } libxflaim-5.1.969/src/flbackup.cpp0000644000175000017500000016061610511001742020331 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Backup and restore Routines // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flbackup.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" // Typedefs typedef struct { FLMUINT64 ui64BytesToDo; FLMUINT64 ui64BytesDone; } DB_BACKUP_INFO, * DB_BACKUP_INFO_p; // Local classes class F_BackerStream : public F_Object { public: F_BackerStream( void); ~F_BackerStream( void); RCODE setup( FLMUINT uiMTUSize, IF_RestoreClient * pRestoreObj); RCODE setup( FLMUINT uiMTUSize, IF_BackupClient * pClient); RCODE startThreads( void); void shutdownThreads( void); RCODE read( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesRead = NULL); RCODE write( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesWritten = NULL); RCODE flush( void); FINLINE FLMUINT64 getByteCount( void) { // Returns the total number of bytes read or written. return( m_ui64ByteCount); } FINLINE FLMUINT getMTUSize( void) { return( m_uiMTUSize); } private: RCODE signalThread( void); RCODE _setup( void); static RCODE FLMAPI readThread( IF_Thread * pThread); static RCODE FLMAPI writeThread( IF_Thread * pThread); FLMBOOL m_bSetup; FLMBOOL m_bFirstRead; FLMBOOL m_bFinalRead; FLMUINT m_uiBufOffset; FLMUINT64 m_ui64ByteCount; IF_RestoreClient * m_pRestoreObj; F_SEM m_hDataSem; F_SEM m_hIdleSem; IF_Thread * m_pThread; RCODE m_rc; FLMBYTE * m_pucInBuf; FLMUINT * m_puiInOffset; FLMBYTE * m_pucOutBuf; FLMUINT * m_puiOutOffset; FLMBYTE * m_pucBufs[ 2]; FLMUINT m_uiOffsets[ 2]; FLMUINT m_uiMTUSize; IF_BackupClient * m_pClient; }; // Constants #define FLM_BACKER_SIGNATURE_OFFSET 0 #define FLM_BACKER_SIGNATURE "!DB_BACKUP_FILE!" #define FLM_BACKER_SIGNATURE_SIZE 16 #define FLM_BACKER_VERSION_OFFSET 16 #define FLM_BACKER_VERSION_5_0_0 500 #define FLM_BACKER_VERSION FLM_BACKER_VERSION_5_0_0 #define FLM_BACKER_DB_BLOCK_SIZE_OFFSET 20 #define FLM_BACKER_BFMAX_OFFSET 24 #define FLM_BACKER_MTU_OFFSET 28 #define FLM_BACKER_TIME_OFFSET 32 #define FLM_BACKER_DB_NAME_OFFSET 36 #define FLM_BACKER_BACKUP_TYPE_OFFSET 40 #define FLM_BACKER_NEXT_INC_SERIAL_NUM 44 #define FLM_BACKER_DB_VERSION 60 // The backer MTU size must be a multiple of the largest // supported block size. Additionally, it must be at least // 2 * FLM_BACKER_MAX_DB_BLOCK_SIZE. DO NOT CHANGE THE MTU // SIZE UNLESS YOU ALSO BUMP THE BACKUP VERSION. #define FLM_BACKER_MTU_SIZE ((FLMUINT) 1024 * 512) #define FLM_BACKER_MAX_FILE_SIZE ((FLMUINT) 1024 * 1024 * 1024 * 2) // 2 Gigabytes #define FLM_BACKER_MIN_DB_BLOCK_SIZE ((FLMUINT) 2 * 1024) #define FLM_BACKER_MAX_DB_BLOCK_SIZE ((FLMUINT) 16 * 1024) // Backup block header #define FLM_BACKER_BLK_HDR_SIZE ((FLMUINT) 4) #define FLM_BACKER_BLK_ADDR_OFFSET 0 // Local prototypes FSTATIC RCODE flmRestoreFile( IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus, F_SuperFileHdl * pSFile, FLMBOOL bIncremental, FLMUINT * puiDbVersion, FLMUINT * puiNextIncSeqNum, FLMBOOL * pbRflPreserved, eRestoreAction * peAction, FLMBOOL * pbOKToRetry); // Functions /*************************************************************************** Desc : Prepares FLAIM to backup a database. Notes: Only one backup of a particular database can be active at any time *END************************************************************************/ RCODE F_Db::backupBegin( eDbBackupType eBackupType, eDbTransType eTransType, FLMUINT uiMaxLockWait, IF_Backup ** ppBackup) { F_Backup * pBackup = NULL; FLMBOOL bBackupFlagSet = FALSE; FLMUINT uiLastCPFileNum; FLMUINT uiLastTransFileNum; FLMUINT uiDbVersion; XFLM_DB_HDR * pDbHdr; RCODE rc = NE_XFLM_OK; // Initialize the handle *ppBackup = NULL; // Make sure we are not being called inside a transaction if( getTransType() != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // Verify that the application has specified a valid transaction type. if( eTransType != XFLM_READ_TRANS && eTransType != XFLM_UPDATE_TRANS) { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } // Make sure a valid backup type has been specified uiDbVersion = getDbVersion(); // See if a backup is currently running against the database. If so, // return an error. Otherwise, set the backup flag on the FFILE. m_pDatabase->lockMutex(); if( m_pDatabase->m_bBackupActive) { m_pDatabase->unlockMutex(); rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); goto Exit; } else { bBackupFlagSet = TRUE; m_pDatabase->m_bBackupActive = TRUE; } m_pDatabase->unlockMutex(); // Allocate the backup handle if( (pBackup = f_new F_Backup) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } pBackup->m_pDb = this; pBackup->m_uiDbVersion = uiDbVersion; // Start a transaction if( RC_BAD( rc = beginTrans( eTransType, uiMaxLockWait, XFLM_DONT_KILL_TRANS | XFLM_DONT_POISON_CACHE, &pBackup->m_dbHdr))) { goto Exit; } pBackup->m_bTransStarted = TRUE; pBackup->m_eTransType = eTransType; pDbHdr = &pBackup->m_dbHdr; // Don't allow an incremental backup to be performed // if a full backup has not yet been done. if( eBackupType == XFLM_INCREMENTAL_BACKUP && pDbHdr->ui64LastBackupTransID == 0) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } pBackup->m_eBackupType = eBackupType; // Set the next incremental backup serial number. This is // done regardless of the backup type to prevent the wrong // set of incremental backup files from being applied // to a database. if( RC_BAD( rc = f_createSerialNumber( pBackup->m_ucNextIncSerialNum))) { goto Exit; } // Get the incremental sequence number from the DB header pBackup->m_uiIncSeqNum = (FLMUINT)pDbHdr->ui32IncBackupSeqNum; // Determine the transaction ID of the last backup pBackup->m_ui64LastBackupTransId = pDbHdr->ui64LastBackupTransID; // Get the block change count pBackup->m_uiBlkChgSinceLastBackup = (FLMUINT)pDbHdr->ui32BlksChangedSinceBackup; // Get the current transaction ID pBackup->m_ui64TransId = pBackup->m_pDb->getTransID(); // Get the logical end of file pBackup->m_uiLogicalEOF = (FLMUINT)pDbHdr->ui32LogicalEOF; // Get the first required RFL file needed by the restore. uiLastCPFileNum = (FLMUINT)pDbHdr->ui32RflLastCPFileNum; uiLastTransFileNum = (FLMUINT)pDbHdr->ui32RflCurrFileNum; flmAssert( uiLastCPFileNum <= uiLastTransFileNum); pBackup->m_uiFirstReqRfl = uiLastCPFileNum < uiLastTransFileNum ? uiLastCPFileNum : uiLastTransFileNum; flmAssert( pBackup->m_uiFirstReqRfl); // Get the database block size pBackup->m_uiBlockSize = getBlockSize(); // Get the database path (void)getDbControlFileName( pBackup->m_szDbPath, sizeof( pBackup->m_szDbPath)); // Done *ppBackup = pBackup; pBackup = NULL; Exit: if( RC_BAD( rc)) { if( pBackup) { if( pBackup->m_bTransStarted) { abortTrans(); } pBackup->Release(); } if( bBackupFlagSet) { m_pDatabase->lockMutex(); m_pDatabase->m_bBackupActive = FALSE; m_pDatabase->unlockMutex(); } } return( rc); } /**************************************************************************** Desc : Constructor ****************************************************************************/ F_Backup::F_Backup() { m_pDb = NULL; m_bTransStarted = FALSE; reset(); } /**************************************************************************** Desc : Destructor ****************************************************************************/ F_Backup::~F_Backup() { endBackup(); } /**************************************************************************** Desc : Reset member variables to their initial state ****************************************************************************/ void F_Backup::reset( void) { if( m_bTransStarted) { m_pDb->abortTrans(); m_bTransStarted = FALSE; } m_pDb = NULL; m_eTransType = XFLM_NO_TRANS; m_ui64TransId = 0; m_ui64LastBackupTransId = 0; m_uiDbVersion = 0; m_uiBlkChgSinceLastBackup = 0; m_uiBlockSize = 0; m_uiLogicalEOF = 0; m_uiFirstReqRfl = 0; m_uiIncSeqNum = 0; m_bCompletedBackup = FALSE; m_eBackupType = XFLM_FULL_BACKUP; m_backupRc = NE_XFLM_OK; } /**************************************************************************** Desc : Streams the contents of a database to the write hook supplied by the application. Notes: This routine attempts to create a backup of a database without excluding any readers or updaters. However, if the backup runs too long in an environment where extensive updates are happening, an old view error could be returned. ****************************************************************************/ RCODE F_Backup::backup( const char * pszBackupPath, const char * pszPassword, IF_BackupClient * ifpClient, IF_BackupStatus * ifpStatus, FLMUINT * puiIncSeqNum) { RCODE rc = NE_XFLM_OK; FLMBOOL bFullBackup = TRUE; FLMINT iFileNum; FLMUINT uiBlkAddr; FLMUINT uiTime; F_CachedBlock * pSCache = NULL; XFLM_DB_HDR * pDbHdr; DB_BACKUP_INFO backupInfo; FLMUINT uiBlockFileOffset; FLMUINT uiCount; FLMUINT uiBlockCount; FLMUINT uiBlockCountLastCB = 0; FLMUINT uiBytesToPad; F_BackerStream * pBackerStream = NULL; FLMBYTE * pucBlkBuf = NULL; FLMUINT uiBlkBufOffset; FLMUINT uiBlkBufSize; FLMUINT uiMaxCSBlocks; FLMUINT uiCPTransOffset; FLMUINT uiMaxFileSize; FLMBOOL bReleaseClient = FALSE; FLMBOOL bMustUnlock = FALSE; if( puiIncSeqNum) { *puiIncSeqNum = 0; } // Setup the status callback info f_memset( &backupInfo, 0, sizeof( DB_BACKUP_INFO)); // Make sure a backup attempt has not been made with this // backup handle. if( m_bCompletedBackup) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } if( RC_BAD( m_backupRc)) { rc = m_backupRc; goto Exit; } // Look at the backup type if( m_eBackupType == XFLM_INCREMENTAL_BACKUP) { if( puiIncSeqNum) { *puiIncSeqNum = m_uiIncSeqNum; } bFullBackup = FALSE; } // Set up the callback if( !ifpClient) { if( !pszBackupPath) { rc = RC_SET( NE_XFLM_INVALID_PARM); goto Exit; } ifpClient = f_new F_DefaultBackupClient( pszBackupPath); if (ifpClient == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } bReleaseClient = TRUE; } // Allocate and initialize the backer stream object if( (pBackerStream = f_new F_BackerStream) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, ifpClient))) { goto Exit; } // Allocate a temporary buffer uiBlkBufSize = FLM_BACKER_MTU_SIZE; uiMaxCSBlocks = uiBlkBufSize / m_uiBlockSize; if( RC_BAD( rc = f_alloc( uiBlkBufSize, &pucBlkBuf))) { goto Exit; } // Setup the backup file header uiBlkBufOffset = 0; f_memset( pucBlkBuf, 0, m_uiBlockSize); f_memcpy( &pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET], FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE); UD2FBA( FLM_BACKER_VERSION, &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]); UD2FBA( (FLMUINT32)m_uiBlockSize, &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); uiMaxFileSize = (FLMUINT)m_dbHdr.ui32MaxFileSize; UD2FBA( (FLMUINT32)uiMaxFileSize, &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); UD2FBA( (FLMUINT32)FLM_BACKER_MTU_SIZE, &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]); f_timeGetSeconds( &uiTime); UD2FBA( (FLMUINT32)uiTime, &pucBlkBuf[ FLM_BACKER_TIME_OFFSET]); uiCount = f_strlen( m_szDbPath); if( uiCount <= 3) { pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET] = (FLMBYTE)m_szDbPath[ uiCount - 6]; pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 1] = (FLMBYTE)m_szDbPath[ uiCount - 5]; pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 2] = (FLMBYTE)m_szDbPath[ uiCount - 4]; pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 3] = '\0'; } UD2FBA( (FLMUINT32)m_eBackupType, &pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]); // Set the next incremental serial number in the backup's // header so that it can be put into the database's log header // after the backup has been restored. f_memcpy( &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM], m_ucNextIncSerialNum, XFLM_SERIAL_NUM_SIZE); // Set the database version number UD2FBA( (FLMUINT32)m_uiDbVersion, &pucBlkBuf[ FLM_BACKER_DB_VERSION]); uiBlkBufOffset += m_uiBlockSize; // Copy the database header into the backup's buffer f_memset( &pucBlkBuf[ uiBlkBufOffset], 0, m_uiBlockSize); f_memcpy( &pucBlkBuf[ uiBlkBufOffset], &m_dbHdr, sizeof( XFLM_DB_HDR)); pDbHdr = (XFLM_DB_HDR *)(&pucBlkBuf[ uiBlkBufOffset]); uiBlkBufOffset += m_uiBlockSize; // Fix up the log header if( !pDbHdr->ui8RflKeepFiles) { // Put zero in as the last transaction offset so that the current // RFL file will be created if it does not exist after the database // is restored. This has basically the same effect as setting the // offset to 512 if the RFL file has already been created. pDbHdr->ui32RflLastTransOffset = 0; uiCPTransOffset = 512; // Create new serial numbers for the RFL. We don't want anyone // to be able to branch into a "no-keep" RFL sequence. if (RC_BAD( rc = f_createSerialNumber( pDbHdr->ucLastTransRflSerialNum))) { goto Exit; } if (RC_BAD( rc = f_createSerialNumber( pDbHdr->ucNextRflSerialNum))) { goto Exit; } } else { uiCPTransOffset = (FLMUINT)pDbHdr->ui32RflLastTransOffset; if( !uiCPTransOffset) { uiCPTransOffset = 512; } } // Set the CP offsets to the last trans offsets. This is done // because the backup could actually read dirty (committed) blocks // from the cache, resulting in a backup set that contains blocks // that are more recent than the ones currently on disk. pDbHdr->ui32RflLastCPFileNum = pDbHdr->ui32RflCurrFileNum; pDbHdr->ui64RflLastCPTransID = pDbHdr->ui64CurrTransID; pDbHdr->ui32RflLastCPOffset = (FLMUINT32)uiCPTransOffset; pDbHdr->ui32RblEOF = (FLMUINT32)m_uiBlockSize; pDbHdr->ui32RblFirstCPBlkAddr = 0; // If a password was used, wrap the database key in that password if( pszPassword && *pszPassword) { FLMBYTE * pucTmp = NULL; // Need to get a lock on the database - mostly to prevent the very // unlikely possibility of another thread attempting to use the // database key at the same time we are. if ((m_pDb->m_uiFlags & FDB_HAS_FILE_LOCK) == 0) { if (RC_BAD( rc = m_pDb->dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bMustUnlock = TRUE; } rc = m_pDb->getDatabase()->m_pWrappingKey->getKeyToStore( &pucTmp, &pDbHdr->ui32DbKeyLen, (FLMBYTE *)pszPassword, NULL); if( bMustUnlock) { m_pDb->dbUnlock(); bMustUnlock = FALSE; } if( RC_BAD( rc)) { if( pucTmp) { f_free( &pucTmp); } goto Exit; } f_memcpy( pDbHdr->DbKey, pucTmp, pDbHdr->ui32DbKeyLen); f_free( &pucTmp); } // Header should already be in native format. flmAssert( !hdrIsNonNativeFormat( pDbHdr)); // Calculate and set the CRC pDbHdr->ui32HdrCRC = calcDbHdrCRC( pDbHdr); // Output the header if( RC_BAD( rc = pBackerStream->write( uiBlkBufOffset, pucBlkBuf))) { goto Exit; } // There is no way to quickly compute the actual number of bytes // that will be written to the backup. This is due, in part, to the // fact that the backup compresses unused space out of blocks before // storing them. backupInfo.ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize, m_uiLogicalEOF); uiBlockFileOffset = 0; uiBlockCount = 0; iFileNum = 1; for( ;;) { if( uiBlockFileOffset >= uiMaxFileSize) { uiBlockFileOffset = 0; iFileNum++; } uiBlkAddr = FSBlkAddress( iFileNum, uiBlockFileOffset); if( !FSAddrIsBelow( uiBlkAddr, m_uiLogicalEOF)) { break; } // Get the block if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, NULL, uiBlkAddr, NULL, &pSCache))) { goto Exit; } if( bFullBackup || pSCache->getBlockPtr()->ui64TransID > m_ui64LastBackupTransId) { uiBlkBufOffset = 0; if ((FLMUINT)pSCache->getBlockPtr()->ui16BlkBytesAvail > m_uiBlockSize - blkHdrSize( pSCache->getBlockPtr())) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Output the backup header for the block UD2FBA( (FLMUINT32)uiBlkAddr, &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]); uiBlkBufOffset += FLM_BACKER_BLK_HDR_SIZE; // Copy the block into the block buffer and prepare it // for writing. f_memcpy( &pucBlkBuf[ uiBlkBufOffset], pSCache->getBlockPtr(), m_uiBlockSize); // Encrypt the block if needed. if (RC_BAD( rc = m_pDb->m_pDatabase->encryptBlock( m_pDb->m_pDict, &pucBlkBuf[ uiBlkBufOffset]))) { goto Exit; } if (RC_BAD( rc = flmPrepareBlockToWrite( m_uiBlockSize, (F_BLK_HDR *)(&pucBlkBuf [uiBlkBufOffset])))) { goto Exit; } uiBlkBufOffset += m_uiBlockSize; // Write the block to the backup stream if( RC_BAD( rc = pBackerStream->write( uiBlkBufOffset, pucBlkBuf))) { goto Exit; } uiBlockCount++; } ScaReleaseCache( pSCache, FALSE); pSCache = NULL; uiBlockFileOffset += m_uiBlockSize; // Call the status callback if ((uiBlockCount - uiBlockCountLastCB) > 100) { if( ifpStatus) { backupInfo.ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize, uiBlkAddr); if( RC_BAD( rc = ifpStatus->backupStatus( backupInfo.ui64BytesToDo, backupInfo.ui64BytesDone))) { goto Exit; } } uiBlockCountLastCB = uiBlockCount; } } // Output the end-of-backup marker f_memset( pucBlkBuf, 0xFF, sizeof( FLM_BACKER_BLK_HDR_SIZE)); if( RC_BAD( rc = pBackerStream->write( FLM_BACKER_BLK_HDR_SIZE, pucBlkBuf))) { goto Exit; } // Pad the backup so that FlmDbRestore will never read more // data from the input stream than the backup wrote to it. uiBytesToPad = (FLMUINT32)(pBackerStream->getMTUSize() - (pBackerStream->getByteCount() % pBackerStream->getMTUSize())); if( uiBytesToPad < pBackerStream->getMTUSize()) { f_memset( pucBlkBuf, 0, uiBytesToPad); if( RC_BAD( rc = pBackerStream->write( uiBytesToPad, pucBlkBuf))) { goto Exit; } } // Because of the double buffering, we need to have one empty // buffer at the end of the file. f_memset( pucBlkBuf, 0, pBackerStream->getMTUSize()); if( RC_BAD( rc = pBackerStream->write( pBackerStream->getMTUSize(), pucBlkBuf))) { goto Exit; } if( RC_BAD( rc = pBackerStream->flush())) { goto Exit; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } if( pBackerStream) { pBackerStream->Release(); } // Call the status callback now that the background // thread has terminated. if( RC_OK( rc) && ifpStatus) { backupInfo.ui64BytesDone = backupInfo.ui64BytesToDo; (void)ifpStatus->backupStatus( backupInfo.ui64BytesToDo, backupInfo.ui64BytesDone); } if( pucBlkBuf) { f_free( &pucBlkBuf); } if( RC_OK( rc)) { m_bCompletedBackup = TRUE; } if ( bReleaseClient) { ifpClient->Release(); } m_backupRc = rc; return( rc); } /**************************************************************************** Desc : Ends the backup, updating the log header if needed. ****************************************************************************/ RCODE F_Backup::endBackup( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; if( !m_bCompletedBackup) { goto Exit; } // End the transaction flmAssert( m_eTransType != XFLM_NO_TRANS); if( RC_BAD( rc = m_pDb->abortTrans())) { goto Exit; } m_eTransType = XFLM_NO_TRANS; m_bTransStarted = FALSE; // Start an update transaction. if( RC_BAD( rc = m_pDb->beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Update log header fields m_pDb->m_pDatabase->m_uncommittedDbHdr.ui64LastBackupTransID = m_ui64TransId; // Since there may have been transactions during the backup, // we need to take into account the number of blocks that have // changed during the backup when updating the // ui32BlksChangedSinceBackup statistic. m_pDb->m_pDatabase->m_uncommittedDbHdr.ui32BlksChangedSinceBackup -= (FLMUINT32)m_uiBlkChgSinceLastBackup; // Bump the incremental backup sequence number if( m_eBackupType == XFLM_INCREMENTAL_BACKUP) { m_pDb->m_pDatabase->m_uncommittedDbHdr.ui32IncBackupSeqNum++; } // Always change the incremental backup serial number. This is // needed so that if the user performs a full backup, runs some // transactions against the database, performs another full backup, // and then performs an incremental backup we will know that the // incremental backup cannot be restored against the first full // backup. f_memcpy( m_pDb->m_pDatabase->m_uncommittedDbHdr.ucIncBackupSerialNum, m_ucNextIncSerialNum, XFLM_SERIAL_NUM_SIZE); // Commit the transaction and perform a checkpoint so that the // modified log header values will be written. bStartedTrans = FALSE; if( RC_BAD( m_pDb->commitTrans( 0, TRUE))) { goto Exit; } Exit: // Abort the active transaction (if any) if( bStartedTrans) { m_pDb->abortTrans(); } // Unset the backup flag if( m_pDb) { m_pDb->m_pDatabase->lockMutex(); m_pDb->m_pDatabase->m_bBackupActive = FALSE; m_pDb->m_pDatabase->unlockMutex(); } // Clear the object reset(); // Done. return( rc); } /**************************************************************************** Desc: Restores a database from backup Notes: This routine does not restore referenced BLOBs. ****************************************************************************/ RCODE F_DbSystem::dbRestore( const char * pszDbPath, // [IN] Path of database that is being restored. This is the // same path format that FlmDbCreate expects // (i.e., c:\flaim\flm.db). const char * pszDataDir, // [IN] Directory where data files are located. const char * pszRflDir, // [IN] RFL log file directory. NULL can be passed to indicate // that the files are located in the same directory as the // database (specified above). const char * pszBackupPath, // [IN] Directory and name of the backup file set. // This parameter is required only if the default // BACKER_READ_HOOK is used. Otherwise, NULL can be // passed as the value of this parameter. const char * pszPassword, // [IN] Password that was used durning the backup IF_RestoreClient * pRestoreObj, // [IN] Object to be used to read data from the backup set. IF_RestoreStatus * pRestoreStatus) // [IN] Object for reporting the status of the restore // operation { RCODE rc = NE_XFLM_OK; IF_FileHdl * pFileHdl = NULL; IF_FileHdl * pLockFileHdl = NULL; F_SuperFileHdl * pSFile = NULL; F_SuperFileClient SFileClient; FLMBYTE szBasePath[ F_PATH_MAX_SIZE]; char szTmpPath[ F_PATH_MAX_SIZE]; FLMUINT uiDbVersion; FLMUINT uiNextIncNum; eRestoreAction eAction = XFLM_RESTORE_ACTION_CONTINUE; // default action... FLMBOOL bRflPreserved; FLMBOOL bMutexLocked = FALSE; IF_Db * pDb = NULL; F_Database * pDatabase = NULL; F_FSRestore * pFSRestoreObj = NULL; FLMBOOL bOKToRetry; // Set up the callback if( !pRestoreObj) { if( !pszBackupPath || *pszBackupPath == 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } if( (pFSRestoreObj = f_new F_FSRestore) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pFSRestoreObj->setup( pszDbPath, pszBackupPath, pszRflDir))) { goto Exit; } // Note: If we wanted to be absolutely correct, we'd do an AddRef on // pFSRestoreObj because there's going to be two pointers pointing at // it. It really doesn't matter in this case, though because // pFSRestoreObj is local to this function and will get deleted before // the function exits. pRestoreObj = (IF_RestoreClient *)pFSRestoreObj; } // Get the base path flmGetDbBasePath( (char *)szBasePath, pszDbPath, NULL); // Lock the global mutex f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; // Look up the file using findDatabase to see if the file is already open. // May unlock and re-lock the global mutex.. if( RC_BAD( rc = findDatabase( pszDbPath, pszDataDir, &pDatabase))) { goto Exit; } // If the database is open, we cannot perform a restore if( pDatabase) { rc = RC_SET( NE_XFLM_DATABASE_OPEN); pDatabase = NULL; f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; goto Exit; } // Allocate the F_Database object. This will prevent other threads from // opening the database while the restore is being performed. if( RC_BAD( rc = allocDatabase( pszDbPath, pszDataDir, FALSE, &pDatabase))) { goto Exit; } // Unlock the global mutex f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // Create a lock file. If this fails, it could indicate // that the destination database exists and is in use by another // process. f_sprintf( szTmpPath, "%s.lck", szBasePath); if( RC_BAD( rc = flmCreateLckFile( szTmpPath, &pLockFileHdl))) { goto Exit; } // Create the control file and set up the super file object if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->createFile( pszDbPath, FLM_IO_RDWR, &pFileHdl))) { goto Exit; } // Allocate a super file object // NOTE: Do not use extended cache for this super-file object. if( (pSFile = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = SFileClient.setup( pszDbPath, pszDataDir, 0))) { goto Exit; } if( RC_BAD( rc = pSFile->setup( &SFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { goto Exit; } // Open the backup set if( RC_BAD( rc = pRestoreObj->openBackupSet())) { goto Exit; } // Restore the data in the backup set if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pRestoreStatus, pSFile, FALSE, &uiDbVersion, &uiNextIncNum, &bRflPreserved, &eAction, NULL))) { goto Exit; } // See if we should continue if( eAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } // Close the backup set if( RC_BAD( rc = pRestoreObj->close())) { goto Exit; } // Apply any available incremental backups. uiNextIncNum will be 0 if // the database version does not support incremental backups. if( uiNextIncNum) { FLMUINT uiCurrentIncNum; for( ;;) { uiCurrentIncNum = uiNextIncNum; if( RC_BAD( rc = pRestoreObj->openIncFile( uiCurrentIncNum))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { goto Exit; } } else { if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pRestoreStatus, pSFile, TRUE, &uiDbVersion, &uiNextIncNum, &bRflPreserved, &eAction, &bOKToRetry))) { RCODE tmpRc; if( !bOKToRetry) { // Cannot retry the operation or continue ... the // database is in an unknown state. goto Exit; } if( pRestoreStatus) { if( RC_BAD( tmpRc = pRestoreStatus->reportError( &eAction, rc))) { rc = tmpRc; goto Exit; } } if( eAction == XFLM_RESTORE_ACTION_RETRY || eAction == XFLM_RESTORE_ACTION_CONTINUE) { // Abort the current file (if any) if( RC_BAD( rc = pRestoreObj->abortFile())) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_CONTINUE) { // Break out and begin processing the RFL break; } // Otherwise, retry opening the incremental file uiNextIncNum = uiCurrentIncNum; continue; } goto Exit; } // See if we should continue if( eAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } // Close the current file if( RC_BAD( rc = pRestoreObj->close())) { goto Exit; } } } } // Force everything out to disk if( RC_BAD( rc = pSFile->flush())) { goto Exit; } pSFile->Release(); pSFile = NULL; // Don't do anything with the RFL if the preserve flag // isn't set. if( !bRflPreserved) { pRestoreObj = NULL; pRestoreStatus = NULL; } // Open the file and apply any available RFL files. The // lock file handle is passed to the openDatabase call so // that we don't have to give up our lock until the // restore is complete. Also, we don't want to resume // any indexing at this point. By not resuming the indexes, // we can perform a DB diff of two restored databases that // should be identical without having differences in the // tracker container due to background indexing. rc = openDatabase( pDatabase, pszDbPath, pszDataDir, pszRflDir, pszPassword, XFLM_DONT_RESUME_THREADS, TRUE, pRestoreObj, pRestoreStatus, pLockFileHdl, &pDb); pLockFileHdl = NULL; if( RC_BAD( rc)) { pDatabase = NULL; goto Exit; } // If a password was needed to open the database, we need to clear it so it //can be opened without a password. if (pszPassword && pszPassword[0] != 0) { if (RC_BAD( rc = pDb->wrapKey())) { goto Exit; } } // Close the database pDb->Release(); pDb = NULL; Exit: if( pSFile) { // Need to release the super file handle before cleaning up the // FFILE because the super file still has a reference to the // FFILE's file ID list. pSFile->Release(); } if( pDatabase) { if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } if (RC_BAD( rc)) { pDatabase->newDatabaseFinish( rc); } if( !pDatabase->m_uiOpenIFDbCount) { pDatabase->freeDatabase(); } f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; } if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } if( pDb) { pDb->Release(); } if( pFileHdl) { pFileHdl->Release(); } if( pLockFileHdl) { pLockFileHdl->Release(); } if( pFSRestoreObj) { pFSRestoreObj->Release(); } // If restore failed, remove all database files (excluding RFL files) if( RC_BAD( rc)) { dbRemove( pszDbPath, pszDataDir, NULL, FALSE); } return( rc); } /*************************************************************************** Desc : Restores a full or incremental backup *END************************************************************************/ FSTATIC RCODE flmRestoreFile( IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus, F_SuperFileHdl * pSFile, FLMBOOL bIncremental, FLMUINT * puiDbVersion, FLMUINT * puiNextIncSeqNum, FLMBOOL * pbRflPreserved, eRestoreAction * peAction, FLMBOOL * pbOKToRetry) { FLMUINT uiBytesWritten; FLMUINT uiLogicalEOF; FLMUINT uiBlkAddr; FLMUINT uiBlockCount = 0; FLMUINT uiBlockSize; FLMUINT uiDbVersion; FLMUINT uiMaxFileSize; FLMUINT uiBackupMaxFileSize; FLMUINT uiPriorBlkFile = 0; FLMUINT uiSectorSize; XFLM_DB_HDR * pDbHdr; FLMBYTE ucIncSerialNum[ XFLM_SERIAL_NUM_SIZE]; FLMBYTE ucNextIncSerialNum[ XFLM_SERIAL_NUM_SIZE]; FLMUINT uiIncSeqNum; FLMBYTE * pucBlkBuf = NULL; char szPath[ F_PATH_MAX_SIZE]; FLMUINT uiBlkBufSize; FLMUINT uiPriorBlkAddr = 0; FLMUINT64 ui64BytesToDo = 0; FLMUINT64 ui64BytesDone = 0; eDbBackupType eBackupType; F_BackerStream * pBackerStream = NULL; RCODE rc = NE_XFLM_OK; FLMUINT32 ui32CRC; F_BLK_HDR * pBlkHdr; // Initialize the "ok-to-retry" flag if( pbOKToRetry) { *pbOKToRetry = TRUE; } // Set up the backer stream object if( (pBackerStream = f_new F_BackerStream) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, pRestoreObj))) { goto Exit; } // Get the path of the .DB file (file 0). if( RC_BAD( rc = pSFile->getFilePath( 0, szPath))) { goto Exit; } // Get the sector size if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->getSectorSize( szPath, &uiSectorSize))) { goto Exit; } // Allocate a temporary buffer. Try to align the buffer on a sector // boundary to avoid memcpy operatons in the file system. uiBlkBufSize = FLM_BACKER_MTU_SIZE; if( uiSectorSize) { uiBlkBufSize = (((uiBlkBufSize / uiSectorSize) + 1) * uiSectorSize); } if( RC_BAD( rc = f_allocAlignedBuffer( uiBlkBufSize, (void **)&pucBlkBuf))) { goto Exit; } // Read and verify the backup header if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_MIN_DB_BLOCK_SIZE, pucBlkBuf))) { goto Exit; } if( FB2UD( &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]) != FLM_BACKER_VERSION) { rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); goto Exit; } if( f_strncmp( (const char *)&pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET], FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE) != 0) { rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); goto Exit; } uiBlockSize = (FLMUINT)FB2UW( &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); if( uiBlockSize > FLM_BACKER_MAX_DB_BLOCK_SIZE) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } // Get the maximum file size from the backup header. uiBackupMaxFileSize = (FLMUINT)FB2UD( &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); // Make sure the MTU is correct if( FB2UD( &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]) != FLM_BACKER_MTU_SIZE) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } // Make sure the backup type is correct eBackupType = (eDbBackupType)FB2UD( &pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]); if( (eBackupType == XFLM_INCREMENTAL_BACKUP && !bIncremental) || (eBackupType == XFLM_FULL_BACKUP && bIncremental)) { // Do not allow an incremental backup to be restored directly. The // only way to restore an incremental backup is to provide the // incremental files when requested by FlmDbRestore. Also, we don't // want to allow the user to mistakenly hand us a full backup when // we are expecting an incremental backup. rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // Grab the "next" incremental backup serial number f_memcpy( ucNextIncSerialNum, &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM], XFLM_SERIAL_NUM_SIZE); // Get the database version from the backup header uiDbVersion = FB2UD( &pucBlkBuf[ FLM_BACKER_DB_VERSION]); if( puiDbVersion) { *puiDbVersion = uiDbVersion; } // Seek to the database header block if( uiBlockSize > FLM_BACKER_MIN_DB_BLOCK_SIZE) { if( RC_BAD( rc = pBackerStream->read( uiBlockSize - FLM_BACKER_MIN_DB_BLOCK_SIZE, pucBlkBuf))) { goto Exit; } } // Read the database header block from the backup if( RC_BAD( rc = pBackerStream->read( uiBlockSize, pucBlkBuf))) { goto Exit; } // Sanity check - make sure the block size in the backup header // is the same as the size in the database header pDbHdr = (XFLM_DB_HDR *)pucBlkBuf; // Calculate the CRC before doing any conversions. ui32CRC = calcDbHdrCRC( pDbHdr); // Convert to native platform format, if necessary. if (hdrIsNonNativeFormat( pDbHdr)) { convertDbHdr( pDbHdr); } // Validate the checksum if (ui32CRC != pDbHdr->ui32HdrCRC) { rc = RC_SET( NE_XFLM_HDR_CRC); goto Exit; } if( uiBlockSize != (FLMUINT)pDbHdr->ui16BlockSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } // Compare the database version in the DB header with // the one extracted from the backup header if( (FLMUINT)pDbHdr->ui32DbVersion != uiDbVersion) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } uiMaxFileSize = (FLMUINT)pDbHdr->ui32MaxFileSize; // Make sure the maximum block file size matches what was read from the // backup header. if( uiBackupMaxFileSize != uiMaxFileSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } // Get the logical EOF from the log header uiLogicalEOF = (FLMUINT)pDbHdr->ui32LogicalEOF; // Are RFL files being preserved? if( pbRflPreserved) { *pbRflPreserved = pDbHdr->ui8RflKeepFiles ? TRUE : FALSE; } // Get the incremental backup sequence number uiIncSeqNum = (FLMUINT)pDbHdr->ui32IncBackupSeqNum; *puiNextIncSeqNum = uiIncSeqNum; if( bIncremental) { (*puiNextIncSeqNum)++; } // Get information about the incremental backup if( bIncremental) { FLMUINT uiTmp; XFLM_DB_HDR dbHdr; f_memcpy( ucIncSerialNum, pDbHdr->ucIncBackupSerialNum, XFLM_SERIAL_NUM_SIZE); // Compare the incremental backup sequence number to the value in the // database's DB header. if( RC_BAD( rc = pSFile->readBlock( 0, sizeof( XFLM_DB_HDR), &dbHdr, &uiTmp))) { goto Exit; } if (hdrIsNonNativeFormat( &dbHdr)) { convertDbHdr( &dbHdr); } if( (FLMUINT)dbHdr.ui32IncBackupSeqNum != uiIncSeqNum) { rc = RC_SET( NE_XFLM_INVALID_FILE_SEQUENCE); goto Exit; } // Compare the incremental backup serial number to the value in the // database's log header. if( f_memcmp( ucIncSerialNum, dbHdr.ucIncBackupSerialNum, XFLM_SERIAL_NUM_SIZE) != 0) { rc = RC_SET( NE_XFLM_SERIAL_NUM_MISMATCH); goto Exit; } // Increment the incremental backup sequence number pDbHdr->ui32IncBackupSeqNum = (FLMUINT32)(uiIncSeqNum + 1); } // At the start of a backup, either incremental or full, // we generate a new incremental serial number. This is needed so // that if the user performs a full backup, runs some transactions // against the database, performs another full backup, and then // performs an incremental backup we will know that the incremental // backup cannot be restored against the first full backup. // Since the new serial number is not written to the database's log // header until after the backup completes, we need to put the // new serial number in the log header during the restore. In doing // so, the log header will contain the correct serial number for a // subsequent incremental backup that may have been made. f_memcpy( pDbHdr->ucIncBackupSerialNum, ucNextIncSerialNum, XFLM_SERIAL_NUM_SIZE); // DB Header is in native format. Set the CRC // before writing it out. pDbHdr->ui32HdrCRC = calcDbHdrCRC( pDbHdr); pDbHdr = NULL; // Set the "ok-to-retry" flag if( pbOKToRetry) { *pbOKToRetry = FALSE; } // Write the database header if( RC_BAD( rc = pSFile->writeBlock( 0, uiBlockSize, pucBlkBuf, &uiBytesWritten))) { goto Exit; } // The status callback will give a general idea of how much work // is left to do. We don't have any way to get the total size // of the stream to give a correct count, so a close estimate // will have to suffice. ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize, uiLogicalEOF); // Write the blocks in the backup file to the database for (;;) { if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_BLK_HDR_SIZE, pucBlkBuf))) { goto Exit; } uiBlockCount++; uiBlkAddr = FB2UD( &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]); // Are we done? if( uiBlkAddr == 0xFFFFFFFF) { break; } if( !uiBlkAddr || !FSAddrIsBelow( uiBlkAddr, uiLogicalEOF) || (uiPriorBlkAddr && !FSAddrIsBelow( uiPriorBlkAddr, uiBlkAddr))) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } // Read and process the block if( RC_BAD( rc = pBackerStream->read( uiBlockSize, pucBlkBuf))) { goto Exit; } pBlkHdr = (F_BLK_HDR *)pucBlkBuf; // Convert the entire block to native format if necessary. if (RC_BAD( rc = flmPrepareBlockForUse( uiBlockSize, pBlkHdr))) { if (rc == NE_XFLM_BLOCK_CRC) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); } goto Exit; } if( (FLMUINT)pBlkHdr->ui32BlkAddr != uiBlkAddr) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } // Prepare the block for writing. if (RC_BAD( rc = flmPrepareBlockToWrite( uiBlockSize, pBlkHdr))) { goto Exit; } // Write the block to the database if( RC_BAD( rc = pSFile->writeBlock( uiBlkAddr, uiBlockSize, pucBlkBuf, &uiBytesWritten))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { // Create a new block file if( FSGetFileNumber( uiBlkAddr) != (uiPriorBlkFile + 1)) { rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); goto Exit; } if( RC_BAD( rc = pSFile->createFile( FSGetFileNumber( uiBlkAddr)))) { goto Exit; } if( RC_BAD( rc = pSFile->writeBlock( uiBlkAddr, uiBlockSize, pucBlkBuf, &uiBytesWritten))) { goto Exit; } } else { goto Exit; } } uiPriorBlkAddr = uiBlkAddr; uiPriorBlkFile = FSGetFileNumber( uiBlkAddr); if( pRestoreStatus && (uiBlockCount & 0x7F) == 0x7F) { ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize, uiBlkAddr); if( RC_BAD( rc = pRestoreStatus->reportProgress( peAction, ui64BytesToDo, ui64BytesDone))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } } if( pRestoreStatus) { // Call the status callback one last time. ui64BytesDone = ui64BytesToDo; if( RC_BAD( rc = pRestoreStatus->reportProgress( peAction, ui64BytesToDo, ui64BytesDone))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { // It is safe to jump to exit at this point goto Exit; } } Exit: if( pucBlkBuf) { f_freeAlignedBuffer( (void **)&pucBlkBuf); } if( pBackerStream) { pBackerStream->Release(); } return( rc); } /**************************************************************************** Desc: Constructor ****************************************************************************/ F_DefaultBackupClient::F_DefaultBackupClient( const char * pszBackupPath) { m_pMultiFileHdl = NULL; m_ui64Offset = 0; m_rc = NE_XFLM_OK; f_strncpy( m_szPath, pszBackupPath, F_PATH_MAX_SIZE - 1); } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_DefaultBackupClient::~F_DefaultBackupClient() { if (m_pMultiFileHdl) { m_pMultiFileHdl->closeFile(); m_pMultiFileHdl->Release(); } } /**************************************************************************** Desc: Default hook for creating a backup file set ****************************************************************************/ RCODE F_DefaultBackupClient::WriteData( const void * pvBuffer, FLMUINT uiBytesToWrite) { FLMUINT uiBytesWritten; RCODE rc = m_rc; if( RC_BAD( rc)) { goto Exit; } if( m_pMultiFileHdl == 0) { // Remove any existing backup files if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl))) { goto Exit; } if( RC_BAD( rc = m_pMultiFileHdl->deleteMultiFile( m_szPath)) && rc != NE_FLM_IO_PATH_NOT_FOUND && rc != NE_FLM_IO_INVALID_FILENAME) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; goto Exit; } if( RC_BAD( rc = m_pMultiFileHdl->createFile( m_szPath))) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; goto Exit; } } rc = m_pMultiFileHdl->write( m_ui64Offset, uiBytesToWrite, (FLMBYTE *)pvBuffer, &uiBytesWritten); m_ui64Offset += uiBytesWritten; Exit: if( RC_BAD( rc)) { m_rc = rc; if( m_pMultiFileHdl) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; } } return( rc); } // F_BackerStream methods /**************************************************************************** Desc: Constructor ****************************************************************************/ F_BackerStream::F_BackerStream( void) { m_bSetup = FALSE; m_bFirstRead = TRUE; m_bFinalRead = FALSE; m_ui64ByteCount = 0; m_uiBufOffset = 0; m_pRestoreObj = NULL; m_hDataSem = F_SEM_NULL; m_hIdleSem = F_SEM_NULL; m_pThread = NULL; m_rc = NE_XFLM_OK; m_pucInBuf = NULL; m_puiInOffset = NULL; m_pucOutBuf = NULL; m_puiOutOffset = NULL; m_pucBufs[ 0] = NULL; m_pucBufs[ 1] = NULL; m_uiOffsets[ 0] = 0; m_uiOffsets[ 1] = 0; m_uiMTUSize = 0; m_pClient = NULL; } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_BackerStream::~F_BackerStream( void) { shutdownThreads(); if( m_hDataSem != F_SEM_NULL) { f_semDestroy( &m_hDataSem); } if( m_hIdleSem != F_SEM_NULL) { f_semDestroy( &m_hIdleSem); } if( m_pucBufs[ 0]) { f_free( &m_pucBufs[ 0]); } if( m_pucBufs[ 1]) { f_free( &m_pucBufs[ 1]); } } /**************************************************************************** Desc: Start any background threads ****************************************************************************/ RCODE F_BackerStream::startThreads( void) { RCODE rc = NE_XFLM_OK; if( m_pThread) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } // The semaphore handles better be null flmAssert( m_hDataSem == F_SEM_NULL); flmAssert( m_hIdleSem == F_SEM_NULL); // Create a semaphore to signal the background thread // that data is available if( RC_BAD( rc = f_semCreate( &m_hDataSem))) { goto Exit; } // Create a semaphore to signal when the background thread // is idle if( RC_BAD( rc = f_semCreate( &m_hIdleSem))) { goto Exit; } // Start the thread if( m_pClient) { if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( &m_pThread, F_BackerStream::writeThread, "backup", 0, 0, (void *)this))) { goto Exit; } } else if( m_pRestoreObj) { if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( &m_pThread, F_BackerStream::readThread, "restore", 0, 0, (void *)this))) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Shut down any background threads ****************************************************************************/ void F_BackerStream::shutdownThreads( void) { if( m_pThread) { // Shut down the background read or write thread. m_pThread->setShutdownFlag(); f_semSignal( m_hDataSem); m_pThread->stopThread(); m_pThread->Release(); m_pThread = NULL; // Now that the thread has terminated, it is safe // to destroy the data and idle semaphores. f_semDestroy( &m_hDataSem); f_semDestroy( &m_hIdleSem); } } /**************************************************************************** Desc: Setup method to use the backer stream as an input stream ****************************************************************************/ RCODE F_BackerStream::setup( FLMUINT uiMTUSize, IF_RestoreClient * pRestoreObj) { RCODE rc = NE_XFLM_OK; flmAssert( pRestoreObj); flmAssert( !m_bSetup); m_pRestoreObj = pRestoreObj; m_uiMTUSize = uiMTUSize; if( RC_BAD( rc = _setup())) { goto Exit; } // Fire up the background threads if( RC_BAD( rc = startThreads())) { goto Exit; } m_bSetup = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Setup method to use the backer stream as an output stream ****************************************************************************/ RCODE F_BackerStream::setup( FLMUINT uiMTUSize, IF_BackupClient * pClient) { RCODE rc = NE_XFLM_OK; flmAssert( pClient); flmAssert( !m_bSetup); m_pClient = pClient; m_uiMTUSize = uiMTUSize; if( RC_BAD( rc = _setup())) { goto Exit; } // Fire up the background threads if( RC_BAD( rc = startThreads())) { goto Exit; } m_bSetup = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Performs setup operations common to read and write streams ****************************************************************************/ RCODE F_BackerStream::_setup( void) { RCODE rc = NE_XFLM_OK; // Allocate a buffer for reading or writing blocks if( (m_uiMTUSize < (2 * FLM_BACKER_MAX_DB_BLOCK_SIZE)) || m_uiMTUSize % FLM_BACKER_MAX_DB_BLOCK_SIZE) { rc = RC_SET( NE_XFLM_INVALID_PARM); goto Exit; } // Allocate buffers for reading or writing if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 0]))) { goto Exit; } if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 1]))) { goto Exit; } m_pucInBuf = m_pucBufs[ 0]; m_puiInOffset = &m_uiOffsets[ 0]; m_pucOutBuf = m_pucBufs[ 1]; m_puiOutOffset = &m_uiOffsets[ 1]; Exit: return( rc); } /**************************************************************************** Desc: Reads data from the input stream ****************************************************************************/ RCODE F_BackerStream::read( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesRead) { FLMUINT uiBufSize; FLMBYTE * pucBuf; FLMUINT uiBytesRead = 0; FLMUINT uiBytesAvail; RCODE rc = NE_XFLM_OK; flmAssert( m_bSetup); flmAssert( m_pRestoreObj); flmAssert( uiLength); if( m_bFirstRead) { m_bFirstRead = FALSE; // Prime the pump. Call signalThread twice ... once to // get the first chunk of data and a second time to have // the background thread pre-fetch the next chunk. A backup // will always have at least two MTU data chunks, so we should // never get an IO_END_OF_FILE error. If we do, the restore // operation needs to abort (which will happen because the // error will be returned to the caller of this routine). if( RC_BAD( rc = signalThread()) || RC_BAD( rc = signalThread())) { goto Exit; } } while( uiLength) { uiBufSize = *m_puiOutOffset; pucBuf = m_pucOutBuf; uiBytesAvail = uiBufSize - m_uiBufOffset; flmAssert( uiBytesAvail); if( uiBytesAvail < uiLength) { f_memcpy( &pucData[ uiBytesRead], &pucBuf[ m_uiBufOffset], uiBytesAvail); m_uiBufOffset += uiBytesAvail; uiBytesRead += uiBytesAvail; uiLength -= uiBytesAvail; } else { f_memcpy( &pucData[ uiBytesRead], &pucBuf[ m_uiBufOffset], uiLength); m_uiBufOffset += uiLength; uiBytesRead += uiLength; uiLength = 0; } if( m_uiBufOffset == uiBufSize) { m_uiBufOffset = 0; if( RC_BAD( rc = signalThread())) { // Since we are reading MTU-sized units and the restore // code knows when to stop reading, we should never // get an IO_END_OF_FILE error back from a call to // signalThread(). If we do, we need to return the // error to the caller (FlmDbRestore). goto Exit; } } } Exit: if( puiBytesRead) { *puiBytesRead = uiBytesRead; } m_ui64ByteCount += (FLMUINT64)uiBytesRead; return( rc); } /**************************************************************************** Desc: Writes data to the output stream ****************************************************************************/ RCODE F_BackerStream::write( FLMUINT uiLength, FLMBYTE * pucData, FLMUINT * puiBytesWritten) { FLMUINT uiMaxWriteSize; FLMUINT uiBytesWritten = 0; RCODE rc = NE_XFLM_OK; flmAssert( m_bSetup); flmAssert( m_pClient); flmAssert( uiLength); while( uiLength) { uiMaxWriteSize = m_uiMTUSize - *m_puiInOffset; flmAssert( uiMaxWriteSize); if( uiMaxWriteSize < uiLength) { f_memcpy( &m_pucInBuf[ *m_puiInOffset], &pucData[ uiBytesWritten], uiMaxWriteSize); (*m_puiInOffset) += uiMaxWriteSize; uiBytesWritten += uiMaxWriteSize; uiLength -= uiMaxWriteSize; } else { f_memcpy( &m_pucInBuf[ *m_puiInOffset], &pucData[ uiBytesWritten], uiLength); (*m_puiInOffset) += uiLength; uiBytesWritten += uiLength; uiLength = 0; } if( (*m_puiInOffset) == m_uiMTUSize) { if( RC_BAD( rc = signalThread())) { goto Exit; } } } Exit: if( puiBytesWritten) { *puiBytesWritten = uiBytesWritten; } m_ui64ByteCount += (FLMUINT64)uiBytesWritten; return( rc); } /**************************************************************************** Desc: Flushes any pending writes to the output stream ****************************************************************************/ RCODE F_BackerStream::flush( void) { RCODE rc = NE_XFLM_OK; flmAssert( m_bSetup); if( m_pClient && m_pThread) { if( *m_puiInOffset) { if( RC_BAD( rc = signalThread())) { goto Exit; } } // Wait for the background thread to become idle. When it // does, we know that all writes have completed. if( RC_BAD( rc = f_semWait( m_hIdleSem, F_WAITFOREVER))) { goto Exit; } // If the background thread set an error code, we need to return it. rc = m_rc; // At this point, we know the background thread is either waiting // for the data semaphore to be signaled or it has exited due to // an error. We need to re-signal the idle semaphore so that // other F_BackerStream calls (i.e., additional calls to // flush, etc.) will not block waiting for it to be signaled // since it won't be signaled by the background thread until // after the data semaphore has been signaled again. f_semSignal( m_hIdleSem); // Jump to exit if we have a bad rc if( RC_BAD( rc)) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Signals the read or write thread indicating that data is needed or that data is available. ****************************************************************************/ RCODE F_BackerStream::signalThread( void) { FLMBYTE * pucTmp; FLMUINT * puiTmp; RCODE rc = NE_XFLM_OK; flmAssert( m_bSetup); // Return an error if we don't have a thread. if( !m_pThread) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } // Wait for the thread to become idle if( RC_BAD( rc = f_semWait( m_hIdleSem, F_WAITFOREVER))) { goto Exit; } if( RC_BAD( rc = m_rc)) { // If m_rc is bad, we know that the background thread has // exited and will not signal the idle semaphore again. // Thus, we will re-signal the idle semaphore so that if the // code using this class happens to call flush() or some // other method that waits on the idle semaphore, we // won't wait forever on something that will never happen. f_semSignal( m_hIdleSem); // Check the error code if( rc == NE_FLM_IO_END_OF_FILE && !m_bFinalRead) { m_bFinalRead = TRUE; } else { goto Exit; } } pucTmp = m_pucOutBuf; puiTmp = m_puiOutOffset; m_pucOutBuf = m_pucInBuf; m_puiOutOffset = m_puiInOffset; m_pucInBuf = pucTmp; m_puiInOffset = puiTmp; *(m_puiInOffset) = 0; if( !m_bFinalRead) { // Signal the thread to read or write data f_semSignal( m_hDataSem); } Exit: return( rc); } /**************************************************************************** Desc: This thread reads data in the background ****************************************************************************/ RCODE FLMAPI F_BackerStream::readThread( IF_Thread * pThread) { F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); RCODE rc = NE_XFLM_OK; for( ;;) { f_semSignal( pBackerStream->m_hIdleSem); if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, F_WAITFOREVER))) { goto Exit; } if( pThread->getShutdownFlag()) { break; } if( RC_BAD( rc = pBackerStream->m_pRestoreObj->read( pBackerStream->m_uiMTUSize, pBackerStream->m_pucInBuf, pBackerStream->m_puiInOffset))) { goto Exit; } } Exit: pBackerStream->m_rc = rc; f_semSignal( pBackerStream->m_hIdleSem); return( rc); } /**************************************************************************** Desc: This thread writes data in the background ****************************************************************************/ RCODE FLMAPI F_BackerStream::writeThread( IF_Thread * pThread) { F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); RCODE rc = NE_XFLM_OK; for( ;;) { f_semSignal( pBackerStream->m_hIdleSem); if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, F_WAITFOREVER))) { goto Exit; } if( *(pBackerStream->m_puiOutOffset)) { if( RC_BAD( rc = pBackerStream->m_pClient->WriteData( pBackerStream->m_pucOutBuf, *(pBackerStream->m_puiOutOffset)))) { goto Exit; } // Reset *puiOutOffset so that we won't re-write // the same data again if we receive a shut-down // signal. *(pBackerStream->m_puiOutOffset) = 0; } // Need to put the thread shutdown check here // so that if the thread is signaled twice, // once to do final work and once to shut down, // we will actually do the work before we exit. if( pThread->getShutdownFlag()) { break; } } Exit: pBackerStream->m_rc = rc; f_semSignal( pBackerStream->m_hIdleSem); return( rc); } libxflaim-5.1.969/src/flchkix.cpp0000644000175000017500000005275010511001742020171 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Check logical integrity of indexes. // // Tabs: 3 // // Copyright (c) 1992, 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flchkix.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /******************************************************************** Desc: This routine builds a key tree from a collated key ********************************************************************/ RCODE F_DbCheck::keyToVector( FLMBYTE * pucKey, FLMUINT uiKeyLen, IF_DataVector ** ppKeyRV ) { RCODE rc = NE_XFLM_OK; // Generate the key tree if ((*ppKeyRV = f_new F_DataVector) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } (*ppKeyRV)->reset(); rc = (*ppKeyRV)->inputKey( m_pDb, m_pIxd->uiIndexNum, pucKey, uiKeyLen); Exit: return( rc); } /******************************************************************** Desc: Retrieves the next key from the sorted result set *********************************************************************/ RCODE F_DbCheck::chkGetNextRSKey( void) { RCODE rc = NE_XFLM_OK; RS_IX_KEY * pCurrRSKey; // Swap current and last key pointers - this allows us to always keep // the last key without having to memcpy the keys. pCurrRSKey = m_pCurrRSKey; m_pCurrRSKey = m_pPrevRSKey; m_pPrevRSKey = pCurrRSKey; pCurrRSKey = m_pCurrRSKey; if (pCurrRSKey == NULL) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // Get the next key - call getFirst because we are deleting the // entry after we process it. if (RC_BAD( rc = m_pIxRSet->getFirst( m_pDb, m_pIxd, NULL, pCurrRSKey->pucRSKeyBuf, XFLM_MAX_KEY_SIZE, &pCurrRSKey->uiRSKeyLen, pCurrRSKey->pucRSDataBuf, XFLM_MAX_KEY_SIZE, &pCurrRSKey->uiRSDataLen))) { goto Exit; } // Verify that the key meets the minimum length requirements flmAssert( pCurrRSKey->uiRSKeyLen); Exit: return( rc); } /******************************************************************** Desc: Verifies the current index key against the result set. *********************************************************************/ RCODE F_DbCheck::verifyIXRSet( STATE_INFO * pStateInfo) { RCODE rc = NE_XFLM_OK; FLMINT iCmpVal = 0; FLMUINT uiIteration = 0; FLMBOOL bRSetEmpty = FALSE; RS_IX_KEY * pCurrRSKey; RS_IX_KEY * pPrevRSKey; if (!m_pCurrRSKey) { m_pCurrRSKey = &m_IxKey1; m_pPrevRSKey = &m_IxKey2; } // Compare index and result set keys while (!bRSetEmpty) { if (m_bGetNextRSKey) { // Get the next key from the result set. If the result set // is empty, then m_uiRSKeyLen will be set to // zero, forcing the problem to be resolved below. if (RC_BAD( rc = chkGetNextRSKey())) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { // Set bRSetEmpty to TRUE so that the loop will exit after the // current key is resolved. Otherwise, conflict resolution on // the current key will be repeated forever (infinite loop). bRSetEmpty = TRUE; rc = NE_XFLM_OK; } else { goto Exit; } } else { // Update statistics m_Progress.ui64NumKeysExamined++; } } pCurrRSKey = m_pCurrRSKey; pPrevRSKey = m_pPrevRSKey; if (pCurrRSKey->uiRSKeyLen == 0 || bRSetEmpty) { // We don't have a key because we got an EOF when // reading the result set. Need to resolve the // fact that the result set does not have a key // that is found in the index. Set iCmpVal to // 1 to force this resolution. iCmpVal = 1; } else { // Compare the index key and result set key. if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, NULL, NULL, NULL, TRUE, TRUE, pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen, pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen, &iCmpVal))) { goto Exit; } } if (iCmpVal < 0) { // The result set key is less than the index key. This could mean // that the result set key needs to be added to the index. if ( RC_BAD( rc = resolveIXMissingKey( pStateInfo))) { // If the key was added to the index (bReposition == TRUE) // or we got some other error, we don't want to get the next // result set key. m_bGetNextRSKey = (bRSetEmpty ? TRUE : FALSE); goto Exit; } else { // False alarm. The index is missing the key because of // a concurrent update. We want to get the next RS key. m_bGetNextRSKey = TRUE; // Delete the current key in the result set so we don't hit it again. if (RC_BAD( rc = m_pIxRSet->deleteEntry( m_pDb, m_pIxd, pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen))) { goto Exit; } } } else if (iCmpVal > 0) { // The result set key is greater than the index key. This could mean // that the index key needs to be deleted from the index. Whether we // delete the index key or not, we don't need to get the next result // set key, but we do want to reposition and get the next index key. m_bGetNextRSKey = (bRSetEmpty ? TRUE : FALSE); if ( RC_BAD( rc = resolveRSetMissingKey( pStateInfo))) { goto Exit; } break; } else { // The index and result set keys are equal. We want to get // the next result set key. m_bGetNextRSKey = TRUE; // Delete the current key in the result set so we don't hit it again. if (RC_BAD( rc = m_pIxRSet->deleteEntry( m_pDb, m_pIxd, pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen))) { goto Exit; } break; } // Call the yield function periodically uiIteration++; if (!(uiIteration & 0x1F) ) { f_yieldCPU(); } } Exit: return( rc); } /******************************************************************** Desc: Resolves the case of a key found in the result set but not in the current index. *********************************************************************/ RCODE F_DbCheck::resolveIXMissingKey( STATE_INFO * pStateInfo) { FLMBOOL bKeyInDoc; FLMBOOL bKeyInIndex; RCODE rc = NE_XFLM_OK; FLMBOOL bFixCorruption = FALSE; RS_IX_KEY * pCurrRSKey = m_pCurrRSKey; XFLM_INDEX_STATUS ixStatus; // Determine if the record generates the key and if the // key is found in the index. if (RC_BAD( rc = getKeySource( pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen, &bKeyInDoc, &bKeyInIndex))) { if (rc == NE_XFLM_INDEX_OFFLINE) { rc = NE_XFLM_OK; } goto Exit; } // If the record does not generate the key or the key+ref is in the index, // the index is not corrupt. if (!bKeyInDoc || bKeyInIndex) { m_Progress.ui64NumConflicts++; goto Exit; } // Otherwise, the index is corrupt. // Update statistics m_Progress.ui64NumDocKeysNotFound++; m_pDbInfo->m_uiLogicalCorruptions++; // Report the error if (RC_BAD( rc = reportIxError( pStateInfo, FLM_KEY_NOT_IN_KEY_REFSET, pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen, &bFixCorruption))) { goto Exit; } // Exit if the application does not want to repair the corruption. if (!bFixCorruption) { // Set the logical corruption flag m_bIndexCorrupt = TRUE; goto Exit; } // Fix the corruption if (RC_BAD( rc = m_pDb->indexStatus( m_pIxd->uiIndexNum, &ixStatus))) { goto Exit; } if (ixStatus.ui64LastDocumentIndexed == (FLMUINT64)~0 && ixStatus.eState != XFLM_INDEX_SUSPENDED) { // Update statistics m_pDbInfo->m_uiLogicalRepairs++; // Add the key if (RC_OK( rc = addDelKeyRef( pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen, FALSE))) { goto Exit; } else { // Set the logical corruption flag m_bIndexCorrupt = TRUE; } } else { // Set the logical corruption flag m_bIndexCorrupt = TRUE; } Exit: return( rc); } /******************************************************************** Desc: Resolves the case of a key found in the current index but not in the result set. *********************************************************************/ RCODE F_DbCheck::resolveRSetMissingKey( STATE_INFO * pStateInfo) { RCODE rc = NE_XFLM_OK; FLMBOOL bKeyInDoc; FLMBOOL bKeyInIndex; FLMBOOL bFixCorruption = FALSE; XFLM_INDEX_STATUS ixStatus; // See if the key is found in the index and/or generated // by the document. if (RC_BAD( rc = getKeySource( pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen, &bKeyInDoc, &bKeyInIndex))) { if (rc == NE_XFLM_INDEX_OFFLINE) { rc = NE_XFLM_OK; } goto Exit; } // If the key is generated by the record or the key is not found // in the index, the index is not corrupt. if (bKeyInDoc || !bKeyInIndex) { m_Progress.ui64NumConflicts++; goto Exit; } // Otherwise, the index is corrupt. // Update statistics m_Progress.ui64NumKeysNotFound++; m_pDbInfo->m_uiLogicalCorruptions++; // Report the error if (RC_BAD( rc = reportIxError( pStateInfo, FLM_IX_KEY_NOT_FOUND_IN_REC, pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen, &bFixCorruption))) { goto Exit; } // Exit if the application does not want to repair the corruption. if (!bFixCorruption) { // Set the logical corruption flag m_bIndexCorrupt = TRUE; goto Exit; } // Fix the corruption if (RC_BAD( rc = m_pDb->indexStatus( m_pIxd->uiIndexNum, &ixStatus))) { goto Exit; } if (ixStatus.ui64LastDocumentIndexed == (FLMUINT64)~0 && ixStatus.eState != XFLM_INDEX_SUSPENDED) { // Update statistics m_pDbInfo->m_uiLogicalRepairs++; // Delete the reference from the index if (RC_BAD( rc = addDelKeyRef( pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen, TRUE))) { // Set the logical corruption flag m_bIndexCorrupt = TRUE; } goto Exit; } else { // Set the logical corruption flag m_bIndexCorrupt = TRUE; } Exit: return( rc); } /******************************************************************** Desc: Verifies that a key component is actually in the document. *********************************************************************/ RCODE F_DbCheck::verifyComponentInDoc( ICD * pIcd, FLMUINT uiComponent, F_DataVector * pKey, FLMBOOL * pbInDoc ) { RCODE rc = NE_XFLM_OK; F_DOMNode * pDOMNode = NULL; FLMUINT64 ui64NodeId; FLMUINT uiNameId; // Get the nodeId. if ((ui64NodeId = pKey->getID( uiComponent)) != 0) { if (RC_BAD( rc = m_pDb->getNode( m_pIxd->uiCollectionNum, ui64NodeId, &pDOMNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } *pbInDoc = FALSE; goto Exit; } // No need to verify the name ID if it is an element root tag. if( pIcd->uiFlags & ICD_IS_ATTRIBUTE) { if( RC_BAD( rc = pDOMNode->hasAttribute( m_pDb, pIcd->uiDictNum))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } *pbInDoc = FALSE; goto Exit; } } else if( pIcd->uiDictNum != ELM_ROOT_TAG) { if (RC_BAD( rc = pDOMNode->getNameId( m_pDb, &uiNameId))) { goto Exit; } if (uiNameId != pIcd->uiDictNum) { *pbInDoc = FALSE; goto Exit; } } // Make sure these are the same type. if ((pKey->isAttr( uiComponent) && !(pIcd->uiFlags & ICD_IS_ATTRIBUTE)) || (!pKey->isAttr( uiComponent) && (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { goto Exit; } // Verify that the node belongs to the document. if (pKey->getDocumentID() != pDOMNode->getDocumentId()) { *pbInDoc = FALSE; goto Exit; } } Exit: if (pDOMNode) { pDOMNode->Release(); } return( rc); } /******************************************************************** Desc: Determines if a key is generated by the current document and/or if the key is found in the current index *********************************************************************/ RCODE F_DbCheck::getKeySource( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL * pbKeyInDoc, FLMBOOL * pbKeyInIndex ) { RCODE rc = NE_XFLM_OK; ICD * pIcd; FLMUINT uiComponent; F_DataVector key; // Initialize return values. *pbKeyInDoc = FALSE; *pbKeyInIndex = FALSE; if (m_pIxd->uiFlags & IXD_OFFLINE) { rc = RC_SET( NE_XFLM_INDEX_OFFLINE); goto Exit; } // See if the key is in the index. if (RC_BAD( rc = chkVerifyKeyExists( pucKey, uiKeyLen, pbKeyInIndex))) { goto Exit; } // Put the key into a data vector structure. if (RC_BAD( rc = key.inputKey( m_pDb, m_pIxd->uiIndexNum, pucKey, uiKeyLen))) { goto Exit; } // See if all of the nodes referenced from the key actually are in the // document. This includes data nodes and context nodes. *pbKeyInDoc = TRUE; uiComponent = 0; pIcd = m_pIxd->pFirstKey; while (pIcd) { if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc))) { goto Exit; } if (!(*pbKeyInDoc)) { goto Exit; } uiComponent++; pIcd = pIcd->pNextKeyComponent; } // Go through data components. pIcd = m_pIxd->pFirstData; while (pIcd) { if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc))) { goto Exit; } if (!(*pbKeyInDoc)) { goto Exit; } uiComponent++; pIcd = pIcd->pNextDataComponent; } // Go through context components pIcd = m_pIxd->pFirstContext; while (pIcd) { if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc))) { goto Exit; } if (!(*pbKeyInDoc)) { goto Exit; } uiComponent++; pIcd = pIcd->pNextKeyComponent; } Exit: return( rc); } /******************************************************************** Desc: Verify that a key is (or is not) found in an index. *********************************************************************/ RCODE F_DbCheck::chkVerifyKeyExists( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL * pbFoundRV) { RCODE rc = NE_XFLM_OK; F_Btree * pbtree = NULL; IXKeyCompare compareObject; compareObject.setIxInfo( m_pDb, m_pIxd); compareObject.setCompareNodeIds( TRUE); compareObject.setCompareDocId( TRUE); *pbFoundRV = FALSE; // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } if( RC_BAD( rc = pbtree->btOpen( m_pDb, &m_pIxd->lfInfo, (m_pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE, FALSE, &compareObject))) { goto Exit; } if( RC_BAD( rc = pbtree->btLocateEntry( pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT))) { if( rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } *pbFoundRV = TRUE; Exit: if (pbtree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); } return( rc ); } /*************************************************************************** Desc: This routine adds or deletes an index key and/or reference. *****************************************************************************/ RCODE F_DbCheck::addDelKeyRef( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bDelete) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKeyBuf[ sizeof( KREF_ENTRY) + XFLM_MAX_KEY_SIZE]; KREF_ENTRY * pKrefEntry = (KREF_ENTRY *)(&ucKeyBuf[ 0]); FLMBOOL bStartedUpdate = FALSE; FLMBOOL bKeyInDoc; FLMBOOL bKeyInIndex; // Start an update transaction, if necessary if (RC_BAD( rc = startUpdate())) { goto Exit; } bStartedUpdate = TRUE; // Verify that the state has not changed if (RC_BAD( rc = getKeySource( pucKey, uiKeyLen, &bKeyInDoc, &bKeyInIndex))) { goto Exit; } if ((bKeyInIndex && bDelete) || (!bKeyInIndex && !bDelete)) { // Setup the KrefEntry structure f_memcpy( &(ucKeyBuf[ sizeof( KREF_ENTRY)]), pucKey, uiKeyLen); pKrefEntry->ui16KeyLen = (FLMUINT16)uiKeyLen; pKrefEntry->uiDataLen = 0; pKrefEntry->ui16IxNum = (FLMUINT16)m_pIxd->uiIndexNum; pKrefEntry->uiSequence = 1; pKrefEntry->bDelete = bDelete; // Add or delete the key/reference. if (RC_BAD( rc = m_pDb->refUpdate( &m_pIxd->lfInfo, m_pIxd, pKrefEntry, FALSE))) { goto Exit; } // Update statistics m_Progress.ui32NumProblemsFixed++; } Exit: // End the update if (bStartedUpdate) { RCODE rc2; if (RC_BAD( rc2 = chkEndUpdate())) { if (RC_OK( rc)) { rc = rc2; } } } return( rc); } /******************************************************************** Desc: Populates the XFLM_CORRUPT_INFO structure and calls the user's callback routine. *********************************************************************/ RCODE F_DbCheck::reportIxError( STATE_INFO * pStateInfo, FLMINT32 i32ErrCode, FLMBYTE * pucErrKey, FLMUINT uiErrKeyLen, FLMBOOL * pbFixErrRV ) { RCODE rc = NE_XFLM_OK; void * pDbPoolMark = NULL; FLMBOOL bResetKRef = FALSE; XFLM_CORRUPT_INFO CorruptInfo; f_memset( &CorruptInfo, 0, sizeof( XFLM_CORRUPT_INFO)); // Need to mark the DB's temporary pool. The index code allocates // memory for new CDL entries from the DB pool. If the pool is not // reset, it grows during the check and becomes VERY large. pDbPoolMark = m_pDb->m_tempPool.poolMark(); // Set up the KRef so that flmGetRecKeys will work if (RC_BAD( rc = m_pDb->krefCntrlCheck())) { goto Exit; } bResetKRef = TRUE; // Fix corruptions by default unless the app says not to. CorruptInfo.ui32ErrLocale = XFLM_LOCALE_INDEX; CorruptInfo.i32ErrCode = i32ErrCode; CorruptInfo.ui32ErrLfNumber = (FLMUINT32)m_pIxd->uiIndexNum; CorruptInfo.ui32ErrElmOffset = (FLMUINT32)pStateInfo->uiElmOffset; // Generate the key tree using the key that caused the error if (RC_BAD( rc = keyToVector( pucErrKey, uiErrKeyLen, &CorruptInfo.ifpErrIxKey))) { goto Exit; } // Report the error *pbFixErrRV = FALSE; if (m_pDbCheckStatus && RC_OK( m_LastStatusRc)) { m_LastStatusRc = m_pDbCheckStatus->reportCheckErr( &CorruptInfo, pbFixErrRV); } Exit: if (CorruptInfo.ifpErrIxKey) { CorruptInfo.ifpErrIxKey->Release(); CorruptInfo.ifpErrIxKey = NULL; } // Remove any keys added to the KRef if (bResetKRef) { m_pDb->krefCntrlFree(); // VISIT: Is this correct? } // Reset the index check pool m_pDb->m_tempPool.poolReset(pDbPoolMark); return( rc); } /*************************************************************************** Desc: Start an update transaction *****************************************************************************/ RCODE F_DbCheck::startUpdate( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bAbortedReadTrans = FALSE; FLMUINT uiSaveIndexNum = m_pIxd->uiIndexNum; // This routine should never be called unless // XFLM_ONLINE flag was passed in to the check. flmAssert( m_uiFlags & XFLM_ONLINE); if (m_pDb->getTransType() == XFLM_READ_TRANS) { // Free the KrefCntrl m_pDb->krefCntrlCheck(); // Abort the read transaction m_pIxd = NULL; if (RC_BAD( rc = m_pDb->transAbort())) { goto Exit; } bAbortedReadTrans = TRUE; // Try to start an update transaction if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { goto Exit; } m_bStartedUpdateTrans = TRUE; // Must reget the IXD. if (RC_BAD( rc = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile, &m_pIxd, TRUE))) { goto Exit; } } if (RC_BAD( m_LastStatusRc)) { rc = m_LastStatusRc; goto Exit; } Exit: // If something went wrong after the update transaction was started, // abort the transaction. if (RC_BAD( rc)) { if (m_bStartedUpdateTrans) { m_pDb->transAbort(); m_bStartedUpdateTrans = FALSE; } } // Re-start the read transaction. if (bAbortedReadTrans && !m_bStartedUpdateTrans) { RCODE rc2; m_pIxd = NULL; if (RC_BAD( rc2 = m_pDb->transBegin( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { if (RC_OK( rc)) { rc = rc2; } } else { if (RC_BAD( rc2 = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile, &m_pIxd, TRUE))) { if (RC_OK( rc)) { rc = rc2; } } } } return( rc); } /*************************************************************************** Desc: End an update transaction. *****************************************************************************/ RCODE F_DbCheck::chkEndUpdate( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiSaveIndexNum = m_pIxd->uiIndexNum; if (m_bStartedUpdateTrans) { // Commit the update transaction that was started. If the transaction // started by the application, do not commit it. m_pIxd = NULL; m_bStartedUpdateTrans = FALSE; if (RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } } Exit: // Re-start read transaction if (m_pDb->getTransType() == XFLM_NO_TRANS) { RCODE rc2; if (RC_BAD( rc2 = m_pDb->transBegin( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { if (RC_OK( rc)) { rc = rc2; } } else { if (RC_BAD( rc2 = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile, &m_pIxd, TRUE))) { if (RC_OK( rc)) { rc = rc2; } } } } return( rc); } libxflaim-5.1.969/src/frestore.cpp0000644000175000017500000001545510511001742020373 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Methods used during restore // // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: frestore.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Constructor ****************************************************************************/ F_FSRestore::F_FSRestore() { m_pFileHdl = NULL; m_pMultiFileHdl = NULL; m_ui64Offset = 0; m_bSetupCalled = FALSE; m_szDbPath[ 0] = 0; m_uiDbVersion = 0; m_szBackupSetPath[ 0] = 0; m_szRflDir[ 0] = 0; m_bOpen = FALSE; } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_FSRestore::~F_FSRestore() { if( m_bOpen) { (void)close(); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::setup( const char * pszDbPath, const char * pszBackupSetPath, const char * pszRflDir) { flmAssert( !m_bSetupCalled); flmAssert( pszDbPath); flmAssert( pszBackupSetPath); f_strcpy( m_szDbPath, pszDbPath); f_strcpy( m_szBackupSetPath, pszBackupSetPath); if( pszRflDir) { f_strcpy( m_szRflDir, pszRflDir); } m_bSetupCalled = TRUE; return( NE_XFLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openBackupSet( void) { RCODE rc = NE_XFLM_OK; flmAssert( m_bSetupCalled); flmAssert( !m_pMultiFileHdl); if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl))) { goto Exit; } if( RC_BAD( rc = m_pMultiFileHdl->openFile( m_szBackupSetPath))) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; goto Exit; } m_ui64Offset = 0; m_bOpen = TRUE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openRflFile( FLMUINT uiFileNum) { RCODE rc = NE_XFLM_OK; char szRflPath[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; FLMUINT uiBaseNameSize; XFLM_DB_HDR dbHdr; IF_FileHdl * pFileHdl = NULL; flmAssert( m_bSetupCalled); flmAssert( uiFileNum); flmAssert( !m_pFileHdl); // Read the database header to determine the version number if( !m_uiDbVersion) { if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( m_szDbPath, gv_XFlmSysData.uiFileOpenFlags, &pFileHdl))) { goto Exit; } if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pFileHdl, &dbHdr))) { goto Exit; } pFileHdl->Release(); pFileHdl = NULL; m_uiDbVersion = (FLMUINT)dbHdr.ui32DbVersion; } // Generate the log file name. if( RC_BAD( rc = rflGetDirAndPrefix( m_szDbPath, m_szRflDir, szRflPath))) { goto Exit; } uiBaseNameSize = sizeof( szBaseName); rflGetBaseFileName( uiFileNum, szBaseName, &uiBaseNameSize, NULL); gv_XFlmSysData.pFileSystem->pathAppend( szRflPath, szBaseName); // Open the file. if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( szRflPath, gv_XFlmSysData.uiFileOpenFlags, &m_pFileHdl))) { goto Exit; } m_bOpen = TRUE; m_ui64Offset = 0; Exit: if( pFileHdl) { pFileHdl->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::openIncFile( FLMUINT uiFileNum) { char szIncPath[ F_PATH_MAX_SIZE]; char szIncFile[ F_FILENAME_SIZE]; RCODE rc = NE_XFLM_OK; flmAssert( m_bSetupCalled); flmAssert( !m_pMultiFileHdl); // Since this is a non-interactive restore, we will "guess" // that incremental backups are located in the same parent // directory as the main backup set. We will further assume // that the incremental backup sets have been named XXXXXXXX.INC, // where X is a hex digit. if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_szBackupSetPath, szIncPath, NULL))) { goto Exit; } f_sprintf( szIncFile, "%08X.INC", (unsigned)uiFileNum); gv_XFlmSysData.pFileSystem->pathAppend( szIncPath, szIncFile); if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl))) { goto Exit; } if( RC_BAD( rc = m_pMultiFileHdl->openFile( szIncPath))) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; goto Exit; } m_ui64Offset = 0; m_bOpen = TRUE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::read( FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesRead) { FLMUINT uiBytesRead = 0; RCODE rc = NE_XFLM_OK; flmAssert( m_bSetupCalled); flmAssert( m_pFileHdl || m_pMultiFileHdl); if( m_pMultiFileHdl) { if( RC_BAD( rc = m_pMultiFileHdl->read( m_ui64Offset, uiLength, pvBuffer, &uiBytesRead))) { goto Exit; } } else { if( RC_BAD( rc = m_pFileHdl->read( (FLMUINT)m_ui64Offset, uiLength, pvBuffer, &uiBytesRead))) { goto Exit; } } Exit: m_ui64Offset += uiBytesRead; if( puiBytesRead) { *puiBytesRead = uiBytesRead; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::close( void) { flmAssert( m_bSetupCalled); if( m_pMultiFileHdl) { m_pMultiFileHdl->Release(); m_pMultiFileHdl = NULL; } if( m_pFileHdl) { m_pFileHdl->Release(); m_pFileHdl = NULL; } m_bOpen = FALSE; m_ui64Offset = 0; return( NE_XFLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FSRestore::abortFile( void) { return( close()); } libxflaim-5.1.969/src/flindex.cpp0000644000175000017500000006717010511001742020174 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains FLAIM API routines that aid the user in managing indexes. // // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flindex.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE FLMAPI flmBackgroundIndexBuildThrd( IF_Thread * pThread); /**************************************************************************** Desc : Return the status of the index. Notes: ****************************************************************************/ RCODE FLMAPI F_Db::indexStatus( FLMUINT uiIndexNum, XFLM_INDEX_STATUS * pIndexStatus) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; F_BKGND_IX * pBackgroundIx; FLMBOOL bMutexLocked = FALSE; flmAssert( pIndexStatus); if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if( m_eTransType != XFLM_NO_TRANS) { if( !okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { // Need to have at least a read transaction going. if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; pBackgroundIx = flmBackgroundIndexGet( m_pDatabase, uiIndexNum, TRUE); if (pBackgroundIx) { f_memcpy( pIndexStatus, &pBackgroundIx->indexStatus, sizeof( XFLM_INDEX_STATUS)); f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; flmAssert( (FLMUINT)pIndexStatus->ui32IndexNum == uiIndexNum); } else { IXD * pIxd; f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } // Populate the index status structure. f_memset( pIndexStatus, 0, sizeof( XFLM_INDEX_STATUS)); pIndexStatus->ui32IndexNum = (FLMUINT32)uiIndexNum; if( !(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE))) { pIndexStatus->ui64LastDocumentIndexed = ~((FLMUINT64)0); pIndexStatus->eState = XFLM_INDEX_ONLINE; } else { // NOTE: If we are in a read transaction, the last node indexed // value may not be read-consistent. It is only guaranteed to // be correct for update transactions. pIndexStatus->ui64LastDocumentIndexed = pIxd->ui64LastDocIndexed; pIndexStatus->eState = (pIxd->uiFlags & IXD_SUSPENDED) ? XFLM_INDEX_SUSPENDED : XFLM_INDEX_BRINGING_ONLINE; } } Exit: if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } if( bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc: Return the number of the next index. Pass in zero to get the first index. ****************************************************************************/ RCODE FLMAPI F_Db::indexGetNext( FLMUINT * puiIndexNum) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; IXD * pIxd; flmAssert( puiIndexNum != NULL); if( RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if( m_eTransType != XFLM_NO_TRANS) { if( !okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { // Need to have at least a read transaction going. if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } if( (pIxd = m_pDict->getNextIndex( *puiIndexNum, FALSE)) == NULL) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } *puiIndexNum = pIxd->uiIndexNum; Exit: if (bStartedTrans) { abortTrans(); } return( rc); } /**************************************************************************** Desc : Suspend the selected index from doing any key updates on records that are equal or higher than the next record ID value in the container that the index references. If the index is offline then the background process will be suspended. If the index is online then it will be suspended. If the index is already suspended NE_XFLM_OK will be returned. A suspended index is not persistent if the database goes down. Notes: An update transaction will be started if necessary. ****************************************************************************/ RCODE F_Db::indexSuspend( FLMUINT uiIndexNum) { RCODE rc = NE_XFLM_OK; IXD * pIxd; FLMUINT64 ui64HighestDocId; FLMBOOL bStartedTrans = FALSE; F_COLLECTION * pCollection; FLMBOOL bMustAbortOnError = FALSE; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMUINT uiRflToken = 0; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } else if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } } else { // Need to have an update transaction going. if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // See if the index is valid if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if (pIxd->uiFlags & IXD_SUSPENDED) { // Index is already suspended. goto Exit; } // If the index is not currently offline, the highest node indexed // is the collection's last document ID. Otherwise, it is // simply the value that is stored in the IXD. if (!(pIxd->uiFlags & IXD_OFFLINE)) { if (RC_BAD( rc = m_pDict->getCollection( pIxd->uiCollectionNum, &pCollection))) { goto Exit; } ui64HighestDocId = pCollection->ui64LastDocId; } else { ui64HighestDocId = pIxd->ui64LastDocIndexed; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Must abort on error bMustAbortOnError = TRUE; // There may be a background thread still assigned to the // index even though the index may be "on-line." This is because // the background thread may have just commited a transaction that // transitioned the index from off-line to on-line, but the thread // has not yet exited (even though it will not do any more work // to update the index). We want to wait for the thread to terminate // before our transaction is allowed to commit. This is so that if // we immediately call resume, it won't find the yet-to-terminate // thread still running in the background. if (!(m_uiFlags & FDB_REPLAYING_RFL)) { if( RC_BAD( rc = addToStopList( uiIndexNum))) { goto Exit; } } if (RC_BAD( rc = setIxStateInfo( uiIndexNum, ui64HighestDocId, IXD_SUSPENDED))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIxd is no // good after this point pIxd = NULL; // Log the suspend packet to the RFL pRfl->enableLogging( &uiRflToken); if (RC_BAD( rc = m_pDatabase->m_pRfl->logIndexSuspendOrResume( this, uiIndexNum, RFL_INDEX_SUSPEND_PACKET))) { goto Exit; } Exit: if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc)) { if( bStartedTrans) { abortTrans(); } else if( bMustAbortOnError) { setMustAbortTrans( rc); } } else if( bStartedTrans) { rc = commitTrans( 0, FALSE); } return( rc); } /**************************************************************************** Desc : If the index was suspended, restart the background process that will get the index up to date so that it will eventually be online. Returns NE_XFLM_OK with no change if the index is already online. Notes: An update transaction will be started if necessary. ****************************************************************************/ RCODE F_Db::indexResume( FLMUINT uiIndexNum) { RCODE rc = NE_XFLM_OK; IXD * pIxd; FLMBOOL bStartedTrans = FALSE; FLMBOOL bMustAbortOnError = FALSE; FLMUINT uiRflToken = 0; F_Rfl * pRfl = m_pDatabase->m_pRfl; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } else if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } } else { // Need to have an update transaction going. if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // See if the index is valid if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } if (!(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE))) { // Index is already on-line goto Exit; } if (!(pIxd->uiFlags & IXD_SUSPENDED)) { // Index is not suspended. It is offline (see test // above), but a thread should already be building the // index. flmAssert( flmBackgroundIndexGet( m_pDatabase, uiIndexNum, FALSE) != NULL); goto Exit; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Point of no return -- must abort on error bMustAbortOnError = TRUE; // Better not have a background thread running flmAssert( flmBackgroundIndexGet( m_pDatabase, uiIndexNum, FALSE) == NULL); // Update the index state info to show "offline" if (RC_BAD( rc = setIxStateInfo( uiIndexNum, pIxd->ui64LastDocIndexed, IXD_OFFLINE))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIxd is no // good after this point pIxd = NULL; // Add an entry to the start list so that an indexing thread // will be started when this transaction commits. if (!(m_uiFlags & FDB_REPLAYING_RFL)) { if (RC_BAD( rc = addToStartList( uiIndexNum))) { goto Exit; } } // Log the resume packet to the RFL pRfl->enableLogging( &uiRflToken); if (RC_BAD( rc = pRfl->logIndexSuspendOrResume( this, uiIndexNum, RFL_INDEX_RESUME_PACKET))) { goto Exit; } Exit: if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc)) { if( bStartedTrans) { abortTrans(); } else if( bMustAbortOnError) { setMustAbortTrans( rc); } } else if( bStartedTrans) { rc = commitTrans( 0, FALSE); } return( rc); } /**************************************************************************** Desc: Add the index to the stop list of background threads. ****************************************************************************/ RCODE F_Db::addToStopList( FLMUINT uiIndexNum) { RCODE rc = NE_XFLM_OK; F_BKGND_IX * pBackgroundIx; F_BKGND_IX * pNextBackgroundIx; // We'd better not be replaying the RFL flmAssert( !(m_uiFlags & FDB_REPLAYING_RFL)); // First look in the start list and remove any index matches. // This is need if you add an index and drop // it within the same transaction. for( pBackgroundIx = m_pIxStartList; pBackgroundIx; pBackgroundIx = pNextBackgroundIx) { pNextBackgroundIx = pBackgroundIx->pNext; if ((FLMUINT)pBackgroundIx->indexStatus.ui32IndexNum == uiIndexNum) { if (pNextBackgroundIx) { pNextBackgroundIx->pPrev = pBackgroundIx->pPrev; } if (pBackgroundIx->pPrev) { pBackgroundIx->pPrev->pNext = pNextBackgroundIx; } else { m_pIxStartList = pNextBackgroundIx; } f_free( &pBackgroundIx); } } // See if we already have an entry in the stop list for the index. There // is no reason to have the index in the list more than once. for (pBackgroundIx = m_pIxStopList; pBackgroundIx; pBackgroundIx = pNextBackgroundIx) { pNextBackgroundIx = pBackgroundIx->pNext; if ((FLMUINT)pBackgroundIx->indexStatus.ui32IndexNum == uiIndexNum) { goto Exit; // Should return NE_XFLM_OK } } // Allocate and add the thread structure to the thread list. if (RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( F_BKGND_IX)), &pBackgroundIx))) { goto Exit; } pBackgroundIx->indexStatus.ui32IndexNum = (FLMUINT32)uiIndexNum; pBackgroundIx->pPrev = NULL; if ((pBackgroundIx->pNext = m_pIxStopList) != NULL) { m_pIxStopList->pPrev = pBackgroundIx; } m_pIxStopList = pBackgroundIx; Exit: return( rc); } /**************************************************************************** Desc: Add the index to the start list of background threads. ****************************************************************************/ RCODE F_Db::addToStartList( FLMUINT uiIndexNum) { RCODE rc = NE_XFLM_OK; F_BKGND_IX * pBackgroundIx; F_BKGND_IX * pNextBackgroundIx; // We'd better not be replaying the RFL flmAssert( !(m_uiFlags & FDB_REPLAYING_RFL)); // Look in the start list to make sure we don't already // have an entry for this index. We don't want to // start more than one thread per index. The background // indexing code is not structured to handle multiple build // threads on the same index. // NOTE: We don't want to remove any entries in the stop // list corresponding to this index. The reason for this // is the index may have been deleted, re-added, deleted, // modified, etc. several times during the transaction. // We want to make sure that an existing background indexing // thread is terminated and a new one is started. The stop // list is always processed first at transaction commit time. // Then new indexing threads (in the start list) are started. for( pBackgroundIx = m_pIxStartList; pBackgroundIx; pBackgroundIx = pNextBackgroundIx) { pNextBackgroundIx = pBackgroundIx->pNext; if ((FLMUINT)pBackgroundIx->indexStatus.ui32IndexNum == uiIndexNum) { goto Exit; // Should return NE_XFLM_OK } } // Allocate and add the thread structure to the pDb thread list. if (RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( F_BKGND_IX)), &pBackgroundIx))) { goto Exit; } pBackgroundIx->indexStatus.ui32IndexNum = (FLMUINT32)uiIndexNum; pBackgroundIx->pPrev = NULL; if ((pBackgroundIx->pNext = m_pIxStartList) != NULL) { m_pIxStartList->pPrev = pBackgroundIx; } m_pIxStartList = pBackgroundIx; Exit: return( rc); } /**************************************************************************** Desc: After Abort and before we unlock, stop and start all indexing. ****************************************************************************/ void F_Db::indexingAfterAbort( void) { F_BKGND_IX * pStartIx; F_BKGND_IX * pStopIx; F_BKGND_IX * pNextIx; pStopIx = m_pIxStopList; m_pIxStopList = NULL; for( ; pStopIx; pStopIx = pNextIx) { pNextIx = pStopIx->pNext; f_free( &pStopIx); } pStartIx = m_pIxStartList; m_pIxStartList = NULL; for( ; pStartIx; pStartIx = pNextIx) { pNextIx = pStartIx->pNext; f_free( &pStartIx); } } /**************************************************************************** Desc: Stops a background indexing thread Notes: This routine DOES NOT assume that the global mutex is locked. It will lock and unlock the mutex as needed. ****************************************************************************/ void F_Db::stopBackgroundIndexThread( FLMUINT uiIndexNum, FLMBOOL bWait, FLMBOOL * pbStopped) { F_BKGND_IX * pBackgroundIx; FLMUINT uiThreadId; FLMBOOL bMutexLocked = FALSE; if (pbStopped) { *pbStopped = FALSE; } for (;;) { // Lock the global mutex if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } // Get the background index if ((pBackgroundIx = flmBackgroundIndexGet( m_pDatabase, uiIndexNum, TRUE, &uiThreadId)) == NULL) { if (pbStopped) { *pbStopped = TRUE; } goto Exit; } // Set the thread's shutdown flag first. gv_XFlmSysData.pThreadMgr->setThreadShutdownFlag( uiThreadId); // Unlock the global mutex f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // The thread may be waiting to start a transaction. m_pDatabase->m_pDatabaseLockObj->timeoutLockWaiter( uiThreadId); m_pDatabase->m_pWriteLockObj->timeoutLockWaiter( uiThreadId); if( !bWait) { break; } // Wait for the thread to terminate f_sleep( 50); } Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } } /**************************************************************************** Desc: After commit and before we unlock, stop and start all indexing. ****************************************************************************/ void F_Db::indexingAfterCommit( void) { F_BKGND_IX * pStartIx; F_BKGND_IX * pStopIx; F_BKGND_IX * pNextIx; FLMBOOL bThreadsActive; FLMBOOL bStopped; // Signal all background indexing threads in the stop list // to shutdown. Poll until all have terminated. for( ;;) { bThreadsActive = FALSE; for( pStopIx = m_pIxStopList; pStopIx; pStopIx = pStopIx->pNext) { stopBackgroundIndexThread( (FLMUINT)pStopIx->indexStatus.ui32IndexNum, FALSE, &bStopped); if( !bStopped) { bThreadsActive = TRUE; } } if( !bThreadsActive) { break; } f_sleep( 50); } // Now that all of the threads have been stopped, discard the stop list pStopIx = m_pIxStopList; m_pIxStopList = NULL; for (; pStopIx; pStopIx = pNextIx) { pNextIx = pStopIx->pNext; f_free( &pStopIx); } // Start threads listed in the index start list. pStartIx = m_pIxStartList; m_pIxStartList = NULL; for (; pStartIx; pStartIx = pNextIx) { pNextIx = pStartIx->pNext; (void)startIndexBuild( (FLMUINT)pStartIx->indexStatus.ui32IndexNum); f_free( &pStartIx); } } /**************************************************************************** Desc: Thread that will build an index in the background. Caller will create a pDb to use. This pDb must be freed at the conclusion of the routine. ****************************************************************************/ RCODE F_Db::startIndexBuild( FLMUINT uiIndexNum) { RCODE rc = NE_XFLM_OK; FLMUINT uiGMT; IXD * pIxd; F_BKGND_IX * pBackgroundIx = NULL; char szThreadName[ F_PATH_MAX_SIZE]; char szBaseName[ F_FILENAME_SIZE]; f_timeGetSeconds( &uiGMT ); if (flmBackgroundIndexGet( m_pDatabase, uiIndexNum, FALSE) != NULL) { // There is already a background thread running on this index. rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } // Allocate the background thread and index status strucutures. if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_BKGND_IX), &pBackgroundIx))) { goto Exit; } pBackgroundIx->pDatabase = m_pDatabase; pBackgroundIx->indexStatus.eState = XFLM_INDEX_BRINGING_ONLINE; pBackgroundIx->indexStatus.ui32IndexNum = (FLMUINT32)uiIndexNum; pBackgroundIx->indexStatus.ui32StartTime = (FLMUINT32)uiGMT; pBackgroundIx->indexStatus.ui64LastDocumentIndexed = pIxd->ui64LastDocIndexed; pBackgroundIx->indexStatus.ui64KeysProcessed = 0; pBackgroundIx->indexStatus.ui64DocumentsProcessed = 0; pBackgroundIx->indexStatus.ui64Transactions = 0; pBackgroundIx->uiIndexingAction = FTHREAD_ACTION_INDEX_OFFLINE; pBackgroundIx->pPrev = NULL; pBackgroundIx->pNext = NULL; // Generate the thread name if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_pDatabase->m_pszDbPath, szThreadName, szBaseName))) { goto Exit; } f_sprintf( (char *)szThreadName, "BldIX %u (%s)", (unsigned)uiIndexNum, szBaseName); // Start the thread in the background indexing thread group. // The new thread will cleanup pBackgroundIx on termination. if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( NULL, flmBackgroundIndexBuildThrd, szThreadName, gv_XFlmSysData.uiIndexingThreadGroup, uiIndexNum, (void *)pBackgroundIx, NULL, 24000))) { goto Exit; } Exit: if (RC_BAD( rc) && pBackgroundIx) { f_free( &pBackgroundIx); } return( rc); } /**************************************************************************** Desc: This routine is called by the thread that performs background indexing. ****************************************************************************/ RCODE F_Db::backgroundIndexBuild( IF_Thread * pThread, FLMBOOL * pbShutdown, FLMINT * piErrorLine) { RCODE rc = NE_XFLM_OK; IXD * pIxd; F_BKGND_IX * pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); FLMBOOL bStartedTrans = FALSE; FLMUINT64 ui64FirstDocumentId; FLMUINT uiIndexNum; FLMBOOL bHitEnd; XFLM_INDEX_STATUS savedIxStatus; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { *piErrorLine = (FLMINT)__LINE__; goto Exit; } flmAssert( m_eTransType == XFLM_NO_TRANS); m_uiFlags |= FDB_BACKGROUND_INDEXING; uiIndexNum = (FLMUINT)pBackgroundIx->indexStatus.ui32IndexNum; for (;;) { // Set the thread's status pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); // See if we should shut down. if (pThread->getShutdownFlag()) { *pbShutdown = TRUE; break; } // Start a transaction if( RC_BAD( rc = beginBackgroundTrans( pThread))) { if (rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); *pbShutdown = TRUE; rc = NE_XFLM_OK; } else { *piErrorLine = (FLMINT)__LINE__; } goto Exit; } bStartedTrans = TRUE; if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { // Index may have been deleted by another transaction, or // there may have been some other error. *piErrorLine = (FLMINT)__LINE__; goto Exit; } // If the index is suspended, this thread should have been // shut down. The suspending thread will keep the database // locked until we exit. So, if we have the database locked, // the index better not be suspended. flmAssert( !(pIxd->uiFlags & IXD_SUSPENDED)); pBackgroundIx->indexStatus.eState = XFLM_INDEX_BRINGING_ONLINE; if ((ui64FirstDocumentId = pIxd->ui64LastDocIndexed) == ~((FLMUINT64)0)) { goto Exit; } // Setup the NODE range we want to index. ui64FirstDocumentId++; // Set the thread's status pThread->setThreadStatus( "Indexing %u:%I64u", (unsigned)pIxd->uiCollectionNum, ui64FirstDocumentId); // Read and index up to the highest node ID (or node higher than ui64EndNodeId) // or until time runs out. The 500 is millisecs to take for the transaction. f_memcpy( &savedIxStatus, &pBackgroundIx->indexStatus, sizeof( XFLM_INDEX_STATUS)); if (RC_BAD( rc = indexSetOfDocuments( uiIndexNum, ui64FirstDocumentId, ~((FLMUINT64)0), NULL, NULL, &pBackgroundIx->indexStatus, &bHitEnd, pThread))) { // Lock the mutex while copying the saved index status back to // the main index status so that someone requesting the index status // won't see the status while the memcpy is in progress. f_mutexLock( gv_XFlmSysData.hShareMutex); f_memcpy( &pBackgroundIx->indexStatus, &savedIxStatus, sizeof( XFLM_INDEX_STATUS)); f_mutexUnlock( gv_XFlmSysData.hShareMutex); *piErrorLine = (FLMINT)__LINE__; goto Exit; } // Commit the transaction (even if we didn't do any indexing work). bStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, FALSE))) { *piErrorLine = (FLMINT)__LINE__; goto Exit; } pBackgroundIx->indexStatus.ui64Transactions++; if (bHitEnd) { break; } } Exit: if (bStartedTrans) { (void)abortTrans(); bStartedTrans = FALSE; } return( rc); } /**************************************************************************** Desc: Thread that will build an index in the background. Caller will create a pDb to use. This pDb must be freed at the conclusion of the routine. ****************************************************************************/ FSTATIC RCODE FLMAPI flmBackgroundIndexBuildThrd( IF_Thread * pThread) { RCODE rc = NE_XFLM_OK; F_BKGND_IX * pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); F_Db * pDb = NULL; FLMBOOL bForcedShutdown = FALSE; FLMUINT uiIndexNum; char szMsg [128]; FLMINT iErrorLine = 0; pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); Loop_Again: rc = NE_XFLM_OK; uiIndexNum = (FLMUINT)pBackgroundIx->indexStatus.ui32IndexNum; flmAssert( pThread->getThreadAppId() == uiIndexNum); pDb = NULL; // We could loop forever on internalDbOpen errors, // check if we should exit. if( pThread->getShutdownFlag()) { bForcedShutdown = TRUE; goto Exit; } if( RC_BAD( rc = gv_pXFlmDbSystem->internalDbOpen( pBackgroundIx->pDatabase, &pDb))) { // If the file is being closed, this is not an error. if (pBackgroundIx->pDatabase->getFlags() & DBF_BEING_CLOSED) { bForcedShutdown = TRUE; rc = NE_XFLM_OK; } else { iErrorLine = (FLMINT)__LINE__; } goto Exit; } if (RC_BAD( rc = pDb->backgroundIndexBuild( pThread, &bForcedShutdown, &iErrorLine))) { goto Exit; } Exit: pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); if (pDb) { pDb->Release(); pDb = NULL; } if (RC_BAD(rc) && !bForcedShutdown) { if (rc == NE_XFLM_MEM || rc == NE_FLM_IO_DISK_FULL || rc == NE_XFLM_MUST_WAIT_CHECKPOINT) { // Log the error f_sprintf( szMsg, "Background indexing thread %u (index %u)", (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); flmLogError( rc, szMsg, __FILE__, iErrorLine); // Sleep a half second and try again. f_sleep( 500); goto Loop_Again; } else { f_sprintf( szMsg, "Background indexing thread %u (index %u) -- unrecoverable error.", (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); flmLogError( rc, szMsg, __FILE__, iErrorLine); } } // Set the thread's app ID to 0, so that it will not // be found now that the thread is terminating (we don't // want flmBackgroundIndexGet to find the thread). pThread->setThreadAppId( 0); // Free the background index structure f_mutexLock( gv_XFlmSysData.hShareMutex); pThread->setParm1( NULL); f_mutexUnlock( gv_XFlmSysData.hShareMutex); f_free( &pBackgroundIx); return( rc); } /**************************************************************************** Desc: Looks for a background indexing thread that is running with a matching action and value. Note: The shared semaphore must be locked on the outside while calling this routine and accessing anything within the F_BKGND_IX structure. ****************************************************************************/ F_BKGND_IX * flmBackgroundIndexGet( F_Database * pDatabase, FLMUINT uiIndexNum, FLMBOOL bMutexLocked, FLMUINT * puiThreadId) { RCODE rc = NE_XFLM_OK; IF_Thread * pThread; FLMUINT uiThreadId; F_BKGND_IX * pBackgroundIx = NULL; if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); } uiThreadId = 0; for( ;;) { if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getNextGroupThread( &pThread, gv_XFlmSysData.uiIndexingThreadGroup, &uiThreadId))) { if( rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; break; } else { RC_UNEXPECTED_ASSERT( rc); } } if (pThread->getThreadAppId()) { F_BKGND_IX * pTmpIx = NULL; pTmpIx = (F_BKGND_IX *)pThread->getParm1(); if ((FLMUINT)pTmpIx->indexStatus.ui32IndexNum == uiIndexNum && pTmpIx->pDatabase == pDatabase) { flmAssert( pThread->getThreadAppId() == uiIndexNum); pBackgroundIx = pTmpIx; pThread->Release(); if( puiThreadId) { *puiThreadId = uiThreadId; } break; } } pThread->Release(); } if (!bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } return( pBackgroundIx); } libxflaim-5.1.969/src/flblddb.cpp0000644000175000017500000017364610511001742020142 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines for rebuilding a corrupted database. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flblddb.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: ****************************************************************************/ typedef union { FLMBYTE * pucBlk; F_BLK_HDR * pBlkHdr; F_BTREE_BLK_HDR * pBTreeBlkHdr; } F_BLOCK_UNION; /**************************************************************************** Desc: ****************************************************************************/ typedef struct { FLMUINT uiBlockSize; FLMUINT uiCollection; FLMUINT64 ui64ElmNodeId; FLMUINT uiElmNumber; FLMBYTE * pucElm; FLMUINT uiElmLen; FLMBYTE * pucElmKey; FLMUINT uiElmKeyLen; FLMBYTE * pucElmData; FLMUINT uiElmDataLen; FLMUINT uiOverallDataLen; FLMUINT uiDataOnlyBlkAddr; FLMUINT32 ui32NextBlkInChain; FLMUINT32 ui32BlkAddr; FLMUINT uiNumKeysInBlk; } F_ELM_INFO; /**************************************************************************** Desc: ****************************************************************************/ typedef struct { FLMUINT uiFileNumber; FLMUINT uiFileOffset; FLMUINT uiBlockSize; FLMUINT uiBlockBytes; FLMUINT uiCurOffset; F_ELM_INFO elmInfo; F_BLOCK_UNION blkUnion; } F_SCAN_STATE; // Local function prototypes FSTATIC void flmGetCreateOpts( XFLM_DB_HDR * pDbHdr, XFLM_CREATE_OPTS * pCreateOpts); FSTATIC FLMINT32 bldGetElmInfo( F_BTREE_BLK_HDR * pBlkHdr, FLMUINT uiBlockSize, FLMUINT uiElmNumber, F_ELM_INFO * pElmInfo); /**************************************************************************** Desc: ****************************************************************************/ FINLINE void bldFreeCachedNode( F_CachedNode ** ppCachedNode) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); (*ppCachedNode)->decrNodeUseCount(); delete *ppCachedNode; *ppCachedNode = NULL; f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } /**************************************************************************** Desc: ****************************************************************************/ class F_RebuildNodeIStream : public IF_IStream { public: F_RebuildNodeIStream() { m_bOpen = FALSE; m_pucFirstElmBlk = NULL; m_pucTmpBlk = NULL; m_pCurState = NULL; m_pDbRebuild = NULL; } ~F_RebuildNodeIStream() { closeStream(); } RCODE openStream( F_DbRebuild * pRebuild, FLMBOOL bRecovDictionary); RCODE FLMAPI closeStream( void); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); RCODE readNode( FLMUINT32 ui32BlkAddr, FLMUINT uiElmNumber, F_CachedNode ** ppCachedNode, FLMBYTE * pucIV); RCODE getNextNode( F_CachedNode ** ppCachedNode, F_ELM_INFO * pElmInfo, FLMBYTE * pucIV); RCODE getNextNodeInfo( F_ELM_INFO * pElmInfo, F_NODE_INFO * pNodeInfo); private: RCODE readBlock( FLMBOOL bCount, FLMUINT uiFileNumber, FLMUINT uiFileOffset, F_SCAN_STATE * pScanState); RCODE readContinuationElm( void); RCODE readNextFirstElm( void); RCODE readNextSequentialBlock( F_SCAN_STATE * pScanState); RCODE readFirstDataOnlyBlock( void); RCODE readNextDataOnlyBlock( void); FINLINE FLMBOOL doCollection( FLMUINT uiCollectionNum, FLMBOOL bDoDictCollections) { if( !bDoDictCollections) { if( isDictCollection( uiCollectionNum)) { return( FALSE); } return( (uiCollectionNum == XFLM_DATA_COLLECTION || uiCollectionNum <= XFLM_MAX_COLLECTION_NUM) ? TRUE : FALSE); } else { return( isDictCollection( uiCollectionNum)); } } F_DbRebuild * m_pDbRebuild; FLMBYTE * m_pucFirstElmBlk; FLMBYTE * m_pucTmpBlk; F_SCAN_STATE m_firstElmState; F_SCAN_STATE m_tmpState; F_SCAN_STATE * m_pCurState; FLMBOOL m_bOpen; FLMBOOL m_bRecovDictionary; }; /*************************************************************************** Desc: Comparison object for node result sets ***************************************************************************/ class F_NodeResultSetCompare : public IF_ResultSetCompare { inline RCODE FLMAPI compare( const void * pvData1, FLMUINT uiLength1, const void * pvData2, FLMUINT uiLength2, FLMINT * piCompare) { FLMBYTE * pucData1 = (FLMBYTE *)pvData1; FLMBYTE * pucData2 = (FLMBYTE *)pvData2; FLMUINT uiCollection1; FLMUINT uiCollection2; FLMUINT64 ui64NodeId1; FLMUINT64 ui64NodeId2; #ifdef FLM_DEBUG flmAssert( uiLength1 == uiLength2); #else F_UNREFERENCED_PARM( uiLength1); F_UNREFERENCED_PARM( uiLength2); #endif if( *pucData1 < *pucData2) { *piCompare = -1; } else if( *pucData1 > *pucData2) { *piCompare = 1; } else { uiCollection1 = f_bigEndianToUINT32( &pucData1[ 1]); uiCollection2 = f_bigEndianToUINT32( &pucData2[ 1]); if( uiCollection1 < uiCollection2) { *piCompare = -1; } else if( uiCollection1 > uiCollection2) { *piCompare = 1; } else { ui64NodeId1 = f_bigEndianToUINT64( &pucData1[ 5]); ui64NodeId2 = f_bigEndianToUINT64( &pucData2[ 5]); if( ui64NodeId1 < ui64NodeId2) { *piCompare = -1; } else if( ui64NodeId1 > ui64NodeId2) { *piCompare = 1; } else { *piCompare = 0; } } } return( NE_XFLM_OK); } virtual FLMINT FLMAPI AddRef( void) { return( IF_ResultSetCompare::AddRef()); } virtual FLMINT FLMAPI Release( void) { return( IF_ResultSetCompare::Release()); } }; /**************************************************************************** Desc : Rebuilds a damaged database. ****************************************************************************/ RCODE F_DbRebuild::dbRebuild( const char * pszSourceDbPath, // [IN] Source database path. This parameter specifies // the path and file name of the database which is to be // rebuilt. const char * pszSourceDataDir, // [IN] Source directory for data files. const char * pszDestDbPath, // [IN] Destination database path. This parameter specifies // the path and file name of a database to be created during // the rebuild. const char * pszDestDataDir, // [IN] Destination directory for data files. const char * pszDestRflDir, // [IN] Destination database's RFL path. This parameter specifies // the path of the destination RFL directory. NULL can be passed // if the RFL files are stored in the same directory as the other // destination database files. const char * pszDictPath, // [IN] Dictionary path. Specifies the path of the // data dictionary file to be used when rebuilding the // database. The file should contain a copy of the // data dictionary that was used when the source // database was originally created. const char * pszPassword, // [IN] Pointer to a password to use when extracting the database key if // the database is encrypted. XFLM_CREATE_OPTS * pCreateOpts, // [IN] Create options. Specifies the create options // which should be used when creating the temporary // database. Once the rebuild is complete, the // temporary database file is copied over the source // database file. Any create options specified for the // temporary database are inherited by the rebuilt source // database. FLMUINT64 * pui64TotNodes, // [OUT] Number of nodes in the source database. // An estimate of the number of nodes that were in the // source database is returned. This may not be exact. FLMUINT64 * pui64NodesRecov, // [OUT] Number of nodes recovered. FLMUINT64 * pui64DiscardedDocs, // [OUT] Number of quarantined nodes. IF_DbRebuildStatus * ifpDbRebuild // [IN] Pointer to a user-provided interface. NULL may be passed as the // value of this parameter if status reporting is not needed. ) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = NULL; FLMBOOL bDatabaseLocked = FALSE; FLMBOOL bWriteLocked = FALSE; IF_LockObject * pWriteLockObj = NULL; IF_LockObject * pDatabaseLockObj = NULL; FLMBOOL bMutexLocked = FALSE; IF_FileHdl * pLockFileHdl = NULL; eLockType currLockType; FLMUINT uiThreadId; FLMUINT uiNumExclQueued; FLMUINT uiNumSharedQueued; FLMUINT uiPriorityCount; FLMBOOL bUsedDatabase = FALSE; FLMBOOL bWaited; FLMBYTE * pucWrappingKey = NULL; F_SEM hWaitSem = F_SEM_NULL; FLMUINT uiRflToken = 0; IF_CCS * pWrappingKey = NULL; FLMUINT32 ui32KeyLen; F_SuperFileClient SFileClient; if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; m_bBadHeader = FALSE; m_cbrc = NE_XFLM_OK; Retry: // See if there is a database object for this file // May unlock and re-lock the global mutex. if( RC_BAD( rc = gv_pXFlmDbSystem->findDatabase( pszSourceDbPath, pszSourceDataDir, &pDatabase))) { goto Exit; } // If we didn't find a database object, get an exclusive lock on the file. if( !pDatabase) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // Attempt to get an exclusive lock on the file. if( RC_BAD( rc = flmCreateLckFile( pszSourceDbPath, &pLockFileHdl))) { goto Exit; } } else { if( RC_BAD( rc = pDatabase->checkState( __FILE__, __LINE__))) { goto Exit; } // The call to flmVerifyFileUse will wait if the file is in // the process of being opened by another thread. if( RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited))) { goto Exit; } if( bWaited) { goto Retry; } // Increment the open count on the Database so it will not // disappear while we are rebuilding the file. pDatabase->incrOpenCount(); bUsedDatabase = TRUE; f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; // See if the thread already has a file lock. If so, there // is no need to obtain another. However, we also want to // make sure there is no write lock. If there is, // we cannot do the rebuild right now. pDatabase->m_pDatabaseLockObj->getLockInfo( 0, &currLockType, &uiThreadId, &uiNumExclQueued, &uiNumSharedQueued, &uiPriorityCount); if( currLockType == FLM_LOCK_EXCLUSIVE && uiThreadId == f_threadId()) { // See if there is already a transaction going. pDatabase->m_pWriteLockObj->getLockInfo( (FLMINT)0, &currLockType, &uiThreadId, &uiNumExclQueued, &uiNumSharedQueued, &uiPriorityCount); if( currLockType == FLM_LOCK_EXCLUSIVE && uiThreadId == f_threadId()) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } } else { pDatabaseLockObj = pDatabase->m_pDatabaseLockObj; pDatabaseLockObj->AddRef(); if (RC_BAD( rc = pDatabaseLockObj->lock( hWaitSem, TRUE, FLM_NO_TIMEOUT, 0))) { goto Exit; } bDatabaseLocked = TRUE; } // Lock the write object to eliminate contention with // the checkpoint thread. pWriteLockObj = pDatabase->m_pWriteLockObj; pWriteLockObj->AddRef(); // Only contention here is with the checkpoint thread. // Wait forever for the checkpoint thread to give // up the lock. if( RC_BAD( rc = pDatabase->dbWriteLock( hWaitSem))) { goto Exit; } bWriteLocked = TRUE; } f_memset( &m_dbHdr, 0, sizeof( XFLM_DB_HDR)); f_memset( &m_createOpts, 0, sizeof( XFLM_CREATE_OPTS)); f_memset( &m_callbackData, 0, sizeof( XFLM_REBUILD_INFO)); f_memset( &m_corruptInfo, 0, sizeof( XFLM_CORRUPT_INFO)); m_pRebuildStatus = ifpDbRebuild; m_uiLastStatusTime = 0; // Open the damaged database if( (m_pSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = SFileClient.setup( pszSourceDbPath, pszSourceDataDir, 0))) { goto Exit; } if( RC_BAD( rc = m_pSFileHdl->setup( &SFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { goto Exit; } // Check the header information to see if we were in the middle // of a previous copy. if( RC_BAD( rc = flmGetHdrInfo( m_pSFileHdl, &m_dbHdr))) { m_bBadHeader = TRUE; if( rc == NE_XFLM_HDR_CRC) { m_bBadHeader = TRUE; rc = NE_XFLM_OK; if (!pCreateOpts) { flmGetCreateOpts( &m_dbHdr, &m_createOpts); pCreateOpts = &m_createOpts; } } else if( rc == NE_XFLM_UNSUPPORTED_VERSION || rc == NE_XFLM_NEWER_FLAIM) { goto Exit; } else if( rc == NE_XFLM_NOT_FLAIM || !gv_pXFlmDbSystem->validBlockSize( m_dbHdr.ui16BlockSize)) { FLMUINT uiSaveBlockSize; FLMUINT uiCalcBlockSize; if( !pCreateOpts) { if( rc != NE_XFLM_NOT_FLAIM) { flmGetCreateOpts( &m_dbHdr, &m_createOpts); } else { flmGetCreateOpts( NULL, &m_createOpts); } // Set block size to zero, so we will always take the calculated // block size below. m_createOpts.ui32BlockSize = 0; pCreateOpts = &m_createOpts; } // Try to determine the correct block size. if( RC_BAD( rc = determineBlkSize( &uiCalcBlockSize))) { goto Exit; } uiSaveBlockSize = pCreateOpts->ui32BlockSize; pCreateOpts->ui32BlockSize = (FLMUINT32)uiCalcBlockSize; // Initialize the database header to useable values. flmInitDbHdr( pCreateOpts, FALSE, FALSE, &m_dbHdr); // Only use the passed-in block size (uiSaveBlockSize) if it // was non-zero. if( uiSaveBlockSize) { pCreateOpts->ui32BlockSize = (FLMUINT32)uiSaveBlockSize; } } else { goto Exit; } } else { if( !pCreateOpts) { flmGetCreateOpts( &m_dbHdr, &m_createOpts); pCreateOpts = &m_createOpts; } } // If the corrupt database has a key, and we are built with NICI, // we will extract it. if( m_dbHdr.ui32DbKeyLen) { if( RC_BAD( rc = flmAllocCCS( &pWrappingKey))) { goto Exit; } if( RC_BAD( rc = pWrappingKey->init( TRUE, FLM_NICI_AES))) { goto Exit; } // If the key was encrypted in a password, then the pszPassword // parameter better be the key used to encrypt it. If the key was // not encrypted in a password, then pszPassword parameter should be NULL. if( RC_BAD( rc = pWrappingKey->setKeyFromStore( m_dbHdr.DbKey, (FLMBYTE *)pszPassword, NULL))) { goto Exit; } } // Delete the destination database in case it already exists. if( RC_BAD( rc = gv_pXFlmDbSystem->dbRemove( pszDestDbPath, pszDestDataDir, pszDestRflDir, TRUE))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } else { goto Exit; } } // If no block size has been specified or determined yet, use what we // read from the database header. if( !pCreateOpts->ui32BlockSize) { pCreateOpts->ui32BlockSize = (FLMUINT32)m_dbHdr.ui16BlockSize; } // Create the destination database if( RC_BAD( rc = gv_pXFlmDbSystem->dbCreate( pszDestDbPath, pszDestDataDir, pszDestRflDir, pszDictPath, NULL, pCreateOpts, (IF_Db **)&m_pDb))) { goto Exit; } // Check for a key from the corrupt database. if( pWrappingKey) { if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT, 0))) { goto Exit; } ui32KeyLen = XFLM_MAX_ENC_KEY_SIZE; if( RC_BAD( rc = pWrappingKey->getKeyToStore( &pucWrappingKey, (FLMUINT32 *)&ui32KeyLen, (FLMBYTE *)pszPassword, NULL))) { goto Exit; } f_memcpy( m_pDb->m_pDatabase->m_uncommittedDbHdr.DbKey, pucWrappingKey, ui32KeyLen); m_pDb->m_pDatabase->m_uncommittedDbHdr.ui32DbKeyLen = ui32KeyLen; f_free( &pucWrappingKey); pucWrappingKey = NULL; // Write out the log header if (RC_BAD( rc = m_pDb->m_pDatabase->writeDbHdr( m_pDb->m_pDbStats, m_pDb->m_pSFileHdl, &m_pDb->m_pDatabase->m_uncommittedDbHdr, NULL, TRUE))) { goto Exit; } m_pDb->m_bHadUpdOper = TRUE; if( RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } } // Need to close the database and re-open it to make the new key active // and/or to disable background threads. m_pDb->Release(); m_pDb = NULL; // Open the destination database without starting the background threads. // We don't want the background threads to be active while the restore // is taking place, because RFL logging is disabled for the duration // of the restore. We don't want the background threads to try to // start transactions in this state. if( RC_BAD( rc = gv_pXFlmDbSystem->openDb( pszDestDbPath, pszDestDataDir, pszDestRflDir, pszPassword, XFLM_DONT_RESUME_THREADS, (IF_Db **)&m_pDb))) { goto Exit; } // Set the rebuild flag m_pDb->m_uiFlags |= FDB_REBUILDING_DATABASE; // Disable RFL logging m_pDb->m_pDatabase->m_pRfl->disableLogging( &uiRflToken); // Rebuild the database if( RC_BAD( rc = rebuildDatabase())) { goto Exit; } Exit: if( pucWrappingKey) { f_free( pucWrappingKey); } if( pWrappingKey) { pWrappingKey->Release(); } if( uiRflToken) { m_pDb->m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } if( bUsedDatabase) { if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } pDatabase->decrOpenCount(); } if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; } // Unlock the file, if it is locked. if( bWriteLocked) { pDatabase->dbWriteUnlock(); bWriteLocked = FALSE; } if( bDatabaseLocked) { RCODE rc3; if( RC_BAD( rc3 = pDatabaseLockObj->unlock())) { if (RC_OK( rc)) { rc = rc3; } } bDatabaseLocked = FALSE; } if( pWriteLockObj) { pWriteLockObj->Release(); pWriteLockObj = NULL; } if( pDatabaseLockObj) { pDatabaseLockObj->Release(); pDatabaseLockObj = NULL; } if( pLockFileHdl) { pLockFileHdl->closeFile(); pLockFileHdl->Release(); pLockFileHdl = NULL; } if( m_pDb) { m_pDb->Release(); m_pDb = NULL; } if( m_pSFileHdl) { m_pSFileHdl->Release(); m_pSFileHdl = NULL; } if( pui64TotNodes) { *pui64TotNodes = m_callbackData.ui64TotNodes; } if( pui64NodesRecov) { *pui64NodesRecov = m_callbackData.ui64NodesRecov; } if( pui64DiscardedDocs) { *pui64DiscardedDocs = m_callbackData.ui64DiscardedDocs; } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_DbRebuild::getDatabaseSize( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiFileNumber = 1; FLMUINT64 ui64FileSize; m_callbackData.ui64FileSize = 0; while (uiFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER) { // Get the actual size of the last file. if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiFileNumber, &ui64FileSize))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { if (uiFileNumber > 1) { rc = NE_XFLM_OK; } else { // Should always be a data file #1 RC_UNEXPECTED_ASSERT( rc); } } goto Exit; } else { m_callbackData.ui64FileSize += ui64FileSize; } uiFileNumber++; } Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_DbRebuild::rebuildDatabase( void) { RCODE rc = NE_XFLM_OK; RCODE rc2; FLMBOOL bStartedTrans = FALSE; m_corruptInfo.ui32ErrLocale = XFLM_LOCALE_B_TREE; m_corruptInfo.ui32ErrLfType = XFLM_LF_COLLECTION; m_callbackData.ui64NodesRecov = 0; m_callbackData.ui64DiscardedDocs = 0; // Do a first pass to recover any dictionary items that may not have // been added from the dictionary file that was passed into the rebuild // function. if( m_dbHdr.ui32DbVersion < XFLM_VER_5_12) { rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); goto Exit; } if (RC_BAD( rc = getDatabaseSize())) { goto Exit; } // Recover the dictionary m_callbackData.i32DoingFlag = REBUILD_RECOVER_DICT; m_callbackData.bStartFlag = TRUE; m_callbackData.ui64BytesExamined = 0; if( RC_BAD( rc = recoverNodes( TRUE))) { goto Exit; } // Reset nodes recovered to zero after the dictionary pass m_callbackData.ui64TotNodes = 0; m_callbackData.ui64NodesRecov = 0; // Recover data m_callbackData.i32DoingFlag = REBUILD_RECOVER_DATA; m_callbackData.bStartFlag = TRUE; m_callbackData.ui64BytesExamined = 0; if( RC_BAD( rc = recoverNodes( FALSE))) { goto Exit; } // Try and preserve other things in the log header if( !m_bBadHeader) { F_Database * pDatabase; XFLM_DB_HDR * pDbHdr; if( RC_BAD( m_pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; pDatabase = m_pDb->getDatabase(); pDbHdr = pDatabase->getUncommittedDbHdr(); // Set the commit count one less than the old database's // This is because it will be incremented if the transaction // successfully commits - which will make it exactly right. if( pDbHdr->ui64TransCommitCnt < m_dbHdr.ui64TransCommitCnt - 1) { pDbHdr->ui64TransCommitCnt = m_dbHdr.ui64TransCommitCnt - 1; } bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } } Exit: rc2 = reportStatus( TRUE); if (RC_OK( rc)) { rc = rc2; } if( bStartedTrans) { m_pDb->transAbort(); } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_DbRebuild::recoverNodes( FLMBOOL bRecoverDictionary) { RCODE rc = NE_XFLM_OK; IF_ResultSet * pRootRSet = NULL; IF_ResultSet * pNonRootRSet = NULL; F_CachedNode * pRecovRoot = NULL; F_RebuildNodeIStream * pIStream = NULL; FLMUINT64 ui64RootCount; FLMUINT64 ui64NonRootCount; FLMUINT64 ui64RSPosition; FLMUINT64 ui64NodeId; FLMUINT64 ui64TransStartPos = 0; FLMUINT64 ui64LastAbortPos = 0; F_ELM_INFO elmInfo; FLMUINT uiRSetEntrySize; FLMUINT uiBlockAddr; FLMUINT uiElmNumber; FLMUINT uiTime; FLMUINT uiTransStartTime = 0; FLMUINT uiMaxTransTime; IF_ResultSetCompare * pCompareRSEntry = NULL; FLMBOOL bStartedTrans = FALSE; F_NODE_INFO nodeInfo; FLMBYTE ucIV[ 16]; FLMBYTE ucRSetBuffer[ REBUILD_RSET_ENTRY_SIZE]; XFLM_REBUILD_INFO transStartRebuildInfo; // Allocate result sets if( (pCompareRSEntry = f_new F_NodeResultSetCompare) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = FlmAllocResultSet( &pRootRSet))) { goto Exit; } if( RC_BAD( rc = pRootRSet->setupResultSet( ".", pCompareRSEntry, 0, TRUE, FALSE))) { goto Exit; } if( RC_BAD( rc = FlmAllocResultSet( &pNonRootRSet))) { goto Exit; } if( RC_BAD( rc = pNonRootRSet->setupResultSet( ".", pCompareRSEntry, 0, TRUE, FALSE))) { goto Exit; } // Allocate and configure the rebuild input stream if( (pIStream = f_new F_RebuildNodeIStream) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pIStream->openStream( this, bRecoverDictionary))) { goto Exit; } if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Recover nodes from the source database for( ;;) { if( RC_BAD( rc = pIStream->getNextNodeInfo( &elmInfo, &nodeInfo))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; break; } if( !nodeInfo.ui64ParentId) { buildRSetEntry( getRSetPrefix( nodeInfo.uiNameId), nodeInfo.uiCollection, nodeInfo.ui64NodeId, elmInfo.ui32BlkAddr, elmInfo.uiElmNumber, ucRSetBuffer); if( RC_BAD( rc = pRootRSet->addEntry( ucRSetBuffer, sizeof( ucRSetBuffer)))) { goto Exit; } } else { buildRSetEntry( 0, nodeInfo.uiCollection, nodeInfo.ui64NodeId, elmInfo.ui32BlkAddr, elmInfo.uiElmNumber, ucRSetBuffer); if( RC_BAD( rc = pNonRootRSet->addEntry( ucRSetBuffer, sizeof( ucRSetBuffer)))) { goto Exit; } } m_callbackData.ui64TotNodes++; } bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transAbort())) { goto Exit; } // Sort the result sets if( RC_BAD( rc = pRootRSet->finalizeResultSet( NULL, &ui64RootCount))) { goto Exit; } if( RC_BAD( rc = pNonRootRSet->finalizeResultSet( NULL, &ui64NonRootCount))) { goto Exit; } m_callbackData.ui64TotNodes = ui64RootCount + ui64NonRootCount; uiMaxTransTime = FLM_SECS_TO_TIMER_UNITS( 30); // Add the nodes to the destination database for( ui64RSPosition = 0; ui64RSPosition < ui64RootCount; ui64RSPosition++) { Retry: uiTime = FLM_GET_TIMER(); if( !bStartedTrans) { if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; ui64TransStartPos = ui64RSPosition; uiTransStartTime = FLM_GET_TIMER(); f_memcpy( &transStartRebuildInfo, &m_callbackData, sizeof( XFLM_REBUILD_INFO)); } if( RC_BAD( rc = pRootRSet->setPosition( ui64RSPosition))) { goto Exit; } if( RC_BAD( rc = pRootRSet->getCurrent( ucRSetBuffer, sizeof( ucRSetBuffer), &uiRSetEntrySize))) { goto Exit; } extractRSetEntry( ucRSetBuffer, NULL, NULL, &uiBlockAddr, &uiElmNumber); if( RC_BAD( rc = pIStream->readNode( (FLMUINT32)uiBlockAddr, uiElmNumber, &pRecovRoot, ucIV))) { if( RC_BAD( m_cbrc)) { rc = m_cbrc; goto Exit; } rc = NE_XFLM_OK; continue; } ui64NodeId = pRecovRoot->getNodeId(); if( pRecovRoot->getCollection() == XFLM_DICT_COLLECTION) { if( ui64NodeId == XFLM_DICTINFO_DOC_ID) { // No need to recover the dictinfo document // since it will be rebuilt as we add nodes // to the destination database m_callbackData.ui64NodesRecov++; continue; } } if( ui64LastAbortPos && ui64RSPosition == ui64LastAbortPos) { bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } ui64LastAbortPos = 0; m_callbackData.ui64DiscardedDocs++; continue; } if( RC_BAD( rc = recoverTree( pIStream, pNonRootRSet, NULL, pRecovRoot, ucIV))) { if( RC_BAD( m_cbrc)) { rc = m_cbrc; goto Exit; } bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transAbort())) { goto Exit; } ui64LastAbortPos = ui64RSPosition; ui64RSPosition = ui64TransStartPos; f_memcpy( &m_callbackData, &transStartRebuildInfo, sizeof( XFLM_REBUILD_INFO)); if( RC_BAD( rc = reportStatus( TRUE))) { goto Exit; } if( !ui64RSPosition) { continue; } goto Retry; } if( FLM_ELAPSED_TIME( uiTime, uiTransStartTime) >= uiMaxTransTime) { bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } } } if( bStartedTrans) { bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } } Exit: if( pRecovRoot) { bldFreeCachedNode( &pRecovRoot); } if( pIStream) { pIStream->Release(); } if( bStartedTrans) { m_pDb->transAbort(); } if( pRootRSet) { pRootRSet->Release(); } if( pNonRootRSet) { pNonRootRSet->Release(); } if( pCompareRSEntry) { pCompareRSEntry->Release(); pCompareRSEntry = NULL; } return( RC_BAD( rc) ? rc : reportStatus( TRUE)); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_DbRebuild::recoverTree( F_RebuildNodeIStream * pIStream, IF_ResultSet * pNonRootRSet, F_DOMNode * pParentNode, F_CachedNode * pRecovCachedNode, FLMBYTE * pucNodeIV) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64NodeId = pRecovCachedNode->getNodeId(); FLMUINT64 ui64ChildId; F_DOMNode * pNode = NULL; F_DOMNode * pAttrNode = NULL; F_CachedNode * pRecovChild = NULL; eDomNodeType eRecovNodeType = pRecovCachedNode->getNodeType(); FLMUINT uiCollection = pRecovCachedNode->getCollection(); FLMUINT uiBlockAddr; FLMUINT uiElmNumber; FLMBYTE ucTmpRSetBuffer[ REBUILD_RSET_ENTRY_SIZE]; FLMBYTE ucChildIV[ 16]; // Create the node if( !pParentNode) { if( eRecovNodeType == DOCUMENT_NODE) { if( RC_BAD( rc = m_pDb->createDocument( uiCollection, (IF_DOMNode **)&pNode, &ui64NodeId))) { goto Exit; } } else if( eRecovNodeType == ELEMENT_NODE) { if( RC_BAD( rc = m_pDb->createRootElement( uiCollection, pRecovCachedNode->getNameId(), (IF_DOMNode **)&pNode, &ui64NodeId))) { goto Exit; } } else { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } } else { if( RC_BAD( rc = pParentNode->createNode( m_pDb, eRecovNodeType, pRecovCachedNode->getNameId(), XFLM_LAST_CHILD, (IF_DOMNode **)&pNode, &ui64NodeId))) { goto Exit; } } // Set the value if( pRecovCachedNode->getDataLength()) { if( pRecovCachedNode->getModeFlags() & FDOM_VALUE_ON_DISK) { FLMUINT uiEncDefId = pRecovCachedNode->getEncDefId(); FLMBYTE ucTmpBuf[ FLM_ENCRYPT_CHUNK_SIZE]; FLMUINT uiBytesRead; for( ;;) { if( RC_BAD( rc = pIStream->read( ucTmpBuf, sizeof( ucTmpBuf), &uiBytesRead))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; if( !uiBytesRead) { break; } } if( uiEncDefId) { if( RC_BAD( rc = m_pDb->decryptData( uiEncDefId, pucNodeIV, ucTmpBuf, uiBytesRead, ucTmpBuf, sizeof( ucTmpBuf)))) { goto Exit; } } if( RC_BAD( rc = pNode->setStorageValue( m_pDb, ucTmpBuf, uiBytesRead, uiEncDefId, FALSE))) { goto Exit; } if( uiBytesRead < sizeof( ucTmpBuf)) { break; } } if( RC_BAD( rc = pNode->setStorageValue( m_pDb, NULL, 0, uiEncDefId, TRUE))) { goto Exit; } } else { if( RC_BAD( rc = pNode->setStorageValue( m_pDb, pRecovCachedNode->getDataPtr(), pRecovCachedNode->getDataLength(), pRecovCachedNode->getEncDefId(), TRUE))) { goto Exit; } } } // Add attributes if( pRecovCachedNode->m_uiAttrCount) { FLMUINT uiLoop; F_AttrItem * pAttrItem; for (uiLoop = 0; uiLoop < pRecovCachedNode->m_uiAttrCount; uiLoop++) { pAttrItem = pRecovCachedNode->m_ppAttrList [uiLoop]; if( RC_BAD( rc = pNode->createAttribute( m_pDb, pAttrItem->m_uiNameId, (IF_DOMNode **)&pAttrNode))) { goto Exit; } if( pAttrItem->getAttrDataLength()) { if( RC_BAD( rc = pAttrNode->setStorageValue( m_pDb, pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength(), pAttrItem->getAttrEncDefId(), TRUE))) { goto Exit; } } if( pAttrItem->getAttrModeFlags()) { if( RC_BAD( rc = pAttrNode->addModeFlags( m_pDb, pAttrItem->getAttrModeFlags()))) { goto Exit; } } } } // Set the node's flags if( pRecovCachedNode->getPersistentFlags()) { if( RC_BAD( rc = pNode->addModeFlags( m_pDb, pRecovCachedNode->getPersistentFlags()))) { goto Exit; } } m_callbackData.ui64NodesRecov++; // Recover the node's children (if any) ui64ChildId = pRecovCachedNode->getFirstChildId(); while( ui64ChildId) { buildRSetEntry( 0, uiCollection, ui64ChildId, 0, 0, ucTmpRSetBuffer); if( RC_BAD( rc = pNonRootRSet->findMatch( ucTmpRSetBuffer, REBUILD_RSET_ENTRY_SIZE, ucTmpRSetBuffer, NULL))) { if( rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } extractRSetEntry( ucTmpRSetBuffer, NULL, NULL, &uiBlockAddr, &uiElmNumber); if( RC_BAD( rc = pIStream->readNode( (FLMUINT32)uiBlockAddr, uiElmNumber, &pRecovChild, ucChildIV))) { goto Exit; } if( RC_BAD( rc = recoverTree( pIStream, pNonRootRSet, pNode, pRecovChild, ucChildIV))) { goto Exit; } ui64ChildId = pRecovChild->getNextSibId(); } // Recover the annotation node (if any) if( pRecovCachedNode->getAnnotationId()) { buildRSetEntry( 0, uiCollection, pRecovCachedNode->getAnnotationId(), 0, 0, ucTmpRSetBuffer); if( RC_BAD( rc = pNonRootRSet->findMatch( ucTmpRSetBuffer, REBUILD_RSET_ENTRY_SIZE, ucTmpRSetBuffer, NULL))) { if( rc == NE_XFLM_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } extractRSetEntry( ucTmpRSetBuffer, NULL, NULL, &uiBlockAddr, &uiElmNumber); if( RC_BAD( rc = pIStream->readNode( (FLMUINT32)uiBlockAddr, uiElmNumber, &pRecovChild, ucChildIV))) { goto Exit; } if( RC_BAD( rc = recoverTree( pIStream, pNonRootRSet, pNode, pRecovChild, ucChildIV))) { goto Exit; } } // Set the metavalue if( pRecovCachedNode->getMetaValue()) { if( RC_BAD( rc = pNode->setMetaValue( m_pDb, pRecovCachedNode->getMetaValue()))) { goto Exit; } } // Set the prefix ID if( pRecovCachedNode->getPrefixId()) { if( RC_BAD( rc = pNode->setPrefixId( m_pDb, pRecovCachedNode->getPrefixId()))) { goto Exit; } } // Call document done if this was a root node if( !pParentNode) { if( RC_BAD( rc = m_pDb->documentDone( pNode))) { goto Exit; } } Exit: if( RC_BAD( rc)) { if( pNode) { (void)pNode->deleteNode( m_pDb); } m_callbackData.ui64DiscardedDocs++; } if( pNode) { pNode->Release(); } if( pAttrNode) { pAttrNode->Release(); } if( pRecovChild) { bldFreeCachedNode( &pRecovChild); } return( rc); } /*************************************************************************** Desc: Function to extract information about the current element ***************************************************************************/ FSTATIC FLMINT32 bldGetElmInfo( F_BTREE_BLK_HDR * pBlkHdr, FLMUINT uiBlockSize, FLMUINT uiElmNumber, F_ELM_INFO * pElmInfo) { FLMINT32 i32ErrCode = 0; FLMBYTE * pucElm = NULL; FLMUINT uiElmLen = 0; FLMUINT uiElmKeyLen = 0; FLMUINT uiElmDataLen = 0; FLMUINT uiOverallDataLen = 0; FLMUINT uiDataOnlyBlkAddr = 0; FLMBYTE * pucElmKey = NULL; FLMBYTE * pucElmData = NULL; FLMBYTE * pucBlkEnd; FLMBOOL bNeg; FLMUINT uiNumKeys = pBlkHdr->ui16NumKeys; FLMUINT uiBytesProcessed; FLMUINT16 * pui16OffsetArray; FLMUINT64 ui64ElmNodeId = 0; if( uiElmNumber >= uiNumKeys) { flmAssert( 0); i32ErrCode = FLM_BAD_ELM_OFFSET; goto Exit; } pui16OffsetArray = (FLMUINT16 *)((FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr)); pucElm = (FLMBYTE *)pBlkHdr + bteGetEntryOffset( pui16OffsetArray, uiElmNumber); pucBlkEnd = (FLMBYTE *)pBlkHdr + uiBlockSize; switch( pBlkHdr->stdBlkHdr.ui8BlkType) { case BT_LEAF: { if( pucElm + 2 > pucBlkEnd) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } uiElmKeyLen = FB2UW( pucElm); uiElmLen = uiElmKeyLen + 2; break; } case BT_LEAF_DATA: { FLMBYTE ucFlags = *pucElm; FLMBYTE * pucPtr = &pucElm[ 1]; if( ucFlags & BTE_FLAG_KEY_LEN) { if( pucPtr + 2 > pucBlkEnd) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } uiElmKeyLen = FB2UW( pucPtr); uiElmLen = uiElmKeyLen + 2; pucPtr += 2; } else { if( pucPtr > pucBlkEnd) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } uiElmKeyLen = *pucPtr; uiElmLen = uiElmKeyLen + 1; pucPtr++; } if( ucFlags & BTE_FLAG_DATA_LEN) { if( pucPtr + 2 > pucBlkEnd) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } uiElmDataLen = FB2UW( pucPtr); uiElmLen += (uiElmDataLen + 2); pucPtr += 2; } else { if( pucPtr > pucBlkEnd) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } uiElmDataLen = *pucPtr; uiElmLen += uiElmDataLen + 1; pucPtr++; } if( ucFlags & BTE_FLAG_OA_DATA_LEN) { uiOverallDataLen = FB2UD( pucPtr); uiElmLen += 4; pucPtr += 4; } pucElmKey = pucPtr; pucPtr += uiElmKeyLen; pucElmData = pucPtr; pucPtr += uiElmDataLen; if( bteDataBlockFlag( pucElm)) { if( uiElmDataLen != 4) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } uiDataOnlyBlkAddr = FB2UD( pucElmData); } break; } default: { i32ErrCode = FLM_BAD_BLK_TYPE; goto Exit; } } if( pucElm + uiElmLen > pucBlkEnd) { i32ErrCode = FLM_BAD_ELM_LEN; goto Exit; } if( uiElmKeyLen) { if( RC_BAD( flmCollation2Number( uiElmKeyLen, pucElmKey, &ui64ElmNodeId, &bNeg, &uiBytesProcessed))) { i32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } if( bNeg || uiBytesProcessed != uiElmKeyLen || !ui64ElmNodeId) { i32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } } else { // If the key length is zero, then this MUST be the last block! if( pBlkHdr->stdBlkHdr.ui32NextBlkInChain) { i32ErrCode = FLM_BAD_ELM_KEY; goto Exit; } } if( !uiOverallDataLen) { uiOverallDataLen = uiElmDataLen; } Exit: pElmInfo->uiCollection = pBlkHdr->ui16LogicalFile; pElmInfo->uiBlockSize = uiBlockSize; pElmInfo->uiElmNumber = uiElmNumber; pElmInfo->pucElm = pucElm; pElmInfo->uiElmLen = uiElmLen; pElmInfo->pucElmKey = pucElmKey; pElmInfo->uiElmKeyLen = uiElmKeyLen; pElmInfo->pucElmData = pucElmData; pElmInfo->uiElmDataLen = uiElmDataLen; pElmInfo->uiOverallDataLen = uiOverallDataLen; pElmInfo->uiDataOnlyBlkAddr = uiDataOnlyBlkAddr; pElmInfo->ui64ElmNodeId = ui64ElmNodeId; pElmInfo->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; pElmInfo->ui32NextBlkInChain = pBlkHdr->stdBlkHdr.ui32NextBlkInChain; pElmInfo->uiNumKeysInBlk = pBlkHdr->ui16NumKeys; return( i32ErrCode); } /**************************************************************************** Desc: Extract create options from the DB header. ****************************************************************************/ FSTATIC void flmGetCreateOpts( XFLM_DB_HDR * pDbHdr, XFLM_CREATE_OPTS * pCreateOpts) { f_memset( pCreateOpts, 0, sizeof( XFLM_CREATE_OPTS)); if( pDbHdr) { pCreateOpts->ui32BlockSize = (FLMUINT32)pDbHdr->ui16BlockSize; pCreateOpts->ui32VersionNum = pDbHdr->ui32DbVersion; pCreateOpts->ui32DefaultLanguage = pDbHdr->ui8DefaultLanguage; pCreateOpts->ui32MinRflFileSize = pDbHdr->ui32RflMinFileSize; pCreateOpts->ui32MaxRflFileSize = pDbHdr->ui32RflMaxFileSize; pCreateOpts->bKeepRflFiles = (FLMBOOL)(pDbHdr->ui8RflKeepFiles ? TRUE : FALSE); pCreateOpts->bLogAbortedTransToRfl = (FLMBOOL)(pDbHdr->ui8RflKeepAbortedTrans ? TRUE : FALSE); } else { pCreateOpts->ui32BlockSize = XFLM_DEFAULT_BLKSIZ; pCreateOpts->ui32VersionNum = XFLM_CURRENT_VERSION_NUM; pCreateOpts->ui32DefaultLanguage = XFLM_DEFAULT_LANG; pCreateOpts->ui32MinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; pCreateOpts->ui32MaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; pCreateOpts->bKeepRflFiles = XFLM_DEFAULT_KEEP_RFL_FILES_FLAG; pCreateOpts->bLogAbortedTransToRfl = XFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG; } } /**************************************************************************** Desc : Rebuilds a damaged database. Notes: All the important stuff is handled by the F_DbRebuild::dbRebuild function (below). All this call does is create an F_DbRebuild object, call dbRebuild on it, delete the obj and return the RCODE. ****************************************************************************/ RCODE FLMAPI F_DbSystem::dbRebuild( const char * pszSourceDbPath, const char * pszSourceDataDir, const char * pszDestDbPath, const char * pszDestDataDir, const char * pszDestRflDir, const char * pszDictPath, const char * pszPassword, XFLM_CREATE_OPTS * pCreateOpts, FLMUINT64 * pui64TotNodes, FLMUINT64 * pui64NodesRecov, FLMUINT64 * pui64DiscardedDocs, IF_DbRebuildStatus * ifpDbRebuild) { RCODE rc = NE_XFLM_OK; F_DbRebuild * pRbldObj = NULL; if( (pRbldObj = f_new F_DbRebuild) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } rc = pRbldObj->dbRebuild( pszSourceDbPath, pszSourceDataDir, pszDestDbPath, pszDestDataDir, pszDestRflDir, pszDictPath, pszPassword, pCreateOpts, pui64TotNodes, pui64NodesRecov, pui64DiscardedDocs, ifpDbRebuild); Exit: if( pRbldObj) { pRbldObj->Release(); } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::openStream( F_DbRebuild * pDbRebuild, FLMBOOL bRecovDictionary) { RCODE rc = NE_XFLM_OK; if( m_bOpen) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } m_pDbRebuild = pDbRebuild; m_pDbRebuild->AddRef(); m_bRecovDictionary = bRecovDictionary; f_memset( &m_firstElmState, 0, sizeof( F_SCAN_STATE)); f_memset( &m_tmpState, 0, sizeof( F_SCAN_STATE)); if( RC_BAD( rc = f_alloc( m_pDbRebuild->getBlockSize(), &m_pucFirstElmBlk))) { goto Exit; } if( RC_BAD( rc = f_alloc( m_pDbRebuild->getBlockSize(), &m_pucTmpBlk))) { goto Exit; } m_firstElmState.blkUnion.pucBlk = m_pucFirstElmBlk; m_tmpState.blkUnion.pucBlk = m_pucTmpBlk; m_pCurState = NULL; m_bOpen = TRUE; Exit: if( RC_BAD( rc)) { closeStream(); } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::closeStream( void) { if( m_pucFirstElmBlk) { f_free( &m_pucFirstElmBlk); } if( m_pucTmpBlk) { f_free( &m_pucTmpBlk); } if( m_pDbRebuild) { m_pDbRebuild->Release(); m_pDbRebuild = NULL; } m_pCurState = NULL; m_bOpen = FALSE; f_memset( &m_firstElmState, 0, sizeof( F_SCAN_STATE)); f_memset( &m_tmpState, 0, sizeof( F_SCAN_STATE)); return( NE_XFLM_OK); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::readBlock( FLMBOOL bCount, FLMUINT uiFileNumber, FLMUINT uiFileOffset, F_SCAN_STATE * pScanState) { RCODE rc = NE_XFLM_OK; F_Dict * pDict; FLMUINT uiBlockSize = m_pDbRebuild->getBlockSize(); FLMUINT uiBlkEnd; FLMUINT16 ui16BlkBytesAvail; FLMUINT32 ui32CRC; F_BLK_HDR * pBlkHdr = pScanState->blkUnion.pBlkHdr; FLMBYTE * pucBlk = pScanState->blkUnion.pucBlk; if( RC_BAD( rc = m_pDbRebuild->m_pSFileHdl->readBlock( FSBlkAddress( uiFileNumber, uiFileOffset), uiBlockSize, pucBlk, NULL))) { goto Exit; } if (bCount) { m_pDbRebuild->incrBytesExamined(); } // Determine if we should convert the block here. Calculation of CRC // should be on unconverted block. ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; if( blkIsNonNativeFormat( pBlkHdr)) { convert16( &ui16BlkBytesAvail); } if( ui16BlkBytesAvail > (uiBlockSize - blkHdrSize( pBlkHdr))) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } uiBlkEnd = (blkIsNewBTree( pBlkHdr) ? uiBlockSize : uiBlockSize - (FLMUINT)ui16BlkBytesAvail); // Compute the checksum and convert the block ui32CRC = calcBlkCRC( pBlkHdr, uiBlkEnd); if( blkIsNonNativeFormat( pBlkHdr)) { convertBlk( uiBlockSize, pBlkHdr); } // Does the checksum match? if( ui32CRC != pBlkHdr->ui32BlkCRC) { rc = RC_SET( NE_XFLM_BLOCK_CRC); goto Exit; } // Make sure the transaction ID looks valid if( !m_pDbRebuild->m_bBadHeader && pBlkHdr->ui64TransID > 1 && pBlkHdr->ui64TransID > m_pDbRebuild->m_dbHdr.ui64LastRflCommitID) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // If this is a data-only block with a next sibling, // the block should be full if( pBlkHdr->ui8BlkType == BT_DATA_ONLY && pBlkHdr->ui32NextBlkInChain && pBlkHdr->ui16BlkBytesAvail) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Decrypt the block if( isEncryptedBlk( pBlkHdr)) { if( RC_BAD( rc = m_pDbRebuild->m_pDb->getDictionary( &pDict))) { goto Exit; } if( RC_BAD( rc = m_pDbRebuild->m_pDb->getDatabase()->decryptBlock( pDict, pucBlk))) { goto Exit; } } pScanState->uiFileNumber = uiFileNumber; pScanState->uiFileOffset = uiFileOffset; pScanState->uiBlockSize = uiBlockSize; pScanState->uiBlockBytes = uiBlockSize - pBlkHdr->ui16BlkBytesAvail; pScanState->uiCurOffset = 0; f_memset( &pScanState->elmInfo, 0, sizeof( F_ELM_INFO)); Exit: return( RC_BAD( rc) ? rc : m_pDbRebuild->reportStatus()); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::readNextSequentialBlock( F_SCAN_STATE * pScanState) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkCollectionNum; FLMUINT uiBlockSize = m_pDbRebuild->getBlockSize(); FLMUINT uiTryNextCount = 0; if( pScanState->uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } for( ;;) { if( pScanState->uiFileOffset + uiBlockSize >= m_pDbRebuild->m_dbHdr.ui32MaxFileSize || !pScanState->uiFileNumber) { TryNextFile: pScanState->uiFileOffset = 0; pScanState->uiFileNumber++; if( pScanState->uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER || uiTryNextCount > 5) { pScanState->uiFileNumber = MAX_DATA_BLOCK_FILE_NUMBER + 1; rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { pScanState->uiFileOffset += uiBlockSize; } if( RC_BAD( rc = readBlock( TRUE, pScanState->uiFileNumber, pScanState->uiFileOffset, pScanState))) { if( rc == NE_FLM_IO_END_OF_FILE || rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = NE_XFLM_OK; uiTryNextCount++; goto TryNextFile; } else if( rc == NE_XFLM_DATA_ERROR || rc == NE_XFLM_BLOCK_CRC) { rc = NE_XFLM_OK; continue; } goto Exit; } // Determine if this is a block that should be processed if( pScanState->blkUnion.pBlkHdr->ui32BlkAddr == FSBlkAddress( pScanState->uiFileNumber, pScanState->uiFileOffset) && (pScanState->blkUnion.pBlkHdr->ui8BlkType == BT_LEAF || pScanState->blkUnion.pBlkHdr->ui8BlkType == BT_LEAF_DATA) && pScanState->blkUnion.pBTreeBlkHdr->ui8BlkLevel == 0 && (uiBlkCollectionNum = pScanState->blkUnion.pBTreeBlkHdr->ui16LogicalFile) != 0 && isContainerBlk( pScanState->blkUnion.pBTreeBlkHdr) && doCollection( uiBlkCollectionNum, m_bRecovDictionary)) { // Make sure the block end looks correct if( pScanState->blkUnion.pBlkHdr->ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pScanState->blkUnion.pBlkHdr)) { if( RC_BAD( rc = m_pDbRebuild->reportCorruption( FLM_BAD_BLK_HDR_BLK_END, pScanState->blkUnion.pBlkHdr->ui32BlkAddr, 0, 0))) { goto Exit; } continue; } if( !m_bRecovDictionary) { if( RC_BAD( rc = m_pDbRebuild->m_pDb->getDict()->getCollection( uiBlkCollectionNum, NULL))) { if( rc != NE_XFLM_BAD_COLLECTION) { goto Exit; } rc = NE_XFLM_OK; continue; } } break; } } Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::readNextFirstElm( void) { RCODE rc = NE_XFLM_OK; FLMINT32 i32ErrCode = 0; m_pCurState = NULL; GetNextElement: if( !m_firstElmState.uiFileNumber || m_firstElmState.elmInfo.uiElmNumber + 1 >= m_firstElmState.blkUnion.pBTreeBlkHdr->ui16NumKeys) { if( RC_BAD( rc = readNextSequentialBlock( &m_firstElmState))) { goto Exit; } } else { m_firstElmState.elmInfo.uiElmNumber++; } // Extract information about the element if( (i32ErrCode = bldGetElmInfo( m_firstElmState.blkUnion.pBTreeBlkHdr, m_firstElmState.uiBlockSize, m_firstElmState.elmInfo.uiElmNumber, &m_firstElmState.elmInfo)) != 0) { if( RC_BAD( rc = m_pDbRebuild->reportCorruption( i32ErrCode, FSBlkAddress( m_firstElmState.uiFileNumber, m_firstElmState.uiFileOffset), m_firstElmState.elmInfo.uiElmNumber, m_firstElmState.elmInfo.ui64ElmNodeId))) { goto Exit; } goto GetNextElement; } if( !bteFirstElementFlag( m_firstElmState.elmInfo.pucElm) || !m_firstElmState.elmInfo.uiElmKeyLen) { goto GetNextElement; } if( m_firstElmState.elmInfo.uiDataOnlyBlkAddr) { if( RC_BAD( rc = readFirstDataOnlyBlock())) { if( RC_BAD( m_pDbRebuild->m_cbrc)) { rc = m_pDbRebuild->m_cbrc; goto Exit; } goto GetNextElement; } } else { m_pCurState = &m_firstElmState; m_pCurState->uiCurOffset = 0; } Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::readContinuationElm( void) { RCODE rc = NE_XFLM_OK; FLMINT32 i32ErrCode = 0; if( m_pCurState->elmInfo.uiElmNumber + 1 >= m_pCurState->blkUnion.pBTreeBlkHdr->ui16NumKeys) { F_BLK_HDR * pBlkHdr = m_pCurState->blkUnion.pBlkHdr; if( RC_BAD( rc = readBlock( FALSE, FSGetFileNumber( pBlkHdr->ui32NextBlkInChain), FSGetFileOffset( pBlkHdr->ui32NextBlkInChain), &m_tmpState))) { goto Exit; } if( m_tmpState.blkUnion.pBlkHdr->ui64TransID < m_firstElmState.blkUnion.pBlkHdr->ui64TransID) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } m_pCurState = &m_tmpState; } else { m_pCurState->elmInfo.uiElmNumber++; } // Extract information about the element if( (i32ErrCode = bldGetElmInfo( m_pCurState->blkUnion.pBTreeBlkHdr, m_pCurState->uiBlockSize, m_pCurState->elmInfo.uiElmNumber, &m_pCurState->elmInfo)) != 0) { if( RC_BAD( rc = m_pDbRebuild->reportCorruption( i32ErrCode, FSBlkAddress( m_pCurState->uiFileNumber, m_pCurState->uiFileOffset), m_pCurState->elmInfo.uiElmNumber, m_pCurState->elmInfo.ui64ElmNodeId))) { goto Exit; } rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( bteFirstElementFlag( m_pCurState->elmInfo.pucElm)) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // This had better not be a LEM element. if( !m_pCurState->elmInfo.uiElmKeyLen) { m_pDbRebuild->reportCorruption( FLM_BAD_LEM, m_pCurState->elmInfo.ui32BlkAddr, m_pCurState->elmInfo.uiElmNumber, m_pCurState->elmInfo.ui64ElmNodeId); goto Exit; } if( m_pCurState->elmInfo.ui64ElmNodeId != m_firstElmState.elmInfo.ui64ElmNodeId) { m_pDbRebuild->reportCorruption( FLM_BAD_CONT_ELM_KEY, m_pCurState->elmInfo.ui32BlkAddr, m_pCurState->elmInfo.uiElmNumber, m_pCurState->elmInfo.ui64ElmNodeId); goto Exit; } m_pCurState->uiCurOffset = 0; Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::readFirstDataOnlyBlock( void) { RCODE rc = NE_XFLM_OK; FLMBYTE ucTmpBuf[ 8]; F_ELM_INFO * pElmInfo = &m_firstElmState.elmInfo; flmAssert( pElmInfo->uiDataOnlyBlkAddr); if( RC_BAD( rc = readBlock( FALSE, FSGetFileNumber( pElmInfo->uiDataOnlyBlkAddr), FSGetFileOffset( pElmInfo->uiDataOnlyBlkAddr), &m_tmpState))) { goto Exit; } if( m_tmpState.blkUnion.pBlkHdr->ui64TransID != m_firstElmState.blkUnion.pBlkHdr->ui64TransID) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } m_pCurState = &m_tmpState; m_pCurState->uiCurOffset = blkHdrSize( m_tmpState.blkUnion.pBlkHdr); m_pCurState->elmInfo.uiCollection = pElmInfo->uiCollection; m_pCurState->elmInfo.ui64ElmNodeId = pElmInfo->ui64ElmNodeId; m_pCurState->elmInfo.uiOverallDataLen = pElmInfo->uiOverallDataLen; // Since this is the first block in the data-only chain, the key // is stored in the first few bytes after the block header. Make // sure it matches the key in m_firstElmState. if( RC_BAD( rc = read( ucTmpBuf, 2, NULL))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } if( FB2UW( ucTmpBuf) != m_firstElmState.elmInfo.uiElmKeyLen) { rc = RC_SET( NE_XFLM_DATA_ERROR); } if( RC_BAD( rc = read( ucTmpBuf, m_firstElmState.elmInfo.uiElmKeyLen, NULL))) { if( rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } if( f_memcmp( ucTmpBuf, m_firstElmState.elmInfo.pucElmKey, m_firstElmState.elmInfo.uiElmKeyLen) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); } Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::readNextDataOnlyBlock( void) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32NextBlkAddr = m_pCurState->blkUnion.pBlkHdr->ui32NextBlkInChain; if( !ui32NextBlkAddr) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } if( RC_BAD( rc = readBlock( FALSE, FSGetFileNumber( ui32NextBlkAddr), FSGetFileOffset( ui32NextBlkAddr), &m_tmpState))) { goto Exit; } if( m_tmpState.blkUnion.pBlkHdr->ui64TransID != m_firstElmState.blkUnion.pBlkHdr->ui64TransID) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } m_pCurState = &m_tmpState; m_pCurState->uiCurOffset = blkHdrSize( m_tmpState.blkUnion.pBlkHdr); m_pCurState->elmInfo.uiCollection = m_firstElmState.elmInfo.uiCollection; m_pCurState->elmInfo.ui64ElmNodeId = m_firstElmState.elmInfo.ui64ElmNodeId; m_pCurState->elmInfo.uiOverallDataLen = m_firstElmState.elmInfo.uiOverallDataLen; Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesRead = 0; FLMUINT uiBytesToCopy; F_BLK_HDR * pBlkHdr; F_ELM_INFO * pElmInfo; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; flmAssert( m_bOpen); flmAssert( m_pCurState); while( uiBytesRead < uiBytesToRead) { pBlkHdr = m_pCurState->blkUnion.pBlkHdr; pElmInfo = &m_pCurState->elmInfo; if( pBlkHdr->ui8BlkType != BT_DATA_ONLY) { uiBytesToCopy = pElmInfo->uiElmDataLen - m_pCurState->uiCurOffset; } else { uiBytesToCopy = m_pCurState->uiBlockBytes - m_pCurState->uiCurOffset; } uiBytesToCopy = (uiBytesToRead - uiBytesRead) < uiBytesToCopy ? (uiBytesToRead - uiBytesRead) : uiBytesToCopy; if( !uiBytesToCopy) { if( pBlkHdr->ui8BlkType != BT_DATA_ONLY) { if( RC_BAD( rc = readContinuationElm())) { goto Exit; } } else { if( RC_BAD( rc = readNextDataOnlyBlock())) { goto Exit; } } continue; } if( pucBuffer) { if( pBlkHdr->ui8BlkType != BT_DATA_ONLY) { f_memcpy( pucBuffer, &pElmInfo->pucElmData[ m_pCurState->uiCurOffset], uiBytesToCopy); } else { f_memcpy( pucBuffer, &m_pCurState->blkUnion.pucBlk[ m_pCurState->uiCurOffset], uiBytesToCopy); } pucBuffer += uiBytesToCopy; } m_pCurState->uiCurOffset += uiBytesToCopy; uiBytesRead += uiBytesToCopy; } if( uiBytesRead < uiBytesToRead) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } Exit: if( puiBytesRead) { *puiBytesRead = uiBytesRead; } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::getNextNode( F_CachedNode ** ppCachedNode, F_ELM_INFO * pElmInfo, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; F_CachedNode * pCachedNode = NULL; if( ppCachedNode) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( *ppCachedNode) { pCachedNode = *ppCachedNode; *ppCachedNode = NULL; pCachedNode->decrNodeUseCount(); pCachedNode->resetNode(); pCachedNode->incrNodeUseCount(); } else { if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocNode( &pCachedNode, TRUE))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pCachedNode->incrNodeUseCount(); } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } // Read the node for( ;;) { if( RC_BAD( rc = readNextFirstElm())) { goto Exit; } if( pElmInfo) { f_memcpy( pElmInfo, &m_firstElmState.elmInfo, sizeof( F_ELM_INFO)); } if( !ppCachedNode) { break; } if( RC_OK( rc = pCachedNode->readNode( m_pDbRebuild->m_pDb, m_firstElmState.elmInfo.uiCollection, m_firstElmState.elmInfo.ui64ElmNodeId, this, m_firstElmState.elmInfo.uiOverallDataLen, pucIV))) { break; } if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_MEM) { goto Exit; } rc = NE_XFLM_OK; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); pCachedNode->decrNodeUseCount(); pCachedNode->resetNode(); pCachedNode->incrNodeUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } if( ppCachedNode) { *ppCachedNode = pCachedNode; pCachedNode = NULL; } Exit: if( pCachedNode) { bldFreeCachedNode( &pCachedNode); } return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_RebuildNodeIStream::getNextNodeInfo( F_ELM_INFO * pElmInfo, F_NODE_INFO * pNodeInfo) { RCODE rc = NE_XFLM_OK; FLMUINT uiTmpFlags; // Read the node info for( ;;) { if( RC_BAD( rc = readNextFirstElm())) { goto Exit; } f_memcpy( pElmInfo, &m_firstElmState.elmInfo, sizeof( F_ELM_INFO)); if( RC_OK( rc = flmReadNodeInfo( m_firstElmState.elmInfo.uiCollection, m_firstElmState.elmInfo.ui64ElmNodeId, this, m_firstElmState.elmInfo.uiOverallDataLen, FALSE, pNodeInfo, &uiTmpFlags))) { break; } if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_MEM) { goto Exit; } rc = NE_XFLM_OK; } Exit: return( rc); } /*************************************************************************** Desc: This routine retrieves one node starting at the current element. *****************************************************************************/ RCODE F_RebuildNodeIStream::readNode( FLMUINT32 ui32BlkAddr, FLMUINT uiElmNumber, F_CachedNode ** ppCachedNode, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; F_CachedNode * pCachedNode = NULL; FLMINT32 i32ErrCode = 0; m_pCurState = NULL; f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); if( *ppCachedNode) { pCachedNode = *ppCachedNode; *ppCachedNode = NULL; pCachedNode->decrNodeUseCount(); pCachedNode->resetNode(); pCachedNode->incrNodeUseCount(); } else { if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocNode( &pCachedNode, TRUE))) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); goto Exit; } pCachedNode->incrNodeUseCount(); } f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); if( RC_BAD( rc = readBlock( FALSE, FSGetFileNumber( ui32BlkAddr), FSGetFileOffset( ui32BlkAddr), &m_firstElmState))) { goto Exit; } m_pCurState = &m_firstElmState; // Extract information about the element if( (i32ErrCode = bldGetElmInfo( m_firstElmState.blkUnion.pBTreeBlkHdr, m_firstElmState.uiBlockSize, uiElmNumber, &m_firstElmState.elmInfo)) != 0) { if( RC_BAD( rc = m_pDbRebuild->reportCorruption( i32ErrCode, FSBlkAddress( m_firstElmState.uiFileNumber, m_firstElmState.uiFileOffset), m_firstElmState.elmInfo.uiElmNumber, m_firstElmState.elmInfo.ui64ElmNodeId))) { goto Exit; } rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( !bteFirstElementFlag( m_firstElmState.elmInfo.pucElm)) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( m_firstElmState.elmInfo.uiDataOnlyBlkAddr) { if( RC_BAD( rc = readFirstDataOnlyBlock())) { goto Exit; } } else { m_pCurState = &m_firstElmState; m_pCurState->uiCurOffset = 0; } // Read the node if( RC_BAD( rc = pCachedNode->readNode( m_pDbRebuild->m_pDb, m_pCurState->elmInfo.uiCollection, m_pCurState->elmInfo.ui64ElmNodeId, this, m_pCurState->elmInfo.uiOverallDataLen, pucIV))) { goto Exit; } *ppCachedNode = pCachedNode; pCachedNode = NULL; Exit: if( pCachedNode) { bldFreeCachedNode( &pCachedNode); } return( rc); } /*************************************************************************** Desc: This routine reads through a database and makes a best guess as to the true block size of the database. *****************************************************************************/ RCODE F_DbRebuild::determineBlkSize( FLMUINT * puiBlkSizeRV) { RCODE rc = NE_XFLM_OK; F_BLK_HDR blkHdr; FLMUINT uiBytesRead; FLMUINT uiBlkAddress; FLMUINT uiFileNumber = 0; FLMUINT uiOffset = 0; FLMUINT uiCount4K = 0; FLMUINT uiCount8K = 0; FLMUINT64 ui64BytesDone = 0; IF_FileHdl * pFileHdl = NULL; // Start from byte offset 0 in the first file. m_callbackData.i32DoingFlag = REBUILD_GET_BLK_SIZ; m_callbackData.bStartFlag = TRUE; for (;;) { if( uiOffset >= m_dbHdr.ui32MaxFileSize || !uiFileNumber) { TryNextFile: uiOffset = 0; uiFileNumber++; } if( RC_BAD( rc = pFileHdl->read( uiOffset, SIZEOF_STD_BLK_HDR, &blkHdr, &uiBytesRead))) { if( rc == NE_XFLM_EOF_HIT || rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = NE_XFLM_OK; goto TryNextFile; } goto Exit; } ui64BytesDone += (FLMUINT64)XFLM_MIN_BLOCK_SIZE; uiBlkAddress = (FLMUINT)blkHdr.ui32BlkAddr; // If the block address does not match up, try converting it. if( FSGetFileOffset( uiBlkAddress) != uiOffset) { convert32( &blkHdr.ui32BlkAddr); uiBlkAddress = (FLMUINT)blkHdr.ui32BlkAddr; } if( FSGetFileOffset( uiBlkAddress) == uiOffset) { if( uiOffset % 4096 == 0) { if( ++uiCount4K >= 1000) { break; } } if( uiOffset % 8192 == 0) { if( ++uiCount8K >= 1000) { break; } } } uiOffset += XFLM_MIN_BLOCK_SIZE; if( RC_BAD( rc = reportStatus())) { goto Exit; } } if( uiCount8K > uiCount4K) { *puiBlkSizeRV = 8192; } else { *puiBlkSizeRV = 4096; } Exit: return( rc); } libxflaim-5.1.969/src/fcollate.h0000644000175000017500000000530110511001742017765 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Header for collation routines // // Tabs: 3 // // Copyright (c) 1991-1992, 1994-2000, 2002-2006 Novell, Inc. // All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fcollate.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FCOLLATE_H #define FCOLLATE_H #define TRUNCATED_FLAG 0x8000 #define EXCLUSIVE_LT_FLAG 0x4000 #define EXCLUSIVE_GT_FLAG 0x2000 #define SEARCH_KEY_FLAG 0x1000 #define KEY_COMPONENT_LENGTH_MASK 0x0FFF #define KEY_LOW_VALUE 0x0FFE #define KEY_HIGH_VALUE 0x0FFF FINLINE FLMBOOL isKeyComponentLTExclusive( const FLMBYTE * pucKeyComponent) { return( (FB2UW( pucKeyComponent) & EXCLUSIVE_LT_FLAG) ? TRUE : FALSE); } FINLINE FLMBOOL isKeyComponentGTExclusive( const FLMBYTE * pucKeyComponent) { return( (FB2UW( pucKeyComponent) & EXCLUSIVE_GT_FLAG) ? TRUE : FALSE); } FINLINE FLMBOOL isKeyComponentTruncated( const FLMBYTE * pucKeyComponent) { return( (FB2UW( pucKeyComponent) & TRUNCATED_FLAG) ? TRUE : FALSE); } FINLINE FLMBOOL isSearchKeyComponent( const FLMBYTE * pucKeyComponent) { return( (FB2UW( pucKeyComponent) & SEARCH_KEY_FLAG) ? TRUE : FALSE); } FINLINE FLMUINT getKeyComponentLength( const FLMBYTE * pucKeyComponent) { return( (FLMUINT)(FB2UW( pucKeyComponent)) & KEY_COMPONENT_LENGTH_MASK); } RCODE flmColText2StorageText( const FLMBYTE * pucColStr, FLMUINT uiColStrLen, FLMBYTE * pucStorageBuf, FLMUINT * puiStorageLen, FLMUINT uiLang, FLMBOOL * pbDataTruncated, FLMBOOL * pbFirstSubstring); RCODE flmUTF8ToColText( IF_PosIStream * pIStream, FLMBYTE * pucCollatedStr, FLMUINT * puiCollatedStrLen, FLMBOOL bCaseInsensitive, FLMUINT * puiCollationLen, FLMUINT * puiCaseLen, FLMUINT uiLanguage, FLMUINT uiCharLimit, FLMBOOL bFirstSubstring, FLMBOOL bDataTruncated, FLMBOOL * pbOriginalCharsLost, FLMBOOL * pbDataTruncated); #endif libxflaim-5.1.969/src/fdbremov.cpp0000644000175000017500000001633110511001742020340 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the F_DbSystem::dbRemove method. // // Tabs: 3 // // Copyright (c) 2001-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fdbremov.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Deletes all files of a database ****************************************************************************/ RCODE F_DbSystem::dbRemove( const char * pszDbName, // [IN] Name of source database to be deleted. const char * pszDataDir, // [IN] Directory where data files are located. const char * pszRflDir, // [IN] RFL directory of database. NULL can be // passed to indicate that the log files are located // in the same directory as the other database files. FLMBOOL bRemoveRflFiles) { RCODE rc = NE_XFLM_OK; FLMUINT uiFileNumber; char * pszTmpName = NULL; char * pszRflDirName; char * pszDataName; char * pszBaseName; char * pszExt; char * pszDataExt; IF_DirHdl * pDirHdl = NULL; // Cannot handle empty database name. if( !pszDbName || !(*pszDbName)) { rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); goto Exit; } // Allocate memory, so as to not consume stack. if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 3 + F_FILENAME_SIZE, &pszTmpName))) { goto Exit; } pszRflDirName = pszTmpName + F_PATH_MAX_SIZE; pszDataName = pszRflDirName + F_PATH_MAX_SIZE; pszBaseName = pszDataName + F_PATH_MAX_SIZE; // First make sure we have closed this database and gotten rid of // it from our internal memory tables - in case it had been open. if (RC_BAD( rc = checkDatabaseClosed( pszDbName, pszDataDir))) { goto Exit; } // Close all unused file handles if( gv_XFlmSysData.pFileHdlCache) { gv_XFlmSysData.pFileHdlCache->closeUnusedFiles(); } if (pszDataDir && *pszDataDir) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( pszDbName, pszDataName, pszBaseName))) { goto Exit; } f_strcpy( pszDataName, pszDataDir); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( pszDataName, pszBaseName))) { goto Exit; } } else { f_strcpy( pszDataName, pszDbName); } f_strcpy( pszTmpName, pszDbName); // Start deleting files, beginning with the main DB file. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->deleteFile( pszDbName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } else { goto Exit; } } // Find where the extension of the database name is pszExt = pszTmpName + f_strlen( pszTmpName) - 1; pszDataExt = pszDataName + f_strlen( pszDataName) - 1; while (pszExt != pszTmpName && *pszExt != '.') { pszExt--; // Both the db name and data name have the same // base name, so we can decrement pszDataExt // at the same time we decrement pszExt. pszDataExt--; } if (*pszExt != '.') { pszExt = pszTmpName + f_strlen( pszTmpName); pszDataExt = pszDataName + f_strlen( pszDataName); } // Delete the .lck file, if any f_strcpy( pszExt, ".lck"); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } else { goto Exit; } } // Delete block (data) files. uiFileNumber = 1; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszDataExt); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->deleteFile( pszDataName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; break; } else { goto Exit; } } if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) { break; } uiFileNumber++; } // Delete rollback log files. uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; for (;;) { F_SuperFileClient::bldSuperFileExtension( uiFileNumber, pszExt); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; break; } else { goto Exit; } } if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) { break; } uiFileNumber++; } if (bRemoveRflFiles) { // Delete roll-forward log files. FLMBOOL bCanDeleteDir; // Scan the RFL directory for RFL files. if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, pszRflDirName))) { goto Exit; } // See if the directory exists. If not, we are done. if (!gv_XFlmSysData.pFileSystem->isDir( pszRflDirName)) { goto Exit; // Should return NE_XFLM_OK } // Open the directory and scan for RFL files. // NOTE: DO NOT just call removeDir. There may be other // things in the directory that we do not want to delete. // Look specifically for files that match our expected // name format for RFL files. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openDir( pszRflDirName, "*", &pDirHdl))) { goto Exit; } // Assume that we can delete the directory. This will only // be set to FALSE if we can't delete all of the files in // the directory - i.e., some don't look like RFL log files. bCanDeleteDir = TRUE; for (;;) { if (RC_BAD( rc = pDirHdl->next())) { if (rc == NE_FLM_IO_NO_MORE_FILES) { rc = NE_XFLM_OK; break; } else { goto Exit; } } pDirHdl->currentItemPath( pszTmpName); if (pDirHdl->currentItemIsDir()) { bCanDeleteDir = FALSE; } else if (!rflGetFileNum( pszTmpName, &uiFileNumber)) { bCanDeleteDir = FALSE; } else { if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->deleteFile( pszTmpName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } else { goto Exit; } } } } // Attempt to delete the directory - if allowed. if (bCanDeleteDir) { // Need to release the directory handle so the // directory will be closed when we try to delete it // below. if (pDirHdl) { pDirHdl->Release(); pDirHdl = NULL; } if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->removeDir( pszRflDirName))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } goto Exit; } } } Exit: if( pszTmpName) { f_free( &pszTmpName); } if( pDirHdl) { pDirHdl->Release(); } return( rc); } libxflaim-5.1.969/src/recover.cpp0000644000175000017500000003111110511001742020172 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains routines for recovering a database after // a failure. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: recover.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: This routine reads the next before-image block from the database. ****************************************************************************/ RCODE F_Db::readRollbackLog( FLMUINT uiLogEOF, // Address of end of rollback log. FLMUINT * puiCurrAddr, // This is the current address we are // reading in the log file. It // will be updated after reading the // data. F_BLK_HDR * pBlkHdr, // This is the buffer that is to hold // the data that is read from the // log file. FLMBOOL * pbIsBeforeImageBlk// Is block a before-image block? ) { RCODE rc = NE_XFLM_OK; FLMUINT uiFilePos; FLMUINT uiBlkSize = m_pDatabase->m_uiBlockSize; FLMUINT uiBytesRead; F_TMSTAMP StartTime; uiFilePos = *puiCurrAddr; // Verify that we are not going to read beyond the log EOF if (!FSAddrIsAtOrBelow( uiFilePos + uiBlkSize, uiLogEOF)) { rc = RC_SET( NE_XFLM_INCOMPLETE_LOG); goto Exit; } // Position to the appropriate place and read the data if (m_pDbStats) { m_pDbStats->bHaveStats = TRUE; m_pDbStats->LogBlockReads.ui64Count++; m_pDbStats->LogBlockReads.ui64TotalBytes += uiBlkSize; f_timeGetTimeStamp( &StartTime); } if (RC_BAD( rc = m_pSFileHdl->readBlock( uiFilePos, uiBlkSize, (FLMBYTE *)pBlkHdr, &uiBytesRead))) { if (rc == NE_FLM_IO_END_OF_FILE) { rc = RC_SET( NE_XFLM_INCOMPLETE_LOG); } if (m_pDbStats) { m_pDbStats->uiReadErrors++; } goto Exit; } if (m_pDbStats) { flmAddElapTime( &StartTime, &m_pDbStats->LogBlockReads.ui64ElapMilli); } if (uiBytesRead != uiBlkSize) { if (m_pDbStats) { m_pDbStats->uiLogBlockChkErrs++; } rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if (RC_BAD( rc = flmPrepareBlockForUse( uiBlkSize, pBlkHdr))) { if (m_pDbStats && rc == NE_XFLM_BLOCK_CRC) { m_pDbStats->uiLogBlockChkErrs++; } goto Exit; } // See if before image bit is set. Need to unset it if it is. *pbIsBeforeImageBlk = (FLMBOOL)((pBlkHdr->ui8BlkFlags & BLK_IS_BEFORE_IMAGE) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE); pBlkHdr->ui8BlkFlags &= ~(BLK_IS_BEFORE_IMAGE); // Adjust the current address for the next read uiFilePos += uiBlkSize; if (FSGetFileOffset( uiFilePos) >= m_pDatabase->m_uiMaxFileSize) { FLMUINT uiFileNumber = FSGetFileNumber( uiFilePos); if (!uiFileNumber) { uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; } else { uiFileNumber++; } if (uiFileNumber > MAX_LOG_BLOCK_FILE_NUMBER) { rc = RC_SET( NE_XFLM_DB_FULL); goto Exit; } uiFilePos = FSBlkAddress( uiFileNumber, 0 ); } *puiCurrAddr = uiFilePos; Exit: return( rc); } /**************************************************************************** Desc: This routine reads and processes a before-image block record in the log file. The reapply flag indicates whether the block should be written back to the database file. ****************************************************************************/ RCODE F_Db::processBeforeImage( FLMUINT uiLogEOF, // Address of the end of the rollback // log. FLMUINT * puiCurrAddrRV, // This is the current offset we are // reading in the log file. // It will be updated after reading the // data. F_BLK_HDR * pBlkHdr, // This is a pointer to a buffer that // will be used to hold the block that // is read. FLMBOOL bDoingRecovery, // Are we doing a recovery as opposed to // rolling back a transaction? FLMUINT64 ui64MaxTransID // Maximum transaction ID to recover to when // bDoingRecovery is TRUE. This parameter // is ignored when bDoingRecover is FALSE. ) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkAddress; FLMUINT uiBlkLength; #ifdef FLM_DBG_LOG FLMUINT64 ui64TransID; #endif FLMUINT uiBytesWritten; FLMBOOL bIsBeforeImageBlk; F_TMSTAMP StartTime; // Read the block from the log if (RC_BAD( rc = readRollbackLog( uiLogEOF, puiCurrAddrRV, pBlkHdr, &bIsBeforeImageBlk))) { goto Exit; } // Determine if we want to restore the block. // If we are doing a recovery, restore the block only if // its checkpoint is <= ui64MaxTransID. If we are // rolling back a transaction, restore the block only if // it is marked as a before-image block. // For the recovery process, multiple versions // of the same block may be restored if there are // multiple versions in the log. However, because // the versions will be in ascending order in the // file, ultimately, the one with the highest // checkpoint that does not exceed ui64MaxTransID // will be restored - which is precisely the one // we want to be restored for a recovery. // For a transaction rollback, it is impossible for us // to see more than one version of a block that is // marked as the before-image version, because we // started from a point in the log where the last // update transaction logged its first block. All // blocks after that point that have the BI bits // set should be restored. Any that do not have // the BI bit set should NOT be restored. if (bDoingRecovery) { if (pBlkHdr->ui64TransID > ui64MaxTransID) { goto Exit; } } else if (!bIsBeforeImageBlk) { goto Exit; } // Determine the block address before setting the checksum. uiBlkAddress = (FLMUINT)pBlkHdr->ui32BlkAddr; uiBlkLength = blkGetEnd( m_pDatabase->m_uiBlockSize, blkHdrSize( pBlkHdr), pBlkHdr); #ifdef FLM_DBG_LOG ui64TransID = pBlkHdr->ui64TransID; #endif if (RC_BAD( rc = flmPrepareBlockToWrite( m_pDatabase->m_uiBlockSize, pBlkHdr))) { goto Exit; } if (m_pDbStats) { m_pDbStats->bHaveStats = TRUE; m_pDbStats->LogBlockRestores.ui64Count++; m_pDbStats->LogBlockRestores.ui64TotalBytes += uiBlkLength; f_timeGetTimeStamp( &StartTime); } m_pSFileHdl->setMaxAutoExtendSize( m_pDatabase->m_uiMaxFileSize); m_pSFileHdl->setExtendSize( m_pDatabase->m_uiFileExtendSize); rc = m_pSFileHdl->writeBlock( uiBlkAddress, uiBlkLength, pBlkHdr, &uiBytesWritten); #ifdef FLM_DBG_LOG flmDbgLogWrite( m_pDatabase, uiBlkAddress, 0, ui64TransID, "ROLLBACK"); #endif if (m_pDbStats) { flmAddElapTime( &StartTime, &m_pDbStats->LogBlockRestores.ui64ElapMilli); if (RC_BAD( rc)) { m_pDbStats->uiWriteErrors++; } } Exit: return( rc); } /*************************************************************************** Desc: Writes the log header to disk. The checksum is calculated before writing the log header to disk. *****************************************************************************/ RCODE F_Database::writeDbHdr( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, XFLM_DB_HDR * pDbHdr, // DB header to be written out. XFLM_DB_HDR * pCPDbHdr, // DB header as it was at the time // of the checkpoint. FLMBOOL bIsCheckpoint // Are we writing a checkpoint? If we // we are, we may write the DB header // as is. Otherwise, we need to make // sure we don't write out certain // parts of the DB header - they must // not be updated on disk until a // checkpoint actually occurs. ) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesWritten; XFLM_DB_HDR * pTmpDbHdr; F_TMSTAMP StartTime; // Force any recent writes to disk before modifying the DB // header. This routine is generally called after having // written out data blocks or rollback blocks. It is // critial that any previous writes be flushed before the // header is updated because the header will generally have // been modified to point to the new things that were added. if (RC_BAD( rc = pSFileHdl->flush())) { goto Exit; } // No need to ever actually write the header to disk if this is // a temporary database if (m_bTempDb) { goto Exit; } pTmpDbHdr = m_pDbHdrWriteBuf; uiBytesWritten = sizeof( XFLM_DB_HDR); f_memcpy( pTmpDbHdr, pDbHdr, sizeof( XFLM_DB_HDR)); // If we are not doing a checkpoint, we don't really want // to write out certain items, so we restore them from // the database header as it was at the time of the last // checkpoint. if (!bIsCheckpoint && pCPDbHdr) { pTmpDbHdr->ui32RflLastCPFileNum = pCPDbHdr->ui32RflLastCPFileNum; pTmpDbHdr->ui32RflLastCPOffset = pCPDbHdr->ui32RflLastCPOffset; pTmpDbHdr->ui64CurrTransID = pCPDbHdr->ui64CurrTransID; pTmpDbHdr->ui64TransCommitCnt = pCPDbHdr->ui64TransCommitCnt; pTmpDbHdr->ui32FirstAvailBlkAddr = pCPDbHdr->ui32FirstAvailBlkAddr; pTmpDbHdr->ui32LogicalEOF = pCPDbHdr->ui32LogicalEOF; pTmpDbHdr->ui32BlksChangedSinceBackup = pCPDbHdr->ui32BlksChangedSinceBackup; pTmpDbHdr->ui64LastRflCommitID = pCPDbHdr->ui64LastRflCommitID; } // Header is always written out in native format. Set the CRC flmAssert( !hdrIsNonNativeFormat( pTmpDbHdr)); pTmpDbHdr->ui32HdrCRC = calcDbHdrCRC( pTmpDbHdr); // Now update the log header record on disk. if (pDbStats) { pDbStats->bHaveStats = TRUE; pDbStats->DbHdrWrites.ui64Count++; pDbStats->DbHdrWrites.ui64TotalBytes += uiBytesWritten; f_timeGetTimeStamp( &StartTime); } if( RC_BAD( rc = pSFileHdl->writeBlock( 0, uiBytesWritten, pTmpDbHdr, &uiBytesWritten))) { if (pDbStats) { pDbStats->uiWriteErrors++; } goto Exit; } if (pDbStats) { flmAddElapTime( &StartTime, &pDbStats->DbHdrWrites.ui64ElapMilli); } // Finally, force the header to disk. if (RC_BAD( rc = pSFileHdl->flush())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine recovers the database to a physically consistent state. Ret: NE_XFLM_OK - Indicates the database has been recovered. other - other FLAIM error codes ****************************************************************************/ RCODE F_Db::physRollback( FLMUINT uiLogEOF, FLMUINT uiFirstLogBlkAddr, // Address of first log block FLMBOOL bDoingRecovery, // Doing recovery? If so, we will // ignore blocks whose transaction // ID is higher than ui64MaxTransID. // Also, we will not check the BI // bits in the logged blocks, because // we are not rolling back a // transaction. FLMUINT64 ui64MaxTransID // Ignored when bDoingRecovery is // FALSE ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCurrAddr; FLMBYTE * pucBlk = NULL; // If the log is empty, no need to do anything. // A uiFirstLogBlkAddr of zero indicates that there // is nothing in the log to rollback. This will be true // if we are rolling back a transaction, and the transaction // has not logged anything or if we are doing a recovery and // nothing was logged since the last checkpoint. if (uiLogEOF == m_pDatabase->m_uiBlockSize || !uiFirstLogBlkAddr) { goto Exit; // Will return NE_XFLM_OK } // Allocate a buffer to be used for reading. if( RC_BAD( rc = f_allocAlignedBuffer( m_pDatabase->m_uiBlockSize, (void **)&pucBlk))) { goto Exit; } // Start from beginning of log and read to EOF restoring before-image // blocks along the way. uiCurrAddr = uiFirstLogBlkAddr; while (FSAddrIsBelow( uiCurrAddr, uiLogEOF)) { if (RC_BAD( rc = processBeforeImage( uiLogEOF, &uiCurrAddr, (F_BLK_HDR *)pucBlk, bDoingRecovery, ui64MaxTransID))) { goto Exit; } } // Force the writes to the file. if (RC_BAD( rc = m_pSFileHdl->flush())) { goto Exit; } Exit: // Free the memory handle, if one was allocated. if (pucBlk) { f_freeAlignedBuffer( (void **)&pucBlk); } return( rc); } libxflaim-5.1.969/src/f_btree.cpp0000644000175000017500000105240610511001742020146 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This class handles all of operations on a given B-Tree. // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: f_btree.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC FLMUINT btGetEntryDataLength( FLMBYTE * pucEntry, const FLMBYTE ** ppucDataRV, FLMUINT * puiOADataLengthRV, FLMBOOL * pbDOBlockRV); FSTATIC RCODE btGetEntryData( FLMBYTE * pucEntry, FLMBYTE * pucBufferRV, FLMUINT uiBufferSize, FLMUINT * puiLenDataRV); /*************************************************************************** Desc: Constructor ****************************************************************************/ F_Btree::F_Btree( void) { m_bOpened = FALSE; m_pStack = NULL; m_uiStackLevels = 0; m_uiRootLevel = 0; f_memset(m_Stack, 0, sizeof(m_Stack)); m_pLFile = NULL; m_pDb = NULL; m_bTempDb = FALSE; m_pucTempBlk = NULL; m_pucTempDefragBlk = NULL; m_bCounts = FALSE; m_bData = TRUE; // Default m_bSetupForRead = FALSE; m_bSetupForWrite = FALSE; m_bSetupForReplace = FALSE; m_uiBlockSize = 0; m_uiDefragThreshold = 0; m_uiOverflowThreshold = 0; m_pReplaceInfo = NULL; m_pReplaceStruct = NULL; m_uiReplaceLevels = 0; m_ui64CurrTransID = 0; m_ui64LastBlkTransId = 0; m_ui64PrimaryBlkTransId = 0; m_uiBlkChangeCnt = 0; m_ui64LowTransId = FLM_MAX_UINT64; m_bMostCurrent = FALSE; m_uiDataLength = 0; m_uiPrimaryDataLen = 0; m_uiOADataLength = 0; m_uiDataRemaining = 0; m_uiOADataRemaining = 0; m_uiOffsetAtStart = 0; m_bDataOnlyBlock = FALSE; m_bOrigInDOBlocks = FALSE; m_ui32PrimaryBlkAddr = 0; m_uiPrimaryOffset = 0; m_ui32DOBlkAddr = 0; m_ui32CurBlkAddr = 0; m_uiCurOffset = 0; m_pucDataPtr = NULL; m_bFirstRead = FALSE; m_pSCache = NULL; m_pBlkHdr = NULL; m_uiSearchLevel = BH_MAX_LEVELS; m_pNext = NULL; m_pCompare = NULL; } /*************************************************************************** Desc: Destructor ****************************************************************************/ F_Btree::~F_Btree( void) { if ( m_bOpened) { btClose(); } } /*************************************************************************** Desc: Function to create a new (empty) B-Tree. To do this, we create the root block. Upon return, the Root block address and the block address will be set in the LFile. ****************************************************************************/ RCODE F_Btree::btCreate( F_Db * pDb, // In LFILE * pLFile, // In/Out FLMBOOL bCounts, // In FLMBOOL bData // In ) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = NULL; F_BTREE_BLK_HDR * pBlkHdr = NULL; FLMUINT16 * pui16Offset; FLMBYTE * pucEntry; FLMBYTE ucLEMEntry[ 3]; FLMUINT uiFlags = 0; FLMUINT uiLEMSize; // We can't create a new Btree if we have already been initialized. if (m_bOpened) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Verify that we are in an update transaction. if (pDb->m_eTransType != XFLM_UPDATE_TRANS && !pDb->m_pDatabase->m_bTempDb) { rc = RC_SET_AND_ASSERT( pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Initialize the returned root block address to 0 incase of an error. pLFile->uiRootBlk = 0; // Call createBlock to create a new block if (RC_BAD( rc = pDb->m_pDatabase->createBlock( pDb, &pSCache))) { goto Exit; } // Save the new block address as the root block address pLFile->uiRootBlk = pSCache->m_uiBlkAddress; // Save the block address and identify the block as the root block. if (RC_BAD( rc = btOpen( pDb, pLFile, bCounts, bData))) { goto Exit; } pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; setRootBlk( pBlkHdr); pBlkHdr->ui16LogicalFile = (FLMUINT16)pLFile->uiLfNum; setBlkLfType( pBlkHdr, pLFile->eLfType); pBlkHdr->ui8BlkLevel = 0; pBlkHdr->stdBlkHdr.ui8BlkType = (bData ? BT_LEAF_DATA : BT_LEAF); pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = 0; pBlkHdr->stdBlkHdr.ui32NextBlkInChain = 0; if (pLFile->uiEncId) { setBlockEncrypted( (F_BLK_HDR *)pBlkHdr); } // Insert a LEM into the block uiFlags = BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT; if (RC_BAD( rc = buildAndStoreEntry( (bData ? BT_LEAF_DATA : BT_LEAF), uiFlags, NULL, 0, NULL, 0, 0, 0, 0, &ucLEMEntry[0], 3, &uiLEMSize))) { goto Exit; } pui16Offset = BtOffsetArray((FLMBYTE *)pBlkHdr, 0); pucEntry = (FLMBYTE *)pBlkHdr + m_uiBlockSize - uiLEMSize; bteSetEntryOffset( pui16Offset, 0, (FLMUINT16)(pucEntry - (FLMBYTE *)pBlkHdr)); f_memcpy( pucEntry, ucLEMEntry, uiLEMSize); // Offset Entry & 2 byte LEM pBlkHdr->stdBlkHdr.ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr(pBlkHdr) - uiLEMSize - 2); pBlkHdr->ui16HeapSize = pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; // There is one entry now. pBlkHdr->ui16NumKeys = 1; Exit: if (pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Btree initialization function. ****************************************************************************/ RCODE F_Btree::btOpen( F_Db * pDb, LFILE * pLFile, FLMBOOL bCounts, FLMBOOL bData, IF_ResultSetCompare * pCompare) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase= pDb->m_pDatabase; if ( m_bOpened) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } if (pDb->m_eTransType == XFLM_NO_TRANS && !pDatabase->m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } if( !pLFile->uiRootBlk) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } m_pLFile = pLFile; m_uiBlockSize = pDatabase->m_uiBlockSize; m_uiDefragThreshold = m_uiBlockSize / 20; m_uiOverflowThreshold = (m_uiBlockSize * 8) / 5; m_bCounts = bCounts; m_bData = bData; m_pDb = pDb; m_bTempDb = pDatabase->m_bTempDb; m_pReplaceInfo = NULL; m_uiReplaceLevels = 0; m_ui64CurrTransID = 0; m_ui64LastBlkTransId = 0; m_ui64PrimaryBlkTransId = 0; m_uiBlkChangeCnt = 0; m_uiSearchLevel = BH_MAX_LEVELS; m_bSetupForRead = FALSE; m_bSetupForWrite = FALSE; m_bSetupForReplace = FALSE; // Buffer is required to hold at least the maximum number of // offsets possible given the block size with a minimum entry size of // 5 bytes. Each offset is 2 bytes. We only need this buffer when we // are in an update transaction. if (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb) { m_uiBufferSize = m_uiBlockSize * 2; } else { m_uiBufferSize = 0; } // If we are in an update transaction, there are certain buffers that // are used. Make sure these are allocated. if( (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb) && !pDatabase->m_pucUpdBuffer) { // Buffer should be at least 80% of two blocks. To make sure the other // structures being allocated here are aligned, we allocate 2 times the // database's block size for the update buffer. pDatabase->m_uiUpdBufferSize = m_uiBlockSize * 2; flmAssert( pDatabase->m_uiUpdBufferSize >= m_uiOverflowThreshold); if( RC_BAD( rc = f_alloc( pDatabase->m_uiUpdBufferSize + (m_uiBlockSize * 2) + m_uiBufferSize + sizeof( BTREE_REPLACE_STRUCT) * BH_MAX_LEVELS, &pDatabase->m_pucUpdBuffer))) { goto Exit; } // Temporary buffers for the F_Btree class will immediately follow // the update buffer. pDatabase->m_pucBTreeTmpBlk = pDatabase->m_pucUpdBuffer + pDatabase->m_uiUpdBufferSize; pDatabase->m_pucBTreeTmpDefragBlk = pDatabase->m_pucBTreeTmpBlk + pDatabase->m_uiBlockSize; pDatabase->m_pucBtreeBuffer = pDatabase->m_pucBTreeTmpDefragBlk + pDatabase->m_uiBlockSize; pDatabase->m_pucReplaceStruct = pDatabase->m_pucBtreeBuffer + m_uiBufferSize; } // NOTE: These temporary buffers may be NULL. They are only allocated the // first time we detect that we are operating inside an update // transaction - because we need to make sure that only one thread // actually does the allocation. The assumption here is that the // buffers are only ever used during update operations, which will // *always* be inside update transactions. m_pucTempBlk = pDatabase->m_pucBTreeTmpBlk; m_pucTempDefragBlk = pDatabase->m_pucBTreeTmpDefragBlk; m_pucBuffer = pDatabase->m_pucBtreeBuffer; m_pReplaceStruct = (BTREE_REPLACE_STRUCT *)pDatabase->m_pucReplaceStruct; flmAssert( !m_pCompare); if ((m_pCompare = pCompare) != NULL) { m_pCompare->AddRef(); } m_bOpened = TRUE; Exit: return( rc); } /*************************************************************************** Desc: Btree close function ****************************************************************************/ void F_Btree::btClose() { FLMUINT uiLoop; // Ok to close multiple times. if (!m_bOpened) { // Btree is not open. Just return. return; } m_pLFile = NULL; m_pDb = NULL; m_bTempDb = FALSE; for (uiLoop = 0; uiLoop < BH_MAX_LEVELS; uiLoop++) { m_Stack[ uiLoop].pucKeyBuf = NULL; } // Release any blocks still held in the stack. btRelease(); if (m_pSCache) { flmAssert( 0); ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } if (m_pCompare) { m_pCompare->Release(); m_pCompare = NULL; } m_bOpened = FALSE; } /*************************************************************************** Desc: Delete the entire tree ****************************************************************************/ RCODE F_Btree::btDeleteTree( IF_DeleteStatus * ifpDeleteStatus) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumLevels; FLMUINT puiBlkAddrs[ BH_MAX_LEVELS]; FLMUINT uiLoop; flmAssert( m_bOpened); // Verify the transaction type if (m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Fill up uiBlkAddrs and calculate the number of levels. if( RC_BAD( rc = btGetBlockChains( puiBlkAddrs, &uiNumLevels))) { goto Exit; } // Iterate over the list of block chains and free all of the blocks for( uiLoop = 0; uiLoop < uiNumLevels; uiLoop++) { if( RC_BAD( rc = btFreeBlockChain( m_pDb, m_pLFile, puiBlkAddrs[ uiLoop], 0, NULL, NULL, ifpDeleteStatus))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE btFreeBlockChain( F_Db * pDb, LFILE * pLFile, FLMUINT uiStartAddr, FLMUINT uiBlocksToFree, FLMUINT * puiBlocksFreed, FLMUINT * puiEndAddr, IF_DeleteStatus * ifpDeleteStatus) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->getDatabase(); F_CachedBlock * pCurrentBlk = NULL; F_CachedBlock * pDOSCache = NULL; FLMBYTE * pBlk; FLMBYTE * pucEntry; FLMUINT uiEntryNum; FLMUINT uiDOBlkAddr; FLMBYTE ucDOBlkAddr[ 4]; FLMUINT uiStatusCounter = 0; FLMUINT uiNextBlkAddr = 0; FLMUINT uiCurrentBlkAddr = 0; FLMUINT uiTreeBlocksFreed = 0; FLMUINT uiDataBlocksFreed = 0; FLMBOOL bFreeAll = FALSE; if( !uiBlocksToFree) { bFreeAll = TRUE; } // Verify the transaction type if( pDb->getTransType() != XFLM_UPDATE_TRANS) { rc = RC_SET_AND_ASSERT( pDb->getTransType() == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Now, go through the chain and delete the blocks... uiCurrentBlkAddr = uiStartAddr; while( uiCurrentBlkAddr) { if( !bFreeAll && uiTreeBlocksFreed >= uiBlocksToFree) { break; } if( RC_BAD( pDatabase->getBlock( pDb, pLFile, uiCurrentBlkAddr, NULL, &pCurrentBlk))) { goto Exit; } pBlk = (FLMBYTE *)pCurrentBlk->getBlockPtr(); uiNextBlkAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; // If this is a leaf block, then there may be entries // with data-only references that will need to be cleaned up too. if( getBlkType( pBlk) == BT_LEAF_DATA) { for( uiEntryNum = 0; uiEntryNum < ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; uiEntryNum++) { pucEntry = BtEntry( pBlk, uiEntryNum); if( bteDataBlockFlag( pucEntry)) { // Get the data-only block address if( RC_BAD( rc = btGetEntryData( pucEntry, &ucDOBlkAddr[ 0], 4, NULL))) { goto Exit; } uiDOBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); while( uiDOBlkAddr) { if( RC_BAD( rc = pDatabase->getBlock( pDb, pLFile, uiDOBlkAddr, NULL, &pDOSCache))) { goto Exit; } uiDOBlkAddr = pDOSCache->getBlockPtr()->ui32NextBlkInChain; rc = pDatabase->blockFree( pDb, pDOSCache); pDOSCache = NULL; if (RC_BAD( rc)) { goto Exit; } uiDataBlocksFreed++; } } } } rc = pDatabase->blockFree( pDb, pCurrentBlk); pCurrentBlk = NULL; if( RC_BAD( rc)) { goto Exit; } if( ifpDeleteStatus && pLFile && ++uiStatusCounter >= 25) { uiStatusCounter = 0; if( RC_BAD( rc = ifpDeleteStatus->reportDelete( uiTreeBlocksFreed + uiDataBlocksFreed, pDatabase->getBlockSize()))) { goto Exit; } } uiTreeBlocksFreed++; uiCurrentBlkAddr = uiNextBlkAddr; } if( puiBlocksFreed) { *puiBlocksFreed = uiTreeBlocksFreed; } if( puiEndAddr) { *puiEndAddr = uiCurrentBlkAddr; } Exit: if( pDOSCache) { ScaReleaseCache( pDOSCache, FALSE); } if( pCurrentBlk) { ScaReleaseCache( pCurrentBlk, FALSE); } return( rc); } /*************************************************************************** Desc: Returns the address of the first block at each level of the tree Note: puiBlockAddrs is assumed to point to a buffer that can store BH_MAX_LEVELS FLMUINT values ****************************************************************************/ RCODE F_Btree::btGetBlockChains( FLMUINT * puiBlockAddrs, FLMUINT * puiNumLevels) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumLevels = 0; FLMUINT uiLFileNum; FLMBOOL bIsIndex; FLMUINT32 ui32NextBlkAddr; F_CachedBlock * pCurrentBlk = NULL; FLMBYTE * pucBlk; FLMBYTE * pucEntry; flmAssert( m_bOpened); // Verify the transaction type if( m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Fill puiBlockAddrs and calculate the number of levels. // NOTE: Normally, level 0 is the leaf level. In this function, // puiBlockAddrs[ 0] is the ROOT and puiBlockAddrs[ uiNumLevels - 1] // is the LEAF! ui32NextBlkAddr = (FLMUINT32)m_pLFile->uiRootBlk; uiLFileNum = m_pLFile->uiLfNum; bIsIndex = m_pLFile->eLfType == XFLM_LF_INDEX ? TRUE : FALSE; while( ui32NextBlkAddr) { puiBlockAddrs[ uiNumLevels++] = ui32NextBlkAddr; if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pCurrentBlk))) { goto Exit; } pucBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; if( getBlkType( pucBlk) == BT_LEAF || getBlkType( pucBlk) == BT_LEAF_DATA) { ui32NextBlkAddr = 0; } else { // The child block address is the first part of the entry pucEntry = BtEntry( pucBlk, 0); ui32NextBlkAddr = bteGetBlkAddr( pucEntry); } // Release the current block ScaReleaseCache( pCurrentBlk, FALSE); pCurrentBlk = NULL; } *puiNumLevels = uiNumLevels; Exit: if( pCurrentBlk) { ScaReleaseCache( pCurrentBlk, FALSE); } return( rc); } /*************************************************************************** Desc: Function to insert an entry into the Btree. ****************************************************************************/ RCODE F_Btree::btInsertEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bFirst, FLMBOOL bLast, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; F_BLK_HDR * pBlkHdr; FLMBYTE pucDOAddr[ 4]; if ( !m_bOpened || m_bSetupForRead || m_bSetupForReplace || (m_bSetupForWrite && bFirst)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); flmAssert( !m_pDb->m_pDatabase->m_pRfl || !m_pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); if( !uiKeyLen) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // Verify the transaction type if( m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Be sure to clear the Data Only flag. if( bFirst) { m_bDataOnlyBlock = FALSE; } if( bLast) { // We need to locate where we should insert the new entry. rc = findEntry( pucKey, uiKeyLen, XFLM_EXACT); // Should not find this entry. If we get back anything other than // an NE_XFLM_NOT_FOUND, then there is a problem. if( rc != NE_XFLM_NOT_FOUND) { if( RC_OK( rc)) { rc = RC_SET( NE_XFLM_NOT_UNIQUE); } goto Exit; } } if( bFirst && (!bLast || (uiKeyLen + uiDataLen > m_uiOverflowThreshold))) { // If bLast is not set, then we will setup to store the data in // data only blocks. The assumption is that whenever we don't see bLast // set when starting an insert, then the data is so large that it must // be placed in a chain of Data Only blocks. There is no way for me to // check the final size of the data ahead of time, so I rely on the // calling routine to figure this out for me. // Get one empty block to begin with. flmAssert( m_pSCache == NULL); if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) { goto Exit; } // The data going in will be stored in Data-only blocks. // Setup the block header... pBlkHdr = m_pSCache->m_pBlkHdr; pBlkHdr->ui8BlkType = BT_DATA_ONLY; pBlkHdr->ui32PrevBlkInChain = 0; pBlkHdr->ui32NextBlkInChain = 0; if( m_pLFile->uiEncId) { ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; setBlockEncrypted( pBlkHdr); } pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)); m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); m_uiDataLength = 0; m_uiOADataLength = 0; m_bDataOnlyBlock = TRUE; m_bSetupForWrite = TRUE; m_ui32DOBlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; m_ui32CurBlkAddr = m_ui32DOBlkAddr; } if( m_bDataOnlyBlock) { if( RC_BAD( rc = storeDataOnlyBlocks( pucKey, uiKeyLen, bFirst, pucData, uiDataLen))) { goto Exit; } } if( bLast) { const FLMBYTE * pucLocalData; FLMUINT uiLocalDataLen; F_ELM_UPD_ACTION eAction; if( m_bDataOnlyBlock) { // build an entry that points to the DO block. UD2FBA( m_ui32DOBlkAddr, pucDOAddr); pucLocalData = &pucDOAddr[0]; uiLocalDataLen = m_uiOADataLength; eAction = ELM_INSERT_DO; } else { pucLocalData = pucData; uiLocalDataLen = uiDataLen; eAction = ELM_INSERT; } if( RC_BAD( rc = updateEntry( pucKey, uiKeyLen, pucLocalData, uiLocalDataLen, eAction))) { goto Exit; } if( pui32BlkAddr) { *pui32BlkAddr = m_ui32PrimaryBlkAddr; } if( puiOffsetIndex) { *puiOffsetIndex = m_uiCurOffset; } m_bSetupForWrite = FALSE; } Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( TRUE); return( rc); } /*************************************************************************** Desc: Function to remove an entry into the Btree. ****************************************************************************/ RCODE F_Btree::btRemoveEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucValue = NULL; FLMUINT uiLen = 0; if ( !m_bOpened) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Verify the Txn type if (m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } flmAssert( !m_pDb->m_pDatabase->m_pRfl || !m_pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); btResetBtree(); // We need to locate where we should remove the entry. if (RC_BAD( rc = findEntry( pucKey, uiKeyLen, XFLM_EXACT))) { goto Exit; } if (RC_BAD( rc = updateEntry( pucKey, uiKeyLen, pucValue, uiLen, ELM_REMOVE))) { goto Exit; } Exit: releaseBlocks( TRUE); return( rc); } /*************************************************************************** Desc: Function to provide a streaming interface for replacing large data elements. ****************************************************************************/ RCODE F_Btree::btReplaceEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bFirst, FLMBOOL bLast, FLMBOOL bTruncate, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry; F_BLK_HDR * pBlkHdr; const FLMBYTE * pucLocalData = NULL; FLMUINT uiOADataLength = 0; FLMBYTE pucDOAddr[ 4]; if( !m_bOpened || m_bSetupForRead || m_bSetupForWrite || (m_bSetupForReplace && bFirst)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); flmAssert( !m_pDb->m_pDatabase->m_pRfl || !m_pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); if (!uiKeyLen) { rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // Verify the Txn type if (m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Be sure to clear the Data Only flags if( bFirst) { m_bDataOnlyBlock = FALSE; m_bOrigInDOBlocks = FALSE; } if( bFirst || bLast) { // We need to locate the entry we want to replace if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, XFLM_EXACT, NULL, pui32BlkAddr, puiOffsetIndex))) { goto Exit; } // We must first determine if the existing entry is stored // in data only blocks. pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); btGetEntryDataLength( pucEntry, &pucLocalData, &uiOADataLength, &m_bOrigInDOBlocks); } if( bFirst && (!bLast || (bLast && !bTruncate && m_bOrigInDOBlocks) || (uiKeyLen + uiDataLen > m_uiOverflowThreshold))) { // If bLast is not set, then we will setup to store the data in // data only blocks. m_bDataOnlyBlock = TRUE; flmAssert( m_pSCache == NULL); if( m_bOrigInDOBlocks) { // Need to get the first DO block, and work from there. m_ui32DOBlkAddr = bteGetBlkAddr( pucLocalData); if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32DOBlkAddr, NULL, &m_pSCache))) { goto Exit; } } else { // Get one empty block to begin with if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) { goto Exit; } // The data going in will be stored in Data-only blocks. // Setup the block header... pBlkHdr = m_pSCache->m_pBlkHdr; pBlkHdr->ui8BlkType = BT_DATA_ONLY; pBlkHdr->ui32PrevBlkInChain = 0; pBlkHdr->ui32NextBlkInChain = 0; if (m_pLFile->uiEncId) { ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; setBlockEncrypted( pBlkHdr); } pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)); } m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr); m_uiDataLength = 0; m_uiOADataLength = 0; m_bDataOnlyBlock = TRUE; m_bSetupForReplace = TRUE; m_ui32DOBlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; m_ui32CurBlkAddr = m_ui32DOBlkAddr; } if( m_bDataOnlyBlock) { if( !bTruncate && !m_bOrigInDOBlocks) { bTruncate = TRUE; } // May need to skip over the key that is stored in the first DO block. // We only want to do this the first time in here. The test to determine // if this is our first time in this block is to see if the m_uiDataLength // is equal to the m_uiDataRemaining. They would only be the same on the // first time for each DO block. if( m_bOrigInDOBlocks && m_pSCache && m_pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0 && !m_uiDataLength) { m_uiDataRemaining -= (uiKeyLen + 2); } if( RC_BAD( rc = replaceDataOnlyBlocks( pucKey, uiKeyLen, !m_bOrigInDOBlocks && bFirst, pucData, uiDataLen, bLast, bTruncate))) { goto Exit; } } // If we were writing to Data Only Blocks and we are not truncating the // data, then we are done here. if( m_bDataOnlyBlock && !bTruncate) { if( bLast && (uiOADataLength <= m_uiOADataLength)) { bTruncate = TRUE; } else { goto Exit; } } // Only replace the entry on the last call. if( bLast) { FLMUINT uiLocalDataLen; F_ELM_UPD_ACTION eAction; if (m_bDataOnlyBlock) { // build an entry that points to the DO block. UD2FBA( m_ui32DOBlkAddr, pucDOAddr); pucLocalData = &pucDOAddr[0]; uiLocalDataLen = m_uiOADataLength; eAction = ELM_REPLACE_DO; } else { pucLocalData = pucData; uiLocalDataLen = uiDataLen; eAction = ELM_REPLACE; } if( RC_BAD( rc = updateEntry( pucKey, uiKeyLen, pucLocalData, uiLocalDataLen, eAction, bTruncate))) { goto Exit; } } Exit: if (RC_OK( rc)) { if (pui32BlkAddr) { *pui32BlkAddr = m_ui32PrimaryBlkAddr; } if (puiOffsetIndex) { *puiOffsetIndex = m_uiCurOffset; } } if( bLast) { m_bSetupForReplace = FALSE; } if (m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( TRUE); return( rc); } /*************************************************************************** Desc: Function to search the Btree for a specific key. ****************************************************************************/ RCODE F_Btree::btLocateEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT uiMatch, FLMUINT * puiPosition, // May be NULL FLMUINT * puiDataLength, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry = NULL; flmAssert( pucKey && uiKeyBufSize && puiKeyLen); if (!m_bOpened || m_bSetupForWrite || m_bSetupForReplace) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } m_bSetupForRead = FALSE; // Verify the Txn type if (m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // Find the entry we are interested in. if (RC_BAD(rc = findEntry( pucKey, *puiKeyLen, uiMatch, puiPosition, pui32BlkAddr, puiOffsetIndex))) { goto Exit; } m_ui64LowTransId = m_pStack->pBlkHdr->stdBlkHdr.ui64TransID; m_bMostCurrent = (m_pStack->pSCache->m_ui64HighTransID == FLM_MAX_UINT64) ? TRUE : FALSE; m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; m_uiPrimaryOffset = m_pStack->uiCurOffset; m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = m_uiPrimaryOffset; // Point to the entry... pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); // Return the optional data length - get the overall data length only. if (puiDataLength && m_pStack->pSCache->m_pBlkHdr->ui8BlkType == BT_LEAF_DATA) { btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); } else if (puiDataLength) { *puiDataLength = 0; } if( RC_BAD( rc = setupReadState( m_pStack->pSCache->m_pBlkHdr, pucEntry))) { goto Exit; } // In case the returning key is not what was originally requested, such as // in the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and possibly XFLM_INCL, // we will pass back the key we actually found. if( uiMatch != XFLM_EXACT) { if( RC_BAD( rc = setReturnKey( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, pucKey, puiKeyLen, uiKeyBufSize))) { goto Exit; } } m_bFirstRead = FALSE; m_bSetupForRead = TRUE; Exit: releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Method to get the data after a call to btLocateEntry, btNextEntry, btPrevEntry, btFirstEntry or btLastEntry. ****************************************************************************/ RCODE F_Btree::btGetEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT uiKeyLen, FLMBYTE * pucData, FLMUINT uiDataBufSize, FLMUINT * puiDataLen ) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry; if( !m_bOpened || !m_bSetupForRead || m_bSetupForWrite || m_bSetupForReplace) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } if( puiDataLen) { *puiDataLen = 0; } // Is there anything there to get? if( m_uiOADataRemaining == 0) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } // If the transaction Id or the Block Change Count has changed, // we must re-sync ourselves. if( !m_bTempDb && ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) { // Test to see if we really need to re-sync ... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // We must call btLocateEntry so we can re-initialize the read if( !m_bFirstRead) { if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, &uiKeyLen, XFLM_EXACT))) { goto Exit; } // Will need a new version of this block. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } else { rc = RC_SET(NE_XFLM_BTREE_BAD_STATE); goto Exit; } } } // Get the current block. It is either a DO or a Btree block. if( m_pSCache == NULL) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, m_pSCache->m_ui64HighTransID); // Now to find where we were the last time through. if( !m_bDataOnlyBlock) { pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); btGetEntryDataLength( pucEntry, &m_pucDataPtr, NULL, NULL); } else { m_pucDataPtr = (FLMBYTE *)m_pSCache->m_pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr); // May need to skip over the key that is stored in the first DO block. // We only want to do this the first time in here. The test to determine // if this is our first time in this block is to see if the m_uiDataLength // is equal to the m_uiDataRemaining. They would only be the same on the // first time for each DO block. if( m_pSCache && m_pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0) { FLMUINT16 ui16KeyLen = FB2UW( m_pucDataPtr); // Key lengths should be the same flmAssert( uiKeyLen == (FLMUINT)ui16KeyLen); m_pucDataPtr += (ui16KeyLen + 2); } } m_pucDataPtr += (m_uiDataLength - m_uiDataRemaining); if( RC_BAD( rc = extractEntryData( pucKey, uiKeyLen, pucData, uiDataBufSize, puiDataLen))) { goto Exit; } // Mark that we have completed our first read operation. // No more read synchronization allowed. m_bFirstRead = TRUE; Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Function to locate the next entry in the Btree. The key buffer and actual size is passed in. ****************************************************************************/ RCODE F_Btree::btNextEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry = NULL; FLMBOOL bAdvanced = FALSE; if( !m_bOpened || !m_bSetupForRead) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Verify the transaction type if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // Make sure we are looking at btree block. If the m_bDataOnlyBlock // flag is set, then the block address in m_ui32CurBlkAddr is a // data only block. We must reset it to the primary block address. if( m_bDataOnlyBlock) { m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; } else { // If the entry did not reference a DO block, then we need to // reset the primary block and offset with where we currently // are incase the current block is further ahead. This saves time // so that we don't have to scan past old blocks we are not intereseted // in. m_ui32PrimaryBlkAddr = m_ui32CurBlkAddr; m_uiPrimaryOffset = m_uiCurOffset; m_ui64PrimaryBlkTransId = m_ui64LastBlkTransId; } // Do we need to resynchronize? if( !m_bTempDb && ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) { // Test to see if we really need to re-sync ... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // Will need a new version of this block. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; // Doing a find with XFLM_EXCL will result in our being positioned at // the next entry. if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, XFLM_EXCL, puiDataLength))) { goto Exit; } bAdvanced = TRUE; } } // Get the current block if we need it. if( !m_pSCache) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } // If we have already advanced due to a resynch, then we don't need to call // the advanceToNextElement function, however, we do need to get the // current entry. if( bAdvanced) { pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); } else { for (;;) { // Advance to the next entry in the block. We don't have a stack so // don't advance it. if( RC_BAD( rc = advanceToNextElement( FALSE))) { goto Exit; } pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); if( m_bData) { if( bteFirstElementFlag(pucEntry)) { break; } } else { break; } } } // Return the optional data length - get the overall data length only. if( puiDataLength) { btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); } if( RC_BAD( rc = setupReadState( m_pSCache->m_pBlkHdr, pucEntry))) { goto Exit; } // Incase the returning key is not what was originally requested, such as in // the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and possibly XFLM_INCL, // we will pass back the key we actually found. if( RC_BAD( rc = setReturnKey( pucEntry, m_pSCache->m_pBlkHdr->ui8BlkType, pucKey, puiKeyLen, uiKeyBufSize))) { goto Exit; } if( pui32BlkAddr) { *pui32BlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; } if( puiOffsetIndex) { *puiOffsetIndex = m_uiCurOffset; } m_bFirstRead = FALSE; Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Function to get the previous entry in the Btree. ****************************************************************************/ RCODE F_Btree::btPrevEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry = NULL; if( !m_bOpened || !m_bSetupForRead) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Verify the transaction type if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // Make sure we are looking at the first block of the // current entry. Reading of the entry could have moved us // to another block, or if it was in a DO block, we would be // looking at the wrong block altogether. m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = m_uiPrimaryOffset; m_ui64LastBlkTransId = m_ui64PrimaryBlkTransId; // Do we need to resynchronize? if( !m_bTempDb && ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) { // Test to see if we really need to re-sync ... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // Will need a new version of this block. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; // Doing a find with XFLM_INCL will allow for the possibility that // the original entry is no longer there. We will still have // to backup to the previous entry. if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, XFLM_INCL, puiDataLength))) { goto Exit; } } } if( !m_pSCache) { // Fetch the current block, then backup from there. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } for (;;) { // Backup to the previous entry in the block. if( RC_BAD( rc = backupToPrevElement( FALSE))) { goto Exit; } // Get the entry, size etc. pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); if( m_bData) { if( bteFirstElementFlag( pucEntry)) { break; } } else { break; } } // Return the optional data length - get the overall data length only. if( puiDataLength) { btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); } if( RC_BAD( rc = setupReadState( m_pSCache->m_pBlkHdr, pucEntry))) { goto Exit; } // In case the returning key is not what was originally requested, such as in // the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and possibly XFLM_INCL, // we will pass back the key we actually found. if( RC_BAD( rc = setReturnKey( pucEntry, m_pSCache->m_pBlkHdr->ui8BlkType, pucKey, puiKeyLen, uiKeyBufSize))) { goto Exit; } if( pui32BlkAddr) { *pui32BlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; } if( puiOffsetIndex) { *puiOffsetIndex = m_uiCurOffset; } m_bFirstRead = FALSE; Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Locate the first entry in the Btree and return the key. ****************************************************************************/ RCODE F_Btree::btFirstEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; m_Stack[ 0].pucKeyBuf = pucKey; if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, XFLM_FIRST, NULL, puiDataLength, pui32BlkAddr, puiOffsetIndex))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Locate the last entry in the Btree and return the key. ****************************************************************************/ RCODE F_Btree::btLastEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; m_Stack[ 0].pucKeyBuf = pucKey; if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, XFLM_LAST, NULL, puiDataLength, pui32BlkAddr, puiOffsetIndex))) { if( rc == NE_XFLM_BOF_HIT) { rc = RC_SET( NE_XFLM_EOF_HIT); } goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Function to search the Btree for a specific key. ****************************************************************************/ RCODE F_Btree::btPositionTo( FLMUINT uiPosition, FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry = NULL; flmAssert( pucKey && uiKeyBufSize && puiKeyLen); m_bSetupForRead = FALSE; if( !m_bOpened || !m_bCounts) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Verify the transaction type if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // Find the entry we are interested in. if( RC_BAD(rc = positionToEntry( uiPosition))) { goto Exit; } m_ui64LowTransId = m_pStack->pBlkHdr->stdBlkHdr.ui64TransID; m_bMostCurrent = (m_pStack->pSCache->m_ui64HighTransID == FLM_MAX_UINT64) ? TRUE : FALSE; m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; m_uiPrimaryOffset = m_pStack->uiCurOffset; m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = m_uiPrimaryOffset; // Point to the entry ... pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); if( RC_BAD( rc = setupReadState( m_pStack->pSCache->m_pBlkHdr, pucEntry))) { goto Exit; } // In case the returning key is not what was originally requested, such // as in the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and // possibly XFLM_INCL, we will pass back the key we actually found. if( RC_BAD( rc = setReturnKey( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, pucKey, puiKeyLen, uiKeyBufSize))) { goto Exit; } m_bFirstRead = FALSE; m_bSetupForRead = TRUE; Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Method to get the actual poisition of the entry. Note: Must be maintaining counts in the Btree AND also have located to an entry first. The key that is passed in is used only if we have to resynchronize due to a transaction change. ****************************************************************************/ RCODE F_Btree::btGetPosition( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT * puiPosition) { RCODE rc = NE_XFLM_OK; FLMUINT uiKeyBufSize = uiKeyLen; FLMUINT uiLocalKeyLen = uiKeyLen; if( !m_bOpened || !m_bSetupForRead || !m_bCounts) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Verify the transaction type if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } *puiPosition = 0; m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = m_uiPrimaryOffset; m_ui64LastBlkTransId = m_ui64PrimaryBlkTransId; // Do we need to resynchronize? if( !m_bTempDb && ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) { // Test to see if we really need to re-sync ... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // We can get the position easily if we have to re-sync. if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, &uiLocalKeyLen, XFLM_EXACT, puiPosition))) { goto Exit; } // Will need a new version of this block. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } } else { // To calculate the position, we will have to reconstruct the stack. m_pStack = &m_Stack[ m_uiStackLevels - 1]; for (;;) { // Get the block at this level. flmAssert( m_pStack->ui32BlkAddr); flmAssert( m_pStack->pSCache == NULL); if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_pStack->ui32BlkAddr, NULL, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; *puiPosition += countRangeOfKeys( m_pStack, 0, m_pStack->uiCurOffset); if( (getBlkType( (FLMBYTE *)m_pStack->pBlkHdr) == BT_LEAF) || (getBlkType( (FLMBYTE *)m_pStack->pBlkHdr) == BT_LEAF_DATA)) { break; } else { // Next level down. (stack is inverted). m_pStack--; } } } Exit: releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Method to rewind back to the beginning of the current entry. ****************************************************************************/ RCODE F_Btree::btRewind( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = NULL; if( !m_bSetupForRead) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = m_uiPrimaryOffset; m_ui64LastBlkTransId = m_ui64PrimaryBlkTransId; // Do we need to resync? if( !m_bTempDb && ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) { flmAssert( m_pSCache == NULL); // Test to see if we really need to re-sync ... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // Won't need this block anymore. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, XFLM_EXACT))) { goto Exit; } goto Exit; } } m_uiOADataRemaining = m_uiOADataLength; // Track the overall length progress m_uiDataLength = m_uiPrimaryDataLen; // Restore the primary block data length m_uiDataRemaining = m_uiDataLength; // Track the local entry progress if( m_bDataOnlyBlock) { m_ui32CurBlkAddr = m_ui32DOBlkAddr; if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32DOBlkAddr, NULL, &pSCache))) { goto Exit; } m_ui64LastBlkTransId = pSCache->m_pBlkHdr->ui64TransID; // Local amount of data in this block m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr((F_BLK_HDR *)pSCache->m_pBlkHdr) - pSCache->m_pBlkHdr->ui16BlkBytesAvail; // Keep the actual local data size for later. m_uiDataLength = m_uiDataRemaining; // Now release the DO Block. We will get it again when we need it. ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } m_bFirstRead = FALSE; m_bSetupForRead = TRUE; Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Method for computing the number of keys and blocks between two points in the Btree. The key count is inclusive of the two end points and the block count is exclusive of the two end points. ****************************************************************************/ RCODE F_Btree::btComputeCounts( F_Btree * pUntilBtree, FLMUINT * puiBlkCount, FLMUINT * puiKeyCount, FLMBOOL * pbTotalsEstimated, FLMUINT uiAvgBlkFullness) { RCODE rc = NE_XFLM_OK; if( !m_bSetupForRead || !pUntilBtree->m_bSetupForRead) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // Ensure that both Btrees are from the same container. if( m_pLFile->uiRootBlk != pUntilBtree->m_pLFile->uiRootBlk) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } rc = computeCounts( m_pStack, pUntilBtree->m_pStack, puiBlkCount, puiKeyCount, pbTotalsEstimated, uiAvgBlkFullness); Exit: releaseBlocks( FALSE); pUntilBtree->releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Function to release the blocks in the stack, and optionally, reset the stack ****************************************************************************/ void F_Btree::releaseBlocks( FLMBOOL bResetStack) { FLMUINT uiLevel; // Release any blocks still held in the stack. for( uiLevel = 0; uiLevel <= m_uiRootLevel; uiLevel++) { if( m_Stack[ uiLevel].pSCache) { if( m_Stack[ uiLevel].pSCache->m_uiUseCount) { ScaReleaseCache( m_Stack[ uiLevel].pSCache, FALSE); } m_Stack[ uiLevel].pSCache = NULL; m_Stack[ uiLevel].pBlkHdr = NULL; } if( bResetStack) { m_Stack[ uiLevel].ui32BlkAddr = 0; m_Stack[ uiLevel].uiKeyLen = 0; m_Stack[ uiLevel].uiCurOffset = 0; m_Stack[ uiLevel].uiLevel = 0; } } if( bResetStack) { m_uiStackLevels = 0; m_uiRootLevel = 0; m_bStackSetup = FALSE; m_pStack = NULL; } } /*************************************************************************** Desc: Function to create a new block at the current level. The new block will always be inserted previous to the current block. All entries that sort ahead of the current insertion point will be moved into the new block. If there is room, the new entry will be inserted into the current block. Otherwise, if there is room, the new entry will be inserted into the new block. If there is still not enough room, then if possible, it try to store a partial entry in the new block. If we still cannot store anything, we will see if we can store a partial entry in the current block. If that does not work, then it will set the remaining amount and return. Another block split will be needed before we store this entry. ****************************************************************************/ RCODE F_Btree::splitBlock( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiOADataLen, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, FLMBOOL * pbBlockSplit) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pNewSCache = NULL; F_CachedBlock * pPrevSCache = NULL; F_BTREE_BLK_HDR * pBlkHdr = NULL; FLMUINT uiBlkAddr; FLMUINT uiEntrySize; FLMBOOL bHaveRoom; FLMBOOL bMovedToPrev = FALSE; FLMBOOL bLastEntry; FLMUINT uiMinEntrySize; FLMBOOL bDefragBlk = FALSE; FLMBOOL bSavedReplaceInfo = FALSE; // If the current block is a root block, then we will have to introduce // a new level into the B-Tree. if( isRootBlk( m_pStack->pBlkHdr)) { if( RC_BAD( rc = createNewLevel())) { goto Exit; } } // If the current block is empty we must insert what we can here. // This scenario only occurs when we are engaged in a ReplaceByInsert // operation. Normal inserts would never result in an empty block. // Since we know we are part of a replace operation, we know that the // parent of this block only needs the counts updated, not the key. if( m_pStack->uiLevel == 0 && m_pStack->pBlkHdr->ui16NumKeys == 0) { if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, puiRemainingLen, FALSE))) { goto Exit; } *pbBlockSplit = FALSE; goto MoveToPrev; } // Create a new block and insert it as previous to this block. if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &pNewSCache))) { goto Exit; } *pbBlockSplit = TRUE; // Setup the header ... pBlkHdr = (F_BTREE_BLK_HDR *)pNewSCache->m_pBlkHdr; unsetRootBlk( pBlkHdr); setBlkLfType( pBlkHdr, m_pLFile->eLfType); pBlkHdr->ui16NumKeys = 0; pBlkHdr->ui8BlkLevel = (FLMUINT8)m_pStack->uiLevel; pBlkHdr->stdBlkHdr.ui8BlkType = m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType; pBlkHdr->ui16LogicalFile = m_pStack->pBlkHdr->ui16LogicalFile; // Check for encrypted block. if( isEncryptedBlk( (F_BLK_HDR *)m_pStack->pBlkHdr)) { setBlockEncrypted( (F_BLK_HDR *)pBlkHdr); } pBlkHdr->stdBlkHdr.ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr(pBlkHdr)); pBlkHdr->ui16HeapSize = (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr(pBlkHdr)); // We are going to make changes to the current block. The pSCache could // have changed since making this call, so we need to update the block // header if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); // Get the current previous block if there is one. uiBlkAddr = m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; if( uiBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiBlkAddr, NULL, &pPrevSCache))) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pPrevSCache))) { goto Exit; } } // Link the new block between the current and it's previous pBlkHdr->stdBlkHdr.ui32NextBlkInChain = m_pStack->ui32BlkAddr; pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = (FLMUINT32)uiBlkAddr; m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = pBlkHdr->stdBlkHdr.ui32BlkAddr; // There may not be a previous block. if( pPrevSCache) { pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = pBlkHdr->stdBlkHdr.ui32BlkAddr; // Release the old previous block since we no longer need it. ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } // We will move all entries in the current block up to but NOT including // the entry pointed to by uiCurOffset to the new block. if( m_pStack->uiCurOffset > 0) { if( RC_BAD( rc = moveToPrev( 0, m_pStack->uiCurOffset - 1, &pNewSCache))) { goto Exit; } // All entries prior to the old insertion point were moved. // Therefore, the new insertion point must be at the beginning. m_pStack->uiCurOffset = 0; // If we emptied the block. This will require us to update the parent. if( m_pStack->pBlkHdr->ui16NumKeys == 0) { if (RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) { goto Exit; } bSavedReplaceInfo = TRUE; } } // If the block is now empty, we will store a partial entry in it here. // This scenario only occurs when we are engaged in a ReplaceByInsert // operation. Normal inserts would never result in an empty block. // Since we know we are part of a replace operation, we know that the // parent of this block only needs the counts updated, not the key. if( m_pStack->uiLevel == 0 && m_pStack->pBlkHdr->ui16NumKeys == 0) { if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, puiRemainingLen, FALSE))) { goto Exit; } goto MoveToPrev; } // Is there room for the new entry now in the current block? if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, uiLen, &uiEntrySize, &bHaveRoom, &bDefragBlk))) { goto Exit; } if( bHaveRoom) { if( bDefragBlk) { if( RC_BAD( rc = defragmentBlock( &m_pStack->pSCache))) { goto Exit; } } if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } if( bLastEntry && !bSavedReplaceInfo) { // Since we just added/replaced an entry to the last position of the // current block. we will need to preserve the current stack so that // we can finish updating the parentage later. Should only happen as // a result of a replace operation where the new entry is larger than // the existing one while in the upper levels. if( RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) { goto Exit; } } // If we are keeping counts, we must update those too. if( m_bCounts && !isRootBlk( m_pStack->pBlkHdr)) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } if( m_pStack->uiLevel == 0) { *ppucRemainingValue = NULL; *puiRemainingLen = 0; } goto MoveToPrev; } // Can we store the whole thing in the new block? if( uiEntrySize <= pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { // If this block has a parent block, and the btree is maintaining counts // we will want to update the counts on the parent block. if( m_bCounts && !isRootBlk( m_pStack->pBlkHdr)) { if (RC_BAD( rc = updateCounts())) { goto Exit; } } // We can release the current block since it is no longer needed. ScaReleaseCache( m_pStack->pSCache, FALSE); m_pStack->pSCache = pNewSCache; pNewSCache = NULL; m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; m_pStack->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); // Setting the uiCurOffset to the actual number of keys will cause the // new entry to go in as the last element. m_pStack->uiCurOffset = m_pStack->pBlkHdr->ui16NumKeys; // We don't need to check to see if we need to defragment this block // because it is "new". Anything that just got written to it will // be contiguous already. if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } flmAssert( bLastEntry); if( m_pStack->uiLevel == 0) { *ppucRemainingValue = NULL; *puiRemainingLen = 0; } bMovedToPrev = TRUE; goto MoveToPrev; } // Can we store part of the new entry into the new block? // Calculate the minimum entry size to store. if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, 1, &uiMinEntrySize, &bHaveRoom, &bDefragBlk))) { goto Exit; } // bHaveRoom refers to the current block, and we want to put this into // the previous block. if( uiMinEntrySize <= pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { // If this block has a parent block, and the btree is maintaining counts // we will want to update the counts on the parent block. if( !isRootBlk( m_pStack->pBlkHdr)) { if( m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } } // We can release the current block since it is no longer needed. ScaReleaseCache( m_pStack->pSCache, FALSE); m_pStack->pSCache = pNewSCache; pNewSCache = NULL; m_pStack->pBlkHdr = pBlkHdr; m_pStack->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); // Setting the uiCurOffset to the actual number of keys will cause the // new entry to go in as the last element. m_pStack->uiCurOffset = m_pStack->pBlkHdr->ui16NumKeys; if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, puiRemainingLen, TRUE))) { goto Exit; } bMovedToPrev = TRUE; } else if( uiMinEntrySize <= m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { // We will store part of the entry in the current block if( RC_BAD( rc = storePartialEntry( pucKey, uiKeyLen, pucValue, uiLen, uiFlags, uiChildBlkAddr, uiCounts, ppucRemainingValue, puiRemainingLen, FALSE))) { goto Exit; } } else { // Couldn't store anything, so try again after updating the parents. *ppucRemainingValue = pucValue; *puiRemainingLen = uiLen; } MoveToPrev: if( *pbBlockSplit) { // Release the current entry if it hasn't already been released. if( !bMovedToPrev && RC_OK( rc)) { // If this block has a parent block, and the btree is maintaining counts // we will want to update the counts on the parent block. if( !isRootBlk( m_pStack->pBlkHdr) && m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } ScaReleaseCache( m_pStack->pSCache, FALSE); flmAssert( pNewSCache); m_pStack->pSCache = pNewSCache; pNewSCache = NULL; m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; m_pStack->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; m_pStack->uiCurOffset = m_pStack->pBlkHdr->ui16NumKeys - 1; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); } } Exit: if( *pbBlockSplit) { if( m_pDb->m_pDbStats) { XFLM_LFILE_STATS * pLFileStats; if( (pLFileStats = m_pDb->getLFileStatPtr( m_pLFile)) != NULL) { pLFileStats->bHaveStats = TRUE; pLFileStats->ui64BlockSplits++; } } } if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } if( pNewSCache) { ScaReleaseCache( pNewSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Function to create a new level in the Btree. This function will ensure that the F_BTSK stack is consistent with the way it was configured before the function was called. This function will create a new block and copy the current contents of the root block into it. It will then insert a single entry into the root block to point to the new child. Note that there is a maximum of BH_MAX_LEVELS levels to the Btree. Any effort to exceed that level will result in an error. ****************************************************************************/ RCODE F_Btree::createNewLevel( void) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pNewSCache = NULL; FLMBYTE * pSrcBlk; FLMBYTE * pDstBlk; F_BTREE_BLK_HDR * pBlkHdr; FLMUINT uiCounts = 0; FLMBYTE * pucEntry; FLMBYTE * pucNull = NULL; FLMBYTE ucBuffer[ XFLM_MAX_KEY_SIZE + BTE_NLC_KEY_START]; FLMUINT uiMaxNLKey = XFLM_MAX_KEY_SIZE + BTE_NLC_KEY_START; FLMUINT uiEntrySize; F_BTSK * pRootStack; FLMUINT uiFlags; // Assert that we are looking at the root block! flmAssert( isRootBlk( m_pStack->pBlkHdr)); // Check the root level if( m_pStack->uiLevel >= BH_MAX_LEVELS - 1) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_FULL); goto Exit; } // Create a new block to copy the contents of the root block into if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &pNewSCache))) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } // Log that we are about to change the root block if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } // Update the stack since the pSCache could have changed m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); // Copy the data from the root block to the new block pSrcBlk = (FLMBYTE *)m_pStack->pui16OffsetArray; pBlkHdr = (F_BTREE_BLK_HDR *)pNewSCache->m_pBlkHdr; // Check for encryption if( isEncryptedBlk( (F_BLK_HDR *)m_pStack->pBlkHdr)) { setBlockEncrypted( (F_BLK_HDR *)pBlkHdr); } pDstBlk = (FLMBYTE *)BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); unsetRootBlk( pBlkHdr); setBlkLfType( pBlkHdr, m_pLFile->eLfType); pBlkHdr->ui16LogicalFile = (FLMUINT16)m_pLFile->uiLfNum; pBlkHdr->ui16NumKeys = m_pStack->pBlkHdr->ui16NumKeys; pBlkHdr->ui8BlkLevel = m_pStack->pBlkHdr->ui8BlkLevel; pBlkHdr->ui16HeapSize = m_pStack->pBlkHdr->ui16HeapSize; pBlkHdr->stdBlkHdr.ui8BlkType = ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType; pBlkHdr->stdBlkHdr.ui16BlkBytesAvail = ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail; pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = 0; pBlkHdr->stdBlkHdr.ui32NextBlkInChain = 0; // Copy the data from the root block to the new block. f_memcpy( pDstBlk, pSrcBlk, m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr)); // Empty out the root block data. #ifdef FLM_DEBUG f_memset( BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0), 0, m_uiBlockSize - sizeofBTreeBlkHdr( m_pStack->pBlkHdr)); #endif m_pStack->pBlkHdr->ui16NumKeys = 0; m_pStack->pBlkHdr->ui16HeapSize = ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr( m_pStack->pBlkHdr)); // Check the root block type to see if we need to change it. The root // block may have been a leaf node. if( (m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF) || (m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA)) { // Need to set the block type to either // BT_NON_LEAF or BT_NON_LEAF_COUNTS if( m_bCounts) { m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType = BT_NON_LEAF_COUNTS; } else { m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType = BT_NON_LEAF; } } // Now add a new entry to the stack. pRootStack = m_pStack; pRootStack++; f_memcpy( pRootStack, m_pStack, sizeof( F_BTSK)); // Now fix the entries in the stack. pRootStack->uiLevel++; pRootStack->pBlkHdr->ui8BlkLevel++; pRootStack->uiCurOffset = 0; // First entry pRootStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pRootStack->pBlkHdr, 0); m_pStack->pBlkHdr = pBlkHdr; // Point to new block m_pStack->ui32BlkAddr = (FLMUINT32)pNewSCache->m_uiBlkAddress; m_pStack->pSCache = pNewSCache; pNewSCache = NULL; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); // Build a new entry for the root block that will point to the newly created // child block. If the root block type is BT_NON_LEAF_COUNTS, then we // need to sum the counts from the child block if( m_bCounts) { uiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } // Create and insert a LEM entry to mark the last position in the block. uiFlags = BTE_FLAG_LAST_ELEMENT | BTE_FLAG_FIRST_ELEMENT; if( RC_BAD( rc = buildAndStoreEntry( ((F_BLK_HDR *)pRootStack->pBlkHdr)->ui8BlkType, uiFlags, pucNull, 0, pucNull, 0, 0, m_pStack->ui32BlkAddr, uiCounts, &ucBuffer[ 0], uiMaxNLKey, &uiEntrySize))) { goto Exit; } // Copy the entry into the root block. pucEntry = (FLMBYTE *)pRootStack->pBlkHdr + m_uiBlockSize - uiEntrySize; f_memcpy( pucEntry, &ucBuffer[ 0], uiEntrySize); bteSetEntryOffset( pRootStack->pui16OffsetArray, 0, (FLMUINT16)(pucEntry - (FLMBYTE *)pRootStack->pBlkHdr)); pRootStack->pBlkHdr->ui16NumKeys++; pRootStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= (FLMUINT16)uiEntrySize + 2; pRootStack->pBlkHdr->ui16HeapSize -= (FLMUINT16)uiEntrySize + 2; m_uiStackLevels++; m_uiRootLevel++; Exit: if( pNewSCache) { ScaReleaseCache( pNewSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to calculate the optimal data length size to store. This method is called when storing a partial entry, and we need to know what the largest data size we c an store is. ****************************************************************************/ RCODE F_Btree::calcOptimalDataLength( FLMUINT uiKeyLen, FLMUINT uiDataLen, FLMUINT uiBytesAvail, FLMUINT * puiNewDataLen) { RCODE rc = NE_XFLM_OK; FLMUINT uiFixedAmounts; FLMUINT uiRemainder; switch( ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType) { case BT_LEAF: case BT_NON_LEAF: case BT_NON_LEAF_COUNTS: { // These blocks do not have any data. *puiNewDataLen = 0; break; } case BT_LEAF_DATA: { // These amounts don't change. Note that the overhead includes the // Overall Data Length Field, even though it may not be there in // the end. uiFixedAmounts = BTE_LEAF_DATA_OVHD + (uiKeyLen > ONE_BYTE_SIZE ? 2 : 1) + uiKeyLen; uiRemainder = uiBytesAvail - uiFixedAmounts; if (uiRemainder >= (ONE_BYTE_SIZE + 2)) { *puiNewDataLen = uiRemainder - 2; } else { *puiNewDataLen = uiRemainder - 1; } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } if( uiDataLen < *puiNewDataLen) { *puiNewDataLen = uiDataLen; } Exit: return( rc); } /*************************************************************************** Desc: This function will count the total number of keys in the block. Typically the value ui16NumKeys will yield this number, however, if the block type is BT_NON_LEAF_COUNTS, we also want to include the counts in each entry. ****************************************************************************/ RCODE F_Btree::updateParentCounts( F_CachedBlock * pChildSCache, F_CachedBlock ** ppParentSCache, FLMUINT uiParentElm) { RCODE rc = NE_XFLM_OK; FLMUINT uiCounts; FLMBYTE * pucCounts; F_CachedBlock * pParentSCache; FLMBYTE * pBlk = (FLMBYTE *)pChildSCache->m_pBlkHdr; flmAssert( getBlkType( pBlk) == BT_NON_LEAF_COUNTS); uiCounts = countKeys( pBlk); if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, ppParentSCache))) { goto Exit; } pParentSCache = *ppParentSCache; pucCounts = BtEntry( (FLMBYTE *)pParentSCache->m_pBlkHdr, uiParentElm); pucCounts += 4; UD2FBA( (FLMUINT32)uiCounts, pucCounts); Exit: return( rc); } /*************************************************************************** Desc: This function will count the total number of keys in the block. Typically the value ui16NumKeys will yield this number, however, if the block type is BT_NON_LEAF_COUNTS, we also want to include the counts in each entry. ****************************************************************************/ FLMUINT F_Btree::countKeys( FLMBYTE * pBlk) { FLMUINT uiTotal = 0; FLMUINT uiIndex; FLMBYTE * pucEntry; FLMUINT16 * puiOffsetArray; puiOffsetArray = BtOffsetArray( pBlk, 0); if( getBlkType(pBlk) != BT_NON_LEAF_COUNTS) { uiTotal = ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; } else { for (uiIndex = 0; uiIndex < ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; uiIndex++) { pucEntry = BtEntry( pBlk, uiIndex); uiTotal += FB2UD( &pucEntry[ BTE_NLC_COUNTS]); } } return( uiTotal); } /*************************************************************************** Desc: Function to store an entry in a Data-only block. ****************************************************************************/ RCODE F_Btree::storeDataOnlyBlocks( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bSaveKey, const FLMBYTE * pucData, FLMUINT uiDataLen) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pPrevSCache = NULL; const FLMBYTE * pucLocalData = pucData; FLMUINT uiDataToWrite = uiDataLen; F_BLK_HDR * pBlkHdr = NULL; FLMBYTE * pDestPtr = NULL; FLMUINT uiAmtToCopy; if( bSaveKey) { if( !m_pSCache) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } // Assert that the current block is empty and has no previous link. flmAssert( m_pSCache->m_pBlkHdr->ui16BlkBytesAvail == m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr)); flmAssert( m_pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0); pBlkHdr = m_pSCache->m_pBlkHdr; pDestPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr); UW2FBA( (FLMUINT16)uiKeyLen, pDestPtr); pDestPtr += sizeof( FLMUINT16); f_memcpy( pDestPtr, pucKey, uiKeyLen); pDestPtr += uiKeyLen; m_uiDataRemaining -= (uiKeyLen + sizeof( FLMUINT16)); pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; } while( uiDataToWrite > 0) { if( !m_pSCache) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } if( !bSaveKey) { pBlkHdr = m_pSCache->m_pBlkHdr; // Now copy as much of the remaining data as we can into the new block. pDestPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr ); pDestPtr += (m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr ) - m_uiDataRemaining); } else { bSaveKey = FALSE; } uiAmtToCopy = (uiDataToWrite <= m_uiDataRemaining ? uiDataToWrite : m_uiDataRemaining); f_memcpy( pDestPtr, pucLocalData, uiAmtToCopy); m_uiDataRemaining -= uiAmtToCopy; m_uiOADataLength += uiAmtToCopy; uiDataToWrite -= uiAmtToCopy; pucLocalData += uiAmtToCopy; pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; // Now get the next block (if needed) if( uiDataToWrite) { pPrevSCache = m_pSCache; m_pSCache = NULL; // Now create a new block if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) { goto Exit; } pBlkHdr = m_pSCache->m_pBlkHdr; pBlkHdr->ui8BlkType = BT_DATA_ONLY; pBlkHdr->ui32PrevBlkInChain = pPrevSCache->m_pBlkHdr->ui32BlkAddr; pBlkHdr->ui32NextBlkInChain = 0; if( m_pLFile->uiEncId) { ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; setBlockEncrypted( pBlkHdr); } pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr)); pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32BlkAddr; m_ui32CurBlkAddr = pBlkHdr->ui32BlkAddr; m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr); if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } } } Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Function to Replace data in data only blocks. ****************************************************************************/ RCODE F_Btree::replaceDataOnlyBlocks( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bSaveKey, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bLast, FLMBOOL bTruncate) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pPrevSCache = NULL; const FLMBYTE * pucLocalData = pucData; FLMUINT uiDataToWrite = uiDataLen; F_BLK_HDR * pBlkHdr = NULL; FLMBYTE * pDestPtr = NULL; FLMUINT uiAmtToCopy; FLMUINT32 ui32NextBlkAddr; // Do we need to store the key too? if( bSaveKey) { if( !m_pSCache) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } // Assert that the current block is empty and has no previous link. flmAssert( m_pSCache->m_pBlkHdr->ui16BlkBytesAvail == m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr)); flmAssert( m_pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0); pBlkHdr = m_pSCache->m_pBlkHdr; pDestPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr ); UW2FBA( (FLMUINT16)uiKeyLen, pDestPtr); pDestPtr += sizeof( FLMUINT16); f_memcpy( pDestPtr, pucKey, uiKeyLen); pDestPtr += uiKeyLen; m_uiDataRemaining -= (uiKeyLen + sizeof( FLMUINT16)); pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; } while( uiDataToWrite > 0) { if( !m_pSCache) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } if( !bSaveKey) { pBlkHdr = m_pSCache->m_pBlkHdr; // Now copy as much of the remaining data as we can into the new block. pDestPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); pDestPtr += (m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr ) - m_uiDataRemaining); } else { bSaveKey = FALSE; } uiAmtToCopy = (uiDataToWrite <= m_uiDataRemaining ? uiDataToWrite : m_uiDataRemaining); f_memcpy( pDestPtr, pucLocalData, uiAmtToCopy); m_uiDataRemaining -= uiAmtToCopy; m_uiOADataLength += uiAmtToCopy; uiDataToWrite -= uiAmtToCopy; pucLocalData += uiAmtToCopy; if( bTruncate || (m_uiDataRemaining < pBlkHdr->ui16BlkBytesAvail)) { pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)m_uiDataRemaining; } // Now get the next block (if needed) if( uiDataToWrite) { pPrevSCache = m_pSCache; m_pSCache = NULL; ui32NextBlkAddr = pPrevSCache->m_pBlkHdr->ui32NextBlkInChain; if( ui32NextBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } pBlkHdr = m_pSCache->m_pBlkHdr; } else { // Now create a new block if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) { goto Exit; } pBlkHdr = m_pSCache->m_pBlkHdr; pBlkHdr->ui8BlkType = BT_DATA_ONLY; pBlkHdr->ui32PrevBlkInChain = pPrevSCache->m_pBlkHdr->ui32BlkAddr; pBlkHdr->ui32NextBlkInChain = 0; if( m_pLFile->uiEncId) { setBlockEncrypted( pBlkHdr); ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId = (FLMUINT32)m_pLFile->uiEncId; } pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr)); } pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32BlkAddr; m_ui32CurBlkAddr = pBlkHdr->ui32BlkAddr; m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr); if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } } } // If this was the last pass to store the data, then see if we need to // remove any left over blocks. We will not truncate the data if // the bTruncate parameter is not set. if( bLast && bTruncate) { flmAssert( m_pSCache); ui32NextBlkAddr = m_pSCache->m_pBlkHdr->ui32NextBlkInChain; m_pSCache->m_pBlkHdr->ui32NextBlkInChain = 0; ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; // If there are any blocks left over, they must be freed. while( ui32NextBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &m_pSCache))) { goto Exit; } ui32NextBlkAddr = m_pSCache->m_pBlkHdr->ui32NextBlkInChain; rc = m_pDb->m_pDatabase->blockFree( m_pDb, m_pSCache); m_pSCache = NULL; if( RC_BAD( rc)) { goto Exit; } } } Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to construct a new leaf entry using the key and value information passed in. ****************************************************************************/ RCODE F_Btree::buildAndStoreEntry( FLMUINT uiBlkType, FLMUINT uiFlags, const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMUINT uiOADataLen, // If zero, it will not be used. FLMUINT uiChildBlkAddr, FLMUINT uiCounts, FLMBYTE * pucBuffer, FLMUINT uiBufferSize, FLMUINT * puiEntrySize) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucTemp = pucBuffer; if( puiEntrySize) { *puiEntrySize = calcEntrySize( uiBlkType, uiFlags, uiKeyLen, uiDataLen, uiOADataLen); if( !(*puiEntrySize) || *puiEntrySize > uiBufferSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } } switch( uiBlkType) { case BT_LEAF: { // No Data in this entry, so it is easy to make. UW2FBA( (FLMUINT16)uiKeyLen, pucTemp); pucTemp += 2; f_memcpy( pucTemp, pucKey, uiKeyLen); break; } case BT_LEAF_DATA: { // Make sure the correct flags are set... if( uiKeyLen > ONE_BYTE_SIZE) { uiFlags |= BTE_FLAG_KEY_LEN; } else { uiFlags &= ~BTE_FLAG_KEY_LEN; } if( uiDataLen > ONE_BYTE_SIZE) { uiFlags |= BTE_FLAG_DATA_LEN; } else { uiFlags &= ~BTE_FLAG_DATA_LEN; } // Only the first element of an entry that spans elements // will hold an OADataLen field. if( uiOADataLen && (uiFlags & BTE_FLAG_FIRST_ELEMENT)) { uiFlags |= BTE_FLAG_OA_DATA_LEN; } else { uiFlags &= ~BTE_FLAG_OA_DATA_LEN; } // Now start setting the elements of the entry. // Flags first. *pucTemp = (FLMBYTE)uiFlags; pucTemp++; // KeyLen if( uiFlags & BTE_FLAG_KEY_LEN) { UW2FBA( (FLMUINT16)uiKeyLen, pucTemp); pucTemp += 2; } else { *pucTemp = (FLMBYTE)uiKeyLen; pucTemp++; } if( uiFlags & BTE_FLAG_DATA_LEN) { UW2FBA( (FLMUINT16)uiDataLen, pucTemp); pucTemp += 2; } else { *pucTemp = (FLMBYTE)uiDataLen; pucTemp++; } if( uiFlags & BTE_FLAG_OA_DATA_LEN) { UD2FBA( (FLMUINT32)uiOADataLen, pucTemp); pucTemp += 4; } // Key f_memcpy( pucTemp, pucKey, uiKeyLen); pucTemp += uiKeyLen; // Data f_memcpy( pucTemp, pucData, uiDataLen); break; } case BT_NON_LEAF: case BT_NON_LEAF_COUNTS: { // Child block address - 4 bytes pucTemp = pucBuffer; flmAssert( uiChildBlkAddr); UD2FBA( (FLMUINT32)uiChildBlkAddr, pucTemp); pucTemp += 4; // Counts - 4 bytes if( uiBlkType == BT_NON_LEAF_COUNTS) { UD2FBA( (FLMUINT32)uiCounts, pucTemp); pucTemp += 4; } // KeyLen field - 2 bytes UW2FBA( (FLMUINT16)uiKeyLen, pucTemp); pucTemp += 2; // Key - variable length (uiKeyLen) f_memcpy( pucTemp, pucKey, uiKeyLen); break; } default: { // Invalid block type rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Method to remove an entry from a block. This method will delete the entry pointed to by the current Stack. This method does NOT defragment the block. If the entry points to any data only blocks, they will also be removed from circulation if the parameter bDeleteDOBlocks is set to true. Otherwise, they will not be freed. This is so we can call this method when we are moving entries between blocks or replacing entries etc. ****************************************************************************/ RCODE F_Btree::remove( FLMBOOL bDeleteDOBlocks) { RCODE rc = NE_XFLM_OK; FLMUINT16 * pui16OffsetArray; FLMUINT uiNumKeys; FLMUINT uiEntrySize; FLMUINT uiTmp; FLMBYTE * pucEntry; FLMBOOL bDOBlock; F_CachedBlock * pSCache = NULL; FLMUINT uiBlkAddr; FLMBYTE * pucEndOfHeap; F_BTREE_BLK_HDR * pBlkHdr; if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } pBlkHdr = m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->getBlockPtr(); m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); uiNumKeys = pBlkHdr->ui16NumKeys; if( !uiNumKeys) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Point to the entry... pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, m_pStack->uiCurOffset); uiEntrySize = getEntrySize( (FLMBYTE *)pBlkHdr, m_pStack->uiCurOffset); pucEndOfHeap = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr(pBlkHdr) + (uiNumKeys * 2) + pBlkHdr->ui16HeapSize; // We are only going to have data only blocks if we are storing data // in the btree. if( m_bData) { bDOBlock = bteDataBlockFlag( pucEntry); // If the data for this entry is in one or more Data Only blocks, then // we must delete those blocks first. if( bDOBlock && bDeleteDOBlocks) { FLMBYTE ucDOBlkAddr[ 4]; // Get the block address of the DO Block. if( RC_BAD( rc = btGetEntryData( pucEntry, ucDOBlkAddr, sizeof( FLMUINT), NULL))) { goto Exit; } uiBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); while( uiBlkAddr) { // We need to delete the data only blocks first. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiBlkAddr, NULL, &pSCache))) { goto Exit; } // Get the next block address (if any) uiBlkAddr = pSCache->m_pBlkHdr->ui32NextBlkInChain; // Now put the block into the Avail list. rc = m_pDb->m_pDatabase->blockFree( m_pDb, pSCache); pSCache = NULL; if( RC_BAD( rc)) { goto Exit; } } } } pui16OffsetArray = m_pStack->pui16OffsetArray; // Move the offsets around to effectively remove the entry. for( uiTmp = m_pStack->uiCurOffset; (uiTmp + 1) < uiNumKeys; uiTmp++) { bteSetEntryOffset( pui16OffsetArray, uiTmp, bteGetEntryOffset( pui16OffsetArray, (uiTmp + 1))); } #ifdef FLM_DEBUG // Erase the last offset entry. bteSetEntryOffset( pui16OffsetArray, uiTmp, 0); #endif pBlkHdr->ui16NumKeys--; pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += (FLMUINT16)uiEntrySize; pBlkHdr->ui16HeapSize += 2; // One offset was removed. // Was this entry we just removed adjacent to the heap space? If // so then we can increase the heap space. if( pucEndOfHeap == pucEntry) { pBlkHdr->ui16HeapSize += (FLMUINT16)actualEntrySize(uiEntrySize); } #ifdef FLM_DEBUG // Let's erase whatever was in the entry space. f_memset( pucEntry, 0, actualEntrySize(uiEntrySize)); #endif Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to remove multiple entries from a block. The entries must be contiguous. If any entries store data in data-only blocks, they will be freed and put into the avail list. ****************************************************************************/ RCODE F_Btree::removeRange( FLMUINT uiStartElm, FLMUINT uiEndElm, FLMBOOL bDeleteDOBlocks) { RCODE rc = NE_XFLM_OK; FLMUINT16 * pui16OffsetArray; FLMUINT uiNumKeys; FLMUINT uiEntrySize; FLMBYTE * pucEntry; FLMBOOL bDOBlock; F_CachedBlock * pSCache = NULL; FLMUINT uiBlkAddr; FLMUINT uiCurOffset; FLMUINT uiCounter; FLMBYTE * pucEndOfHeap; FLMBYTE * pucStartOfHeap; F_BTREE_BLK_HDR * pBlkHdr; if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } pBlkHdr = m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->getBlockPtr(); m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); uiNumKeys = pBlkHdr->ui16NumKeys; if( !uiNumKeys) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } flmAssert( uiEndElm < uiNumKeys); // Point to the entry ... for( uiCurOffset = uiStartElm; uiCurOffset <= uiEndElm; uiCurOffset++) { pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiCurOffset); uiEntrySize = getEntrySize( (FLMBYTE *)pBlkHdr, uiCurOffset); pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += (FLMUINT16)uiEntrySize; pBlkHdr->ui16NumKeys--; bDOBlock = bteDataBlockFlag(pucEntry); // If the data for this entry is in a Data Only block, then we must delete // those blocks first. if( bDOBlock && bDeleteDOBlocks) { FLMBYTE ucDOBlkAddr[ 4]; // Get the block address of the DO Block. if( RC_BAD( rc = btGetEntryData( pucEntry, ucDOBlkAddr, 4, NULL))) { goto Exit; } uiBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); while( uiBlkAddr) { // We need to delete the data only blocks first. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiBlkAddr, NULL, &pSCache))) { goto Exit; } // Get the next block address (if any) uiBlkAddr = pSCache->m_pBlkHdr->ui32NextBlkInChain; // Now put the block into the Avail list. rc = m_pDb->m_pDatabase->blockFree( m_pDb, pSCache); pSCache = NULL; if( RC_BAD( rc)) { goto Exit; } } } // Now erase the old entry #ifdef FLM_DEBUG f_memset( pucEntry, 0, actualEntrySize(uiEntrySize)); #endif } // Move the offsets around to effectively remove the entries. pui16OffsetArray = m_pStack->pui16OffsetArray; if( uiEndElm < (uiNumKeys - 1)) { // We will need to move the remaining offsets forward // to delete the desired range. for (uiCurOffset = uiStartElm, uiCounter = 0; uiCounter < (uiNumKeys - (uiEndElm + 1)); uiCounter++, uiCurOffset++) { bteSetEntryOffset( pui16OffsetArray, uiCurOffset, bteGetEntryOffset( pui16OffsetArray, (uiEndElm + uiCounter + 1))); } } #ifdef FLM_DEBUG // Erase the remaining offsets while (uiCurOffset < (uiNumKeys - 1)) { bteSetEntryOffset( pui16OffsetArray, uiCurOffset++, 0); } #endif // We need to determine if we have gained any more heap space. We start // by pointing to the end of the block, them moving forward until we reach // the closest entry. pucEndOfHeap = (FLMBYTE *)pBlkHdr + m_uiBlockSize; for ( uiCurOffset = 0; uiCurOffset < pBlkHdr->ui16NumKeys; uiCurOffset++) { pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiCurOffset); if (pucEntry < pucEndOfHeap) { pucEndOfHeap = pucEntry; } } // Now clean up the heap space. pucStartOfHeap = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr) + (pBlkHdr->ui16NumKeys * 2); pBlkHdr->ui16HeapSize = (FLMUINT16)(pucEndOfHeap - pucStartOfHeap); #ifdef FLM_DEBUG f_memset( pucStartOfHeap, 0, pBlkHdr->ui16HeapSize); #endif Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to try to move entries (whole) from the target block to the previous block. The entries may be moved, up to but not including the current entry position. We do not want to change the parentage of this block. We need to use the stack to fix up the parentage of the previous block. Entries are moved from the lowest to highest. ****************************************************************************/ RCODE F_Btree::moveEntriesToPrevBlk( FLMUINT uiNewEntrySize, F_CachedBlock ** ppPrevSCache, FLMBOOL * pbEntriesWereMoved) { RCODE rc = NE_XFLM_OK; FLMUINT uiLocalAvailSpace; FLMUINT uiAvailSpace; FLMUINT uiHeapSize; F_CachedBlock * pPrevSCache = NULL; FLMUINT uiPrevBlkAddr; FLMUINT uiOAEntrySize = 0; FLMUINT uiStart; FLMUINT uiFinish; FLMUINT uiCount; FLMUINT uiOffset; // Assume nothing to move. *pbEntriesWereMoved = FALSE; // If we are already at the first entry in the block, there // is nothing that we can move since we will always insert ahead of // the current position. if( !m_pStack->uiCurOffset) { goto Exit; } // Get the previous block. if( (uiPrevBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32PrevBlkInChain) == 0) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiPrevBlkAddr, NULL, &pPrevSCache))) { goto Exit; } uiLocalAvailSpace = m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; uiAvailSpace = pPrevSCache->m_pBlkHdr->ui16BlkBytesAvail; uiHeapSize = ((F_BTREE_BLK_HDR *)pPrevSCache->m_pBlkHdr)->ui16HeapSize; // If we add the available space in this block and the previous block, would // it be enough to make room for the new entry? If so, then we will // see if we can make that room by moving ( whole) entries. if( (uiAvailSpace + uiLocalAvailSpace) < uiNewEntrySize) { goto Exit; } uiStart = 0; uiFinish = m_pStack->uiCurOffset; // Get the size of each entry until we are over the available size limit for( uiOffset = 0, uiCount = 0 ; uiOffset < uiFinish; uiOffset++) { FLMUINT uiLocalEntrySize; uiLocalEntrySize = getEntrySize( (FLMBYTE *)m_pStack->pBlkHdr, uiOffset); if( (uiLocalEntrySize + uiOAEntrySize) < uiAvailSpace) { uiOAEntrySize += uiLocalEntrySize; uiLocalAvailSpace += uiLocalEntrySize; uiCount++; } else { break; } } if( !uiCount) { goto Exit; } // It looks like we can move at least one entry. // Will this give use enough room to store the new entry? if( uiLocalAvailSpace < uiNewEntrySize) { // Moving these entries will not benefit us, so don't bother goto Exit; } // Do we need to defragment the block first? if( uiHeapSize < uiOAEntrySize) { flmAssert( uiHeapSize != uiAvailSpace); if( RC_BAD( rc = defragmentBlock( &pPrevSCache))) { goto Exit; } } // We are going to get some benefit from moving, so let's do it... if (RC_BAD( rc = moveToPrev( uiStart, uiStart + uiCount - 1, &pPrevSCache))) { goto Exit; } // We will need to return this block. *ppPrevSCache = pPrevSCache; pPrevSCache = NULL; // Adjust the current offset in the stack so we are still pointing to the // same entry. m_pStack->uiCurOffset -= uiCount; // If this block has a parent block, and the btree is maintaining counts // we will want to update the counts on the parent block. if( !isRootBlk( m_pStack->pBlkHdr)) { if( m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } } *pbEntriesWereMoved = TRUE; Exit: if (pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } return( rc); } /*************************************************************************** Desc: This method will move entries beginning at uiStart, up to and including uiFinish from the current block (m_pStack) to pPrevSCache. As a part of this operation, both the target block and the source block will be changed. A call to logPhysBlock will be made before each block is changed. Never move the highest key in the block because we don't want to have to update the parentage of the current block... ****************************************************************************/ RCODE F_Btree::moveToPrev( FLMUINT uiStart, FLMUINT uiFinish, F_CachedBlock ** ppPrevSCache) { RCODE rc = NE_XFLM_OK; FLMUINT16 * pui16DstOffsetA = NULL; F_BTREE_BLK_HDR * pSrcBlkHdr = NULL; F_BTREE_BLK_HDR * pDstBlkHdr = NULL; FLMBYTE * pucSrcEntry; FLMBYTE * pucDstEntry; FLMUINT uiEntrySize; FLMUINT uiIndex; F_CachedBlock * pPrevSCache; FLMBOOL bEntriesCombined = FALSE; // Make sure we have logged the block we are changing. // Note that the source block will be logged in the removeRange method. if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, ppPrevSCache))) { goto Exit; } pPrevSCache = *ppPrevSCache; pSrcBlkHdr = m_pStack->pBlkHdr; pDstBlkHdr = (F_BTREE_BLK_HDR *)pPrevSCache->m_pBlkHdr; pui16DstOffsetA = BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0); pucDstEntry = getBlockEnd( pDstBlkHdr); // Beginning at the start, copy each entry over from the source // to the destination block. for( uiIndex = uiStart; uiIndex <= uiFinish; uiIndex++) { if( RC_BAD( rc = combineEntries( pSrcBlkHdr, uiIndex, pDstBlkHdr, (pDstBlkHdr->ui16NumKeys ? pDstBlkHdr->ui16NumKeys - 1 : 0), &bEntriesCombined, &uiEntrySize))) { goto Exit; } if( bEntriesCombined) { F_BTSK tmpStack; F_BTSK_p pTmpStack; tmpStack.pSCache = pPrevSCache; tmpStack.pBlkHdr = pDstBlkHdr; tmpStack.uiCurOffset = pDstBlkHdr->ui16NumKeys - 1; // Last entry pTmpStack = m_pStack; m_pStack = &tmpStack; rc = remove( FALSE); m_pStack = pTmpStack; if( RC_BAD( rc)) { goto Exit; } if( pDstBlkHdr->ui16HeapSize != pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { if( RC_BAD( rc = defragmentBlock( &pPrevSCache))) { goto Exit; } } pucDstEntry = getBlockEnd( pDstBlkHdr) - uiEntrySize; f_memcpy( pucDstEntry, m_pucTempBlk, uiEntrySize); bteSetEntryOffset( pui16DstOffsetA, pDstBlkHdr->ui16NumKeys++, (FLMUINT16)(pucDstEntry - (FLMBYTE *)pDstBlkHdr)); pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= ((FLMUINT16)uiEntrySize + 2); pDstBlkHdr->ui16HeapSize -= ((FLMUINT16)uiEntrySize + 2); bEntriesCombined = FALSE; } else { pucSrcEntry = BtEntry( (FLMBYTE *)pSrcBlkHdr, uiIndex); uiEntrySize = getEntrySize( (FLMBYTE *)pSrcBlkHdr, uiIndex); pucDstEntry -= actualEntrySize(uiEntrySize); f_memcpy( pucDstEntry, pucSrcEntry, actualEntrySize(uiEntrySize)); bteSetEntryOffset( pui16DstOffsetA, pDstBlkHdr->ui16NumKeys++, (FLMUINT16)(pucDstEntry - (FLMBYTE *)pDstBlkHdr)); pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= (FLMUINT16)uiEntrySize; pDstBlkHdr->ui16HeapSize -= (FLMUINT16)uiEntrySize; } } // Now remove the entries from the Src block. if( RC_BAD( rc = removeRange( uiStart, uiFinish, FALSE))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Method to try to move entries (whole) from the target block to the next block. The entries may be moved up to but not including the current entry position depending on how much room is available if any. Entries are moved from the highest to lowest. ****************************************************************************/ RCODE F_Btree::moveEntriesToNextBlk( FLMUINT uiNewEntrySize, FLMBOOL * pbEntriesWereMoved) { RCODE rc = NE_XFLM_OK; FLMUINT uiLocalAvailSpace; FLMUINT uiAvailSpace; FLMUINT uiHeapSize; F_CachedBlock * pNextSCache = NULL; FLMUINT uiNextBlkAddr; FLMUINT uiOAEntrySize = 0; FLMUINT uiStart; FLMUINT uiFinish; FLMUINT uiCount; FLMUINT uiOffset; F_CachedBlock * pChildSCache = NULL; F_CachedBlock * pParentSCache = NULL; F_BTSK_p pParentStack; FLMUINT uiLevel; FLMBOOL bReleaseChild = FALSE; FLMBOOL bReleaseParent = FALSE; FLMBOOL bCommonParent = FALSE; // Assume nothing to move. *pbEntriesWereMoved = FALSE; // Get the next block. if( (uiNextBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32NextBlkInChain) == 0) { goto Exit; } if( (FLMUINT16)m_pStack->uiCurOffset >= (m_pStack->pBlkHdr->ui16NumKeys - 1)) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiNextBlkAddr, NULL, &pNextSCache))) { goto Exit; } // Our first task is to determine if we can move anything at all. // How much free space is there in the next block? uiLocalAvailSpace = m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; uiAvailSpace = pNextSCache->m_pBlkHdr->ui16BlkBytesAvail; uiHeapSize = ((F_BTREE_BLK_HDR *)pNextSCache->m_pBlkHdr)->ui16HeapSize; // If we add the available space in this block and the next block, would // it be enough to make room for the new entry? If so, then we will // see if we can make that room by moving ( whole) entries. if( (uiAvailSpace + uiLocalAvailSpace) < uiNewEntrySize) { goto Exit; } // Begin at the last entry and work backward. uiStart = m_pStack->pBlkHdr->ui16NumKeys - 1; uiFinish = m_pStack->uiCurOffset; // Get the size of each entry (plus 2 for the offset entry) until we are // over the available size limit. for( uiOffset = uiStart, uiCount = 0 ; uiOffset > uiFinish; uiOffset--) { FLMUINT uiLocalEntrySize; uiLocalEntrySize = getEntrySize( (FLMBYTE *)m_pStack->pBlkHdr, uiOffset); if( (uiLocalEntrySize + uiOAEntrySize) < uiAvailSpace) { uiOAEntrySize += uiLocalEntrySize; uiLocalAvailSpace += uiLocalEntrySize; uiCount++; } else { break; } } if( uiCount == 0) { goto Exit; } // It looks like we can move at least one entry. // Will this give use enough room to store the new entry? if( uiLocalAvailSpace < uiNewEntrySize) { goto Exit; } flmAssert( uiStart > uiFinish); // Do we need to defragment the block first before we do the move? if( uiHeapSize < uiOAEntrySize) { flmAssert( uiHeapSize != uiAvailSpace); if( RC_BAD( rc = defragmentBlock( &pNextSCache))) { goto Exit; } } // We are going to get some benefit from moving, so let's do it... if( RC_BAD( rc = moveToNext( uiStart, uiStart - uiCount + 1, &pNextSCache))) { goto Exit; } // If this block has a parent block, and the btree is maintaining counts // we will need to update the counts on the parent blocks. if( m_bCounts) { for( uiLevel = m_pStack->uiLevel; uiLevel < m_uiStackLevels - 1; uiLevel++) { pParentStack = &m_Stack[ uiLevel + 1]; // If we are at "current" level, then we want to use the pNextSCache // block as the child. Otherwise, we want to use the previous parent // block as the child. if( uiLevel == m_pStack->uiLevel) { pChildSCache = pNextSCache; bReleaseChild = TRUE; pNextSCache = NULL; } else { pChildSCache = pParentSCache; bReleaseChild = bReleaseParent; bReleaseParent = FALSE; } // Check to see if the parent entry is the last entry in the // block. If it is, then we will need to get the next block. // If the parent block is the same for both blocks, then we // only need to reference the next entry. We don't want to release // the parent as it is referenced in the stack. if( bCommonParent || (pParentStack->uiCurOffset < (FLMUINT)(pParentStack->pBlkHdr->ui16NumKeys - 1))) { pParentSCache = pParentStack->pSCache; bReleaseParent = FALSE; if (RC_BAD( rc = updateParentCounts( pChildSCache, &pParentSCache, (bCommonParent ? pParentStack->uiCurOffset : pParentStack->uiCurOffset + 1)))) { goto Exit; } // The parent has changed, so update the stack. pParentStack->pBlkHdr = (F_BTREE_BLK_HDR *)pParentSCache->m_pBlkHdr; pParentStack->pSCache = pParentSCache; bCommonParent = TRUE; } else { // We need to get the next block at the parent level first. We // release the previous parent if there was one. uiNextBlkAddr = pParentStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain; flmAssert( uiNextBlkAddr); if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiNextBlkAddr, NULL, &pParentSCache))) { goto Exit; } bReleaseParent = TRUE; if( RC_BAD( rc = updateParentCounts( pChildSCache, &pParentSCache, 0))) { goto Exit; } } if( bReleaseChild) { ScaReleaseCache( pChildSCache, FALSE); pChildSCache = NULL; bReleaseChild = FALSE; } } } *pbEntriesWereMoved = TRUE; Exit: if( pChildSCache && bReleaseChild) { ScaReleaseCache( pChildSCache, FALSE); } if( pParentSCache && bReleaseParent) { ScaReleaseCache( pParentSCache, FALSE); } if( pNextSCache) { ScaReleaseCache( pNextSCache, FALSE); } return( rc); } /*************************************************************************** Desc: This method will move entries beginning at uiStart, down to and including uiFinish from the current block (m_pStack) to pNextSCache. As a part of this operation, both the target block and the source block will be changed. ****************************************************************************/ RCODE F_Btree::moveToNext( FLMUINT uiStart, FLMUINT uiFinish, F_CachedBlock ** ppNextSCache) { RCODE rc = NE_XFLM_OK; FLMUINT16 * pui16DstOffsetA = NULL; F_BTREE_BLK_HDR * pSrcBlkHdr = NULL; F_BTREE_BLK_HDR * pDstBlkHdr = NULL; FLMBYTE * pucSrcEntry; FLMBYTE * pucDstEntry; FLMUINT uiEntrySize; FLMINT iIndex; FLMUINT uiBytesToCopy; FLMUINT uiNumKeysToAdd; F_CachedBlock * pNextSCache = *ppNextSCache; FLMBOOL bEntriesCombined; FLMBYTE * pucOffsetArray; // Make sure we have logged the block we are changing. // Note that the source block will be logged in the removeRange method. if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pNextSCache))) { goto Exit; } // SCache block may have changed. Need to pass it back. *ppNextSCache = pNextSCache; pSrcBlkHdr = m_pStack->pBlkHdr; pDstBlkHdr = (F_BTREE_BLK_HDR *)pNextSCache->m_pBlkHdr; // We will need to save off the current offset array. We will do this // by copying it into our temporary block. uiBytesToCopy = pDstBlkHdr->ui16NumKeys * 2; if( uiBytesToCopy > m_uiBufferSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } pui16DstOffsetA = BtOffsetArray((FLMBYTE *)pDstBlkHdr, 0); pucOffsetArray = &m_pucBuffer[ m_uiBufferSize] - uiBytesToCopy; f_memcpy( pucOffsetArray, (FLMBYTE *)pui16DstOffsetA, uiBytesToCopy); // Point to the last entry in the block. pucDstEntry = getBlockEnd( pDstBlkHdr); // Beginning at the start, copy each entry over from the Src to the Dst // block. Note that the uiStart parameter represents a higher position // in the block. In otherwords, we are actually copying from the end or // highest position to a lower position in the block. Therefore we want // to make sure the offset array is copied in the same way, otherwise it // would reverse the order of the entries. uiNumKeysToAdd = uiStart - uiFinish + 1; pui16DstOffsetA = (FLMUINT16 *)pucOffsetArray; for( iIndex = uiStart; iIndex >= (FLMINT)uiFinish; iIndex--) { if( RC_BAD( rc = combineEntries( pSrcBlkHdr, iIndex, pDstBlkHdr, 0, &bEntriesCombined, &uiEntrySize))) { goto Exit; } if( bEntriesCombined) { F_BTSK tmpStack; F_BTSK_p pTmpStack; tmpStack.pSCache = pNextSCache; tmpStack.pBlkHdr = pDstBlkHdr; tmpStack.uiCurOffset = 0; // 1st entry. pTmpStack = m_pStack; m_pStack = &tmpStack; rc = remove( FALSE); m_pStack = pTmpStack; if (RC_BAD( rc)) { goto Exit; } if( pDstBlkHdr->ui16HeapSize != pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { if( RC_BAD( rc = defragmentBlock( &pNextSCache))) { goto Exit; } // Refresh the saved offset array. uiBytesToCopy -= 2; pucOffsetArray = &m_pucBuffer[ m_uiBufferSize] - uiBytesToCopy; f_memcpy( pucOffsetArray, (FLMBYTE *)BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0), uiBytesToCopy); } pucDstEntry = getBlockEnd( pDstBlkHdr) - uiEntrySize; f_memcpy( pucDstEntry, m_pucTempBlk, uiEntrySize); bteSetEntryOffset( pui16DstOffsetA, 0, (FLMUINT16)(pucDstEntry - (FLMBYTE *)pDstBlkHdr)); pDstBlkHdr->ui16NumKeys++; pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= ((FLMUINT16)uiEntrySize + 2); pDstBlkHdr->ui16HeapSize -= ((FLMUINT16)uiEntrySize + 2); bEntriesCombined = FALSE; } else { pucSrcEntry = BtEntry( (FLMBYTE *)pSrcBlkHdr, iIndex); uiEntrySize = getEntrySize( (FLMBYTE *)pSrcBlkHdr, iIndex); pucDstEntry -= actualEntrySize(uiEntrySize); f_memcpy( pucDstEntry, pucSrcEntry, actualEntrySize(uiEntrySize)); pui16DstOffsetA--; bteSetEntryOffset( pui16DstOffsetA, 0, (FLMUINT16)(pucDstEntry - (FLMBYTE *)pDstBlkHdr)); pDstBlkHdr->ui16NumKeys++; pDstBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= (FLMUINT16)uiEntrySize; pDstBlkHdr->ui16HeapSize -= (FLMUINT16)uiEntrySize; } } // Now put the new offset array into the block. f_memcpy( BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0), pui16DstOffsetA, &m_pucBuffer[ m_uiBufferSize] - (FLMBYTE *)pui16DstOffsetA); // Now remove the entries from the Src block. if( RC_BAD( rc = removeRange( uiFinish, uiStart, FALSE))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Method to advance to the next entry. If there are no more entries in the block, it will release the current block and get the next in the chain. If there are no more entries, i.e. no more blocks in the chain, NE_XFLM_EOF_HIT will be returned. ****************************************************************************/ RCODE F_Btree::advanceToNextElement( FLMBOOL bAdvanceStack) { RCODE rc = NE_XFLM_OK; F_BTREE_BLK_HDR * pBlkHdr; flmAssert( m_pSCache); pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; if( m_uiCurOffset + 1 >= pBlkHdr->ui16NumKeys) { // We are out of entries in this block, so we will release it // and get the next block in the chain (if any). if( RC_BAD( rc = getNextBlock( &m_pSCache))) { goto Exit; } m_ui32PrimaryBlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; m_uiPrimaryOffset = 0; m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = 0; if( bAdvanceStack) { if( RC_BAD( rc = moveStackToNext( m_pSCache))) { goto Exit; } // This block now has two uses. It will be released twice. m_pSCache->m_uiUseCount++; } } else { m_uiPrimaryOffset++; m_uiCurOffset++; m_pStack->uiCurOffset++; } Exit: // We do not want to release the m_pSCache here. That is to be done by the // caller. return( rc); } /*************************************************************************** Desc: Method to backup the stack to the previous entry. If there are no more entries in the block, it will release the current block and get the previous in the chain. If there are no more entries, i.e. no more blocks in the chain, NE_XFLM_BOF_HIT will be returned. ****************************************************************************/ RCODE F_Btree::backupToPrevElement( FLMBOOL bBackupStack) { RCODE rc = NE_XFLM_OK; F_BTREE_BLK_HDR * pBlkHdr; flmAssert( m_pSCache); pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; if( !m_uiCurOffset) { // We are out of entries in this block, so we will release it // and get the previous block in the chain (if any). if( RC_BAD( rc = getPrevBlock( &m_pSCache))) { goto Exit; } m_ui32PrimaryBlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; m_uiPrimaryOffset = ((F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr)->ui16NumKeys - 1; m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; m_uiCurOffset = m_uiPrimaryOffset; if( bBackupStack) { if( RC_BAD( rc = moveStackToPrev( m_pSCache))) { goto Exit; } // This block now has two uses. It will be released twice. m_pSCache->m_uiUseCount++; } } else { m_uiPrimaryOffset--; m_uiCurOffset--; m_pStack->uiCurOffset--; } Exit: // We do not want to release the m_pSCache here. That is to be done by the // caller. return( rc); } /*************************************************************************** Desc: Method to extract the key length from a given entry. The optional pucKeyRV is a buffer where we can return the address of the start of the actual key. ****************************************************************************/ FLMUINT F_Btree::getEntryKeyLength( FLMBYTE * pucEntry, FLMUINT uiBlockType, const FLMBYTE ** ppucKeyRV) { FLMUINT uiKeyLength; FLMBYTE * pucTmp = NULL; // The way we get the key length depends on the type of block we have. switch( uiBlockType) { case BT_LEAF_DATA: { pucTmp = &pucEntry[ 1]; // skip past the flags if( bteKeyLenFlag( pucEntry)) { uiKeyLength = FB2UW( pucTmp); pucTmp += 2; } else { uiKeyLength = *pucTmp; pucTmp += 1; } if( bteDataLenFlag(pucEntry)) { pucTmp += 2; } else { pucTmp += 1; } // Check for the presence of the OverallDataLength field (4 bytes). if( bteOADataLenFlag( pucEntry)) { pucTmp += 4; } break; } case BT_LEAF: { uiKeyLength = FB2UW( pucEntry); if( ppucKeyRV) { pucTmp = &pucEntry[ BTE_KEY_START]; } break; } case BT_NON_LEAF: { uiKeyLength = FB2UW( &pucEntry[ BTE_NL_KEY_LEN]); if( ppucKeyRV) { pucTmp = &pucEntry[ BTE_NL_KEY_START]; } break; } case BT_NON_LEAF_COUNTS: { uiKeyLength = FB2UW( &pucEntry[ BTE_NLC_KEY_LEN]); if( ppucKeyRV) { pucTmp = &pucEntry[ BTE_NLC_KEY_START]; } break; } default: { flmAssert( 0); uiKeyLength = 0; pucTmp = NULL; break; } } // Do we need to return the key pointer? if( ppucKeyRV) { *ppucKeyRV = pucTmp; } return( uiKeyLength); } /*************************************************************************** Desc: Method to extract the data length from a given entry. The parameter pucDataRV is an optional return value that will hold the address of the beginning of the data in the entry. This method ** assumes ** the entry is from a BT_LEAF_DATA block. No other block type has any data. ****************************************************************************/ FSTATIC FLMUINT btGetEntryDataLength( FLMBYTE * pucEntry, const FLMBYTE ** ppucDataRV, // Optional FLMUINT * puiOADataLengthRV, // Optional FLMBOOL * pbDOBlockRV) // Optional { const FLMBYTE * pucTmp; FLMUINT uiDataLength; FLMUINT uiKeyLength; pucTmp = &pucEntry[ 1]; // skip past the flags if( bteKeyLenFlag( pucEntry)) { uiKeyLength = FB2UW( pucTmp); pucTmp += 2; } else { uiKeyLength = *pucTmp; pucTmp += 1; } if( bteDataLenFlag(pucEntry)) { uiDataLength = FB2UW( pucTmp); pucTmp += 2; } else { uiDataLength = *pucTmp; pucTmp += 1; } // Check for the presence of the OverallDataLength field (4 bytes). if( bteOADataLenFlag(pucEntry)) { if( puiOADataLengthRV) { *puiOADataLengthRV = FB2UD( pucTmp); } pucTmp += 4; } else if (puiOADataLengthRV) { *puiOADataLengthRV = uiDataLength; } // Are we to return a pointer to the data? if( ppucDataRV) { // Advance to the Data since we are currently pointing to the Key. *ppucDataRV = (FLMBYTE *)(pucTmp + uiKeyLength); } if( pbDOBlockRV) { *pbDOBlockRV = bteDataBlockFlag( pucEntry); } return( uiDataLength); } /*************************************************************************** Desc: Method to extract the data value from a given block. This method expects to receive a buffer to copy the data into. This method does not read data across blocks. The puiLenDataRV is an optional parameter that will hold the actual data size returned. ****************************************************************************/ FSTATIC RCODE btGetEntryData( FLMBYTE * pucEntry, // Pointer to the entry containing the data FLMBYTE * pucBufferRV, FLMUINT uiBufferSize, FLMUINT * puiLenDataRV) { RCODE rc = NE_XFLM_OK; FLMUINT uiDataLength; const FLMBYTE * pucData; // Get the data length uiDataLength = btGetEntryDataLength( pucEntry, &pucData, NULL, NULL); if( uiDataLength > uiBufferSize) { rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } #ifdef FLM_DEBUG f_memset( pucBufferRV, 0, uiBufferSize); #endif f_memcpy( pucBufferRV, pucData, uiDataLength); // Do we need to return the data length? if( puiLenDataRV) { *puiLenDataRV = uiDataLength; } Exit: return( rc); } /*************************************************************************** Desc: This method will return the overall size of the entry at uiOffset in pBlk. The size returned includes a two byte allowance for the offset entry used by this entry. ****************************************************************************/ FLMUINT F_Btree::getEntrySize( FLMBYTE * pBlk, FLMUINT uiOffset, FLMBYTE ** ppucEntry) { FLMBYTE * pucEntry; FLMUINT uiEntrySize; // Point to the entry ... pucEntry = BtEntry( pBlk, uiOffset); if( ppucEntry) { *ppucEntry = pucEntry; } // Different block types have different entry formats. switch( getBlkType( pBlk)) { case BT_LEAF: { uiEntrySize = 4 + FB2UW( pucEntry); break; } case BT_LEAF_DATA: { FLMBYTE * pucTmp = &pucEntry[ 1]; // Stuff we know uiEntrySize = 3; // Get the key length if( *pucEntry & BTE_FLAG_KEY_LEN) { uiEntrySize += FB2UW( pucTmp) + 2; pucTmp += 2; } else { uiEntrySize += (*pucTmp + 1); pucTmp++; } // Get the data length if( *pucEntry & BTE_FLAG_DATA_LEN) { // 2 byte data length field uiEntrySize += (FB2UW( pucTmp) + 2); } else { // 1 byte data length field uiEntrySize += (FLMUINT)*pucTmp + 1; } // Get the Overall Data length (if present) if( *pucEntry & BTE_FLAG_OA_DATA_LEN) { uiEntrySize += 4; } break; } case BT_NON_LEAF: { uiEntrySize = 8 + FB2UW( &pucEntry[ BTE_NL_KEY_LEN]); break; } case BT_NON_LEAF_COUNTS: { uiEntrySize = 12 + FB2UW( &pucEntry[ BTE_NLC_KEY_LEN]); break; } default: { flmAssert( 0); uiEntrySize = 0; break; } } return( uiEntrySize); } /*************************************************************************** Desc: Method to search the BTree for a specific entry. Upon a successful return from this method, the local stack will be setup and pointing to either the desired entry, or if the entry does not exist, it will be pointing to the entry that would be immediately after the desired entry. This method therefore can be used both for reads and updates where we want to insert a new entry into the BTree. ****************************************************************************/ RCODE F_Btree::findEntry( const FLMBYTE * pucKey, // In FLMUINT uiKeyLen, // In FLMUINT uiMatch, // In FLMUINT * puiPosition, // Out FLMUINT32 * pui32BlkAddr, // In/Out FLMUINT * puiOffsetIndex) // In/Out { RCODE rc = NE_XFLM_OK; F_BTSK * pStack = NULL; FLMUINT32 ui32BlkAddress; F_CachedBlock * pSCache = NULL; FLMBYTE * pucEntry; FLMUINT uiPrevCounts = 0; FLMUINT uiLevel; // Make sure the stack is clean before we start. btRelease(); // No input key is needed to get the first or last key. if( uiMatch == XFLM_FIRST || uiMatch == XFLM_LAST) { uiKeyLen = 0; } if( uiKeyLen > XFLM_MAX_KEY_SIZE) { rc = RC_SET( NE_XFLM_BTREE_KEY_SIZE); goto Exit; } // Have we been passed a block address to look in? if( pui32BlkAddr && *pui32BlkAddr) { if( RC_OK( rc = findInBlock( pucKey, uiKeyLen, uiMatch, puiPosition, pui32BlkAddr, puiOffsetIndex))) { goto Exit; } } // Beginning at the root node, we will scan until we find the first key // that is greater than or equal to our target key. If we don't find any // key that is larger than our target key, we will use the last block found. ui32BlkAddress = (FLMUINT32)m_pLFile->uiRootBlk; for( ;;) { // Get the block - Note that this will place a use on the block. // It must be properly released when done. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddress, NULL, &pSCache))) { goto Exit; } // We are building the stack inverted to make traversing it a bit easier. uiLevel = ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BlkLevel; pStack = &m_Stack[ uiLevel]; m_uiStackLevels++; pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; pStack->ui32BlkAddr = ui32BlkAddress; pStack->pSCache = pSCache; pSCache = NULL; pStack->uiLevel = uiLevel; pStack->uiKeyLen = uiKeyLen; pStack->pucKeyBuf = pucKey; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pStack->pBlkHdr, 0); if( isRootBlk( pStack->pBlkHdr)) { m_uiRootLevel = uiLevel; } // Search the block for the key. When we return from this method // the pStack will be pointing to the last entry we looked at. if( RC_BAD( rc = scanBlock( pStack, uiMatch))) { // It is okay if we couldn't find the key. Especially if // we are still in the upper levels of the B-tree. if( (rc != NE_XFLM_NOT_FOUND) && (rc != NE_XFLM_EOF_HIT)) { goto Exit; } } // Are we at the end of our search? if( (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) || (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF) || (m_uiStackLevels - 1 >= m_uiSearchLevel)) { if( m_bCounts && puiPosition) { flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); *puiPosition = uiPrevCounts + pStack->uiCurOffset; } // If this is a search for the last entry, then we should adjust the // uiCurOffset so that it points to a valid entry. if( uiMatch == XFLM_LAST) { m_pStack = pStack; for (;;) { if( RC_BAD( rc = moveStackToPrev( NULL))) { goto Exit; } // If we are on the leaf level, we need to make sure we are // looking at a first occurrence of an entry. if( pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) { pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); if( bteFirstElementFlag( pucEntry)) { break; } } else { break; } } } break; } else { if( m_bCounts && puiPosition) { uiPrevCounts += countRangeOfKeys( pStack, 0, pStack->uiCurOffset); } // Get the Child Block Address pucEntry = BtEntry( (FLMBYTE *)pStack->pSCache->m_pBlkHdr, pStack->uiCurOffset); ui32BlkAddress = bteGetBlkAddr( pucEntry); } } // Return the block and offset if needed. if( pui32BlkAddr) { *pui32BlkAddr = pStack->ui32BlkAddr; } if( puiOffsetIndex) { *puiOffsetIndex = pStack->uiCurOffset; } m_bStackSetup = TRUE; Exit: if( RC_OK( rc) || (rc == NE_XFLM_NOT_FOUND) || (rc == NE_XFLM_EOF_HIT)) { if( pStack) { m_pStack = pStack; } } if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Private method to search for a particular key in a pre-designted block offset. If we don't find it at the given offset, we will do a binary search for it. Note that a uiMatch of XFLM_FIRST & XFLM_LAST will be ignored if we locate the entry by the puiOffsetIndex parameter. Also, this method does not setup the full stack. Only the level where the block address passed in resides. ****************************************************************************/ RCODE F_Btree::findInBlock( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiMatch, FLMUINT * puiPosition, FLMUINT32 * pui32BlkAddr, FLMUINT * puiOffsetIndex) { RCODE rc = NE_XFLM_OK; F_BTSK * pStack; F_CachedBlock * pSCache = NULL; FLMBYTE * pucEntry; const FLMBYTE * pucBlkKey; FLMUINT uiBlkKeyLen; // Get the block - Note that this will place a use on the block. // It must be properly released when done. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, *pui32BlkAddr, NULL, &pSCache))) { goto Exit; } if( !blkIsBTree( pSCache->getBlockPtr())) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } // Verify that the block belongs to the correct collection number if( ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui16LogicalFile != m_pLFile->uiLfNum) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } // Verify that we are looking at the same type of block, // i.e. collection vs index. if( ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BTreeFlags & BLK_IS_INDEX && m_pLFile->eLfType != XFLM_LF_INDEX) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } // If the block is not a leaf block, the caller will // need to do a full search down the B-Tree if( ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BlkLevel != 0) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } pStack = &m_Stack[ 0]; m_uiStackLevels++; pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; pStack->ui32BlkAddr = *pui32BlkAddr; pStack->pSCache = pSCache; pSCache = NULL; pStack->uiLevel = 0; pStack->uiKeyLen = uiKeyLen; pStack->pucKeyBuf = pucKey; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pStack->pBlkHdr, 0); pStack->uiCurOffset = puiOffsetIndex ? *puiOffsetIndex : 0; if( isRootBlk( pStack->pBlkHdr)) { m_uiRootLevel = 0; } // See if the entry we are looking for is at the passed offset if( puiOffsetIndex) { if( *puiOffsetIndex < pStack->pBlkHdr->ui16NumKeys) { pucEntry = BtEntry( (FLMBYTE *)pStack->pBlkHdr, *puiOffsetIndex); uiBlkKeyLen = getEntryKeyLength( pucEntry, getBlkType( (FLMBYTE *)pStack->pBlkHdr), &pucBlkKey); if( uiKeyLen == uiBlkKeyLen) { if( f_memcmp( pucKey, pucBlkKey, uiKeyLen) == 0) { goto GotEntry; } } } } // Search the block for the key. When we return from this method // the pStack will be pointing to the last entry we looked at. if( RC_BAD( rc = scanBlock( pStack, uiMatch))) { goto Exit; } GotEntry: if( m_bCounts && puiPosition) { flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); // VISIT: These counts aren't accurate in this case. *puiPosition = pStack->uiCurOffset; } // Verify that we are looking at an entry with the firstElement flag set. m_pStack = pStack; for (;;) { // If we are on the leaf level, we need to make sure we are // looking at a first occurrence of an entry. if( m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) { pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); if( bteFirstElementFlag( pucEntry)) { break; } } else { break; } if( RC_BAD( rc = moveStackToPrev( NULL))) { goto Exit; } } *pui32BlkAddr = m_pStack->ui32BlkAddr; if( puiOffsetIndex) { *puiOffsetIndex = m_pStack->uiCurOffset; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } if( RC_BAD( rc)) { btRelease(); } return( rc); } /*************************************************************************** Desc: Method to search through a BTree block to find a specific key. If that key cannot be found, then the pStack will be positioned right after the last entry in the block. The search is a binary search that is looking for the first key that is >= the target key. The uiMatch parameter further qualifies the search. The XFLM_FIRST & XFLM_LAST values will ignore the key altogether and just return the first or last key respectively. The XFLM_INCL value will return the key if found or the first key following if not found. The XFLM_EXACT will return an NE_XFLM_NOT_FOUND if the key cannot be found. XFLM_EXCL will return the first key following the target key. ****************************************************************************/ RCODE F_Btree::scanBlock( F_BTSK_p pStack, FLMUINT uiMatch) { RCODE rc = NE_XFLM_OK; FLMUINT uiTop; FLMUINT uiMid; FLMUINT uiBottom; FLMINT iResult; F_CachedBlock * pSCache = NULL; const FLMBYTE * pucBlockKey; FLMBYTE * pucEntry; FLMUINT uiBlockKeyLen; if( pStack->pBlkHdr->ui16NumKeys == 0) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } uiTop = 0; uiBottom = (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1); if( uiMatch == XFLM_FIRST) { pStack->uiCurOffset = uiTop; goto Exit; } if( uiMatch == XFLM_LAST || pStack->uiKeyLen == 0) { pStack->uiCurOffset = uiBottom; goto Exit; } flmAssert( uiMatch == XFLM_INCL || uiMatch == XFLM_EXCL || uiMatch == XFLM_EXACT); // Test the first entry pucEntry = (FLMBYTE *)pStack->pBlkHdr + bteGetEntryOffset( pStack->pui16OffsetArray, uiTop); uiBlockKeyLen = getEntryKeyLength( pucEntry, ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, &pucBlockKey); // Compare the entries ... if( !uiBlockKeyLen) { // The LEM entry will always sort last!! iResult = 1; goto ResultGreater1; } else { if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) { goto Exit; } } if( iResult >= 0) { ResultGreater1: if( iResult && uiMatch == XFLM_EXACT) { rc = RC_SET( NE_XFLM_NOT_FOUND); } uiMid = uiTop; goto VerifyPosition; } // If there is more than one entry in the block, we can skip the first // one since we have already seen it. if( uiTop < uiBottom) { uiTop++; } // Test the last pucEntry = (FLMBYTE *)pStack->pBlkHdr + bteGetEntryOffset( pStack->pui16OffsetArray, uiBottom); uiBlockKeyLen = getEntryKeyLength( pucEntry, ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, &pucBlockKey); if( !uiBlockKeyLen) { // The LEM entry will always sort last!! iResult = 1; goto ResultGreater2; } else { if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) { goto Exit; } } if( iResult <= 0) { if( iResult < 0 && uiMatch != XFLM_INCL) { rc = RC_SET( NE_XFLM_NOT_FOUND); } uiMid = uiBottom; goto VerifyPosition; } ResultGreater2: for( ;;) { if( uiTop == uiBottom) { // We're done - didn't find it. if( uiMatch == XFLM_EXACT) { rc = RC_SET( NE_XFLM_NOT_FOUND); } uiMid = uiTop; break; } // Get the midpoint uiMid = (uiTop + uiBottom) / 2; pucEntry = (FLMBYTE *)pStack->pBlkHdr + bteGetEntryOffset( pStack->pui16OffsetArray, uiMid); uiBlockKeyLen = getEntryKeyLength( pucEntry, ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, &pucBlockKey); // Compare the entries if( !uiBlockKeyLen) { // The LEM entry will always sort last!! iResult = 1; goto ResultGreater; } else { if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) { goto Exit; } } if( iResult > 0) { ResultGreater: // Midpoint (block key) is > Target key uiBottom = uiMid; continue; } if( iResult < 0) { // Midpoint (block key) is < Target key // Since we want to find the first key that is >= to the target key, // and we have aleady visited the key at uiMid and know that it is < // our target key, we can skip it and advance to the key that is one // beyond it. flmAssert( uiMid < uiBottom); uiTop = uiMid + 1; continue; } break; } VerifyPosition: if( uiMatch != XFLM_EXCL) { // Verify that we are looking at the first occurrence of this key. while( iResult == 0) { if( uiMid > 0) { pucEntry = (FLMBYTE *)pStack->pBlkHdr + bteGetEntryOffset( pStack->pui16OffsetArray, (uiMid - 1)); uiBlockKeyLen = getEntryKeyLength( pucEntry, ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, &pucBlockKey); if( !uiBlockKeyLen) { // The LEM entry will always sort last!! iResult = 1; } else { if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) { goto Exit; } if( iResult == 0) { uiMid--; } } } else { break; } } pStack->uiCurOffset = uiMid; } else if( uiMatch == XFLM_EXCL) { // If we are at the leaf level, then we want to see if // this is the last entry in the last block. // If it is, then we cannot satisfy the request, otherwise // we will position to the next key and return ok. if( pStack->pBlkHdr->ui8BlkLevel == 0 && pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == 0 && uiMid == (FLMUINT)pStack->pBlkHdr->ui16NumKeys - 1 && iResult == 0) { rc = RC_SET( NE_XFLM_EOF_HIT); } else if( pStack->pBlkHdr->ui8BlkLevel == 0) { // Check for the next entry at leaf level while( iResult == 0) { // Are we on the last key? if( uiMid == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)) { if( pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == 0) { rc = RC_SET( NE_XFLM_NOT_FOUND); } else { pStack->uiCurOffset = uiMid; m_pStack = pStack; if( RC_BAD( rc = moveStackToNext( NULL))) { goto Exit; } uiMid = 0; } } else { uiMid++; } pucEntry = (FLMBYTE *)pStack->pBlkHdr + bteGetEntryOffset( pStack->pui16OffsetArray, uiMid); uiBlockKeyLen = getEntryKeyLength( pucEntry, ((F_BLK_HDR *)pStack->pBlkHdr)->ui8BlkType, &pucBlockKey); if( !uiBlockKeyLen) { // The LEM entry will always sort last!! iResult = 1; } else { if( RC_BAD( rc = compareBlkKeys( pucBlockKey, uiBlockKeyLen, pStack->pucKeyBuf, pStack->uiKeyLen, &iResult))) { goto Exit; } } } pStack->uiCurOffset = uiMid; if( uiMid == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1) && pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == 0) { rc = RC_SET( NE_XFLM_EOF_HIT); } } else { pStack->uiCurOffset = uiMid; } } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: This method will compare two key fields. Returned values: 0 - Keys are equal 1 - Key in Block is > Target key -1 - Key in Block is < Target key ****************************************************************************/ RCODE F_Btree::compareKeys( const FLMBYTE * pucKey1, FLMUINT uiKeyLen1, const FLMBYTE * pucKey2, FLMUINT uiKeyLen2, FLMINT * piCompare) { RCODE rc = NE_XFLM_OK; if( !m_pCompare) { if( (*piCompare = f_memcmp( pucKey1, pucKey2, f_min( uiKeyLen1, uiKeyLen2))) == 0) { *piCompare = uiKeyLen1 == uiKeyLen2 ? 0 : uiKeyLen1 < uiKeyLen2 ? -1 : 1; } } else { if( RC_BAD( rc = m_pCompare->compare( pucKey1, uiKeyLen1, pucKey2, uiKeyLen2, piCompare))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Method for positioning to a specific entry. ****************************************************************************/ RCODE F_Btree::positionToEntry( FLMUINT uiPosition) { RCODE rc = NE_XFLM_OK; F_BTSK * pStack = NULL; FLMUINT32 ui32BlkAddress; F_CachedBlock * pSCache = NULL; FLMUINT uiLevel; FLMBYTE * pucEntry; FLMUINT uiPrevCounts = 0; // Make sure the stack is clean before we start. btRelease(); // Beginning at the root node. ui32BlkAddress = (FLMUINT32)m_pLFile->uiRootBlk; // Get the block - Note that this will place a use on the block. // It must be properly released when done. while( ui32BlkAddress) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddress, NULL, &pSCache))) { goto Exit; } uiLevel = ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BlkLevel; pStack = &m_Stack[ uiLevel]; pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; pStack->ui32BlkAddr = ui32BlkAddress; pStack->pSCache = pSCache; pSCache = NULL; pStack->uiLevel = uiLevel; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pStack->pBlkHdr, 0); m_uiStackLevels++; if( RC_BAD( rc = searchBlock( pStack->pBlkHdr, &uiPrevCounts, uiPosition, &pStack->uiCurOffset))) { goto Exit; } if( (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA) || (pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF)) { ui32BlkAddress = 0; } else { // Get the next child block address pucEntry = BtEntry( (FLMBYTE *)pStack->pBlkHdr, pStack->uiCurOffset); ui32BlkAddress = bteGetBlkAddr( pucEntry); } } m_uiRootLevel = m_uiStackLevels - 1; Exit: if( RC_OK( rc) || (rc == NE_XFLM_NOT_FOUND) || (rc == NE_XFLM_EOF_HIT)) { m_pStack = pStack; } if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Btree::searchBlock( F_BTREE_BLK_HDR * pBlkHdr, FLMUINT * puiPrevCounts, FLMUINT uiPosition, FLMUINT * puiOffset) { RCODE rc = NE_XFLM_OK; FLMUINT uiOffset; FLMUINT uiNumKeys; FLMUINT uiCounts; FLMBYTE * pucEntry; uiNumKeys = pBlkHdr->ui16NumKeys; if( getBlkType( (FLMBYTE *)pBlkHdr) != BT_NON_LEAF_COUNTS) { flmAssert( uiPosition >= *puiPrevCounts); uiOffset = uiPosition - *puiPrevCounts; *puiPrevCounts = uiPosition; } else { for( uiOffset = 0; uiOffset < uiNumKeys; uiOffset++) { pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiOffset); pucEntry += 4; uiCounts = FB2UD( pucEntry); if( *puiPrevCounts + uiCounts >= (uiPosition + 1)) { break; } else { *puiPrevCounts += uiCounts; } } } if( uiOffset >= uiNumKeys) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); } *puiOffset = uiOffset; return( rc); } /*************************************************************************** Desc: Method to move all the data in the block into a contiguous space. ****************************************************************************/ RCODE F_Btree::defragmentBlock( F_CachedBlock ** ppSCache) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumKeys; FLMBOOL bSorted; FLMBYTE * pucCurEntry; FLMBYTE * pucPrevEntry; FLMBYTE * pucTempEntry; FLMUINT uiTempToMove; FLMUINT uiIndex; FLMUINT uiAmtToMove; FLMUINT uiFirstHole; FLMUINT16 ui16BlkBytesAvail; FLMUINT16 * pui16OffsetArray; F_CachedBlock * pSCache = *ppSCache; F_BTREE_BLK_HDR * pBlk = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; F_BTREE_BLK_HDR * pOldBlk = NULL; FLMBYTE * pucHeap; FLMBYTE * pucBlkEnd; F_CachedBlock * pOldSCache = NULL; flmAssert( pBlk->stdBlkHdr.ui16BlkBytesAvail != pBlk->ui16HeapSize); if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache, &pOldSCache))) { goto Exit; } pBlk = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; *ppSCache = pSCache; uiNumKeys = getBlkEntryCount( (FLMBYTE *)pBlk); // Determine if the entries are sorted pucPrevEntry = (FLMBYTE *)pBlk + m_uiBlockSize; bSorted = TRUE; uiFirstHole = 0; pucHeap = (FLMBYTE *)pBlk + m_uiBlockSize; for( uiIndex = 0; uiIndex < uiNumKeys; uiIndex++) { pucCurEntry = BtEntry( (FLMBYTE *)pBlk, uiIndex); if( pucPrevEntry < pucCurEntry) { bSorted = FALSE; break; } else { uiAmtToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pBlk, uiIndex)); pucHeap -= uiAmtToMove; if( !uiFirstHole && pucHeap != pucCurEntry) { uiFirstHole = uiIndex + 1; } } pucPrevEntry = pucCurEntry; } ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - sizeofBTreeBlkHdr( pBlk)) - (FLMUINT16)(uiNumKeys * 2); pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlk, 0); pucBlkEnd = (FLMBYTE *)pBlk + m_uiBlockSize; if( uiFirstHole > 1) { uiFirstHole--; pucHeap = BtEntry( (FLMBYTE *)pBlk, uiFirstHole - 1); ui16BlkBytesAvail -= (FLMUINT16)(pucBlkEnd - pucHeap); } else { uiFirstHole = 0; pucHeap = pucBlkEnd; } if( !bSorted) { FLMUINT16 * pui16OldOffsetArray; // If old and new blocks are the same (because of a // prior call to logBlock), we need to save a copy of the block // before making changes. if( !pOldSCache) { f_memcpy( m_pucTempDefragBlk, pSCache->m_pBlkHdr, m_uiBlockSize); pOldBlk = (F_BTREE_BLK_HDR *)m_pucTempDefragBlk; } else { pOldBlk = (F_BTREE_BLK_HDR *)pOldSCache->m_pBlkHdr; } pui16OldOffsetArray = BtOffsetArray( (FLMBYTE *)pOldBlk, 0); // Rebuild the block so that all of the entries are in order for( uiIndex = uiFirstHole; uiIndex < uiNumKeys; uiIndex++) { pucCurEntry = BtEntry( (FLMBYTE *)pOldBlk, uiIndex); uiAmtToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pOldBlk, uiIndex)); pucHeap -= uiAmtToMove; bteSetEntryOffset( pui16OffsetArray, uiIndex, (FLMUINT16)(pucHeap - (FLMBYTE *)pBlk)); uiIndex++; while( uiIndex < uiNumKeys) { pucTempEntry = BtEntry( (FLMBYTE *)pOldBlk, uiIndex); uiTempToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pOldBlk, uiIndex)); if ((pucCurEntry - uiTempToMove) != pucTempEntry) { uiIndex--; break; } else { pucCurEntry -= uiTempToMove; pucHeap -= uiTempToMove; uiAmtToMove += uiTempToMove; bteSetEntryOffset( pui16OffsetArray, uiIndex, (FLMUINT16)(pucHeap - (FLMBYTE *)pBlk)); uiIndex++; } } f_memcpy( pucHeap, pucCurEntry, uiAmtToMove); ui16BlkBytesAvail -= (FLMUINT16)uiAmtToMove; } } else { // Work back from the first hole. Move entries to fill all of the // holes in the block. for( uiIndex = uiFirstHole; uiIndex < uiNumKeys; uiIndex++) { pucCurEntry = BtEntry( (FLMBYTE *)pBlk, uiIndex); uiAmtToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pBlk, uiIndex)); pucHeap -= uiAmtToMove; if( pucHeap != pucCurEntry) { // We have a hole. We don't want to move just one entry // if we can avoid it. We would like to continue searching // until we find either the end, or another hole. Then we // can move a larger block of data instead of one entry. bteSetEntryOffset( pui16OffsetArray, uiIndex, (FLMUINT16)(pucHeap - (FLMBYTE *)pBlk)); uiIndex++; while( uiIndex < uiNumKeys) { pucTempEntry = BtEntry( (FLMBYTE *)pBlk, uiIndex); uiTempToMove = actualEntrySize( getEntrySize( (FLMBYTE *)pBlk, uiIndex)); if( (pucCurEntry - uiTempToMove) != pucTempEntry) { uiIndex--; break; } else { pucCurEntry -= uiTempToMove; pucHeap -= uiTempToMove; uiAmtToMove += uiTempToMove; bteSetEntryOffset( pui16OffsetArray, uiIndex, (FLMUINT16)(pucHeap - (FLMBYTE *)pBlk)); uiIndex++; } } } // Now move the range we have determined. f_memmove( pucHeap, pucCurEntry, uiAmtToMove); ui16BlkBytesAvail -= (FLMUINT16)(uiAmtToMove); } } // Set the available space. If there are no keys in this block, we should // set the it to the calculated available space if( !uiNumKeys) { pBlk->stdBlkHdr.ui16BlkBytesAvail = ui16BlkBytesAvail; } flmAssert( pBlk->stdBlkHdr.ui16BlkBytesAvail == ui16BlkBytesAvail); pBlk->ui16HeapSize = ui16BlkBytesAvail; // Clean up the heap space. #ifdef FLM_DEBUG f_memset( getBlockEnd( pBlk) - ui16BlkBytesAvail, 0, ui16BlkBytesAvail); #endif Exit: if( pOldSCache) { ScaReleaseCache( pOldSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to handle the insertion, deletion and replacment of a single entry in a block. Assumption: The find method has already been called to locate the insertion point, so the stack has already been setup. ****************************************************************************/ RCODE F_Btree::updateEntry( const FLMBYTE * pucKey, // In FLMUINT uiKeyLen, // In const FLMBYTE * pucValue, // In FLMUINT uiLen, // In F_ELM_UPD_ACTION eAction, FLMBOOL bTruncate) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucRemainingValue = NULL; FLMUINT uiRemainingLen = 0; const FLMBYTE * pucSavKey = pucKey; FLMUINT uiSavKeyLen = uiKeyLen; FLMUINT uiChildBlkAddr = 0; FLMUINT uiCounts = 0; FLMUINT uiFlags = BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT; FLMBOOL bMoreToRemove = FALSE; FLMBOOL bDone = FALSE; FLMUINT uiOrigDataLen = uiLen; FLMBOOL bOrigTruncate = bTruncate; flmAssert( m_pReplaceInfo == NULL); // For each level that needs modifying... while( !bDone) { switch( eAction) { case ELM_INSERT_DO: { // In this case, the uiLen parameter represents the OADataLength. uiFlags = BTE_FLAG_DATA_BLOCK | BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT | BTE_FLAG_OA_DATA_LEN; if( RC_BAD( rc = insertEntry( &pucKey, &uiKeyLen, pucValue, uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, &uiRemainingLen, &eAction))) { goto Exit; } // Not needed for upper levels of the Btree. pucValue = NULL; uiLen = 0; break; } case ELM_INSERT: { // This function will return all info needed to handle the next // level up in the Btree (if anything), including setting up // the stack. pucKey & uiKeyLen will be pointing to the key that // the upper level needs to insert, replace or delete. // // It will be pointing to an entry in a lower level block, so that // block must not be released until after we are all done. if( RC_BAD( rc = insertEntry( &pucKey, &uiKeyLen, pucValue, uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, &uiRemainingLen, &eAction))) { goto Exit; } // Not needed for upper levels of the Btree. pucValue = NULL; uiLen = 0; break; } case ELM_REPLACE_DO: { // In this case, the uiLen parameter represents the OADataLength. uiFlags = BTE_FLAG_DATA_BLOCK | BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT | BTE_FLAG_OA_DATA_LEN; // Should only get here if we are able to truncate the data. flmAssert( bTruncate); if( RC_BAD( rc = replaceEntry( &pucKey, &uiKeyLen, pucValue, uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, &uiRemainingLen, &eAction))) { goto Exit; } // Not needed for upper levels of the Btree. pucValue = NULL; uiLen = 0; bTruncate = TRUE; break; } case ELM_REPLACE: { if( RC_BAD( rc = replaceEntry( &pucKey, &uiKeyLen, pucValue, uiLen, uiFlags, &uiChildBlkAddr, &uiCounts, &pucRemainingValue, &uiRemainingLen, &eAction, bTruncate))) { goto Exit; } // Not needed for upper levels of the Btree. pucValue = NULL; uiLen = 0; bTruncate = TRUE; break; } case ELM_REMOVE: { if (RC_BAD( rc = removeEntry( &pucKey, &uiKeyLen, &uiChildBlkAddr, &uiCounts, &bMoreToRemove, &eAction))) { goto Exit; } // Not needed for upper levels of the B-Tree. pucValue = NULL; uiLen = 0; break; } case ELM_DONE: { if( m_pReplaceInfo) { // This info structure gets generated when the replaced entry in // the upper levels is the last entry in the block and we had to // move entries to a previous block to accommodate it. // We will therefore need to update the parent block with this // new information. We need to take care of this before we check // for any additional data to store. if( RC_BAD( rc = restoreReplaceInfo( &pucKey, &uiKeyLen, &uiChildBlkAddr, &uiCounts))) { goto Exit; } bTruncate = bOrigTruncate; eAction = ELM_REPLACE; } else if( bMoreToRemove) { eAction = ELM_REMOVE; // We need to locate where we should remove the entry. if( RC_BAD( rc = findEntry( pucSavKey, uiSavKeyLen, XFLM_EXACT))) { goto Exit; } } else if( pucRemainingValue && uiRemainingLen) { eAction = ELM_INSERT; // We need to locate where we should insert the new entry. rc = findEntry( pucSavKey, uiSavKeyLen, XFLM_EXCL); // We could find this entry. If we get back anything other than // an NE_XFLM_EOF_HIT or NE_XFLM_OK, then there is a problem. if( rc != NE_XFLM_OK && rc != NE_XFLM_EOF_HIT && rc != NE_XFLM_NOT_FOUND) { goto Exit; } pucValue = pucRemainingValue; uiLen = uiRemainingLen; pucKey = pucSavKey; uiKeyLen = uiSavKeyLen; // Make certain that the BTE_FIRST_ELEMENT flag is NOT set if // the first part of the data was stored. if( uiOrigDataLen != uiLen) { uiFlags = BTE_FLAG_LAST_ELEMENT; } else { uiFlags = BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT; } } else { bDone = TRUE; } break; } // Should never get this! case ELM_BLK_MERGE: { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } } Exit: return( rc); } /*************************************************************************** Desc: This method will coordinate inserting an entry into a block. If it cannot fit it all in, then it may have to break the entry up so that it spans more than one block. It will also setup for the next level before returning. ****************************************************************************/ RCODE F_Btree::insertEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucDataValue = pucValue; FLMUINT uiDataLen = uiLen; FLMUINT uiOADataLen = 0; FLMUINT uiEntrySize = 0; FLMBOOL bEntriesWereMoved = FALSE; FLMBOOL bHaveRoom; FLMBOOL bLastEntry; const FLMBYTE * pucKey = *ppucKey; FLMUINT uiKeyLen = *puiKeyLen; FLMUINT uiChildBlkAddr = *puiChildBlkAddr; FLMUINT uiCounts = *puiCounts; F_CachedBlock * pPrevSCache = NULL; FLMBYTE * pucEntry; FLMBOOL bDefragBlk = FALSE; FLMBOOL bBlockSplit; if( m_pStack->uiLevel == 0) { // We are only safe to do this when we are working on level 0 // (leaf level) of the Btree. *ppucRemainingValue = NULL; *puiRemainingLen = 0; } if( *peAction == ELM_INSERT_DO) { // Adjust the data entry sizes as the data passed in is the // OA Data Length. uiOADataLen = uiLen; uiDataLen = 4; } // Process until we are done StartOver: if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, uiDataLen, &uiEntrySize, &bHaveRoom, &bDefragBlk))) { goto Exit; } // Does the entry fit into the block? if( bHaveRoom) { if( bDefragBlk) { // We will have to defragment the block before we can store the data if( RC_BAD( rc = defragmentBlock( &m_pStack->pSCache))) { goto Exit; } } if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucDataValue, uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } if( (bLastEntry || m_bCounts) && !isRootBlk( m_pStack->pBlkHdr)) { // Are we in here because of the counts only? If so, then we // can update the counts right here, no need to continue. if( !bLastEntry) { if( RC_BAD( rc = updateCounts())) { goto Exit; } *peAction = ELM_DONE; } else { // Ensure we are updating with the correct key. pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); *puiKeyLen = getEntryKeyLength( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); *puiChildBlkAddr = m_pStack->ui32BlkAddr; // Do we need counts for the next level? if( m_bCounts) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } m_pStack++; *peAction = ELM_REPLACE; } } else { *peAction = ELM_DONE; } goto Exit; } // Can we move entries around at all to make some room? if( RC_BAD( rc = moveEntriesToPrevBlk( uiEntrySize, &pPrevSCache, &bEntriesWereMoved))) { goto Exit; } if( bEntriesWereMoved) { // Only defragment the block if the heap size is not big enough. if( uiEntrySize > m_pStack->pBlkHdr->ui16HeapSize) { if( RC_BAD( rc = defragmentBlock( &m_pStack->pSCache))) { goto Exit; } } // Store the entry now because we know there is enough room if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucDataValue, uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } // Ordinarily, this would NEVER be the last element in the // block because we need to adjust the stack to take care of the // elements we just moved! There is only one condition where we would // insert as the last entry in the block, and that is when this // insert is actually a part of a replace operation where the data // is too large to fit in the block. We had to remove the entry, then // insert the new one and we are in the upper levels of the // btree. (i.e. not at the leaf). // // VISIT: Should I assert that we are not at the leaf level // if we get in here? if( bLastEntry) { // Since we just added an entry to the last position of the // current block. We will need to preserve the current stack so // that we can finish updating the parentage later. Should only // happen as a result of a replace operation where the new entry // is larger than the existing one while in the upper levels. if( RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) { goto Exit; } } // Need to update the counts of the parents if we are maintining // counts before we abandon if( m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } // This method will release any blocks no longer referenced // in the stack. Then pull in the previous block information into // the stack. if( RC_BAD( rc = moveStackToPrev( pPrevSCache))) { goto Exit; } // If we are maintaining counts, then lets return a count of the // current number of keys referenced below this point. if( m_bCounts) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } flmAssert( !isRootBlk( m_pStack->pBlkHdr)); // Return the key to the last entry in the prevous block. // Recall that we have changed that stack now so that it // is referencing the changed block (pPrevSCache). pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); *puiKeyLen = getEntryKeyLength( pucEntry, pPrevSCache->m_pBlkHdr->ui8BlkType, ppucKey); // Return the new child block address *puiChildBlkAddr = m_pStack->ui32BlkAddr; // Set up to fixup the parentage of the previous block on return... m_pStack++; // Return the new action for the parent block. *peAction = ELM_REPLACE; goto Exit; } // Try moving to the next block... if( RC_BAD( rc = moveEntriesToNextBlk( uiEntrySize, &bEntriesWereMoved))) { goto Exit; } if( bEntriesWereMoved) { // Only defragment the block if the heap size is not big enough. if( uiEntrySize > m_pStack->pBlkHdr->ui16HeapSize) { if( RC_BAD( rc = defragmentBlock( &m_pStack->pSCache))) { goto Exit; } } // Store the entry now because we know there is enough room if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucDataValue, uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } // Return the key to the last entry in the current block. // Note: If bLastEntry is TRUE, we already know what the key is. if( !bLastEntry) { // Get the last key from the block. pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); *puiKeyLen = getEntryKeyLength( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); } flmAssert( !isRootBlk( m_pStack->pBlkHdr)); // if we are maintaining counts, then lets return a count of the // current number of keys referenced below this point. if( m_bCounts) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } // Return the new child block address *puiChildBlkAddr = m_pStack->ui32BlkAddr; // Set up to fixup the parentage of the this block on return... m_pStack++; *peAction = ELM_REPLACE; goto Exit; } // Before we incur the expense of a block split, see if we can store this // entry in the previous block. If we can, we will save some space. This // will only happen if we are trying to insert at the first position in // this block. We would only ever get into this block of code once for // each level of the btree. if( m_pStack->uiCurOffset == 0 && m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain, NULL, &pPrevSCache))) { goto Exit; } if( RC_BAD( rc = moveStackToPrev( pPrevSCache))) { goto Exit; } // Increment so we point to one past the last entry. m_pStack->uiCurOffset++; goto StartOver; } // We will have to split the block to make room for this entry. if( RC_BAD( rc = splitBlock( *ppucKey, *puiKeyLen, pucDataValue, uiDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, ppucRemainingValue, puiRemainingLen, &bBlockSplit))) { goto Exit; } // Return the new key value. pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); *puiKeyLen = getEntryKeyLength( pucEntry, ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType, ppucKey); // Return the child block address and the counts (if needed). *puiChildBlkAddr = m_pStack->ui32BlkAddr; // Return the counts if we are maintaining them if( m_bCounts) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } // The bBlockSplit boolean will only be FALSE if we were involved in a // ReplaceByInsert operation and the call to split resulted in an empty // block. Thus we were able to store the new entry. In such cases, // only the count (if any) need to be updated, not the keys. if( bBlockSplit) { *peAction = ELM_INSERT; m_pStack++; } else { *peAction = ELM_DONE; } Exit: return( rc); } /*************************************************************************** Desc: Method to handle the insertion of a single entry into a block. Assumption: The find method has already been called to locate the insertion point, so the stack has already been setup. ****************************************************************************/ RCODE F_Btree::storeEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiOADataLen, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, FLMUINT uiEntrySize, FLMBOOL * pbLastEntry) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkType = m_pStack->pSCache->m_pBlkHdr->ui8BlkType; FLMBYTE * pucInsertAt; FLMUINT16 * pui16OffsetArray; FLMUINT uiNumKeys; FLMUINT uiTmp; F_BTREE_BLK_HDR * pBlk; // Assume this is not the last entry for now. // We will change it later if needed. *pbLastEntry = FALSE; // We can go ahead and insert this entry as it is. All checking has been // made before getting to this point. uiEntrySize = calcEntrySize( uiBlkType, uiFlags, uiKeyLen, uiLen, uiOADataLen); // Log this block before making any changes to it. Since the // pSCache could change, we must update the block header after the call. if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } pBlk = m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlk, 0); uiNumKeys = getBlkEntryCount( (FLMBYTE *)pBlk); pucInsertAt = getBlockEnd( pBlk) - uiEntrySize; pui16OffsetArray = m_pStack->pui16OffsetArray; if( RC_BAD( rc = buildAndStoreEntry( uiBlkType, uiFlags, pucKey, uiKeyLen, pucValue, uiLen, uiOADataLen, uiChildBlkAddr, uiCounts, pucInsertAt, uiEntrySize, NULL))) { goto Exit; } // Now to update the offset in the offset array. This will move all // entries that sort after the new entry down by one position. for( uiTmp = uiNumKeys; uiTmp > m_pStack->uiCurOffset; uiTmp--) { bteSetEntryOffset( pui16OffsetArray, uiTmp, bteGetEntryOffset( pui16OffsetArray, uiTmp - 1)); } bteSetEntryOffset( pui16OffsetArray, m_pStack->uiCurOffset, (FLMUINT16)(pucInsertAt - (FLMBYTE *)pBlk)); // Update the available space and the number of keys. // Account for the new offset entry too. m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail -= (FLMUINT16)(uiEntrySize + 2); m_pStack->pBlkHdr->ui16HeapSize -= (FLMUINT16)(uiEntrySize + 2); m_pStack->pBlkHdr->ui16NumKeys++; // Check to see if this was the last entry if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) { *pbLastEntry = TRUE; } if( !m_pStack->uiLevel && (uiFlags & BTE_FLAG_FIRST_ELEMENT)) { m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; m_uiCurOffset = m_pStack->uiCurOffset; } Exit: return( rc); } /*************************************************************************** Desc: This method will coordinate removing an entry from a block. If the entry spans more than one block, it will set the flag pbMoreToRemove. It will also setup for the next level before returning. ****************************************************************************/ RCODE F_Btree::removeEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, FLMBOOL * pbMoreToRemove, F_ELM_UPD_ACTION * peAction) { RCODE rc = NE_XFLM_OK; FLMBOOL bLastEntry = FALSE; FLMBYTE * pucEntry; FLMBOOL bMergedWithPrev = FALSE; FLMBOOL bMergedWithNext = FALSE; if( m_pStack->uiLevel == 0) { // We are only safe to do this when we are working on level 0 // (leaf level) of the Btree. *pbMoreToRemove = FALSE; } // Check the current entry to see if it spans more than a single block. pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); // We only need to worry about data spanning more than one block if it is // at level zero (i.e. leaf block) and the lastElement flag is not set. if( (m_pStack->uiLevel == 0) && m_bData && !bteLastElementFlag( pucEntry)) { *pbMoreToRemove = TRUE; } // Find out if we are looking at the last entry in the block. if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) { bLastEntry = TRUE; } // Now we remove the entry... Will also remove any chained Data Only blocks if( RC_BAD( rc = remove( TRUE))) { goto Exit; } // If the block is now empty, we will free the block. if( !m_pStack->pBlkHdr->ui16NumKeys) { FLMBOOL bIsRoot; // Test for root block. bIsRoot = isRootBlk( m_pStack->pBlkHdr); if( RC_BAD( rc = deleteEmptyBlock())) { goto Exit; } // Need to remove the parent entry referencing the deleted block. if( !bIsRoot) { *peAction = ELM_REMOVE; m_pStack++; } else { // If we ever get here, it means we have just deleted the root block. // I have put in the possibility, but typically, deleting the Btree // is done by calling btDeleteTree. *peAction = ELM_DONE; } } else { if( ((((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail * 100) / m_uiBlockSize) >= BT_LOW_WATER_MARK) { // We will need to check to see if we can merge two blocks into one to // conserve space. if( RC_BAD( rc = mergeBlocks( bLastEntry, &bMergedWithPrev, &bMergedWithNext, peAction))) { goto Exit; } } // If the entry that we just removed was the last entry in the block and // we did not merge any blocks, we will need to prep for an update to the // parent with a new key. if( bLastEntry && !bMergedWithPrev && !bMergedWithNext) { if( m_bCounts) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } // Backup to the new "last" entry (remove() does not adjust the offset // in the stack). flmAssert( m_pStack->uiCurOffset > 0); m_pStack->uiCurOffset--; pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); *puiKeyLen = getEntryKeyLength( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); *puiChildBlkAddr = m_pStack->ui32BlkAddr; *peAction = ELM_REPLACE; m_pStack++; } else { // Are we tracking counts? if( !bMergedWithPrev && !bMergedWithNext) { if( m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } *peAction = ELM_DONE; } } } Exit: return( rc); } /*************************************************************************** Desc: Method to replace an existing entry with a new one. ****************************************************************************/ RCODE F_Btree::replaceEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction, FLMBOOL bTruncate) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucDataValue = pucValue; FLMUINT uiDataLen = uiLen; FLMUINT uiOADataLen = 0; FLMBYTE * pucEntry = NULL; FLMUINT32 ui32OrigDOAddr = 0; const FLMBYTE * pucData = NULL; if( m_pStack->uiLevel == 0) { *ppucRemainingValue = NULL; *puiRemainingLen = 0; } if( *peAction == ELM_REPLACE_DO) { // Adjust the data entry sizes as the data passed in // is the OA Data Length. uiOADataLen = uiLen; uiDataLen = 4; } if( m_pStack->uiLevel == 0 && m_bData) { if( m_bOrigInDOBlocks) { flmAssert( bTruncate); pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); btGetEntryDataLength( pucEntry, &pucData, NULL, NULL); ui32OrigDOAddr = bteGetBlkAddr( pucData); } } // We only have to worry about updating the upper levels of the Btree // when we are doing a replacement at a non-leaf level or we are maintaining // counts. Replacements at the leaf level do not require a change in the // parent block. The only exception is when the old entry spanned to // another block, but the new one did not. This results in removing the // excess part of the old entry unless we are not truncating the element. // Even then, we only update the parent if the excess entry was the only key // in the block, i.e. the block became empty as a result of the removal. // All of this would have been handled already by the time we return from // this call. // When bTruncate is FALSE we do not trim back the entry so we don't worry // about updating the parentage. if( RC_BAD( rc = replaceOldEntry( ppucKey, puiKeyLen, pucDataValue, uiDataLen, uiFlags, uiOADataLen, puiChildBlkAddr, puiCounts, ppucRemainingValue, puiRemainingLen, peAction, bTruncate))) { goto Exit; } // Do we need to free the original DO blocks since they are not // used in the new entry? if( m_bOrigInDOBlocks && !m_bDataOnlyBlock && m_pStack->uiLevel == 0) { if( RC_BAD( rc = removeDOBlocks( ui32OrigDOAddr))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Method to handle replacing a single entry in a block. ASSUMPTION: The find method has already been called to locate the insertion point, so the stack has already been setup. ****************************************************************************/ RCODE F_Btree::replaceOldEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiOADataLen, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction, FLMBOOL bTruncate) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldEntrySize; FLMBYTE * pucEntry = NULL; FLMBYTE * pucData = NULL; FLMUINT uiEntrySize; FLMBOOL bLastEntry = FALSE; FLMBOOL bLastElement = TRUE; FLMBOOL bHaveRoom; FLMBOOL bDefragBlk; FLMUINT uiDataLen = 0; FLMUINT uiOldOADataLen = 0; FLMBOOL bRemoveOADataAllowance = FALSE; uiOldEntrySize = actualEntrySize( getEntrySize( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset, &pucEntry)); if( m_pStack->uiLevel == 0 && m_bData) { bLastElement = bteLastElementFlag( pucEntry); uiDataLen = btGetEntryDataLength( pucEntry, (const FLMBYTE **)&pucData, &uiOldOADataLen, NULL); // Test to see if we need to worry about the bTruncate flag. if( uiDataLen == uiOldOADataLen) { if( uiLen > uiDataLen) { bTruncate = TRUE; } else if( uiLen <= uiDataLen && uiOADataLen == 0) { bRemoveOADataAllowance = TRUE; } } else { if( uiLen > uiOldOADataLen) { bTruncate = TRUE; } } } // bTruncate has no meaning if we have no data or we are not at the // leaf level. if( m_pStack->uiLevel != 0 || !m_bData) { bTruncate = TRUE; } // The calcNewEntrySize function will tack on 2 bytes for the offset. // It also adds an extra 4 bytes for the OADataLen, even though it may // not be needed. We will need to be aware of this here as it may affect // our decision as to how we will replace the entry. if( RC_BAD( rc = calcNewEntrySize( *puiKeyLen, uiLen, &uiEntrySize, &bHaveRoom, &bDefragBlk))) { goto Exit; } if( bRemoveOADataAllowance) { uiEntrySize -= 4; } // Since this is a replace operation, we don't need to know about the offset // as that won't be a factor in what we are doing. 'actualEntrySize' will // remove those two bytyes from the size. uiEntrySize = actualEntrySize( uiEntrySize); if( uiEntrySize <= uiOldEntrySize) { if( !bTruncate) { flmAssert( uiLen <= uiDataLen); f_memcpy( pucData, pucValue, uiLen); if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) { bLastEntry = TRUE; } } else { // We can go ahead and replace this entry as it is. All checking // has been made before getting to this point. if( RC_BAD( rc = buildAndStoreEntry( m_pStack->pSCache->m_pBlkHdr->ui8BlkType, uiFlags, *ppucKey, *puiKeyLen, pucValue, uiLen, uiOADataLen, *puiChildBlkAddr, *puiCounts, m_pucTempBlk, m_uiBlockSize, &uiEntrySize))) { goto Exit; } if( RC_BAD( rc = replace( m_pucTempBlk, uiEntrySize, &bLastEntry))) { goto Exit; } } if( !bLastElement && bTruncate) { // The element that we replaced actually spans more than one entry. // We will have to remove the remaining entries. if( RC_BAD( rc = removeRemainingEntries( *ppucKey, *puiKeyLen))) { goto Exit; } } if( (bLastEntry || m_bCounts) && !isRootBlk( m_pStack->pBlkHdr) && (m_pStack->uiLevel != 0)) { // Are we in here because of the counts only? If so, then make // sure we don't change the key in the parent. if( !bLastEntry) { if( RC_BAD( rc = updateCounts())) { goto Exit; } *peAction = ELM_DONE; } else { // Return the key to the last entry in the block. pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); *puiKeyLen = getEntryKeyLength( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); *puiChildBlkAddr = m_pStack->ui32BlkAddr; // Do we need counts for the next level? if( m_bCounts) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } m_pStack++; *peAction = ELM_REPLACE; } } else { *peAction = ELM_DONE; } goto Exit; } // If we do not have a stack setup yet (which can happen if the replace // is trying to shortcut to the previously known block address and offset), // then at this point, we must build the stack, since it may be required // to adjust the upper levels of the btree. if( !m_bStackSetup) { if( RC_BAD( rc = findEntry( *ppucKey, *puiKeyLen, XFLM_EXACT))) { goto Exit; } } // The new entry will not fit into the original entry's space. // If we remove the entry in the block, will there be enough room // to put it in? if( bTruncate && m_pStack->pSCache->m_pBlkHdr->ui16BlkBytesAvail + uiOldEntrySize >= uiEntrySize) { // First remove the current entry. Do not delete any DO blocks chained // to this entry. if( RC_BAD( rc = remove( FALSE))) { goto Exit; } if( (m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail != m_pStack->pBlkHdr->ui16HeapSize) && ((uiEntrySize + 2) > m_pStack->pBlkHdr->ui16HeapSize)) { if( RC_BAD( rc = defragmentBlock( &m_pStack->pSCache))) { goto Exit; } } // Now insert the new entry. if( RC_BAD( rc = storeEntry( *ppucKey, *puiKeyLen, pucValue, uiLen, uiFlags, uiOADataLen, *puiChildBlkAddr, *puiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } // Check if the original element spanned more than one entry if( !bLastElement) { // The element that we replaced actually spans more than one entry. // We will have to remove the remaining entries. if( RC_BAD( rc = removeRemainingEntries( *ppucKey, *puiKeyLen))) { goto Exit; } } if( (bLastEntry || m_bCounts) && !isRootBlk( m_pStack->pBlkHdr) && (m_pStack->uiLevel != 0)) { // Are we in here because of the counts only? if( !bLastEntry) { if( RC_BAD( rc = updateCounts())) { goto Exit; } *peAction = ELM_DONE; } else { // Set the key to the last entry in the block. pucEntry = BtLastEntry( (FLMBYTE *)m_pStack->pBlkHdr); *puiKeyLen = getEntryKeyLength( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, ppucKey); *puiChildBlkAddr = m_pStack->ui32BlkAddr; // Do we need counts for the next level? if( m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF_COUNTS) { *puiCounts = countKeys( (FLMBYTE *)m_pStack->pBlkHdr); } m_pStack++; *peAction = ELM_REPLACE; } } else { *peAction = ELM_DONE; } goto Exit; } // If the original element does not span multiple entries and we still don't // have room for the replacement, then we will remove this entry and insert // the replacement. When the insert happens, it will take care of moving // things around or splitting the block as needed to get it in. If bTruncate // is FALSE, and the new entry is larger than the original, we can ignore it. if( bLastElement) { if( RC_BAD( rc = replaceByInsert( ppucKey, puiKeyLen, pucValue, uiLen, uiOADataLen, uiFlags, puiChildBlkAddr, puiCounts, ppucRemainingValue, puiRemainingLen, peAction))) { goto Exit; } goto Exit; } if( bTruncate) { if( RC_BAD( rc = replaceMultiples( ppucKey, puiKeyLen, pucValue, uiLen, uiFlags, puiChildBlkAddr, puiCounts, ppucRemainingValue, puiRemainingLen, peAction))) { goto Exit; } } else { if( RC_BAD( rc = replaceMultiNoTruncate( ppucKey, puiKeyLen, pucValue, uiLen, uiFlags, puiChildBlkAddr, puiCounts, ppucRemainingValue, puiRemainingLen, peAction))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: This method is called whenever a replacement entry will not fit in the block, even if we remove the existing entry. It ASSUMES that the original element does not continue to another entry, either in the same block or in another block. ****************************************************************************/ RCODE F_Btree::replaceByInsert( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucDataValue, FLMUINT uiDataLen, FLMUINT uiOADataLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen = uiDataLen; if( *peAction == ELM_REPLACE_DO) { uiLen = uiOADataLen; *peAction = ELM_INSERT_DO; } else { *peAction = ELM_INSERT; } // At this point, it is clear that this new entry is larger than the // old entry. We will remove the old entry first. Then we can treat // this whole operation as an insert rather than as a replace. if( RC_BAD( rc = remove( FALSE))) { goto Exit; } if( RC_BAD( rc = insertEntry( ppucKey, puiKeyLen, pucDataValue, uiLen, uiFlags, puiChildBlkAddr, puiCounts, ppucRemainingValue, puiRemainingLen, peAction))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Method to replace an entry in a block and update the available space. This method expects to receive a buffer with an entry already prepared to be written to the block. ****************************************************************************/ RCODE F_Btree::replace( FLMBYTE * pucEntry, FLMUINT uiEntrySize, FLMBOOL * pbLastEntry) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucReplaceAt; FLMUINT uiNumKeys; FLMBYTE * pBlk; FLMUINT uiOldEntrySize; *pbLastEntry = FALSE; // Log this block before making any changes to it. Since the // pSCache could change, we must update the block header after the call. if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; pBlk = (FLMBYTE *)m_pStack->pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( pBlk, 0); uiNumKeys = getBlkEntryCount( pBlk); uiOldEntrySize = actualEntrySize( getEntrySize( pBlk, m_pStack->uiCurOffset)); flmAssert( uiOldEntrySize >= uiEntrySize); pucReplaceAt = BtEntry( pBlk, m_pStack->uiCurOffset); // Let's go ahead and copy the entry into the block now. f_memcpy( pucReplaceAt, pucEntry, uiEntrySize); #ifdef FLM_DEBUG // Clean up the empty space (if any) if( uiOldEntrySize > uiEntrySize) { pucReplaceAt += uiEntrySize; f_memset( pucReplaceAt, 0, uiOldEntrySize - uiEntrySize); } #endif // Update the available space. It may not have changed at all if the // two entries are the same size. The Heap size will not have changed. // This is because we write the entry into the same location as the // original. Even though the new entry may be smaller, we start at // the same location, possibly leaving a hole in the block. m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += (FLMUINT16)(uiOldEntrySize - uiEntrySize); if( m_pStack->uiCurOffset == (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys - 1)) { *pbLastEntry = TRUE; } // Preserve the block and offset index in case it is wanted on the way out. if( !m_pStack->uiLevel && bteFirstElementFlag( pucEntry)) { m_ui32PrimaryBlkAddr = m_pStack->ui32BlkAddr; m_uiCurOffset = m_pStack->uiCurOffset; } Exit: return( rc); } /*************************************************************************** Desc: Method to rebuild the stack so that it references the parentage of the parameter pSCache block. The assumption is that we will begin at whatever level m_pStack is currently sitting at. Therefore, this method can be called for any level in the Btree. ****************************************************************************/ RCODE F_Btree::moveStackToPrev( F_CachedBlock * pSCache) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkAddr; F_BTREE_BLK_HDR * pBlkHdr; F_BTSK_p pStack = m_pStack; F_CachedBlock * pPrevSCache = NULL; if( pSCache) { if( pStack->pSCache) { // Make sure the block we passed in really is the previous // block in the chain. if( pSCache->m_uiBlkAddress != pStack->pSCache->m_pBlkHdr->ui32PrevBlkInChain) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Cannot be the same block. if( pSCache == pStack->pSCache) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Release the current block. We don't need to fetch // the new block because it was passed in to us. If // we encounter this situation further up the tree, // we will have to fetch the block as well. ScaReleaseCache( pStack->pSCache, FALSE); } pStack->pSCache = pSCache; pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; pStack->pBlkHdr = pBlkHdr; pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; pStack->uiCurOffset = pBlkHdr->ui16NumKeys - 1; // Last entry pStack->uiLevel = pBlkHdr->ui8BlkLevel; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); // Now walk up the stack until done. pStack++; } for (;;) { // If we don't have this block in the stack, we must first get it. if( pStack->pSCache == NULL) { // Don't continue if we don't have this level in the stack. if( pStack->ui32BlkAddr == 0) { break; } if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pStack->ui32BlkAddr, NULL, &pStack->pSCache))) { goto Exit; } pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pSCache->m_pBlkHdr; } // See if we need to go to the previous block. if( pStack->uiCurOffset == 0) { // If this is the root block and we are looking at the first // entry in the block, then we have a problem. if( !isRootBlk( pStack->pBlkHdr)) { // When the stack is pointing to the first entry, this // means that we want the target stack to point to the previous // block in the chain. uiBlkAddr = pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; flmAssert( uiBlkAddr); // Fetch the new block if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiBlkAddr, NULL, &pPrevSCache))) { goto Exit; } // Release the old block ScaReleaseCache( pStack->pSCache, FALSE); pBlkHdr = (F_BTREE_BLK_HDR *)pPrevSCache->m_pBlkHdr; pStack->pSCache = pPrevSCache; pPrevSCache = NULL; pStack->pBlkHdr = pBlkHdr; pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; pStack->uiCurOffset = pBlkHdr->ui16NumKeys - 1; // Last Entry pStack->uiLevel = pBlkHdr->ui8BlkLevel; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); } else { // We have no previous. rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } } else { pStack->uiCurOffset--; // Previous Entry break; } pStack++; } Exit: if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to rebuild the stack so that it references the parentage of the parameter pSCache block. The assumption is that we will begin at whatever level m_pStack is currently sitting at. Therefore, this method can be called for any level in the Btree. ****************************************************************************/ RCODE F_Btree::moveStackToNext( F_CachedBlock * pSCache, FLMBOOL bReleaseCurrent) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkAddr; F_BTREE_BLK_HDR * pBlkHdr; F_BTSK_p pStack = m_pStack; F_CachedBlock * pNextSCache = NULL; if( pSCache) { if( pStack->pSCache) { // Make sure the block we passed in really is the next in chain. if( pSCache->m_uiBlkAddress != pStack->pSCache->m_pBlkHdr->ui32NextBlkInChain) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Cannot be the same block. if( pSCache == pStack->pSCache) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Release the current block. We don't need to fetch // the new block because it was passed in to us. If // we encounter this situation further up the tree, // we will have to fetch the block as well. if( bReleaseCurrent) { ScaReleaseCache( pStack->pSCache, FALSE); } } pStack->pSCache = pSCache; pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; pStack->pBlkHdr = pBlkHdr; pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; pStack->uiCurOffset = 0; // First entry pStack->uiLevel = pBlkHdr->ui8BlkLevel; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); // Now walk up the stack until done. pStack++; } for (;;) { // If we don't currently have this block, let's get it. if( pStack->pSCache == NULL) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pStack->ui32BlkAddr, NULL, &pStack->pSCache))) { goto Exit; } pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pSCache->m_pBlkHdr; } // See if we need to go to the next block. if( pStack->uiCurOffset == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)) { // If this is the root block and we are looking at the last entry in the // block, then we have a problem. if( !isRootBlk( pStack->pBlkHdr)) { // When the stack is pointing to the last entry, this // means that we want the target stack to point the next block in // the chain. uiBlkAddr = pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain; flmAssert( uiBlkAddr); // Get the next block if( RC_BAD( rc = getNextBlock( &pStack->pSCache))) { goto Exit; } pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pSCache->m_pBlkHdr; pStack->pBlkHdr = pBlkHdr; pStack->ui32BlkAddr = ((F_BLK_HDR *)pBlkHdr)->ui32BlkAddr; pStack->uiCurOffset = 0; // First Entry pStack->uiLevel = pBlkHdr->ui8BlkLevel; pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); } else { // We should never have to attempt to get a previous block // on the root. rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } else { pStack->uiCurOffset++; // Next Entry break; } pStack++; } Exit: if( pNextSCache) { ScaReleaseCache( pNextSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to calculate the actual entry size of a new entry ****************************************************************************/ RCODE F_Btree::calcNewEntrySize( FLMUINT uiKeyLen, FLMUINT uiDataLen, FLMUINT * puiEntrySize, FLMBOOL * pbHaveRoom, FLMBOOL * pbDefragBlk) { RCODE rc = NE_XFLM_OK; // Calculate the entry size. switch( m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType) { case BT_LEAF: { // This block type is a leaf block, No Data *puiEntrySize = BTE_LEAF_OVHD + uiKeyLen; break; } case BT_LEAF_DATA: { // Leaf block with data *puiEntrySize = BTE_LEAF_DATA_OVHD + (uiKeyLen > ONE_BYTE_SIZE ? 2 : 1) + (uiDataLen > ONE_BYTE_SIZE ? 2 : 1) + uiKeyLen + uiDataLen; break; } case BT_NON_LEAF: { *puiEntrySize = BTE_NON_LEAF_OVHD + uiKeyLen; break; } case BT_NON_LEAF_COUNTS: { *puiEntrySize = BTE_NON_LEAF_COUNTS_OVHD + uiKeyLen; break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); *puiEntrySize = 0; goto Exit; } } // See if we have room in the heap first. If not, maybe we can make // room by defraging the block. if( *puiEntrySize <= m_pStack->pBlkHdr->ui16HeapSize) { *pbDefragBlk = FALSE; *pbHaveRoom = TRUE; } else if( *puiEntrySize <= m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { // A defrag of the block is required to make room. We will only defrag // if we can recover a minimum of 5% of the total block size. if( m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail >= m_uiDefragThreshold) { *pbHaveRoom = TRUE; *pbDefragBlk = TRUE; } else { *pbHaveRoom = FALSE; *pbDefragBlk = FALSE; } } else { *pbHaveRoom = FALSE; *pbDefragBlk = FALSE; } Exit: return( rc); } /*************************************************************************** Desc: Function to save the replacement information that we could not store on the current go round. The replace function will check for the presence of this structure and deal with it later. ****************************************************************************/ RCODE F_Btree::saveReplaceInfo( const FLMBYTE * pucNewKey, FLMUINT uiNewKeyLen) { RCODE rc = NE_XFLM_OK; BTREE_REPLACE_STRUCT * pPrev; F_BTSK_p pStack = m_pStack; const FLMBYTE * pucParentKey; FLMBYTE * pucEntry; if( m_uiReplaceLevels + 1 >= BH_MAX_LEVELS) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } pPrev = m_pReplaceInfo; m_pReplaceInfo = &m_pReplaceStruct[ m_uiReplaceLevels++]; m_pReplaceInfo->pPrev = (void *)pPrev; // We should not be at the root level already! flmAssert( pStack->uiLevel != m_uiStackLevels - 1); m_pReplaceInfo->uiParentLevel = pStack->uiLevel+1; m_pReplaceInfo->uiNewKeyLen = uiNewKeyLen; m_pReplaceInfo->uiChildBlkAddr = pStack->ui32BlkAddr; if( m_bCounts) { m_pReplaceInfo->uiCounts = countKeys( (FLMBYTE *)pStack->pBlkHdr); } else { m_pReplaceInfo->uiCounts = 0; } f_memcpy( &m_pReplaceInfo->pucNewKey[0], pucNewKey, uiNewKeyLen); pStack++; pucEntry = BtEntry( (FLMBYTE *)pStack->pBlkHdr, pStack->uiCurOffset); m_pReplaceInfo->uiParentKeyLen = getEntryKeyLength( pucEntry, pStack->pBlkHdr->stdBlkHdr.ui8BlkType, &pucParentKey); f_memcpy( &m_pReplaceInfo->pucParentKey[0], pucParentKey, m_pReplaceInfo->uiParentKeyLen); m_pReplaceInfo->uiParentChildBlkAddr = bteGetBlkAddr( pucEntry); Exit: return( rc); } /*************************************************************************** Desc: Method to restore the stack to a state where we can finish updating the parent with the new key information. ****************************************************************************/ RCODE F_Btree::restoreReplaceInfo( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts) { RCODE rc = NE_XFLM_OK; RCODE rcTmp = NE_XFLM_OK; FLMUINT uiLoop; FLMBYTE * pucEntry; FLMUINT uiKeyLen; const FLMBYTE * pucKey; FLMUINT uiSearchLevel = m_uiSearchLevel; FLMUINT uiStackLevels = m_uiStackLevels; // We will need to redo our stack from the top down to // make sure we are looking at the correct blocks. m_uiSearchLevel = m_uiStackLevels - m_pReplaceInfo->uiParentLevel - 1; rcTmp = findEntry( m_pReplaceInfo->pucParentKey, m_pReplaceInfo->uiParentKeyLen, XFLM_EXACT); m_uiSearchLevel = uiSearchLevel; if ((rcTmp != NE_XFLM_OK) && (rcTmp != NE_XFLM_NOT_FOUND) && (rcTmp != NE_XFLM_EOF_HIT)) { rc = RC_SET( rcTmp); goto Exit; } // Set the stack pointer to the parent level that we want to replace. m_pStack = &m_Stack[ m_pReplaceInfo->uiParentLevel]; // There is always the possibility that the key we are searching for // has a duplicate key ahead of it, as a result of a continuation element. // We really must replace the entry we were looking at when the information // was stored, therefore, we will verify that we have the right entry. for( ;;) { pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); uiKeyLen = getEntryKeyLength( pucEntry, m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType, &pucKey); if( uiKeyLen != m_pReplaceInfo->uiParentKeyLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( f_memcmp( &m_pReplaceInfo->pucParentKey[0], pucKey, uiKeyLen) == 0) { if( bteGetBlkAddr( pucEntry) != m_pReplaceInfo->uiParentChildBlkAddr) { // Try moving forward to the next entry ... if( RC_BAD( rc = moveStackToNext( NULL))) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } else { break; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } // Now return the other important stuff *puiChildBlkAddr = m_pReplaceInfo->uiChildBlkAddr; *puiKeyLen = m_pReplaceInfo->uiNewKeyLen; *puiCounts = m_pReplaceInfo->uiCounts; for( uiLoop = 0; uiLoop < m_uiStackLevels; uiLoop++) { m_Stack[ uiLoop].uiKeyLen = m_pReplaceInfo->uiNewKeyLen; } m_uiStackLevels = uiStackLevels; // Point to the key *ppucKey = &m_pReplaceInfo->pucNewKey[ 0]; // Free the current ReplaceInfo Buffer m_pReplaceInfo = (BTREE_REPLACE_STRUCT *)m_pReplaceInfo->pPrev; m_uiReplaceLevels--; Exit: return( rc); } /*************************************************************************** Desc: Method to set the key to be returned to the caller. ****************************************************************************/ FINLINE RCODE F_Btree::setReturnKey( FLMBYTE * pucEntry, FLMUINT uiBlockType, FLMBYTE * pucKey, FLMUINT * puiKeyLen, FLMUINT uiKeyBufSize) { RCODE rc = NE_XFLM_OK; FLMUINT uiKeyLen; const FLMBYTE * pucKeyRV; uiKeyLen = getEntryKeyLength( pucEntry, uiBlockType, &pucKeyRV); if( uiKeyLen == 0) { // We hit the LEM, hence the EOF error rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } if( uiKeyLen <= uiKeyBufSize) { f_memcpy( pucKey, pucKeyRV, uiKeyLen); *puiKeyLen = uiKeyLen; } else { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Method to return the data from either the BTREE block or the DO block. It will update the tracking variables too. This method assumes that the m_pSCache has already been setup for the 1st go-round. ****************************************************************************/ RCODE F_Btree::extractEntryData( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBYTE * pucBuffer, FLMUINT uiBufSiz, FLMUINT * puiDataLen) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucDestPtr = pucBuffer; FLMUINT32 ui32BlkAddr = 0; FLMBOOL bNewBlock; FLMUINT uiDataLen = 0; flmAssert( m_pSCache); if( puiDataLen) { *puiDataLen = 0; } #ifdef FLM_DEBUG if( pucBuffer) { f_memset( pucBuffer, 0, uiBufSiz); } #endif // Is there anything to read? if( m_uiOADataRemaining == 0) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } while( m_uiOADataRemaining && (uiDataLen < uiBufSiz)) { if( m_uiDataRemaining <= (uiBufSiz - uiDataLen)) { // Let's take what we have left in this block first. if( pucDestPtr) { f_memcpy( pucDestPtr, m_pucDataPtr, m_uiDataRemaining); pucDestPtr += m_uiDataRemaining; } uiDataLen += m_uiDataRemaining; m_uiOADataRemaining -= m_uiDataRemaining; m_uiDataRemaining = 0; } else { // Buffer is too small to hold everything in this block. if( pucDestPtr) { f_memcpy( pucDestPtr, m_pucDataPtr, uiBufSiz - uiDataLen); pucDestPtr += (uiBufSiz - uiDataLen); } m_pucDataPtr += (uiBufSiz - uiDataLen); m_uiOADataRemaining -= (uiBufSiz - uiDataLen); m_uiDataRemaining -= (uiBufSiz - uiDataLen); uiDataLen += (uiBufSiz - uiDataLen); } // If there is still more overall data remaining, we need to get the // next DO block or standard block and setup to read it too. // i.e. More to come, but nothing left in this block. if( (m_uiOADataRemaining > 0) && (m_uiDataRemaining == 0)) { if (!m_bDataOnlyBlock && (m_uiCurOffset < (FLMUINT)(((F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr)->ui16NumKeys - 1))) { m_uiCurOffset++; bNewBlock = FALSE; } else { // Get the next block address ui32BlkAddr = m_pSCache->m_pBlkHdr->ui32NextBlkInChain; // Release the current block before we get the next one. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; if( ui32BlkAddr == 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddr, NULL, &m_pSCache))) { goto Exit; } updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, m_pSCache->m_ui64HighTransID); m_ui64LastBlkTransId = m_pSCache->m_pBlkHdr->ui64TransID; bNewBlock = TRUE; } // If this is a data only block, then we can get the local data size // from the header. if( m_bDataOnlyBlock) { flmAssert( m_pSCache->m_pBlkHdr->ui8BlkType == BT_DATA_ONLY); m_pucDataPtr = (FLMBYTE *)m_pSCache->m_pBlkHdr + sizeofDOBlkHdr( m_pSCache->m_pBlkHdr); m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( m_pSCache->m_pBlkHdr) - m_pSCache->m_pBlkHdr->ui16BlkBytesAvail; m_uiDataLength = m_uiDataRemaining; m_ui32CurBlkAddr = ui32BlkAddr; } else { F_BTREE_BLK_HDR * pBlkHdr; FLMBYTE * pucEntry; // In a BTREE block, we MUST ensure that the first entry is a // continuation of the previous entry in the previous block. pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; if( pBlkHdr->ui16NumKeys == 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( bNewBlock) { m_uiCurOffset = 0; } // Point to the first entry ... pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, m_uiCurOffset); if( !checkContinuedEntry( pucKey, uiKeyLen, NULL, pucEntry, pBlkHdr->stdBlkHdr.ui8BlkType)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } m_uiDataRemaining = btGetEntryDataLength( pucEntry, &m_pucDataPtr, NULL, NULL); m_uiDataLength = m_uiDataRemaining; if( bNewBlock) { m_ui32CurBlkAddr = ui32BlkAddr; } } // Update the offset at the begining of the current entry. m_uiOffsetAtStart = m_uiOADataLength - m_uiOADataRemaining; } } Exit: if( puiDataLen) { *puiDataLen = uiDataLen; } if( m_pSCache) { // We must release the SCache block ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } return( rc); } /*************************************************************************** Desc: Method to prepare the Btree state for reading. Since several APIs do the same thing, this has been put into a private method. ****************************************************************************/ RCODE F_Btree::setupReadState( F_BLK_HDR * pBlkHdr, FLMBYTE * pucEntry) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = NULL; const FLMBYTE * pucData; // Is there any data? Check the block type. if( pBlkHdr->ui8BlkType == BT_LEAF_DATA) { // How large is the value for this entry? m_uiDataLength = btGetEntryDataLength( pucEntry, &pucData, &m_uiOADataLength, &m_bDataOnlyBlock); m_uiPrimaryDataLen = m_uiDataLength; } else { m_uiDataLength = 0; m_uiOADataLength = 0; m_bDataOnlyBlock = FALSE; } // Represents the offset at the beginning entry in the first block. This // will change as we move through the blocks. m_uiOffsetAtStart = 0; // Watch the transaction id and the transaction count during streaming // read operations. If either changes after an initial read, then // we abort the operation. m_ui64CurrTransID = m_pDb->m_ui64CurrTransID; m_uiBlkChangeCnt = m_pDb->m_uiBlkChangeCnt; m_ui64LastBlkTransId = pBlkHdr->ui64TransID; m_ui64PrimaryBlkTransId = pBlkHdr->ui64TransID; // Track the overall length progress m_uiOADataRemaining = m_uiOADataLength; // Track the local entry progress m_uiDataRemaining = m_uiDataLength; if( m_bDataOnlyBlock) { m_ui32DOBlkAddr = bteGetBlkAddr( pucData); m_ui32CurBlkAddr = m_ui32DOBlkAddr; if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32DOBlkAddr, NULL, &pSCache))) { goto Exit; } m_ui64LastBlkTransId = pSCache->m_pBlkHdr->ui64TransID; // Local amount of data in this block m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr((F_BLK_HDR *)pSCache->m_pBlkHdr) - pSCache->m_pBlkHdr->ui16BlkBytesAvail; // Keep the actual local data size for later. m_uiDataLength = m_uiDataRemaining; // Adjust for the key at the beginning of the first block. if( pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0) { FLMBYTE * pucPtr = (FLMBYTE *)pSCache->m_pBlkHdr + sizeofDOBlkHdr((F_BLK_HDR *)pSCache->m_pBlkHdr); FLMUINT16 ui16KeyLen = FB2UW( pucPtr); m_uiDataLength -= (ui16KeyLen + 2); m_uiDataRemaining -= (ui16KeyLen + 2); } // Now release the DO Block. We will get it again when we need it. ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to remove extra entries after a replace operation. ****************************************************************************/ RCODE F_Btree::removeRemainingEntries( const FLMBYTE * pucKey, FLMUINT uiKeyLen) { RCODE rc = NE_XFLM_OK; F_BTREE_BLK_HDR * pBlkHdr; FLMBOOL bLastElement = FALSE; FLMBYTE * pucEntry; FLMBOOL bFirst = TRUE; // We should never get to this function when in the upper levels. flmAssert( m_pStack->uiLevel == 0); // If we do not have a stack setup yet (which can happen if the replace // is trying to shortcut to the previously known block address and offset), // then at this point, we must build the stack, since it may be required // to adjust the upper levels of the btree. if( !m_bStackSetup) { if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, XFLM_EXACT))) { goto Exit; } } while( !bLastElement) { // Begin each iteration at the leaf level. m_pStack = &m_Stack[ 0]; // Advance the stack to the next entry. if (bFirst || m_pStack->uiCurOffset >= (FLMUINT)(m_pStack->pBlkHdr->ui16NumKeys)) { if( RC_BAD( rc = moveStackToNext( NULL))) { goto Exit; } } bFirst = FALSE; pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); if( !checkContinuedEntry( pucKey, uiKeyLen, &bLastElement, pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr))) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Remove the entry from this block. if( RC_BAD( rc = remove( FALSE))) { goto Exit; } pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; // Is the block empty now? If it is, then we will want to remove this // block and remove the entry in the parent that points to this block. if( pBlkHdr->ui16NumKeys == 0) { for (;;) { flmAssert( !isRootBlk( m_pStack->pBlkHdr)); // Remove this block, then update the parent. if( RC_BAD( rc = deleteEmptyBlock())) { goto Exit; } // Now update the parent blocks m_pStack++; if( RC_BAD( rc = remove( FALSE))) { goto Exit; } // Update the counts if keeping counts. if( m_bCounts && !isRootBlk(pBlkHdr)) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } if( m_pStack->pBlkHdr->ui16NumKeys > 0) { break; } } // Rebuild the stack to the beginning after a delete block operation. if( RC_BAD( findEntry( pucKey, uiKeyLen, XFLM_EXACT))) { goto Exit; } bFirst = TRUE; } else { // Update the counts if keeping counts. if( m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } } } Exit: return( rc); } /*************************************************************************** Desc: Method to delete an empty block. The block that will be deleted is the current block pointed to by m_pStack. ****************************************************************************/ RCODE F_Btree::deleteEmptyBlock( void) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32PrevBlkAddr; FLMUINT32 ui32NextBlkAddr; F_CachedBlock * pSCache = NULL; // Get the previous block address so we can back everything up in the stack ui32PrevBlkAddr = m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; ui32NextBlkAddr = m_pStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain; // Free the block rc = m_pDb->m_pDatabase->blockFree(m_pDb, m_pStack->pSCache); m_pStack->pSCache = NULL; m_pStack->pBlkHdr = NULL; if( RC_BAD( rc)) { goto Exit; } // Update the previous block. if( ui32PrevBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32PrevBlkAddr, NULL, &pSCache))) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) { goto Exit; } pSCache->m_pBlkHdr->ui32NextBlkInChain = ui32NextBlkAddr; ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } // Update the next block if( ui32NextBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pSCache))) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) { goto Exit; } pSCache->m_pBlkHdr->ui32PrevBlkInChain = ui32PrevBlkAddr; ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to remove (free) all data only blocks that are linked to the data only block whose address is passed in (inclusive). ****************************************************************************/ RCODE F_Btree::removeDOBlocks( FLMUINT32 ui32BlkAddr) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32NextBlkAddr; F_CachedBlock * pSCache = NULL; ui32NextBlkAddr = ui32BlkAddr; while( ui32NextBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pSCache))) { goto Exit; } flmAssert( getBlkType( (FLMBYTE *)pSCache->m_pBlkHdr) == BT_DATA_ONLY); ui32NextBlkAddr = pSCache->m_pBlkHdr->ui32NextBlkInChain; rc = m_pDb->m_pDatabase->blockFree( m_pDb, pSCache); pSCache = NULL; if( RC_BAD( rc)) { goto Exit; } } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method used to replace entries where the original spans multiple elements and we are NOT to truncate it. To do this, we will attempt to fill each block until we have stored everything. ****************************************************************************/ RCODE F_Btree::replaceMultiples( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucDataValue, FLMUINT uiLen, FLMUINT, //uiFlags, FLMUINT *, //puiChildBlkAddr, FLMUINT *, //puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction) { RCODE rc = NE_XFLM_OK; FLMBOOL bLastElement = FALSE; FLMUINT uiRemainingData = uiLen; const FLMBYTE * pucRemainingValue = pucDataValue; FLMBYTE * pucEntry = NULL; FLMBYTE * pucData; FLMUINT uiDataLength; FLMUINT uiOADataLength = uiLen; FLMUINT uiOldOADataLength; FLMUINT uiAmtCopied; // Must be at the leaf level! flmAssert( m_pStack->uiLevel == 0); while( uiRemainingData) { if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); // Get a pointer to the current entry pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); // Determine the data size for this entry uiDataLength = btGetEntryDataLength( pucEntry, (const FLMBYTE **)&pucData, &uiOldOADataLength, NULL); // Now over-write as much of the data as we can if( uiRemainingData >= uiDataLength) { f_memcpy( pucData, pucRemainingValue, uiDataLength); uiAmtCopied = uiDataLength; pucRemainingValue += uiDataLength; uiRemainingData -= uiDataLength; } else { f_memcpy( pucData, pucRemainingValue, uiRemainingData); uiAmtCopied = uiRemainingData; pucRemainingValue += uiRemainingData; uiRemainingData = 0; } // Do we need to adjust the data length? if( uiDataLength > uiAmtCopied) { FLMBYTE * pucTmp = pucEntry; // Skip the flag pucTmp++; if( bteKeyLenFlag( pucEntry)) { pucTmp += 2; } else { pucTmp++; } if( bteDataLenFlag( pucEntry)) { UW2FBA( (FLMUINT16)uiAmtCopied, pucTmp); pucTmp += 2; } else { *pucTmp = (FLMBYTE)uiAmtCopied; pucTmp++; } // We need to adjust the free space in the block too. m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail += (FLMUINT16)(uiDataLength - uiAmtCopied); #ifdef FLM_DEBUG // Clear the unused portion of the block now. pucTmp = pucData + uiAmtCopied; f_memset( pucTmp, 0, (uiDataLength - uiAmtCopied)); #endif } // Adjust the OA Data length if needed. We only need to worry about this // on the first element. No others have it. if( bteFirstElementFlag( pucEntry) && uiOADataLength != uiOldOADataLength) { FLMBYTE * pucTmp = pucEntry; flmAssert( bteOADataLenFlag( pucEntry)); pucTmp++; if( bteKeyLenFlag( pucEntry)) { pucTmp += 2; } else { pucTmp++; } if( bteDataLenFlag( pucEntry)) { pucTmp += 2; } else { pucTmp++; } UD2FBA( (FLMUINT32)uiOADataLength, pucTmp); } // If we just updated the last member of this entry so break out. if( uiRemainingData == 0) { break; } // Was this the last element for this entry? if( bteLastElementFlag(pucEntry)) { FLMBYTE * pucTmp = pucEntry; // Turn off the lastElement flag on this entry. *pucTmp &= ~BTE_FLAG_LAST_ELEMENT; // No more to replace, the rest is going to be new data. *ppucRemainingValue = pucRemainingValue; *puiRemainingLen = uiRemainingData; break; } // Advance to the next entry, this block or the next... // The function expects to find the block in m_pSCache, so // let's put it there for now. if( RC_BAD( rc = moveStackToNext( NULL))) { goto Exit; } pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); // Make sure we are still looking at the same key etc. if( !checkContinuedEntry( *ppucKey, *puiKeyLen, &bLastElement, pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr))) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } // Are there any more entries to remove? if( !bteLastElementFlag( pucEntry) && !uiRemainingData) { *pucEntry |= BTE_FLAG_LAST_ELEMENT; if( RC_BAD( rc = removeRemainingEntries( *ppucKey, *puiKeyLen))) { goto Exit; } } *peAction = ELM_DONE; Exit: // Only release the m_pSCache if the use count is greater than 1. It is // pointed to by the stack also. if( m_pSCache && m_pSCache->m_uiUseCount > 1) { ScaReleaseCache( m_pSCache, FALSE); } m_pSCache = NULL; return( rc); } /*************************************************************************** Desc: Method used to replace entries where the original spans multiple elements and we are not to truncate it. To do this, we will attempt to fill each block until we have stored everything. ****************************************************************************/ RCODE F_Btree::replaceMultiNoTruncate( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucDataValue, FLMUINT uiLen, FLMUINT, //uiFlags, FLMUINT *, //puiChildBlkAddr, FLMUINT *, //puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction) { RCODE rc = NE_XFLM_OK; FLMBOOL bLastElement = FALSE; FLMUINT uiRemainingData = uiLen; const FLMBYTE * pucRemainingValue = pucDataValue; FLMBYTE * pucEntry; FLMBYTE * pucData; FLMUINT uiDataLength; // Must be at the leaf level flmAssert( m_pStack->uiLevel == 0); while( uiRemainingData) { if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); // Get a pointer to the current entry pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); // Determine the data size for this entry uiDataLength = btGetEntryDataLength( pucEntry, (const FLMBYTE **)&pucData, NULL, NULL); // Now over-write as much of the data as we can. if( uiRemainingData > uiDataLength) { f_memcpy( pucData, pucRemainingValue, uiDataLength); pucRemainingValue += uiDataLength; uiRemainingData -= uiDataLength; } else { f_memcpy( pucData, pucRemainingValue, uiRemainingData); pucRemainingValue += uiRemainingData; uiRemainingData = 0; } // We just updated the last member of this entry so break out. if( uiRemainingData == 0) { break; } // Was this the last element for this entry? if( bteLastElementFlag( pucEntry)) { // No more to replace, the rest is going to be new data. *ppucRemainingValue = pucRemainingValue; *puiRemainingLen = uiRemainingData; break; } // Advance to the next entry, this block or the next... // The function expects to find the block in m_pSCache, so // let's put it there f or now. if( RC_BAD( rc = moveStackToNext( NULL))) { goto Exit; } pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); // Make sure we are still looking at the same key etc. if( !checkContinuedEntry( *ppucKey, *puiKeyLen, &bLastElement, pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr))) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } *peAction = ELM_DONE; Exit: // Only release the m_pSCache if the use count is greater than 1. It is // pointed to by the stack also. if( m_pSCache && m_pSCache->m_uiUseCount > 1) { ScaReleaseCache( m_pSCache, FALSE); } m_pSCache = NULL; return( rc); } /*************************************************************************** Desc: Private method to retrieve the next block in the chain relative to the block that is passed in. The block that is passed in is always released prior to getting the next block. ****************************************************************************/ FINLINE RCODE F_Btree::getNextBlock( F_CachedBlock ** ppSCache) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32BlkAddr; ui32BlkAddr = (*ppSCache)->m_pBlkHdr->ui32NextBlkInChain; ScaReleaseCache( *ppSCache, FALSE); *ppSCache = NULL; if( ui32BlkAddr == 0) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddr, NULL, ppSCache))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Private method to retrieve the previous block in the chain relative to the block that is passed in. The block that is passed in is always released prior to getting the previous block. ****************************************************************************/ FINLINE RCODE F_Btree::getPrevBlock( F_CachedBlock ** ppSCache) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32BlkAddr; ui32BlkAddr = (*ppSCache)->m_pBlkHdr->ui32PrevBlkInChain; ScaReleaseCache( *ppSCache, FALSE); *ppSCache = NULL; if( ui32BlkAddr == 0) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddr, NULL, ppSCache))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Private method to verify that the entry we are looking at in the stack is a continuation entry. The key must match the key we pass in and the entry must be marked as a continuation, i.e. not the first element. ****************************************************************************/ FLMBOOL F_Btree::checkContinuedEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL * pbLastElement, FLMBYTE * pucEntry, FLMUINT uiBlkType) { FLMBOOL bOk = TRUE; FLMUINT uiBlkKeyLen; const FLMBYTE * pucBlkKey; if( pbLastElement) { *pbLastElement = bteLastElementFlag( pucEntry); } uiBlkKeyLen = getEntryKeyLength( pucEntry, uiBlkType, &pucBlkKey); // Must be the same size key! if( uiKeyLen != uiBlkKeyLen) { bOk = FALSE; goto Exit; } // Must be identical! if( f_memcmp( pucKey, pucBlkKey, uiKeyLen) != 0) { bOk = FALSE; goto Exit; } // Must not be the first element! if( bteFirstElementFlag( pucEntry)) { bOk = FALSE; goto Exit; } Exit: return( bOk); } /*************************************************************************** Desc: Private method to assend the tree, updating the counts for a particular block. This method allows us to update the counts quickly without the need to continually loop, replacing existing keys with new counts. ****************************************************************************/ RCODE F_Btree::updateCounts( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiLevel; for( uiLevel = m_pStack->uiLevel; uiLevel < m_uiStackLevels - 1; uiLevel++) { if( RC_BAD( rc = updateParentCounts( m_Stack[ uiLevel].pSCache, &m_Stack[ uiLevel + 1].pSCache, m_Stack[ uiLevel + 1].uiCurOffset))) { goto Exit; } m_Stack[ uiLevel + 1].pBlkHdr = (F_BTREE_BLK_HDR *)m_Stack[ uiLevel + 1].pSCache->m_pBlkHdr; } Exit: return( rc); } /*************************************************************************** Desc: Private method to store part of an entry in a block. This method will determine how much of the data can be stored in the block. The amount that does not get stored will be returned in ppucRemainingValue and puiRemainingLen. ****************************************************************************/ RCODE F_Btree::storePartialEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, FLMBOOL bNewBlock) { RCODE rc = NE_XFLM_OK; FLMUINT uiNewDataLen; FLMUINT uiOADataLen = 0; FLMUINT uiEntrySize; FLMBOOL bHaveRoom; FLMBOOL bDefragBlk; FLMBOOL bLastEntry; if( RC_BAD( rc = calcOptimalDataLength( uiKeyLen, uiLen, m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail, &uiNewDataLen))) { goto Exit; } if( uiNewDataLen < uiLen) { // Turn off the last element flag. uiFlags &= ~BTE_FLAG_LAST_ELEMENT; if( uiFlags & BTE_FLAG_FIRST_ELEMENT) { // Store the overall data length from this point forward. uiOADataLen = uiLen; } } if( RC_BAD( rc = calcNewEntrySize( uiKeyLen, uiNewDataLen, &uiEntrySize, &bHaveRoom, &bDefragBlk))) { goto Exit; } // We will defragment the block first if the avail and heap // are not the same size. if( m_pStack->pBlkHdr->ui16HeapSize != m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail) { if( RC_BAD( rc = defragmentBlock( &m_pStack->pSCache))) { goto Exit; } } if( RC_BAD( rc = storeEntry( pucKey, uiKeyLen, pucValue, uiNewDataLen, uiFlags, uiOADataLen, uiChildBlkAddr, uiCounts, uiEntrySize, &bLastEntry))) { goto Exit; } // If this block has a parent block, and the btree is maintaining counts // we will want to update the counts on the parent block. if( !isRootBlk( m_pStack->pBlkHdr) && m_bCounts && !bNewBlock) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } if( uiNewDataLen < uiLen) { // Save the portion of the data that was not written. // It will be written later. *ppucRemainingValue = pucValue + uiNewDataLen; *puiRemainingLen = uiLen - uiNewDataLen; } Exit: return( rc); } /*************************************************************************** Desc: Private meethod for checking the down links in the btree to make sure they are not corrupt. ****************************************************************************/ RCODE F_Btree::checkDownLinks( void) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pParentSCache = NULL; if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_pLFile->uiRootBlk, NULL, &pParentSCache))) { goto Exit; } if( (pParentSCache->m_pBlkHdr->ui8BlkType == BT_NON_LEAF) || (pParentSCache->m_pBlkHdr->ui8BlkType == BT_NON_LEAF_COUNTS)) { if( RC_BAD( rc = verifyChildLinks( pParentSCache))) { goto Exit; } } Exit: if( pParentSCache) { ScaReleaseCache( pParentSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Private method (recursive) that checks the child links in the given blocks to ensure they are correct. ****************************************************************************/ RCODE F_Btree::verifyChildLinks( F_CachedBlock * pParentSCache) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumKeys; F_CachedBlock * pChildSCache = NULL; F_BTREE_BLK_HDR * pParentBlkHdr; F_BTREE_BLK_HDR * pChildBlkHdr; FLMUINT uiCurOffset; FLMBYTE * pucEntry; FLMUINT32 ui32BlkAddr; const FLMBYTE * pucParentKey; FLMBYTE * pucChildEntry; const FLMBYTE * pucChildKey; FLMUINT uiParentKeyLen; FLMUINT uiChildKeyLen; pParentBlkHdr = (F_BTREE_BLK_HDR *)pParentSCache->m_pBlkHdr; uiNumKeys = pParentBlkHdr->ui16NumKeys; for( uiCurOffset = 0; uiCurOffset < uiNumKeys; uiCurOffset++) { pucEntry = BtEntry( (FLMBYTE *)pParentBlkHdr, uiCurOffset); // Non-leaf nodes have children. ui32BlkAddr = bteGetBlkAddr( pucEntry); flmAssert( ui32BlkAddr); if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddr, NULL, &pChildSCache))) { goto Exit; } pChildBlkHdr = (F_BTREE_BLK_HDR *)pChildSCache->m_pBlkHdr; // Get key from the parent entry and compare it to the // last key in the child block. uiParentKeyLen = getEntryKeyLength( pucEntry, pParentBlkHdr->stdBlkHdr.ui8BlkType, &pucParentKey); // Get the last entry in the child block. pucChildEntry = BtLastEntry( (FLMBYTE *)pChildBlkHdr); uiChildKeyLen = getEntryKeyLength( pucChildEntry, pChildBlkHdr->stdBlkHdr.ui8BlkType, &pucChildKey); if( uiParentKeyLen != uiChildKeyLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( f_memcmp( pucParentKey, pucChildKey, uiParentKeyLen) != 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( (pChildBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF) || (pChildBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF_COUNTS)) { if( RC_BAD( rc = verifyChildLinks( pChildSCache))) { goto Exit; } } ScaReleaseCache( pChildSCache, FALSE); pChildSCache = NULL; } Exit: if( pChildSCache) { ScaReleaseCache( pChildSCache, FALSE); } return( rc); } /*************************************************************************** Desc: This is a private method that computes the number of entries (keys) and the number of blocks between two points in the Btree. ****************************************************************************/ RCODE F_Btree::computeCounts( F_BTSK_p pFromStack, F_BTSK_p pUntilStack, FLMUINT * puiBlockCount, FLMUINT * puiKeyCount, FLMBOOL * pbTotalsEstimated, FLMUINT uiAvgBlkFullness) { RCODE rc = NE_XFLM_OK; FLMUINT uiTotalKeys = 0; FLMUINT uiTempKeyCount = 0; FLMUINT uiEstKeyCount = 0; FLMUINT uiTotalBlocksBetween = 0; FLMUINT uiEstBlocksBetween = 0; uiTotalBlocksBetween = 0; *pbTotalsEstimated = FALSE; // The stack that we are looking at does not hold the blocks // we need. We first need to restore the blocks as needed. if( RC_BAD( rc = getCacheBlocks( pFromStack, pUntilStack))) { goto Exit; } // Are the from and until positions in the same block? if( pFromStack->ui32BlkAddr == pUntilStack->ui32BlkAddr) { rc = blockCounts( pFromStack, pFromStack->uiCurOffset, pUntilStack->uiCurOffset, &uiTotalKeys, NULL); goto Exit; } // Are we maintaining counts on this Btree? If so, we can just // use the counts we have... The blocks count may still be estimated. if( m_bCounts) { return( getStoredCounts( pFromStack, pUntilStack, puiBlockCount, puiKeyCount, pbTotalsEstimated, uiAvgBlkFullness)); } // Since we are not keeping counts on this Btree, we will need to // count them and possibly estimate them. // Gather the counts in the from and until leaf blocks. if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, pFromStack->pBlkHdr->ui16NumKeys - 1, &uiTotalKeys, NULL))) { goto Exit; } if( RC_BAD( rc = blockCounts( pUntilStack, 0, pUntilStack->uiCurOffset, &uiTempKeyCount, NULL))) { goto Exit; } uiTotalKeys += uiTempKeyCount; // Do the obvious check to see if the blocks are neighbors. If they // are, we are done. if( pFromStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == pUntilStack->ui32BlkAddr) { goto Exit; } // Estimate the number of elements in the parent block. *pbTotalsEstimated = TRUE; uiEstKeyCount = getAvgKeyCount( pFromStack, pUntilStack, uiAvgBlkFullness); uiEstBlocksBetween = 1; for (;;) { FLMUINT uiElementCount; FLMUINT uiTempElementCount; FLMUINT uiEstElementCount; // Go up a b-tree level and check out how far apart the elements are. pFromStack++; pUntilStack++; if( RC_BAD( rc = getCacheBlocks( pFromStack, pUntilStack))) { goto Exit; } // Share the same block? if( pFromStack->ui32BlkAddr == pUntilStack->ui32BlkAddr) { if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, pUntilStack->uiCurOffset, NULL, &uiElementCount))) { goto Exit; } // Don't count the pFromStack or the pUntilStack current elements. uiElementCount -= 2; uiTotalBlocksBetween += uiEstBlocksBetween * (uiElementCount > 0 ? uiElementCount : 1); uiTotalKeys += uiEstKeyCount * (uiElementCount > 0 ? uiElementCount : 1); goto Exit; } // Gather the counts in the from and until non-leaf blocks. if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, pFromStack->pBlkHdr->ui16NumKeys - 1, NULL, &uiElementCount))) { goto Exit; } // Don't count the first element. uiElementCount--; if( RC_BAD( rc = blockCounts( pUntilStack, 0, pUntilStack->uiCurOffset, NULL, &uiTempElementCount))) { goto Exit; } uiElementCount += (uiTempElementCount - 1); uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; uiTotalKeys += uiEstKeyCount * uiElementCount; // Do the obvious check to see if the blocks are neighbors. if( (FLMUINT)pFromStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == pUntilStack->ui32BlkAddr) { goto Exit; } // Recompute the estimated element count on every b-tree level // because the compression is better the lower in the b-tree we go. uiEstElementCount = getAvgKeyCount( pFromStack, pUntilStack, uiAvgBlkFullness); // Adjust the estimated key/ref count to be the counts from a complete // (not partial) block starting at this level going to the leaf. uiEstKeyCount *= uiEstElementCount; uiEstBlocksBetween *= uiEstElementCount; } Exit: if( puiKeyCount) { *puiKeyCount = uiTotalKeys; } if( puiBlockCount) { *puiBlockCount = uiTotalBlocksBetween; } return( rc); } /*************************************************************************** Desc: Private method to count the number of unique keys between two points. The count returned is inclusive of the first and last offsets. ****************************************************************************/ RCODE F_Btree::blockCounts( F_BTSK_p pStack, FLMUINT uiFirstOffset, FLMUINT uiLastOffset, FLMUINT * puiKeyCount, FLMUINT * puiElementCount) { RCODE rc = NE_XFLM_OK; FLMUINT uiKeyCount; FLMUINT uiElementCount; FLMBYTE * pucBlk; FLMBYTE * pucEntry; // Debug checks. flmAssert( uiFirstOffset <= uiLastOffset); flmAssert( uiLastOffset <= (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)); uiKeyCount = uiElementCount = 0; pucBlk = (FLMBYTE *)pStack->pBlkHdr; // Loop gathering the statistics. while( uiFirstOffset <= uiLastOffset) { uiElementCount++; if( puiKeyCount) { pucEntry = BtEntry( pucBlk, uiFirstOffset); // We only have to worry about first key elements when we are at the // leaf level and we are keeping data at that level. if( pStack->uiLevel == 0 && m_bData) { if( bteFirstElementFlag( pucEntry)) { uiKeyCount++; } } else { uiKeyCount++; } } // Next element. if( uiFirstOffset == (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1)) { break; } else { uiFirstOffset++; } } if( puiKeyCount) { *puiKeyCount = uiKeyCount; } if( puiElementCount) { *puiElementCount = uiElementCount; } return( rc); } /*************************************************************************** Desc: Similar to computeCounts, except we use the stored counts. ****************************************************************************/ RCODE F_Btree::getStoredCounts( F_BTSK_p pFromStack, F_BTSK_p pUntilStack, FLMUINT * puiBlockCount, FLMUINT * puiKeyCount, FLMBOOL * pbTotalsEstimated, FLMUINT uiAvgBlkFullness) { RCODE rc = NE_XFLM_OK; FLMUINT uiOmittedKeys; FLMUINT uiTotalKeys; FLMUINT uiEstBlocksBetween; FLMUINT uiTotalBlocksBetween; *pbTotalsEstimated = FALSE; *puiBlockCount = 0; uiTotalBlocksBetween = 0; // Are these blocks adjacent? if( pFromStack->pBlkHdr->stdBlkHdr.ui32NextBlkInChain == pUntilStack->ui32BlkAddr) { *puiKeyCount = (pFromStack->pBlkHdr->ui16NumKeys - pFromStack->uiCurOffset) + pUntilStack->uiCurOffset + 1; goto Exit; } *pbTotalsEstimated = TRUE; // How many keys are excluded in the From and Until blocks? uiOmittedKeys = countRangeOfKeys( pFromStack, 0, pFromStack->uiCurOffset) - 1; uiOmittedKeys += countRangeOfKeys( pUntilStack, pUntilStack->uiCurOffset, pUntilStack->pBlkHdr->ui16NumKeys - 1) - 1; uiTotalKeys = 0; uiEstBlocksBetween = 1; for( ;;) { FLMUINT uiElementCount; FLMUINT uiTempElementCount; FLMUINT uiEstElementCount; // Go up a b-tree level and check out how far apart the elements are. pFromStack++; pUntilStack++; if( RC_BAD( rc = getCacheBlocks( pFromStack, pUntilStack))) { goto Exit; } // Share the same block? We can get the actual key count now. if( pFromStack->ui32BlkAddr == pUntilStack->ui32BlkAddr) { if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, pUntilStack->uiCurOffset, NULL, &uiElementCount))) { goto Exit; } // Don't count the pFromStack current element. uiElementCount -= 2; uiTotalBlocksBetween += uiEstBlocksBetween * (uiElementCount > 0 ? uiElementCount : 1); // Add one to the lasty offset to include the last entry in the count. uiTotalKeys = countRangeOfKeys( pFromStack, pFromStack->uiCurOffset, pUntilStack->uiCurOffset); *puiKeyCount = uiTotalKeys - uiOmittedKeys; *puiBlockCount = uiTotalBlocksBetween; goto Exit; } // How many to exclude from the From & Until blocks. if( pFromStack->uiCurOffset) { uiOmittedKeys += countRangeOfKeys( pFromStack, 0, pFromStack->uiCurOffset - 1); } uiOmittedKeys += countRangeOfKeys( pUntilStack, pUntilStack->uiCurOffset + 1, pUntilStack->pBlkHdr->ui16NumKeys - 1); // Gather the counts in the from and until non-leaf blocks. if( RC_BAD( rc = blockCounts( pFromStack, pFromStack->uiCurOffset, pFromStack->pBlkHdr->ui16NumKeys - 1, NULL, &uiElementCount))) { goto Exit; } // Don't count the first element. uiElementCount--; if( RC_BAD( rc = blockCounts( pUntilStack, 0, pUntilStack->uiCurOffset, NULL, &uiTempElementCount))) { goto Exit; } uiElementCount += (uiTempElementCount - 1); uiTotalBlocksBetween += uiEstBlocksBetween * uiElementCount; // We are not going to check if these blocks are neighbors here because // we want to find the common parent. That will tell us what the actual // counts are at the leaf level. // Recompute the estimated element count on every b-tree level // because the compression is better the lower in the b-tree we go. uiEstElementCount = getAvgKeyCount( pFromStack, pUntilStack, uiAvgBlkFullness); uiEstBlocksBetween *= uiEstElementCount; } Exit: return( rc); } /*************************************************************************** Desc: Retrieve the blocks identified in the two stack entries. Used in computing counts (btComputeCounts etc.) ****************************************************************************/ RCODE F_Btree::getCacheBlocks( F_BTSK_p pStack1, F_BTSK_p pStack2) { RCODE rc = NE_XFLM_OK; // If these blocks are at the root level, we must ensure that we retrieve // the root block. The root block can potentially change address, so // we wil reset it here to be sure. if( pStack1->uiLevel == m_uiRootLevel) { pStack1->ui32BlkAddr = (FLMUINT32)m_pLFile->uiRootBlk; } if( pStack2->uiLevel == m_uiRootLevel) { pStack2->ui32BlkAddr = (FLMUINT32)m_pLFile->uiRootBlk; } if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pStack1->ui32BlkAddr, NULL, &pStack1->pSCache))) { goto Exit; } pStack1->pBlkHdr = (F_BTREE_BLK_HDR *)pStack1->pSCache->m_pBlkHdr; if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pStack2->ui32BlkAddr, NULL, &pStack2->pSCache))) { goto Exit; } pStack2->pBlkHdr = (F_BTREE_BLK_HDR *)pStack2->pSCache->m_pBlkHdr; Exit: return( rc); } /*************************************************************************** Desc: Method to tally the counts in a block between (inclusive) the uiFromOffset & uiUntilOffset parameters. ****************************************************************************/ FLMUINT F_Btree::countRangeOfKeys( F_BTSK_p pFromStack, FLMUINT uiFromOffset, FLMUINT uiUntilOffset) { FLMUINT uiCount = 0; FLMBYTE * pucBlk; FLMUINT uiLoop = uiFromOffset; FLMBYTE * pucEntry; FLMUINT uiBlkType; pucBlk = (FLMBYTE *)pFromStack->pBlkHdr; uiBlkType = getBlkType( pucBlk); if( uiBlkType == BT_NON_LEAF_COUNTS) { while( uiLoop < uiUntilOffset) { pucEntry = BtEntry( pucBlk, uiLoop); pucEntry += 4; uiCount += FB2UD( pucEntry); uiLoop++; } } else { uiCount = uiUntilOffset; } return( uiCount); } /*************************************************************************** Desc: Method to estimate the averge number of keys, based on the anticipated average block usage (passed in) and the actual block usage. ****************************************************************************/ FINLINE FLMUINT F_Btree::getAvgKeyCount( F_BTSK_p pFromStack, F_BTSK_p pUntilStack, FLMUINT uiAvgBlkFullness) { FLMUINT uiFromUsed; FLMUINT uiUntilUsed; FLMUINT uiTotalUsed; FLMUINT uiFromKeys; FLMUINT uiUntilKeys; FLMUINT uiTotalKeys; uiFromUsed = m_uiBlockSize - ((F_BLK_HDR *)pFromStack->pBlkHdr)->ui16BlkBytesAvail; uiUntilUsed = m_uiBlockSize - ((F_BLK_HDR *)pUntilStack->pBlkHdr)->ui16BlkBytesAvail; uiTotalUsed = uiFromUsed + uiUntilUsed; uiFromKeys = pFromStack->pBlkHdr->ui16NumKeys; uiUntilKeys = pUntilStack->pBlkHdr->ui16NumKeys; uiTotalKeys = uiFromKeys + uiUntilKeys; return( (uiAvgBlkFullness * uiTotalKeys) / uiTotalUsed); } /*************************************************************************** Desc: Method to test if two blocks can be merged together to make a single block. This is done only after a remove operation and is intended to try to consolidate space as much as possible. If we can consolidate two blocks, we will do it, then update the tree. ****************************************************************************/ RCODE F_Btree::mergeBlocks( FLMBOOL bLastEntry, FLMBOOL * pbMergedWithPrev, FLMBOOL * pbMergedWithNext, F_ELM_UPD_ACTION * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32PrevBlkAddr; F_CachedBlock * pPrevSCache = NULL; FLMUINT32 ui32NextBlkAddr; F_CachedBlock * pNextSCache = NULL; *pbMergedWithPrev = FALSE; *pbMergedWithNext = FALSE; // Our first check is to see if we can merge the current block with its // previous block. ui32PrevBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32PrevBlkInChain; if( ui32PrevBlkAddr) { // Get the block. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32PrevBlkAddr, NULL, &pPrevSCache))) { goto Exit; } // Is there room to merge? if( (FLMUINT)(pPrevSCache->m_pBlkHdr->ui16BlkBytesAvail + m_pStack->pSCache->m_pBlkHdr->ui16BlkBytesAvail) >= (FLMUINT)(m_uiBlockSize - sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr))) { // Looks like we can merge these two. We will move the content // of the previous block into this one. if( RC_BAD( rc = merge( &pPrevSCache, &m_pStack->pSCache))) { goto Exit; } // Save the changed block header address m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; // Update the counts for the current block before releasing it. if( m_bCounts) { if( RC_BAD( rc = updateCounts())) { goto Exit; } } if( bLastEntry) { // Need to save the replace information for the last entry in // the block before we move to the previous block. This will // allow us to do the replace later. FLMBYTE * pucEntry; const FLMBYTE * pucKey; FLMUINT uiKeyLen; pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->pBlkHdr->ui16NumKeys - 1); uiKeyLen = getEntryKeyLength( pucEntry, getBlkType( (FLMBYTE *)m_pStack->pBlkHdr), &pucKey); if( RC_BAD( rc = saveReplaceInfo( pucKey, uiKeyLen))) { goto Exit; } } // Move the stack to the previous entry if( RC_BAD( rc = moveStackToPrev( pPrevSCache))) { goto Exit; } pPrevSCache = NULL; flmAssert( m_pStack->pBlkHdr->ui16NumKeys == 0); // Free the empty block. if( RC_BAD( rc = deleteEmptyBlock())) { goto Exit; } // Now we want to remove the parent entry for the block that was // freed. m_pStack++; *peAction = ELM_REMOVE; *pbMergedWithPrev = TRUE; goto Exit; } else { // No room here so release the block. ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } } // Can we merge with the next block? ui32NextBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32NextBlkInChain; if( ui32NextBlkAddr) { // Get the block. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pNextSCache))) { goto Exit; } // Is there room to merge? if( (FLMUINT)(pNextSCache->m_pBlkHdr->ui16BlkBytesAvail + m_pStack->pSCache->m_pBlkHdr->ui16BlkBytesAvail) >= (FLMUINT)(m_uiBlockSize - sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr))) { // Looks like we can merge these two. if( RC_BAD( rc = merge( &m_pStack->pSCache, &pNextSCache))) { goto Exit; } // Save the changed block header address. m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; // Update the counts for the current block and the next block. if( m_bCounts) { pPrevSCache = m_pStack->pSCache; // Need to move the stack to the next entry. Don't let the current // block get released because we still need it. if( RC_BAD( rc = moveStackToNext( pNextSCache, FALSE))) { goto Exit; } pNextSCache = NULL; if( RC_BAD( rc = updateCounts())) { goto Exit; } // Move back to the original stack again. It's okay to release the // now current block. if( RC_BAD( rc = moveStackToPrev( pPrevSCache))) { goto Exit; } pPrevSCache = NULL; } flmAssert( m_pStack->pBlkHdr->ui16NumKeys == 0); // Free the empty block. if( RC_BAD( rc = deleteEmptyBlock())) { goto Exit; } // Now we want to remove the parent entry for the block that was freed. m_pStack++; *peAction = ELM_REMOVE; *pbMergedWithNext = TRUE; goto Exit; } else { // No room here so release the block. ScaReleaseCache( pNextSCache, FALSE); pNextSCache = NULL; } } Exit: if( *pbMergedWithPrev || *pbMergedWithNext) { if( m_pDb->m_pDbStats != NULL) { XFLM_LFILE_STATS * pLFileStats; if( (pLFileStats = m_pDb->getLFileStatPtr( m_pLFile)) != NULL) { pLFileStats->bHaveStats = TRUE; pLFileStats->ui64BlockCombines++; } } } if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } if( pNextSCache) { ScaReleaseCache( pNextSCache, FALSE); } return( rc); } /*************************************************************************** Desc: Method to move the contents of the ppFromSCache block into the ppToSCache block. Note that all merges are a move to next operation. ****************************************************************************/ RCODE F_Btree::merge( F_CachedBlock ** ppFromSCache, F_CachedBlock ** ppToSCache) { RCODE rc = NE_XFLM_OK; F_BTSK tempStack; F_BTSK_p pStack = NULL; F_CachedBlock * pSCache; F_BTREE_BLK_HDR * pBlkHdr; // May need to defragment the blocks first. pBlkHdr = (F_BTREE_BLK_HDR *)(*ppToSCache)->m_pBlkHdr; if( pBlkHdr->stdBlkHdr.ui16BlkBytesAvail != pBlkHdr->ui16HeapSize) { if( RC_BAD( rc = defragmentBlock( ppToSCache))) { goto Exit; } } // Make a temporary stack entry so we can "fool" the moveToNext // function into moving the entries for us. pSCache = *ppFromSCache; tempStack.pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; tempStack.ui32BlkAddr = pSCache->m_pBlkHdr->ui32BlkAddr; tempStack.pSCache = pSCache; tempStack.uiCurOffset = 0; tempStack.uiLevel = m_pStack->uiLevel; tempStack.pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pSCache->m_pBlkHdr, 0); // Save the current m_pStack. pStack = m_pStack; m_pStack = &tempStack; // Now do the move if( RC_BAD( rc = moveToNext( tempStack.pBlkHdr->ui16NumKeys - 1, 0, ppToSCache))) { goto Exit; } // Return the changed block structure *ppFromSCache = tempStack.pSCache; Exit: // Must always restore the stack. m_pStack = pStack; return( rc); } /*************************************************************************** Desc: Method to test if the src and dst entries can be combined into one entry. If they can, then they will be combined and stored in the m_pucTempBlk buffer. ****************************************************************************/ RCODE F_Btree::combineEntries( F_BTREE_BLK_HDR * pSrcBlkHdr, FLMUINT uiSrcOffset, F_BTREE_BLK_HDR * pDstBlkHdr, FLMUINT uiDstOffset, FLMBOOL * pbEntriesCombined, FLMUINT * puiEntrySize) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucSrcEntry; FLMBYTE * pucDstEntry; FLMUINT uiSrcKeyLen; FLMUINT uiDstKeyLen; const FLMBYTE * pucSrcKey; const FLMBYTE * pucDstKey; FLMUINT uiFlags = 0; FLMBYTE * pucTmp; FLMUINT uiSrcOADataLen; FLMUINT uiDstOADataLen; const FLMBYTE * pucSrcData; const FLMBYTE * pucDstData; FLMUINT uiSrcDataLen; FLMUINT uiDstDataLen; FLMUINT uiEntrySize; *pbEntriesCombined = FALSE; *puiEntrySize = 0; if( pDstBlkHdr->ui16NumKeys == 0) { goto Exit; } if( pSrcBlkHdr->ui16NumKeys == 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( getBlkType( (FLMBYTE *)pSrcBlkHdr) != BT_LEAF_DATA) { goto Exit; } pucSrcEntry = BtEntry( (FLMBYTE *)pSrcBlkHdr, uiSrcOffset); pucDstEntry = BtEntry( (FLMBYTE *)pDstBlkHdr, uiDstOffset); // Do we have the same key? uiSrcKeyLen = getEntryKeyLength( pucSrcEntry, BT_LEAF_DATA, &pucSrcKey); uiDstKeyLen = getEntryKeyLength( pucDstEntry, BT_LEAF_DATA, &pucDstKey); if( uiSrcKeyLen != uiDstKeyLen) { // Not the same key. goto Exit; } if( f_memcmp( pucSrcKey, pucDstKey, uiSrcKeyLen) != 0) { // Not the same key. goto Exit; } // They match, so we can combine them. pucTmp = &m_pucTempBlk[ 1]; // Key length position uiFlags = (pucDstEntry[0] & (BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT)) | (pucSrcEntry[0] & (BTE_FLAG_FIRST_ELEMENT | BTE_FLAG_LAST_ELEMENT)); uiEntrySize = 1; if( uiSrcKeyLen > ONE_BYTE_SIZE) { uiFlags |= BTE_FLAG_KEY_LEN; UW2FBA( (FLMUINT16)uiSrcKeyLen, pucTmp); pucTmp += 2; uiEntrySize += 2; } else { *pucTmp = (FLMBYTE)uiSrcKeyLen; pucTmp++; uiEntrySize++; } uiSrcDataLen = btGetEntryDataLength( pucSrcEntry, &pucSrcData, &uiSrcOADataLen, NULL); uiDstDataLen = btGetEntryDataLength( pucDstEntry, &pucDstData, &uiDstOADataLen, NULL); if( (uiSrcDataLen + uiDstDataLen) > ONE_BYTE_SIZE) { uiFlags |= BTE_FLAG_DATA_LEN; UW2FBA( (FLMUINT16)(uiSrcDataLen + uiDstDataLen), pucTmp); pucTmp += 2; uiEntrySize += 2; } else { *pucTmp = (FLMBYTE)(uiSrcDataLen + uiDstDataLen); pucTmp++; uiEntrySize++; } // Verify the OA Data length if( (*pucSrcEntry & BTE_FLAG_OA_DATA_LEN) && (uiSrcOADataLen > (uiSrcDataLen + uiDstDataLen))) { uiFlags |= BTE_FLAG_OA_DATA_LEN; UD2FBA( (FLMUINT32)uiSrcOADataLen, pucTmp); pucTmp += 4; uiEntrySize += 4; } else if( (*pucDstEntry & BTE_FLAG_OA_DATA_LEN) && (uiDstOADataLen > (uiSrcDataLen + uiDstDataLen))) { uiFlags |= BTE_FLAG_OA_DATA_LEN; UD2FBA( (FLMUINT32)uiDstOADataLen, pucTmp); pucTmp += 4; uiEntrySize += 4; } f_memcpy( pucTmp, pucSrcKey, uiSrcKeyLen); pucTmp += uiSrcKeyLen; uiEntrySize += uiSrcKeyLen; // Need to put the entry together in the right order. If the Src block is // before the Dst block, then we will put down the Src data first. if( pSrcBlkHdr->stdBlkHdr.ui32NextBlkInChain == pDstBlkHdr->stdBlkHdr.ui32BlkAddr) { f_memcpy( pucTmp, pucSrcData, uiSrcDataLen); pucTmp += uiSrcDataLen; uiEntrySize += uiSrcDataLen; f_memcpy( pucTmp, pucDstData, uiDstDataLen); uiEntrySize += uiDstDataLen; } else { f_memcpy( pucTmp, pucDstData, uiDstDataLen); uiEntrySize += uiDstDataLen; pucTmp += uiDstDataLen; f_memcpy( pucTmp, pucSrcData, uiSrcDataLen); uiEntrySize += uiSrcDataLen; } m_pucTempBlk[ 0] = (FLMBYTE)uiFlags; *puiEntrySize = uiEntrySize; *pbEntriesCombined = TRUE; Exit: return( rc); } /*************************************************************************** Desc: Method to move a block from one location to another. ****************************************************************************/ RCODE F_Btree::btMoveBlock( FLMUINT32 ui32FromBlkAddr, FLMUINT32 ui32ToBlkAddr) { RCODE rc = NE_XFLM_OK; FLMUINT uiType; if( !m_bOpened || m_bSetupForRead || m_bSetupForReplace || (m_bSetupForWrite)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); // Verify the Txn type if( m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS ? NE_XFLM_NO_TRANS_ACTIVE : NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // Get the From block and retrieve the last key in the block. Make note // of the level of the block. We will need this to make sure we get the // right block. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32FromBlkAddr, NULL, &m_pSCache))) { goto Exit; } // Find out if this is a Btree block or a DO block. uiType = getBlkType((FLMBYTE *)m_pSCache->m_pBlkHdr); if( uiType == BT_FREE) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( uiType == BT_DATA_ONLY) { if( RC_BAD( rc = moveDOBlock( ui32FromBlkAddr, ui32ToBlkAddr))) { goto Exit; } } else { if( RC_BAD( rc = moveBtreeBlock( ui32FromBlkAddr, ui32ToBlkAddr))) { goto Exit; } } Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } return( rc); } /*************************************************************************** Desc: Move a Btree block from one address to another, updating its parent. ****************************************************************************/ RCODE F_Btree::moveBtreeBlock( FLMUINT32 ui32FromBlkAddr, FLMUINT32 ui32ToBlkAddr) { RCODE rc = NE_XFLM_OK; F_BTREE_BLK_HDR * pBlkHdr = NULL; F_BTREE_BLK_HDR * pNewBlkHdr = NULL; FLMBYTE * pucEntry; const FLMBYTE * pucKeyRV = NULL; FLMBYTE * pucKey = NULL; FLMUINT uiBlkLevel; FLMBYTE * pucSrc; FLMBYTE * pucDest; F_CachedBlock * pSCache = NULL; FLMUINT uiKeyLen; // m_pSCache has already been retrieved. flmAssert( m_pSCache); pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; uiBlkLevel = pBlkHdr->ui8BlkLevel; pucEntry = BtLastEntry( (FLMBYTE *)pBlkHdr); uiKeyLen = getEntryKeyLength( pucEntry, getBlkType((FLMBYTE *)pBlkHdr), &pucKeyRV); if( RC_BAD( rc = f_calloc( uiKeyLen, &pucKey))) { goto Exit; } f_memcpy( pucKey, pucKeyRV, uiKeyLen); // Release the block and search for the key. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, XFLM_EXACT))) { // We must find it! RC_UNEXPECTED_ASSERT( rc); goto Exit; } // Verify that we found the right block. m_pStack = &m_Stack[ uiBlkLevel]; if( ui32FromBlkAddr != m_pStack->ui32BlkAddr) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)m_pStack->pBlkHdr, 0); pBlkHdr = m_pStack->pBlkHdr; // Get the new block and verify that it is a free block. if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32ToBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( getBlkType( (FLMBYTE *)m_pSCache->m_pBlkHdr) != BT_FREE) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Update the header of the new block to point to the prev and next // blocks etc ... if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } pNewBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; pNewBlkHdr->stdBlkHdr.ui32PrevBlkInChain = pBlkHdr->stdBlkHdr.ui32PrevBlkInChain; pNewBlkHdr->stdBlkHdr.ui32NextBlkInChain = pBlkHdr->stdBlkHdr.ui32NextBlkInChain; pNewBlkHdr->stdBlkHdr.ui16BlkBytesAvail = pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; pNewBlkHdr->stdBlkHdr.ui8BlkType = pBlkHdr->stdBlkHdr.ui8BlkType; pNewBlkHdr->stdBlkHdr.ui8BlkFlags = pBlkHdr->stdBlkHdr.ui8BlkFlags; pNewBlkHdr->ui16LogicalFile = pBlkHdr->ui16LogicalFile; pNewBlkHdr->ui16NumKeys = pBlkHdr->ui16NumKeys; pNewBlkHdr->ui8BlkLevel = pBlkHdr->ui8BlkLevel; pNewBlkHdr->ui8BTreeFlags = pBlkHdr->ui8BTreeFlags; pNewBlkHdr->ui16HeapSize = pBlkHdr->ui16HeapSize; // Get the previous and next blocks and set their next and prev addresses. if( pBlkHdr->stdBlkHdr.ui32PrevBlkInChain) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pBlkHdr->stdBlkHdr.ui32PrevBlkInChain, NULL, &pSCache))) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) { goto Exit; } flmAssert( pSCache->m_pBlkHdr->ui32NextBlkInChain == ui32FromBlkAddr); pSCache->m_pBlkHdr->ui32NextBlkInChain = ui32ToBlkAddr; ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } if( pBlkHdr->stdBlkHdr.ui32NextBlkInChain) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pBlkHdr->stdBlkHdr.ui32NextBlkInChain, NULL, &pSCache))) { goto Exit; } if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) { goto Exit; } flmAssert( pSCache->m_pBlkHdr->ui32PrevBlkInChain == ui32FromBlkAddr); pSCache->m_pBlkHdr->ui32PrevBlkInChain = ui32ToBlkAddr; ScaReleaseCache( pSCache, FALSE); pSCache = NULL; } // Copy the content of the old block into the new block. pucSrc = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr); pucDest = (FLMBYTE *)pNewBlkHdr + sizeofBTreeBlkHdr( pNewBlkHdr); f_memcpy( pucDest, pucSrc, m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr)); if( isRootBlk( pBlkHdr)) { // We need to update LFile to reflect the new root block address. F_COLLECTION * pCollection = NULL; F_Database * pDatabase= m_pDb->m_pDatabase; m_pLFile->uiRootBlk = ui32ToBlkAddr; if( m_pLFile->eLfType == XFLM_LF_COLLECTION) { // Cannot use collections in a temporary DB. flmAssert( !m_bTempDb); if( RC_BAD( rc = m_pDb->m_pDict->getCollection( m_pLFile->uiLfNum, &pCollection))) { goto Exit; } } rc = pDatabase->lFileWrite( m_pDb, pCollection, m_pLFile); goto Exit; } // Move up one level to the parent entry. m_pStack++; flmAssert( m_pStack->pSCache); // Log that we are making a change to the block. if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) { goto Exit; } m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; // Update the parent block with a new address for the new block. pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); UD2FBA( ui32ToBlkAddr, pucEntry); Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } if( pSCache) { ScaReleaseCache( pSCache, FALSE); } f_free( &pucKey); releaseBlocks( TRUE); return( rc); } /*************************************************************************** Desc: Move a DO block from one address to another, updating its reference btree entry. ****************************************************************************/ RCODE F_Btree::moveDOBlock( FLMUINT32 ui32FromBlkAddr, FLMUINT32 ui32ToBlkAddr) { RCODE rc = NE_XFLM_OK; F_BLK_HDR * pBlkHdr = NULL; F_BLK_HDR * pNewBlkHdr = NULL; FLMBYTE * pucEntry; FLMBYTE * pucKey = NULL; FLMBYTE * pucSrc; FLMBYTE * pucDest; F_CachedBlock * pSCache = NULL; F_CachedBlock * pPrevSCache = NULL; F_CachedBlock * pNextSCache = NULL; FLMUINT uiKeyLen; FLMUINT uiOADataLen; const FLMBYTE * pucData; FLMUINT32 ui32DOBlkAddr; FLMUINT uiDataLen; FLMBYTE ucDataBuffer[ sizeof(FLMUINT32)]; FLMUINT uiBlkHdrSize; // m_pSCache has already been retrieved. flmAssert( m_pSCache); // Log that we are changing this block. if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) { goto Exit; } pBlkHdr = m_pSCache->m_pBlkHdr; // Get the new block and verify that it is a free block. if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32ToBlkAddr, NULL, &pSCache))) { goto Exit; } if( getBlkType( (FLMBYTE *)pSCache->m_pBlkHdr) != BT_FREE) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Update the header of the new block to point to the prev and next // blocks etc.. if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) { goto Exit; } pNewBlkHdr = pSCache->m_pBlkHdr; pNewBlkHdr->ui32PrevBlkInChain = pBlkHdr->ui32PrevBlkInChain; pNewBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32NextBlkInChain; pNewBlkHdr->ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; pNewBlkHdr->ui8BlkType = pBlkHdr->ui8BlkType; pNewBlkHdr->ui8BlkFlags = pBlkHdr->ui8BlkFlags; // Get the previous and next blocks and set their next and prev addresses. if( pBlkHdr->ui32PrevBlkInChain) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pBlkHdr->ui32PrevBlkInChain, NULL, &pPrevSCache))) { goto Exit; } if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pPrevSCache))) { goto Exit; } flmAssert( pPrevSCache->m_pBlkHdr->ui32NextBlkInChain == ui32FromBlkAddr); pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = ui32ToBlkAddr; ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } if( pBlkHdr->ui32NextBlkInChain) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, pBlkHdr->ui32NextBlkInChain, NULL, &pNextSCache))) { goto Exit; } if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pNextSCache))) { goto Exit; } flmAssert( pNextSCache->m_pBlkHdr->ui32PrevBlkInChain == ui32FromBlkAddr); pNextSCache->m_pBlkHdr->ui32PrevBlkInChain = ui32ToBlkAddr; ScaReleaseCache( pNextSCache, FALSE); pNextSCache = NULL; } // Copy the content of the old block into the new block. uiBlkHdrSize = sizeofDOBlkHdr((F_BLK_HDR *)pBlkHdr); pucSrc = (FLMBYTE *)pBlkHdr + uiBlkHdrSize; pucDest = (FLMBYTE *)pNewBlkHdr + uiBlkHdrSize; f_memcpy( pucDest, pucSrc, m_uiBlockSize - uiBlkHdrSize); // Do we need to update the reference btree entry. if( pBlkHdr->ui32PrevBlkInChain == 0) { // Get the key from the beginning of the block. uiKeyLen = FB2UW( pucDest); pucKey = pucDest + sizeof( FLMUINT16); if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, XFLM_EXACT))) { // We must find it! RC_UNEXPECTED_ASSERT( rc); goto Exit; } // Verify that we found the right block. pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); if( !bteDataBlockFlag( pucEntry)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } uiDataLen = btGetEntryDataLength( pucEntry, &pucData, &uiOADataLen, NULL); ui32DOBlkAddr = bteGetBlkAddr( pucData); if( ui32DOBlkAddr != ui32FromBlkAddr) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( uiDataLen != sizeof( ucDataBuffer)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Make the data entry with the new block address UD2FBA( ui32ToBlkAddr, ucDataBuffer); if( RC_BAD( rc = updateEntry( pucKey, uiKeyLen, ucDataBuffer, uiOADataLen, ELM_REPLACE_DO))) { goto Exit; } } Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } if( pSCache) { ScaReleaseCache( pSCache, FALSE); } if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } if( pNextSCache) { ScaReleaseCache( pNextSCache, FALSE); } releaseBlocks( TRUE); return( rc); } /*************************************************************************** Desc: Method to move the read point in an entry to a particular position within the entry. This method will move to a previous or a later position. ****************************************************************************/ RCODE F_Btree::btSetReadPosition( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiPosition) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEntry; F_BLK_HDR * pBlkHdr = NULL; FLMUINT32 ui32BlkAddr; FLMBOOL bLastElement = FALSE; if( !m_bOpened || !m_bSetupForRead) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } // We cannot position to a point beyond the end of the current entry. if( uiPosition >= m_uiOADataLength) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // If the transaction Id or the Block Change Count has changed, // we must re-sync ourselves. if( (m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt)) { // Test to see if we really need to re-synch... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || ( m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // We must call btLocateEntry so we can re-initialize the read. if( !m_bFirstRead) { if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT))) { goto Exit; } // Will need a new version of this block. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } else { rc = RC_SET(NE_XFLM_BTREE_BAD_STATE); goto Exit; } } } // The easiest case to handle is when we want to position within the // current entry. We should not have to worry about the data only blocks // because the m_uiDataLength and m_uiDataRemaining are being set correctly // in setupReadState (via btLocateEntry, btNextEntry, btPrevEntry, // btFirstEntry and btLastEntry) which is always called before this method is // called. if( (uiPosition < (m_uiOffsetAtStart + m_uiDataLength)) && (uiPosition >= m_uiOffsetAtStart)) { m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); m_uiOADataRemaining = m_uiOADataLength - uiPosition; goto Exit; } // Get the current block. It is either a DO or a Btree block. if( m_pSCache == NULL) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } } // The next case is when the new position is in a *previous* entry, possibly // a previous block. while( uiPosition < m_uiOffsetAtStart) { pBlkHdr = m_pSCache->m_pBlkHdr; // Are we dealing with DataOnly blocks? if( m_bDataOnlyBlock) { ui32BlkAddr = pBlkHdr->ui32PrevBlkInChain; flmAssert( ui32BlkAddr); ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddr, NULL, &m_pSCache))) { goto Exit; } m_ui32CurBlkAddr = ui32BlkAddr; pBlkHdr = m_pSCache->m_pBlkHdr; m_uiDataLength = m_uiBlockSize - pBlkHdr->ui16BlkBytesAvail - sizeofDOBlkHdr((F_BLK_HDR *)pBlkHdr); if( !pBlkHdr->ui32PrevBlkInChain) { FLMBYTE * pucPtr = (FLMBYTE *)pBlkHdr + sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); FLMUINT16 ui16KeyLen = FB2UW( pucPtr); // We need to adjust for the key in the first block. m_uiDataLength -= ui16KeyLen; } // Decrement by the size of the current data m_uiOffsetAtStart -= m_uiDataLength; } else { // Backup to the previous element. This may or may not get // another block if( RC_BAD( rc = backupToPrevElement( FALSE))) { goto Exit; } pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); // Make sure we are still looking at the same key etc. if( !checkContinuedEntry( pucKey, uiKeyLen, &bLastElement, pucEntry, getBlkType( (FLMBYTE *)m_pSCache->m_pBlkHdr))) { // Should always match at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } m_uiDataLength = btGetEntryDataLength( pucEntry, NULL, NULL, NULL); m_uiOffsetAtStart -= m_uiDataLength; } } // Did we find the block? if( (uiPosition < (m_uiOffsetAtStart + m_uiDataLength)) && (uiPosition >= m_uiOffsetAtStart)) { m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); m_uiOADataRemaining = m_uiOADataLength - uiPosition; goto Exit; } // Finally, we realize that the new position is beyond the current entry. while( uiPosition >= (m_uiOffsetAtStart + m_uiDataLength)) { flmAssert( m_uiDataLength + m_uiOffsetAtStart <= m_uiOADataLength); // Get the next entry. pBlkHdr = m_pSCache->m_pBlkHdr; // Are we dealing with DataOnly blocks? if( m_bDataOnlyBlock) { ui32BlkAddr = pBlkHdr->ui32NextBlkInChain; flmAssert( ui32BlkAddr); ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32BlkAddr, NULL, &m_pSCache))) { goto Exit; } m_ui32CurBlkAddr = ui32BlkAddr; pBlkHdr = m_pSCache->m_pBlkHdr; // Increment by the size of the previous data. Note that in this // case, we do not have to be concerned about the key in the first // DO block since we will never move forward to it. m_uiOffsetAtStart += m_uiDataLength; m_uiDataLength = m_uiBlockSize - pBlkHdr->ui16BlkBytesAvail - sizeofDOBlkHdr((F_BLK_HDR *)pBlkHdr); } else { // Advance to the next element. This may or may not get another block. // Be sure we do not advance the stack since we do not have one. if( RC_BAD( rc = advanceToNextElement( FALSE))) { goto Exit; } pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); // Make sure we are still looking at the same key etc. if( !checkContinuedEntry( pucKey, uiKeyLen, &bLastElement, pucEntry, getBlkType( (FLMBYTE *)m_pSCache->m_pBlkHdr))) { // Should always match at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } // Get the data length of the current entry. m_uiOffsetAtStart += m_uiDataLength; m_uiDataLength = btGetEntryDataLength( pucEntry, NULL, NULL, NULL); } } // Did we find the block? If we still don't find it, then we // have a big problem. if( (uiPosition >= (m_uiOffsetAtStart + m_uiDataLength)) || (uiPosition < m_uiOffsetAtStart)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); m_uiOADataRemaining = m_uiOADataLength - uiPosition; updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, m_pSCache->m_ui64HighTransID); Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_Btree::btGetReadPosition( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT * puiPosition) { RCODE rc = NE_XFLM_OK; if( !m_bOpened || !m_bSetupForRead) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); goto Exit; } flmAssert( puiPosition); // If the transaction ID or the block change count has changed, // we must re-sync ourselves. if( !m_bTempDb && ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) { // Test to see if we really need to re-sync ... if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, m_ui32CurBlkAddr, NULL, &m_pSCache))) { goto Exit; } if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) { // We must call btLocateEntry so we can re-initialize the read if( !m_bFirstRead) { if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT))) { goto Exit; } // Will need a new version of this block. ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } else { rc = RC_SET(NE_XFLM_BTREE_BAD_STATE); goto Exit; } } } *puiPosition = m_uiOffsetAtStart + (m_uiDataLength - m_uiDataRemaining); if( m_pSCache) { updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, m_pSCache->m_ui64HighTransID); } Exit: if( m_pSCache) { ScaReleaseCache( m_pSCache, FALSE); m_pSCache = NULL; } releaseBlocks( FALSE); return( rc); } /*************************************************************************** Desc: Performs a consistancy check on the BTree NOTE: Must be performed inside of a read transaction! ****************************************************************************/ RCODE F_Btree::btCheck( BTREE_ERR_STRUCT * pErrStruct) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32NextBlkAddr = 0; FLMUINT32 ui32NextLevelBlkAddr = 0; FLMUINT32 ui32ChildBlkAddr = 0; FLMUINT32 ui32DOBlkAddr = 0; FLMUINT uiNumKeys; const FLMBYTE * pucPrevKey; FLMUINT uiPrevKeySize; const FLMBYTE * pucCurKey; FLMUINT uiCurKeySize; F_CachedBlock * pCurrentBlk = NULL; F_CachedBlock * pPrevSCache = NULL; FLMBYTE * pBlk = NULL; FLMBYTE * pucEntry = NULL; FLMBYTE * pucPrevEntry = NULL; F_CachedBlock * pChildBlk = NULL; FLMUINT16 * puiOffsetArray; BTREE_ERR_STRUCT localErrStruct; FLMINT iCmpResult; FLMUINT uiOADataLength = 0; // Verify the Txn type if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // Initial setup... ui32NextLevelBlkAddr = (FLMUINT32)m_pLFile->uiRootBlk; f_memset( &localErrStruct, 0, sizeof( localErrStruct)); localErrStruct.uiBlockSize = m_uiBlockSize; // While there's a next level.... while( ui32NextLevelBlkAddr) { localErrStruct.uiLevels++; ui32NextBlkAddr = ui32NextLevelBlkAddr; // Update uiNextLevelBlkAddr if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pCurrentBlk))) { localErrStruct.type = SCA_GET_BLOCK_FAILED; f_sprintf( localErrStruct.szMsg, "Failed to get block at %X", ui32NextBlkAddr); goto Exit; } pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; puiOffsetArray = BtOffsetArray( pBlk, 0); if( (getBlkType( pBlk) == BT_LEAF) || (getBlkType( pBlk) == BT_LEAF_DATA)) { ui32NextLevelBlkAddr = 0; } else { pucEntry = BtEntry( pBlk, 0); // The child block address is the first part of the entry ui32NextLevelBlkAddr = bteGetBlkAddr( pucEntry); } if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } // While there's another block on this level... while( ui32NextBlkAddr) { // This loop assumes that pCurrentBlk and pBlk are already initialized. localErrStruct.uiBlocksChecked++; localErrStruct.uiAvgFreeSpace = (localErrStruct.uiAvgFreeSpace * (localErrStruct.uiBlocksChecked - 1) / localErrStruct.uiBlocksChecked) + (getBlkAvailSpace(pBlk) / localErrStruct.uiBlocksChecked); localErrStruct.ui64FreeSpace += getBlkAvailSpace(pBlk); localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiBlkCnt++; localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiBytesUsed += (m_uiBlockSize - getBlkAvailSpace(pBlk)); uiNumKeys = ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; // VISIT: Verify the block header fields /* ui32PrevBlkInChain = ui32BlkCRC = ui16BlkBytesAvail < ? ui8BlkLevel?? ui8BlkIsRoot?? */ // Verify that the keys are in order... // Make sure that we check the keys between blocks as well. if( pPrevSCache) { pucEntry = BtLastEntry( (FLMBYTE *)pPrevSCache->m_pBlkHdr); uiPrevKeySize = getEntryKeyLength( pucEntry, getBlkType( (FLMBYTE *)pPrevSCache->m_pBlkHdr), &pucPrevKey); } else { pucEntry = BtEntry( pBlk, 0); uiPrevKeySize = getEntryKeyLength( pucEntry, getBlkType( pBlk), &pucPrevKey); if( getBlkType(pBlk) == BT_LEAF_DATA) { if( bteFirstElementFlag( pucEntry)) { localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiFirstKeyCnt++; } } else { // Everything else is a first key. localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiFirstKeyCnt++; } } for( FLMUINT uiLoop = (pPrevSCache ? 0: 1); uiLoop < uiNumKeys; uiLoop++) { pucPrevEntry = pucEntry; pucEntry = BtEntry( pBlk, uiLoop); if( getBlkType(pBlk) == BT_LEAF_DATA) { if( bteFirstElementFlag( pucEntry)) { localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiFirstKeyCnt++; } } else { // Everything else is a first key. localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiFirstKeyCnt++; } uiCurKeySize = getEntryKeyLength( pucEntry, getBlkType( pBlk), &pucCurKey); // The last key in the last block of each level is an infinity marker // It must have a 0 keylength and if it's a leaf node, a 0 datalength. if( (uiLoop == uiNumKeys - 1) && (((F_BLK_HDR *)pBlk)->ui32NextBlkInChain == 0)) { // If the key size is not 0, or we're a leaf block, and the // data size is not 0 ... if( (uiCurKeySize != 0) || (((getBlkType( pBlk) == BT_LEAF_DATA)) && (btGetEntryDataLength( pucEntry, NULL, NULL, NULL) > 0))) { localErrStruct.type = INFINITY_MARKER; localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; f_sprintf( localErrStruct.szMsg, "Invalid Infinity Marker %ul", uiLoop); rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } } else { // Do a comparison of the previous and current keys ... if( RC_BAD( rc = compareKeys( pucPrevKey, uiPrevKeySize, pucCurKey, uiCurKeySize, &iCmpResult))) { goto Exit; } if( iCmpResult > 0) { localErrStruct.type = KEY_ORDER; localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; f_sprintf( localErrStruct.szMsg, "Key Number %ul", uiLoop); rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } if( getBlkType(pBlk) == BT_LEAF_DATA) { if( iCmpResult < 0) { flmAssert( *pucEntry & BTE_FLAG_FIRST_ELEMENT); } else if( iCmpResult == 0) { flmAssert( (*pucEntry & BTE_FLAG_FIRST_ELEMENT) == 0); flmAssert( (*pucPrevEntry & BTE_FLAG_LAST_ELEMENT) == 0); } } } pucPrevKey = pucCurKey; uiPrevKeySize = uiCurKeySize; } localErrStruct.uiNumKeys += uiNumKeys; localErrStruct.LevelStats[ localErrStruct.uiLevels - 1].uiKeyCnt += uiNumKeys; // If this is a leaf block, check for any pointers to data-only // blocks. Verify the blocks... if( getBlkType( pBlk) == BT_LEAF || getBlkType( pBlk) == BT_LEAF_DATA) { if( getBlkType( pBlk) == BT_LEAF_DATA) { for( FLMUINT uiLoop = 0; uiLoop < uiNumKeys; uiLoop++) { pucEntry = BtEntry( pBlk, uiLoop); if( bteDataBlockFlag( pucEntry)) { FLMBYTE ucDOBlkAddr[ 4]; if( RC_BAD( rc = btGetEntryData( pucEntry, &ucDOBlkAddr[ 0], 4, NULL))) { RC_UNEXPECTED_ASSERT( rc); localErrStruct.type = CATASTROPHIC_FAILURE; localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; f_sprintf( localErrStruct.szMsg, "getEntryData couldn't get the DO blk addr."); goto Exit; } ui32DOBlkAddr = bteGetBlkAddr( (FLMBYTE *)&ucDOBlkAddr[ 0]); // Verify that there is an OverallDataLength field if( bteOADataLenFlag( pucEntry) == 0) { localErrStruct.type = MISSING_OVERALL_DATA_LENGTH; localErrStruct.uiBlkAddr = ((F_BLK_HDR *)pBlk)->ui32BlkAddr; f_sprintf( localErrStruct.szMsg, "OverallDataLength field is missing"); } else { if( bteKeyLenFlag( pucEntry)) { uiOADataLength = FB2UD( pucEntry + 4); } else { uiOADataLength = FB2UD( pucEntry + 3); } } if( RC_BAD( rc = verifyDOBlkChain( ui32DOBlkAddr, uiOADataLength , &localErrStruct))) { goto Exit; } } } } } else { // This is a non-leaf block, verify that blocks exist for all // the child block addresses // NOTE: Also need to somehow verify that no two elements have the // same child block address... for( FLMUINT uiLoop = 0; uiLoop < uiNumKeys; uiLoop++) { pucEntry = BtEntry( pBlk, uiLoop); ui32ChildBlkAddr = bteGetBlkAddr( pucEntry); if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32ChildBlkAddr, NULL, &pChildBlk))) { localErrStruct.type = SCA_GET_BLOCK_FAILED; f_sprintf( localErrStruct.szMsg, "Failed to get block at %X", ui32ChildBlkAddr); goto Exit; } ScaReleaseCache( pChildBlk, FALSE); } } // Release the current block and get the next one ui32NextBlkAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); pPrevSCache = NULL; } pPrevSCache = pCurrentBlk; pCurrentBlk = NULL; if( ui32NextBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pCurrentBlk))) { localErrStruct.type = SCA_GET_BLOCK_FAILED; f_sprintf( localErrStruct.szMsg, "Failed to get block at %X", ui32ChildBlkAddr); goto Exit; } pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; } } } if( m_bCounts) { if( RC_BAD( rc = verifyCounts( &localErrStruct))) { goto Exit; } } Exit: if( pPrevSCache) { ScaReleaseCache( pPrevSCache, FALSE); } if( pCurrentBlk) { ScaReleaseCache( pCurrentBlk, FALSE); } f_memcpy( pErrStruct, &localErrStruct, sizeof( localErrStruct)); return( rc); } /*************************************************************************** Desc: Performs an integrity check on a chain of data-only blocks. Should only be called from btCheck(). Note that unlike btCheck(), errStruct CANNOT be NULL here. ****************************************************************************/ RCODE F_Btree::verifyDOBlkChain( FLMUINT uiDOAddr, // Address of first block in chain FLMUINT uiDataLength, // The length of the entire entry BTREE_ERR_STRUCT * errStruct) { RCODE rc = NE_XFLM_OK; FLMUINT uiRunningLength = 0; // A running total of the DataLength fields // for all of the blocks in this chain F_CachedBlock * pCurrentBlk = NULL; FLMUINT32 ui32NextAddr = (FLMUINT32)uiDOAddr; FLMBYTE * pBlk; FLMUINT uiDataSize; while( ui32NextAddr) { errStruct->LevelStats[ errStruct->uiLevels - 1].uiDOBlkCnt++; // Get the next block if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, ui32NextAddr, NULL, &pCurrentBlk))) { errStruct->type = SCA_GET_BLOCK_FAILED; f_sprintf( errStruct->szMsg, "Failed to get block at %X", uiDOAddr); goto Exit; } pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; // Verify that it's really a DO Block if( getBlkType( pBlk) != BT_DATA_ONLY) { rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); errStruct->type = NOT_DATA_ONLY_BLOCK; goto Exit; } // Update counts info in errStruct errStruct->LevelStats[ errStruct->uiLevels - 1].uiDOBytesUsed += m_uiBlockSize - pCurrentBlk->m_pBlkHdr->ui16BlkBytesAvail; // Update the data length running total uiDataSize = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlk) - ((F_BLK_HDR *)pBlk)->ui16BlkBytesAvail; if( ((F_BLK_HDR *)pBlk)->ui32PrevBlkInChain == 0) { FLMBYTE * pucPtr = pBlk + sizeofDOBlkHdr( (F_BLK_HDR *)pBlk); FLMUINT16 ui16KeyLen = FB2UW( pucPtr); uiDataSize -= (ui16KeyLen + 2); } uiRunningLength += uiDataSize; // Update ui32nextAddr ui32NextAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; // Release it when we no longer need it. ScaReleaseCache( pCurrentBlk, FALSE); pCurrentBlk = NULL; } // Check the calculated overall length vs. uiDataLength if( uiRunningLength != uiDataLength) { errStruct->type = BAD_DO_BLOCK_LENGTHS; rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } Exit: if( pCurrentBlk) { ScaReleaseCache( pCurrentBlk, FALSE); } if( rc == NE_XFLM_BTREE_ERROR) { f_sprintf( errStruct->szMsg, "Corrupt DO chain starting at %X", uiDOAddr); } return( NE_XFLM_OK); } /*************************************************************************** Desc: Method to check the counts in a database with counts. ****************************************************************************/ RCODE F_Btree::verifyCounts( BTREE_ERR_STRUCT * pErrStruct) { RCODE rc = NE_XFLM_OK; FLMUINT uiNextLevelBlkAddr; FLMUINT uiNextBlkAddr; FLMUINT uiChildBlkAddr; F_CachedBlock * pCurrentBlk = NULL; F_CachedBlock * pChildBlk = NULL; FLMBYTE * pucEntry; FLMUINT uiNumKeys; FLMUINT uiEntryNum; FLMUINT uiParentCounts; FLMUINT uiChildCounts; FLMBYTE * pBlk; FLMBOOL bDone = FALSE; flmAssert( m_bCounts); // Repeat at each level, starting at the root. uiNextLevelBlkAddr = m_pLFile->uiRootBlk; while( uiNextLevelBlkAddr) { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiNextLevelBlkAddr, NULL, &pCurrentBlk))) { goto Exit; } if( pCurrentBlk->m_pBlkHdr->ui8BlkType != BT_NON_LEAF_COUNTS) { ScaReleaseCache( pCurrentBlk, FALSE); pCurrentBlk = NULL; break; } pucEntry = BtEntry( (FLMBYTE *)pCurrentBlk->m_pBlkHdr, 0); uiNextLevelBlkAddr = bteGetBlkAddr( pucEntry); // For every entry in the block, and for every block on this level, // check that the counts match the actual counts in the corresponding // child block. bDone = FALSE; while( !bDone) { uiNumKeys = ((F_BTREE_BLK_HDR *)pCurrentBlk->m_pBlkHdr)->ui16NumKeys; pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; // Now check every entry in this block. for( uiEntryNum = 0; uiEntryNum < uiNumKeys; uiEntryNum++) { pucEntry = BtEntry( pBlk, uiEntryNum); uiChildBlkAddr = bteGetBlkAddr( pucEntry); pucEntry += 4; uiParentCounts = FB2UD( pucEntry); if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiChildBlkAddr, NULL, &pChildBlk))) { goto Exit; } uiChildCounts = countKeys( (FLMBYTE *)pChildBlk->m_pBlkHdr); if( uiChildCounts != uiParentCounts) { pErrStruct->type = BAD_COUNTS; pErrStruct->uiBlkAddr = pChildBlk->m_pBlkHdr->ui32BlkAddr; f_sprintf( pErrStruct->szMsg, "Counts do not match. Expected %d, got %d", uiParentCounts, uiChildCounts); rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); goto Exit; } ScaReleaseCache( pChildBlk, FALSE); pChildBlk = NULL; } // Now get the next block at this level. uiNextBlkAddr = pCurrentBlk->m_pBlkHdr->ui32NextBlkInChain; ScaReleaseCache( pCurrentBlk, FALSE); pCurrentBlk = NULL; if( uiNextBlkAddr == 0) { bDone = TRUE; } else { if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, uiNextBlkAddr, NULL, &pCurrentBlk))) { goto Exit; } } } } Exit: if( pCurrentBlk) { ScaReleaseCache( pCurrentBlk, FALSE); } if( pChildBlk) { ScaReleaseCache( pChildBlk, FALSE); } return( rc); } libxflaim-5.1.969/src/fslfileu.cpp0000644000175000017500000017420010511001742020345 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines to perform dictionary updates. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fslfileu.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" typedef struct FTravTag * F_TRAV_p; typedef struct FTravTag { F_DOMNode * pNode; ICD * pRefIcd; ICD * pIcd; IX_CONTEXT * pIxContext; F_TRAV_p pParent; F_TRAV_p pChild; } F_TRAV; FSTATIC void kyFreeIxContext( IXD * pIxd, IX_CONTEXT * pIxContext, IX_CONTEXT ** ppIxContextList); /**************************************************************************** Desc: Check a dictionary definition for duplicate names or numbers. If no number was assigned, assign one. If deleting, verify that the delete is allowed at this point. Freeze certain attributes so they cannot be changed. ****************************************************************************/ RCODE F_Db::checkDictDefInfo( FLMUINT64 ui64DocumentID, FLMBOOL bDeleting, FLMUINT * puiDictType, FLMUINT * puiDictNumber) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_DOMNode * pAttr = NULL; F_DOMNode * pTmpNode = NULL; FLMBYTE szTmpBuf [80]; FLMUINT uiNameId; F_DataVector key1; F_DataVector key2; FLMUNICODE * puzName = NULL; FLMUNICODE * puzNamespace = NULL; FLMUINT uiState = 0; FLMBOOL bFoundState = FALSE; FLMUINT uiMaxTagNum; FLMBOOL bAmbiguous; FLMBOOL bHasAttrs; FLMUINT uiInsertPos; FLM_TAG_INFO * pTagInfo; *puiDictType = 0; *puiDictNumber = 0; // Get the root node of the definition document if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) { goto Exit; } if( RC_BAD( rc = pNode->getNameId( this, puiDictType))) { goto Exit; } // Ignore anything that is one one of our predefined types. switch (*puiDictType) { case ELM_ELEMENT_TAG: uiMaxTagNum = XFLM_MAX_ELEMENT_NUM; break; case ELM_ATTRIBUTE_TAG: uiMaxTagNum = XFLM_MAX_ATTRIBUTE_NUM; break; case ELM_INDEX_TAG: uiMaxTagNum = XFLM_MAX_INDEX_NUM; break; case ELM_PREFIX_TAG: uiMaxTagNum = XFLM_MAX_PREFIX_NUM; break; case ELM_COLLECTION_TAG: uiMaxTagNum = XFLM_MAX_COLLECTION_NUM; break; case ELM_ENCDEF_TAG: #ifndef FLM_HAS_ENCRYPTION rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); goto Exit; #else uiMaxTagNum = XFLM_MAX_ENCDEF_NUM; break; #endif default: *puiDictType = 0; goto Exit; } if( RC_BAD( rc = pNode->hasAttributes( this, &bHasAttrs))) { goto Exit; } if( !bHasAttrs) { goto Exit; } if( pNode->getNodeType() != ELEMENT_NODE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } // Cycle through the attributes, pulling out stuff we are interested // in. for( ;;) { if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) { goto Exit; } switch (uiNameId) { case ATTR_NAME_TAG: { if (RC_BAD( rc = pAttr->getUnicode( this, &puzName))) { goto Exit; } // Put a freeze on name if we are not deleting. // Modify is allowed, but delete is not. if (!bDeleting) { if (RC_BAD( rc = pAttr->addModeFlags( this, FDOM_CANNOT_DELETE))) { goto Exit; } } break; } case ATTR_TARGET_NAMESPACE_TAG: { if (RC_BAD( rc = pAttr->getUnicode( this, &puzNamespace))) { goto Exit; } break; } case ATTR_TYPE_TAG: { // Put a freeze on data type if we are not deleting. if (!bDeleting) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } break; } case ATTR_STATE_TAG: { if (RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) { goto Exit; } if (*puiDictType == ELM_INDEX_TAG) { if (RC_BAD( rc = fdictGetIndexState( (char *)szTmpBuf, &uiState))) { goto Exit; } } else if (*puiDictType == ELM_ELEMENT_TAG || *puiDictType == ELM_ATTRIBUTE_TAG) { if (RC_BAD( rc = fdictGetState( (char *)szTmpBuf, &uiState))) { goto Exit; } } // Put a freeze on state if we are not deleting. if (!bDeleting && (*puiDictType == ELM_INDEX_TAG || *puiDictType == ELM_ELEMENT_TAG || *puiDictType == ELM_ATTRIBUTE_TAG)) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } bFoundState = TRUE; break; } case ATTR_STATE_CHANGE_COUNT_TAG: { // Put a freeze on state change count if we are not deleting. if (!bDeleting && (*puiDictType == ELM_ELEMENT_TAG || *puiDictType == ELM_ATTRIBUTE_TAG)) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } break; } case ATTR_DICT_NUMBER_TAG: { if (RC_BAD( rc = pAttr->getUINT( this, puiDictNumber))) { goto Exit; } // If we are deleting, no need to verify or alter // dictionary number. if (bDeleting) { break; } // If the set dictionary number was zero, allocate a new // one, set it, and freeze it. if (!(*puiDictNumber)) { if (RC_BAD( rc = m_pDict->allocNextDictNum( this, *puiDictType, puiDictNumber))) { goto Exit; } // *puiDictNumber will be zero coming back if the // dictionary type does not keep track of a next // dictionary number (like for prefixes) if (*puiDictNumber) { if( RC_BAD( rc = pAttr->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if (RC_BAD( rc = pAttr->setUINT( this, *puiDictNumber))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } } else { // Set the next unused dictionary number for this type - but only // if this number is >= the one already stored. if (RC_BAD( rc = m_pDict->setNextDictNum( this, *puiDictType, *puiDictNumber))) { goto Exit; } } break; } default: { // Ignore all other attributes for now. break; } } if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } } // If dictionary number is missing, create one and add it in. if (!(*puiDictNumber) && !bDeleting) { // Allocate the next unused dictionary number for this type. if (RC_BAD( rc = m_pDict->allocNextDictNum( this, *puiDictType, puiDictNumber))) { goto Exit; } // *puiDictNumber will be zero coming back if the // dictionary type does not keep track of a next // dictionary number for this type (like for prefixes) if (*puiDictNumber) { // Create a dict number attribute, set it to the newly // allocated value, and freeze it. if (RC_OK( rc = pNode->createAttribute( this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) { if (RC_OK( rc = pAttr->setUINT( this, *puiDictNumber))) { if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } } } } if (!bDeleting) { // Must have a name specified, and it must be unique if (!puzName) { switch (*puiDictType) { case ELM_ELEMENT_TAG: rc = RC_SET( NE_XFLM_MISSING_ELEMENT_NAME); break; case ELM_ATTRIBUTE_TAG: rc = RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NAME); break; case ELM_INDEX_TAG: rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME); break; case ELM_PREFIX_TAG: rc = RC_SET( NE_XFLM_MISSING_PREFIX_NAME); break; case ELM_COLLECTION_TAG: rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NAME); break; case ELM_ENCDEF_TAG: rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NAME); break; default: // Should never hit this case! flmAssert( 0); break; } goto Exit; // Will return NE_XFLM_OK } // Verify name uniqueness key1.reset(); if (RC_BAD( rc = key1.setUINT( 0, *puiDictType))) { goto Exit; } if (RC_BAD( rc = key1.setUnicode( 1, puzName))) { goto Exit; } if ((*puiDictType == ELM_ELEMENT_TAG || *puiDictType == ELM_ATTRIBUTE_TAG) && puzNamespace) { if (RC_BAD( rc = key1.setUnicode( 2, puzNamespace))) { goto Exit; } } if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, &key1, XFLM_EXACT, &key2))) { if (rc == NE_XFLM_NOT_FOUND) { // We should have found the thing! It should have // already been indexed! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } // See if there is another one after this for the same // key. if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, &key2, XFLM_EXCL | XFLM_KEY_EXACT | XFLM_MATCH_IDS, &key1))) { if (rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; // See if it is defined in our name table and is a number in // our reserved range. In that case, it should never be // coming through here - it is a name conflict we should prevent. if ((pTagInfo = m_pDict->getNameTable()->findTagByTypeAndName( *puiDictType, puzName, NULL, TRUE, puzNamespace, &bAmbiguous, &uiInsertPos)) != NULL) { if (pTagInfo->uiTagNum > uiMaxTagNum) { goto Have_Name_Conflict; } } } else { Have_Name_Conflict: // We should NOT have found another one with this same name switch (*puiDictType) { case ELM_ELEMENT_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_ELEMENT_NAME); goto Exit; case ELM_ATTRIBUTE_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_ATTRIBUTE_NAME); goto Exit; case ELM_INDEX_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_INDEX_NAME); goto Exit; case ELM_COLLECTION_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_COLLECTION_NAME); goto Exit; case ELM_PREFIX_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_PREFIX_NAME); goto Exit; default: // VISIT: Do we care on other dictionary types that // we have a duplicate name? break; } } // If this is an attribute definition, and the name is "xmlns" or // begins with "xmlns:", it cannot have a target namespace. if (*puiDictType == ELM_ATTRIBUTE_TAG && puzNamespace && *puzNamespace && isXMLNS( puzName) && (puzName [5] == 0 || (puzName [5] == ':' && puzName [6]))) { rc = RC_SET( NE_XFLM_NAMESPACE_NOT_ALLOWED); goto Exit; } } // Verify dictionary number uniqueness if (*puiDictNumber && !bDeleting) { key1.reset(); key2.reset(); if (RC_BAD( rc = key1.setUINT( 0, *puiDictType))) { goto Exit; } if (RC_BAD( rc = key1.setUINT( 1, *puiDictNumber))) { goto Exit; } if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, &key1, XFLM_EXACT, &key2))) { if (rc == NE_XFLM_NOT_FOUND) { // We should have found the thing! It should have // already been indexed! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } // See if there is another one after this for the same // key. if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, &key2, XFLM_EXCL | XFLM_KEY_EXACT | XFLM_MATCH_IDS, &key1))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { goto Exit; } } else { // We should NOT have found another one with this same number switch (*puiDictType) { case ELM_ELEMENT_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_ELEMENT_NUM); goto Exit; case ELM_ATTRIBUTE_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_ATTRIBUTE_NUM); goto Exit; case ELM_INDEX_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_INDEX_NUM); goto Exit; case ELM_COLLECTION_TAG: rc = RC_SET( NE_XFLM_DUPLICATE_COLLECTION_NUM); goto Exit; default: // Dictionary number is not used for other types // so we really don't care if there is a duplicate // in these cases. break; } } } // Is it a delete? if (bDeleting) { if (*puiDictType == ELM_ELEMENT_TAG) { if (*puiDictNumber) { // Make sure that elements are in the right state // to be deleted. if( !m_bItemStateUpdOk) { rc = RC_SET( NE_XFLM_CANNOT_DEL_ELEMENT); goto Exit; } } } else if (*puiDictType == ELM_ATTRIBUTE_TAG) { if (*puiDictNumber) { // Make sure that attributes are in the right state // to be deleted. if( !m_bItemStateUpdOk) { rc = RC_SET( NE_XFLM_CANNOT_DEL_ATTRIBUTE); goto Exit; } } } else if (*puiDictType == ELM_COLLECTION_TAG) { if (*puiDictNumber) { if (RC_BAD( rc = m_pDict->checkCollectionReferences( *puiDictNumber))) { goto Exit; } } } } else { // Set a state attribute and freeze it if it was missing. // This makes it so that the state can only be changed by a call // to changeItemState. This routine ensures that the state // change is legal. if( (*puiDictType == ELM_ATTRIBUTE_TAG || *puiDictType == ELM_ELEMENT_TAG || *puiDictType == ELM_ENCDEF_TAG) && !bFoundState) { if (RC_BAD( rc = pNode->createAttribute( this, ATTR_STATE_TAG, (IF_DOMNode **)&pTmpNode))) { goto Exit; } if (RC_BAD( rc = pTmpNode->setUTF8( this, (FLMBYTE *)XFLM_ACTIVE_OPTION_STR))) { goto Exit; } if( RC_BAD( rc = pTmpNode->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } } Exit: if (pNode) { pNode->Release(); } if (pAttr) { pAttr->Release(); } if (pTmpNode) { pTmpNode->Release(); } if (puzName) { f_free( &puzName); } if (puzNamespace) { f_free( &puzNamespace); } return( rc); } /**************************************************************************** Desc: This routine is called when a dictionary document is done being modified. It will check the definition and then generate any dictionary updates that need to be done. ****************************************************************************/ RCODE F_Db::dictDocumentDone( FLMUINT64 ui64DocumentID, FLMBOOL bDeleting, FLMUINT * puiDictDefType) { RCODE rc = NE_XFLM_OK; FLMUINT uiDictType; FLMUINT uiDictNumber; if (puiDictDefType) { *puiDictDefType = 0; } // Document ID 1 is the one that is reserved for next element, next // attribute, next index, and next collection. if (ui64DocumentID == XFLM_DICTINFO_DOC_ID) { goto Exit; } // Flush any index keys before making any changes to dictionary items. // Dictionary changes may change index definitions, etc. if( RC_BAD( rc = keysCommit( FALSE))) { goto Exit; } // Clear out the cdl table in case it changes. krefCntrlFree(); // Retrieve the root element of the definition if( RC_BAD( rc = checkDictDefInfo( ui64DocumentID, bDeleting, &uiDictType, &uiDictNumber))) { flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } // Was the document one of our known dictionary document types? // Also, had a dictionary number been defined. If bDeleting // is TRUE and no dictionary number had been defined, there // is nothing to be done on the internal dictionary. if (!uiDictType || !uiDictNumber) { goto Exit; // Will return NE_XFLM_OK } // Create a separate dictionary object if one has not already // been created. if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) { if (RC_BAD( rc = dictClone())) { goto Exit; } } // Update the dictionary and create/drop indexes or containers // if it is that type of definition if (RC_BAD( rc = m_pDict->updateDict( this, uiDictType, ui64DocumentID, uiDictNumber, FALSE, bDeleting))) { goto Exit; } // Return the type of definition if( puiDictDefType) { *puiDictDefType = uiDictType; } Exit: if( RC_BAD( rc)) { setMustAbortTrans( rc); } return( rc ); } /**************************************************************************** Desc: Copies an existing dictionary to a new dictionary. ****************************************************************************/ RCODE F_Db::dictClone( void) { RCODE rc = NE_XFLM_OK; F_Dict * pNewDict = NULL; // Allocate a new FDICT structure if ((pNewDict = f_new F_Dict) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } // Nothing to do is not a legal state. if (!m_pDict) { flmAssert( 0); m_pDict = pNewDict; goto Exit; } // Copy the dictionary. if (RC_BAD( rc = pNewDict->cloneDict( m_pDict))) { goto Exit; } m_pDatabase->lockMutex(); unlinkFromDict(); m_pDatabase->unlockMutex(); m_pDict = pNewDict; pNewDict = NULL; m_uiFlags |= FDB_UPDATED_DICTIONARY; Exit: if (RC_BAD( rc) && pNewDict) { pNewDict->Release(); } return( rc); } /**************************************************************************** Desc: Build an index. ****************************************************************************/ RCODE F_Db::buildIndex( FLMUINT uiIndexNum, FLMUINT uiState) { RCODE rc = NE_XFLM_OK; LFILE * pIxLFile; IXD * pIxd; // Flush any KY keys and free the tables because they may grow! if (RC_BAD( rc = keysCommit( TRUE))) { goto Exit; } if (RC_BAD( rc = krefCntrlCheck())) { goto Exit; } if (RC_BAD(rc = m_pDict->getIndex( uiIndexNum, &pIxLFile, &pIxd, TRUE))) { goto Exit; } // NON-BLOCKING INDEX BUILD - NOTE: The IXD_SUSPENDED flag may // also be set, which indicates that we should NOT start the // background maintenance thread right now. if (uiState & IXD_OFFLINE) { if (RC_BAD( rc = setIxStateInfo( pIxd->uiIndexNum, 0, uiState))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIxd is no // good after this point pIxd = NULL; // Don't schedule a maintenance thread if index is to start // out life in a suspended state, or if we are replaying // the roll-forward log. if (!(uiState & IXD_SUSPENDED) && !(m_uiFlags & FDB_REPLAYING_RFL)) { if (RC_BAD( rc = addToStartList( uiIndexNum))) { goto Exit; } } // Done goto Exit; } // There may be "new" nodes in the node cache. // Need to flush them to the database so that // the B-Tree lookups done by the indexing code will // work correctly if( RC_BAD( rc = flushDirtyNodes())) { goto Exit; } // NORMAL INDEX BUILD - BLOCKING. uiIndexToBeUpdated better be // zero at this point since we are not working in the background. if (RC_BAD( rc = indexSetOfDocuments( uiIndexNum, 1, ~((FLMUINT64)0), m_pIxStatus, m_pIxClient, NULL, NULL))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Logs information about an index being built ****************************************************************************/ void flmLogIndexingProgress( FLMUINT uiIndexNum, FLMUINT64 ui64LastDocumentId) { IF_LogMessageClient * pLogMsg = NULL; char szMsg[ 128]; if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) { pLogMsg->changeColor( FLM_YELLOW, FLM_BLACK); if (ui64LastDocumentId) { f_sprintf( (char *)szMsg, "Indexing progress: Index %u is offline. Last document processed = %I64u.", (unsigned)uiIndexNum, ui64LastDocumentId); } else { f_sprintf( (char *)szMsg, "Indexing progress: Index %u is online.", (unsigned)uiIndexNum); } pLogMsg->appendString( szMsg); } flmEndLogMessage( &pLogMsg); } /**************************************************************************** Desc: Unlink and free an IX_CONTEXT structure. ****************************************************************************/ FSTATIC void kyFreeIxContext( IXD * pIxd, IX_CONTEXT * pIxContext, IX_CONTEXT ** ppIxContextList ) { if (pIxContext->pPrev) { pIxContext->pPrev->pNext = pIxContext->pNext; } else { *ppIxContextList = pIxContext->pNext; } if (pIxContext->pNext) { pIxContext->pNext->pPrev = pIxContext->pPrev; } kyReleaseCdls( pIxd, pIxContext->pCdlTbl); if (pIxContext->pPool) { pIxContext->pPool->poolFree(); pIxContext->pPool->Release(); } f_free( &pIxContext); } /**************************************************************************** Desc: Output the keys for a particular IX_CONTEXT structure. Also, free the IX_CONTEXT structure and its associated CDLs, etc. ****************************************************************************/ RCODE F_Db::outputContextKeys( FLMUINT64 ui64DocumentId, IXD * pIxd, IX_CONTEXT * pIxContext, IX_CONTEXT ** ppIxContextList) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = buildKeys( ui64DocumentId, pIxd, pIxContext->pCdlTbl, TRUE, TRUE))) { goto Exit; } // Free the IX_CONTEXT structure - unlinks from list too. kyFreeIxContext( pIxd, pIxContext, ppIxContextList); // Flush keys if over threshhold - get key count before flushing. if( pIxd->uiIndexNum && isKrefOverThreshold()) { processDupKeys( pIxd); if (RC_BAD( rc = keysCommit( FALSE, FALSE))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Output the keys for a particular IX_CONTEXT structure and remove CDLs for the specified ICD, if any. ****************************************************************************/ RCODE F_Db::removeCdls( FLMUINT64 ui64DocumentId, IXD * pIxd, IX_CONTEXT * pIxContext, ICD * pRefIcd) { RCODE rc = NE_XFLM_OK; CDL * pCdl; CDL * pOldCdlList; ICD * pIcd; if (RC_BAD( rc = buildKeys( ui64DocumentId, pIxd, pIxContext->pCdlTbl, TRUE, TRUE))) { goto Exit; } pIcd = pRefIcd; while (pIcd) { // Free the CDLs for the specified ICD, if any. // Put the CDLs into a table for reuse. pCdl = pIxContext->pCdlTbl [pIcd->uiCdl].pCdlList; pIxContext->pCdlTbl [pIcd->uiCdl].pCdlList = NULL; if (pCdl) { pOldCdlList = pIxContext->pCdlList; pIxContext->pCdlList = pCdl; for (;;) { if (pCdl->pNode) { pCdl->pNode->Release(); pCdl->pNode = NULL; } if (!pCdl->pNext) { pCdl->pNext = pOldCdlList; break; } pCdl = pCdl->pNext; } } if (pIcd == pRefIcd->pParent) { break; } if ((pIcd = pIcd->pNextSibling) == NULL) { // Also do reference ICD's parent ICD. This is not absolutely // necessary, since this CDLs will be ignored when we verify // context, but we might as well do it to save just a little more // memory space. if ((pIcd = pRefIcd->pParent) == NULL) { break; } } } // Flush keys if over threshhold - get key count before flushing. if( pIxd->uiIndexNum && isKrefOverThreshold()) { processDupKeys( pIxd); if (RC_BAD( rc = keysCommit( FALSE, FALSE))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Index a particular document for a particular index. ****************************************************************************/ RCODE F_Db::indexDocument( IXD * pIxd, F_DOMNode * pDocNode) { RCODE rc = NE_XFLM_OK; ICD * pIcd; ICD * pChildIcd; CDL * pCdl; IX_CONTEXT * pIxContextList = NULL; IX_CONTEXT * pIxContext; F_DOMNode * pTmpNode = NULL; F_TRAV * pTrav = NULL; void * pvMark = m_tempPool.poolMark(); CDL_HDR * pCdlHdr; FLMUINT64 ui64DocId; // Root of a document cannot be an attribute node flmAssert( pDocNode->getNodeType() != ATTRIBUTE_NODE); // Get the document ID if( RC_BAD( rc = pDocNode->getNodeId( this, &ui64DocId))) { goto Exit; } // If the index number is zero, we are generating keys for a query // but not to store in the database. We want the kref table cleared // out. if (!pIxd->uiIndexNum) { if (!m_bKrefSetup) { if (RC_BAD( rc = krefCntrlCheck())) { goto Exit; } } else if (m_eTransType == XFLM_UPDATE_TRANS) { if (RC_BAD( rc = keysCommit( FALSE))) { goto Exit; } } else { // Empty the table out so that the only keys added in this // call are for this index, this document. m_pKrefPool->poolReset( NULL, TRUE); m_uiKrefCount = 0; m_uiTotalKrefBytes = 0; } flmAssert( !m_uiKrefCount && !m_uiTotalKrefBytes); } else { if (RC_BAD( rc = krefCntrlCheck())) { goto Exit; } } // Do an in-order traversal of the document. if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( F_TRAV), (void **)&pTrav))) { goto Exit; } pTrav->pNode = pDocNode; pTrav->pNode->AddRef(); // pTrav->pRefIcd = NULL; // Set by poolCalloc // pTrav->pIcd = NULL; // Set by poolCalloc // pTrav->pIxContext = NULL; // Set by poolCalloc // pTrav->pParent = NULL; // Set by poolCalloc // pTrav->pChild = NULL; // Set by poolCalloc for (;;) { FLMUINT uiNameId; eDomNodeType eNodeType = pTrav->pNode->getNodeType(); if( RC_BAD( rc = pTrav->pNode->getNameId( this, &uiNameId))) { goto Exit; } if (uiNameId && (eNodeType == ELEMENT_NODE || eNodeType == ATTRIBUTE_NODE)) { FLMBOOL bCheckedIcdTreeRoot = FALSE; // See if the node has an ICD in the current context. if ((pIcd = pTrav->pRefIcd) == NULL) { pIcd = pIxd->pIcdTree; bCheckedIcdTreeRoot = TRUE; } if (pIcd->uiDictNum == ELM_ROOT_TAG) { if (pTrav->pNode->getParentId()) { pIcd = NULL; } } else if (eNodeType == ELEMENT_NODE) { while (pIcd && (pIcd->uiDictNum != uiNameId || (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } // If we did not start at the root of the ICD tree, // need to go back there to see if we need to start // a new context. However, if the root if the ICD tree // is ELM_ROOT_TAG, it is pointless, because the current // pTrev->pNode should be a child of some node if // bCheckedIcdTreeRoot is FALSE. if (!pIcd && !bCheckedIcdTreeRoot && pIxd->pIcdTree->uiDictNum != ELM_ROOT_TAG) { pIcd = pIxd->pIcdTree; while (pIcd && (pIcd->uiDictNum != uiNameId || (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } if (pIcd) { // Reset these so that a new context will be // created below. pTrav->pRefIcd = NULL; pTrav->pIxContext = NULL; } } } else { while (pIcd && (pIcd->uiDictNum != uiNameId || !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } // If we did not start at the root of the ICD tree, // need to go back there to see if we need to start // a new context. However, if the root if the ICD tree // is ELM_ROOT_TAG, it is pointless, because the current // pTrev->pNode should be a child of some node if // bCheckedIcdTreeRoot is FALSE. if (!pIcd && !bCheckedIcdTreeRoot && pIxd->pIcdTree->uiDictNum != ELM_ROOT_TAG) { pIcd = pIxd->pIcdTree; while (pIcd && (pIcd->uiDictNum != uiNameId || !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } if (pIcd) { // Reset these so that a new context will be // created below. pTrav->pRefIcd = NULL; pTrav->pIxContext = NULL; } } } // If we found a matching ICD, see if we want to save the node // in a CDL table. if ((pTrav->pIcd = pIcd) != NULL) { // If there is no indexing context, start one. if (!pTrav->pIxContext) { flmAssert( !pTrav->pRefIcd); if (RC_BAD( rc = f_calloc( sizeof( IX_CONTEXT), &pIxContext))) { goto Exit; } if ((pIxContext->pNext = pIxContextList) != NULL) { pIxContextList->pPrev = pIxContext; } pIxContextList = pIxContext; if ((pIxContext->pPool = f_new F_Pool) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } pIxContext->pPool->poolInit( 512); if (RC_BAD( rc = pIxContext->pPool->poolCalloc( sizeof( CDL_HDR) * pIxd->uiNumIcds, (void **)&pIxContext->pCdlTbl))) { goto Exit; } pTrav->pIxContext = pIxContext; pTrav->pRefIcd = pIxd->pIcdTree; } else { pIxContext = pTrav->pIxContext; } // If this node has a parent, and the front of the // list is a "missing" place holder for the node, // replace that CDL. pCdlHdr = &pIxContext->pCdlTbl [pIcd->uiCdl]; pCdl = pCdlHdr->pCdlList; if (pCdl && !pCdl->pNode && pTrav->pNode->getParentId() && pCdl->ui64ParentId == pTrav->pNode->getParentId()) { pCdl->pNode = pTrav->pNode; pCdl->bInNodeSubtree = TRUE; pCdl->pNode->AddRef(); } else { // Reuse a CDL if one is available. if (pIxContext->pCdlList) { pCdl = pIxContext->pCdlList; pIxContext->pCdlList = pCdl->pNext; // pCdl->pNode should have been released when it was // put into the CDL list! flmAssert( !pCdl->pNode); } else { if (RC_BAD( rc = pIxContext->pPool->poolAlloc( sizeof( CDL), (void **)&pCdl))) { goto Exit; } } pCdl->pNode = pTrav->pNode; pCdl->ui64ParentId = pTrav->pNode->getParentId(); pCdl->bInNodeSubtree = TRUE; pCdl->pNode->AddRef(); pCdl->pNext = pCdlHdr->pCdlList; pCdlHdr->pCdlList = pCdl; } // Add "missing" place-holders for any child ICDs pChildIcd = pIcd->pFirstChild; while (pChildIcd) { // Reuse a CDL if one is available. if (pIxContext->pCdlList) { pCdl = pIxContext->pCdlList; pIxContext->pCdlList = pCdl->pNext; // pCdl->pNode should have been released when it was // put into the CDL list! flmAssert( !pCdl->pNode); } else { if (RC_BAD( rc = pIxContext->pPool->poolAlloc( sizeof( CDL), (void **)&pCdl))) { goto Exit; } } if( RC_BAD( rc = pTrav->pNode->getNodeId( this, &pCdl->ui64ParentId))) { goto Exit; } pCdl->pNode = NULL; pCdl->bInNodeSubtree = TRUE; pCdlHdr = &pIxContext->pCdlTbl [pChildIcd->uiCdl]; pCdl->pNext = pCdlHdr->pCdlList; pCdlHdr->pCdlList = pCdl; pChildIcd = pChildIcd->pNextSibling; } } } // Go to the next node. if( eNodeType == ATTRIBUTE_NODE) { if (RC_OK( rc = pTrav->pNode->getNextSibling( this, (IF_DOMNode **)&pTrav->pNode))) { pTrav->pIcd = NULL; continue; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { rc = NE_XFLM_OK; } // No siblings, go back to parent node and see if it has any // siblings. if (pTrav->pNode) { pTrav->pNode->Release(); pTrav->pNode = NULL; } // If the parent has a different IX_CONTEXT (including NULL) // free this one before going back to the parent. if (pTrav->pIxContext && pTrav->pParent && pTrav->pParent->pIxContext != pTrav->pIxContext) { if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, pTrav->pIxContext, &pIxContextList))) { goto Exit; } pTrav->pIxContext = NULL; pTrav->pRefIcd = NULL; } else if (pTrav->pIxContext && pIxd->uiFlags & IXD_SINGLE_PATH) { if (RC_BAD( rc = removeCdls( ui64DocId, pIxd, pTrav->pIxContext, pTrav->pRefIcd))) { goto Exit; } } pTrav = pTrav->pParent; // Has to be a parent at this point! flmAssert( pTrav); // Fall through to do element's siblings. } else if (eNodeType == ELEMENT_NODE || eNodeType == DOCUMENT_NODE) { if (RC_OK( rc = pTrav->pNode->getFirstChild( this, (IF_DOMNode **)&pTmpNode))) { Setup_Child: if (!pTrav->pChild) { F_TRAV * pNewTrav; if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( F_TRAV), (void **)&pNewTrav))) { goto Exit; } pNewTrav->pParent = pTrav; pTrav->pChild = pNewTrav; } pTrav = pTrav->pChild; if (pTrav->pNode) { pTrav->pNode->Release(); pTrav->pNode = NULL; } pTrav->pNode = pTmpNode; pTrav->pNode->AddRef(); pTmpNode->Release(); pTmpNode = NULL; pTrav->pRefIcd = (pTrav->pParent->pIcd ? pTrav->pParent->pIcd->pFirstChild : NULL); pTrav->pIcd = NULL; pTrav->pIxContext = pTrav->pParent->pIxContext; if (!pTrav->pRefIcd) { pTrav->pIxContext = NULL; } continue; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { rc = NE_XFLM_OK; } // See if the node has any attributes if( pTrav->pNode->getNodeType() == ELEMENT_NODE) { if (RC_OK( rc = pTrav->pNode->getFirstAttribute( this, (IF_DOMNode **)&pTmpNode))) { goto Setup_Child; } else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } else { rc = NE_XFLM_OK; } } } // Follow sibling chain. Go to parents until we find a // parent that has a sibling. for (;;) { // If the parent has a different IX_CONTEXT // free this one before going back to the parent. if (pTrav->pIxContext && pTrav->pParent && pTrav->pParent->pIxContext && pTrav->pParent->pIxContext != pTrav->pIxContext) { if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, pTrav->pIxContext, &pIxContextList))) { goto Exit; } pTrav->pRefIcd = (pTrav->pParent->pIcd ? pTrav->pParent->pIcd->pFirstChild : NULL); pTrav->pIcd = NULL; pTrav->pIxContext = pTrav->pParent->pIxContext; if (!pTrav->pRefIcd) { pTrav->pIxContext = NULL; } } if (RC_BAD( rc = pTrav->pNode->getNextSibling( this, (IF_DOMNode **)&pTrav->pNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // If there is no parent node, we are done. if (!pTrav->pParent) { goto Done_With_Document; } // See if the parent has a a first attribute. if( pTrav->pParent->pNode->getNodeType() == ELEMENT_NODE) { if (RC_BAD( rc = pTrav->pParent->pNode->getFirstAttribute( this, (IF_DOMNode **)&pTrav->pNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { // Will continue processing attributes as siblings. break; } } // At this point we will be going back to the parent node. if (pTrav->pNode) { pTrav->pNode->Release(); pTrav->pNode = NULL; } // If the parent has no context, we need to output the keys we may // have collected so far. NOTE: If parent's context is non-NULL and // just different than our context, we will already have output the // keys above. pTrav->pParent must be non-NULL at this point. if (pTrav->pIxContext && !pTrav->pParent->pIxContext) { if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, pTrav->pIxContext, &pIxContextList))) { goto Exit; } } else if (pTrav->pIxContext && pIxd->uiFlags & IXD_SINGLE_PATH) { if (RC_BAD( rc = removeCdls( ui64DocId, pIxd, pTrav->pIxContext, pTrav->pRefIcd))) { goto Exit; } } pTrav = pTrav->pParent; } else if (pTrav->pNode->getNodeType() == ELEMENT_NODE) { pTrav->pIcd = NULL; break; } else { // better not be in a list of attribute nodes at this point! flmAssert( pTrav->pNode->getNodeType() != ATTRIBUTE_NODE); } } } Done_With_Document: // Need to build keys for each index context that we have. while (pIxContextList) { if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, pIxContextList, &pIxContextList))) { goto Exit; } } // Flush keys - get key count before flushing. if (!pIxd->uiIndexNum) { processDupKeys( pIxd); } else { if( isKrefOverThreshold()) { processDupKeys( pIxd); if (RC_BAD( rc = keysCommit( FALSE, FALSE))) { goto Exit; } } } Exit: if (pTmpNode) { pTmpNode->Release(); } while (pTrav && pTrav->pParent) { pTrav = pTrav->pParent; } while (pTrav) { if (pTrav->pNode) { pTrav->pNode->Release(); pTrav->pNode = NULL; } pTrav = pTrav->pChild; } while (pIxContextList) { kyFreeIxContext( pIxd, pIxContextList, &pIxContextList); } m_tempPool.poolReset( pvMark); return( rc); } /**************************************************************************** Desc: Index a set of documents or until time runs out. ****************************************************************************/ RCODE F_Db::indexSetOfDocuments( FLMUINT uiIxNum, FLMUINT64 ui64StartDocumentId, FLMUINT64 ui64EndDocumentId, IF_IxStatus * ifpIxStatus, IF_IxClient * ifpIxClient, XFLM_INDEX_STATUS * pIndexStatus, FLMBOOL * pbHitEnd, IF_Thread * pThread) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64DocumentId; FLMUINT64 ui64LastDocumentId = 0; IXD * pIxd = NULL; F_COLLECTION * pCollection; IF_LockObject * pDatabaseLockObj = m_pDatabase->m_pDatabaseLockObj; FLMBOOL bHitEnd = FALSE; FLMUINT uiCurrTime; FLMUINT uiLastStatusTime = 0; FLMUINT uiStartTime; FLMUINT uiMinTU; FLMUINT uiStatusIntervalTU; FLMUINT64 ui64DocumentsProcessed = 0; FLMBOOL bUpdateTracker = FALSE; FLMBOOL bRelinquish = FALSE; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; void * pvTmpPoolMark = m_tempPool.poolMark(); F_Btree * pbtree = NULL; FLMBOOL bNeg; FLMUINT uiBytesProcessed; F_DOMNode * pNode = NULL; uiMinTU = FLM_MILLI_TO_TIMER_UNITS( 500); uiStatusIntervalTU = FLM_SECS_TO_TIMER_UNITS( 10); uiStartTime = FLM_GET_TIMER(); if (RC_BAD( rc = krefCntrlCheck())) { goto Exit; } if (RC_BAD( rc = m_pDict->getIndex( uiIxNum, NULL, &pIxd, TRUE))) { goto Exit; } flmAssert( !(pIxd->uiFlags & IXD_SUSPENDED)); // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } if (RC_BAD( rc = m_pDict->getCollection( pIxd->uiCollectionNum, &pCollection))) { goto Exit; } if (RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } uiKeyLen = sizeof( ucKey); if (RC_BAD( rc = flmNumber64ToStorage( ui64StartDocumentId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pbtree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; bHitEnd = TRUE; goto Commit_Keys; } goto Exit; } // Make sure we hit a root node. If not, continue reading until we do // or until we hit the end. Root nodes are always linked together in // ascending order, so if there is another document, we will find it // simply by searching forward from where we are. Then we can follow // document links. for (;;) { if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64DocumentId, &bNeg, &uiBytesProcessed))) { goto Exit; } if (RC_BAD( rc = getNode( pIxd->uiCollectionNum, ui64DocumentId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Better be able to find the node at this point! rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } } // If the node is a root node, we have a document we can // process. if (pNode->isRootNode()) { // This is a root node - has no parent and is not linked // into orphan list. break; } // Need to go to the next node. if (RC_BAD( rc = pbtree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; bHitEnd = TRUE; goto Commit_Keys; } goto Exit; } } for (;;) { if( RC_BAD( rc = pNode->getNodeId( this, &ui64DocumentId))) { goto Exit; } if (ui64DocumentId > ui64EndDocumentId) { break; } if (RC_BAD( rc = indexDocument( pIxd, pNode))) { goto Exit; } // See if there is an indexing callback if (ifpIxClient) { if (RC_BAD( rc = ifpIxClient->doIndexing( this, uiIxNum, pIxd->uiCollectionNum, pNode))) { goto Exit; } } ui64LastDocumentId = ui64DocumentId; ui64DocumentsProcessed++; if (pIndexStatus) { pIndexStatus->ui64DocumentsProcessed++; pIndexStatus->ui64LastDocumentIndexed = ui64LastDocumentId; } // Get the current time uiCurrTime = FLM_GET_TIMER(); // Break out if someone is waiting for an update transaction. if (pThread) { if (pThread->getShutdownFlag()) { bRelinquish = TRUE; break; } if (pDatabaseLockObj->getWaiterCount()) { // See if our minimum run time has elapsed if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >= uiMinTU) { if (ui64DocumentsProcessed < 50) { // If there are higher priority waiters in the lock queue, // we want to relinquish. if (pDatabaseLockObj->haveHigherPriorityWaiter( FLM_BACKGROUND_LOCK_PRIORITY)) { bRelinquish = TRUE; break; } } else { bRelinquish = TRUE; break; } } } else { // Even if no one has requested a lock for a long time, we // still want to periodically commit our transaction so // we won't lose more than uiMaxCPInterval timer units worth // of work if we crash. We will run until we exceed the checkpoint // interval and we see that someone (the checkpoint thread) is // waiting for the write lock. if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > gv_XFlmSysData.uiMaxCPInterval && m_pDatabase->m_pWriteLockObj->getWaiterCount()) { bRelinquish = TRUE; break; } } } if (FLM_ELAPSED_TIME( uiCurrTime, uiLastStatusTime) >= uiStatusIntervalTU) { uiLastStatusTime = uiCurrTime; if( ifpIxStatus) { if( RC_BAD( rc = ifpIxStatus->reportIndex( ui64LastDocumentId))) { goto Exit; } } // Send indexing completed event notification if( gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( XFLM_EVENT_UPDATES, XFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(), 0, uiIxNum, ui64LastDocumentId, NE_XFLM_OK); } // Log a progress message flmLogIndexingProgress( uiIxNum, ui64LastDocumentId); } // Need to go to the next document. if (RC_BAD( rc = pNode->getNextDocument( this, (IF_DOMNode **)&pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; bHitEnd = TRUE; break; } } } Commit_Keys: if (RC_BAD( rc = keysCommit( TRUE))) { goto Exit; } // If at the end, change trans ID to the current transaction. if (bHitEnd) { if (RC_BAD( rc = setIxStateInfo( uiIxNum, ~((FLMUINT64)0), 0))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIxd is no // good after this point pIxd = NULL; } else if (ui64DocumentsProcessed || bUpdateTracker) { if (RC_BAD( rc = setIxStateInfo( uiIxNum, ui64LastDocumentId, IXD_OFFLINE))) { goto Exit; } // setIxStateInfo may have changed to a new dictionary, so pIxd is no // good after this point pIxd = NULL; } Exit: // We want to make one last call if we are in the foreground or if // we actually did some indexing. if (gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList) { flmDoEventCallback( XFLM_EVENT_UPDATES, XFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(), 0, uiIxNum, (FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastDocumentId), NE_XFLM_OK); } flmLogIndexingProgress( uiIxNum, (FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastDocumentId)); if (ifpIxStatus) { (void) ifpIxStatus->reportIndex( ui64LastDocumentId); } if (pbHitEnd) { *pbHitEnd = bHitEnd; } krefCntrlFree(); m_tempPool.poolReset( pvTmpPoolMark); if (pbtree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); } if (pNode) { pNode->Release(); } return( rc); } /**************************************************************************** Desc: Set information in the tracker record for the index. ****************************************************************************/ RCODE F_Db::setIxStateInfo( FLMUINT uiIndexNum, FLMUINT64 ui64LastDocIndexed, FLMUINT uiState) { RCODE rc = NE_XFLM_OK; IXD_FIXUP * pIxdFixup; IXD * pIxd; F_DOMNode * pAttr = NULL; F_DOMNode * pElement = NULL; FLMBOOL bMustAbortOnError = FALSE; // Get the IXD - even if the index is offline. if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } // See if this index is in our fixup list. pIxdFixup = m_pIxdFixups; while (pIxdFixup && pIxdFixup->uiIndexNum != uiIndexNum) { pIxdFixup = pIxdFixup->pNext; } if (!pIxdFixup) { if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( IXD_FIXUP), &pIxdFixup))) { goto Exit; } pIxdFixup->pNext = m_pIxdFixups; m_pIxdFixups = pIxdFixup; pIxdFixup->uiIndexNum = uiIndexNum; pIxdFixup->ui64LastDocIndexed = pIxd->ui64LastDocIndexed; } bMustAbortOnError = TRUE; // Update the last node indexed, if it changed. if (pIxdFixup->ui64LastDocIndexed != ui64LastDocIndexed) { pIxdFixup->ui64LastDocIndexed = ui64LastDocIndexed; // First, retrieve the root element of the index definition. if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pIxd->ui64IxDefNodeId, (F_DOMNode **)&pElement))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } // No need to create a new node if we are just setting it // to the default value - see fdict.cpp, getIndexDef for where // the default value gets set. if (ui64LastDocIndexed != ~((FLMUINT64)0)) { // Create a new dictionary - so that we can set the // ui64LastDocIndexedNodeId on the IXD. If the transaction // aborts, the whole dictionary will go away. if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) { if (RC_BAD( rc = dictClone())) { goto Exit; } // Get a pointer to the new IXD if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } } // Create a new attribute node on the index definition to hold // the last node indexed value. if (RC_BAD( rc = pElement->createAttribute( this, ATTR_LAST_DOC_INDEXED_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } } else { if( RC_BAD( rc = pElement->getAttribute( this, ATTR_LAST_DOC_INDEXED_TAG, (IF_DOMNode **)&pAttr))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } } if( pAttr) { if (RC_BAD( rc = pAttr->setUINT64( this, ui64LastDocIndexed))) { goto Exit; } } } // If IXD_SUSPENDED is set, then IXD_OFFLINE must also be set. // There are places in the code that only check for IXD_OFFLINE // that don't care if the index is also suspended. if (uiState & IXD_SUSPENDED) { uiState = IXD_SUSPENDED | IXD_OFFLINE; } else if (uiState & IXD_OFFLINE) { uiState = IXD_OFFLINE; } else { uiState = 0; } // See if we need to change state. if ((pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE)) != uiState) { const char * pszStateStr; if (uiState & IXD_SUSPENDED) { pszStateStr = XFLM_INDEX_SUSPENDED_STR; } else if (uiState & IXD_OFFLINE) { pszStateStr = XFLM_INDEX_OFFLINE_STR; } else { pszStateStr = XFLM_INDEX_ONLINE_STR; } // At this point we know we need to change the state. That means we need // to create a new dictionary, if we have not already done so. if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) { if (RC_BAD( rc = dictClone())) { goto Exit; } // Get a pointer to the new IXD if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) { goto Exit; } } // pElement may have been fetched above. Don't need to get it // here if that is the case. if (!pElement) { // First, retrieve the root element of the index definition. if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pIxd->ui64IxDefNodeId, (F_DOMNode **)&pElement))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } } // Create a new attribute node on the index definition to hold // the last node indexed value. if (RC_BAD( rc = pElement->createAttribute( this, ATTR_STATE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } // May need to unfreeze the state to change it. if( RC_BAD( rc = pAttr->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pszStateStr))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Put the state into the IXD. pIxd->uiFlags = (pIxd->uiFlags & (~(IXD_SUSPENDED | IXD_OFFLINE))) | uiState; } Exit: if (pAttr) { pAttr->Release(); } if (pElement) { pElement->Release(); } if( RC_BAD( rc) && bMustAbortOnError) { setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: See if any IXD structures need indexing in the background. ****************************************************************************/ RCODE F_Db::startBackgroundIndexing( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMUINT uiIndexNum; IXD * pIxd; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } if (m_eTransType != XFLM_NO_TRANS) { if (!okToCommitTrans()) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } } else { // Need to have at least a read transaction going. if (RC_BAD( rc = beginTrans( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } if (m_pDict->getIndexCount( FALSE)) { uiIndexNum = 0; for (;;) { if ((pIxd = m_pDict->getNextIndex( uiIndexNum, FALSE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; // Restart any indexes that are off-line but not suspended if ((pIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) == IXD_OFFLINE) { flmAssert( flmBackgroundIndexGet( m_pDatabase, uiIndexNum, FALSE) == NULL); if (RC_BAD( rc = startIndexBuild( uiIndexNum))) { goto Exit; } } } } Exit: if (bStartedTrans) { (void)abortTrans(); } return( rc); } /**************************************************************************** Desc: Check and set the next dictionary number for a specific dictionary type. ****************************************************************************/ RCODE F_Db::setNextDictNum( FLMUINT uiDictType, FLMUINT uiDictNumber ) { RCODE rc = NE_XFLM_OK; // Make sure an update transaction is active if (m_eTransType == XFLM_NO_TRANS) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } if (m_eTransType == XFLM_READ_TRANS) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); goto Exit; } // See if the transaction needs to be aborted if (RC_BAD( rc = m_AbortRc)) { goto Exit; } // The number must be greater than 1 if (uiDictNumber < 2) { goto Exit; } // Set the next dictionary number if (RC_BAD( rc = m_pDict->setNextDictNum( this, uiDictType, uiDictNumber))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: *****************************************************************************/ RCODE F_Database::startMaintThread( void) { RCODE rc = NE_XFLM_OK; char szThreadName[ F_PATH_MAX_SIZE]; char szBaseName[ 32]; flmAssert( !m_pMaintThrd); flmAssert( m_hMaintSem == F_SEM_NULL); // Generate the thread name if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_pszDbPath, szThreadName, szBaseName))) { goto Exit; } f_sprintf( (char *)szThreadName, "Maintenance (%s)", (char *)szBaseName); // Create the maintenance semaphore if( RC_BAD( rc = f_semCreate( &m_hMaintSem))) { goto Exit; } // Start the thread. if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( &m_pMaintThrd, F_Database::maintenanceThread, szThreadName, 0, 0, this, NULL, 32000))) { goto Exit; } // Signal the thread to check for any queued work f_semSignal( m_hMaintSem); Exit: if( RC_BAD( rc)) { if( m_hMaintSem != F_SEM_NULL) { f_semDestroy( &m_hMaintSem); } } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_Db::beginBackgroundTrans( IF_Thread * pThread) { RCODE rc = NE_XFLM_OK; RetryLock: // Obtain the file lock flmAssert( !(m_uiFlags & FDB_HAS_FILE_LOCK)); if( RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->lock( m_hWaitSem, TRUE, FLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY, m_pDbStats ? &m_pDbStats->LockStats : NULL))) { if( rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); } goto Exit; } // The lock needs to be marked as implicit so that commitTrans // will unlock the database and allow the next update transaction to // begin before all writes are complete. m_uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); // If there are higher priority waiters in the lock queue, // we want to relinquish. if( m_pDatabase->m_pDatabaseLockObj->haveHigherPriorityWaiter( FLM_BACKGROUND_LOCK_PRIORITY)) { if( pThread->getShutdownFlag()) { rc = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); goto Exit; } if( RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->unlock())) { goto Exit; } m_uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); goto RetryLock; } // If we are shutting down, relinquish and exit. if( pThread->getShutdownFlag()) { rc = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); goto Exit; } // Start an update transaction if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { if( rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) { // This would only happen if we were signaled to shut down. // So, it's ok to exit flmAssert( pThread->getShutdownFlag()); } goto Exit; } Exit: if( RC_BAD( rc)) { if( m_uiFlags & FDB_HAS_FILE_LOCK) { (void)m_pDatabase->m_pDatabaseLockObj->unlock(); m_uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); } } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_Database::maintenanceThread( IF_Thread * pThread) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = (F_Database *)pThread->getParm1(); F_Db * pDb = NULL; F_DOMNode * pDoc = NULL; F_DOMNode * pNextDoc = NULL; FLMUINT64 ui64DocId; FLMUINT64 ui64TmpTransId; FLMUINT64 ui64SweepTransId; FLMUINT uiNameId; FLMBOOL bStartedTrans; FLMBOOL bShutdown; Retry: bStartedTrans = FALSE; bShutdown = FALSE; pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); if( RC_BAD( rc = gv_pXFlmDbSystem->internalDbOpen( pDatabase, &pDb))) { // If the file is being closed, this is not an error. if( pDatabase->getFlags() & DBF_BEING_CLOSED) { rc = NE_XFLM_OK; bShutdown = TRUE; } goto Exit; } for( ;;) { pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); ui64DocId = 0; for( ;;) { if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread))) { goto Exit; } bStartedTrans = TRUE; if( RC_BAD( pDb->getDocument( XFLM_MAINT_COLLECTION, XFLM_INCL, ui64DocId, (IF_DOMNode **)&pDoc))) { break; } ui64DocId = pDoc->getDocumentId(); if( RC_BAD( rc = pDoc->getNameId( pDb, &uiNameId))) { goto Exit; } if( uiNameId == ELM_DELETE_TAG) { if( RC_BAD( rc = pDb->maintBlockChainFree( ui64DocId, 25, 0, NULL))) { goto Exit; } bStartedTrans = FALSE; if( RC_BAD( rc = pDb->commitTrans( 0, FALSE))) { goto Exit; } } else if( uiNameId == ELM_SWEEP_TAG) { ui64SweepTransId = pDb->getTransID(); pDb->abortTrans(); bStartedTrans = FALSE; if( RC_BAD( rc = pDb->sweep( pThread))) { goto Exit; } // Delete the sweep documents from the tracker if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread))) { goto Exit; } bStartedTrans = TRUE; for( ;;) { if( RC_BAD( rc = pDoc->getNextDocument( pDb, (IF_DOMNode **)&pNextDoc))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } rc = NE_XFLM_OK; break; } if( RC_BAD( rc = pDoc->getNameId( pDb, &uiNameId))) { goto Exit; } if( uiNameId == ELM_SWEEP_TAG) { if( RC_BAD( rc = pDoc->getAttributeValueUINT64( pDb, ATTR_TRANSACTION_TAG, &ui64TmpTransId, 0))) { goto Exit; } if( ui64TmpTransId > ui64SweepTransId) { break; } if( RC_BAD( rc = pDoc->removeModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pDoc->deleteNode( pDb))) { goto Exit; } } pDoc->Release(); // Use the reference on pNextDoc and set it to NULL so that // it doesn't get released. pDoc = pNextDoc; pNextDoc = NULL; } bStartedTrans = FALSE; if( RC_BAD( rc = pDb->commitTrans( 0, FALSE))) { goto Exit; } } else { flmAssert( bStartedTrans); pDb->abortTrans(); bStartedTrans = FALSE; } ui64DocId++; } if( bStartedTrans) { pDb->abortTrans(); bStartedTrans = FALSE; } pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING); f_semWait( pDatabase->m_hMaintSem, F_WAITFOREVER); if( pThread->getShutdownFlag()) { bShutdown = TRUE; goto Exit; } } Exit: pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); if( pDoc) { pDoc->Release(); pDoc = NULL; } if( pNextDoc) { pNextDoc->Release(); pNextDoc = NULL; } if( bStartedTrans) { pDb->abortTrans(); } if( pDb) { pDb->Release(); pDb = NULL; } if( !bShutdown) { flmAssert( RC_BAD( rc)); f_sleep( 250); f_semSignal( pDatabase->m_hMaintSem); goto Retry; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_Db::maintBlockChainFree( FLMUINT64 ui64MaintDocID, FLMUINT uiBlocksToFree, FLMUINT uiExpectedEndAddr, FLMUINT * puiBlocksFreed) { RCODE rc = NE_XFLM_OK; FLMUINT uiTmp; FLMUINT uiBlocksFreed = 0; FLMUINT uiStartAddr = 0; FLMUINT uiEndAddr = 0; F_DOMNode * pDoc = NULL; F_DOMNode * pChainNode = NULL; F_DOMNode * pAddrNode = NULL; FLMUINT uiRflToken = 0; // Make sure an update transaction is going and that a // non-zero number of blocks was specified if( getTransType() != XFLM_UPDATE_TRANS || !uiBlocksToFree) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } // Retrieve the maintenance document if( RC_BAD( rc = getNode( XFLM_MAINT_COLLECTION, ui64MaintDocID, XFLM_EXACT, &pDoc))) { goto Exit; } m_pDatabase->m_pRfl->disableLogging( &uiRflToken); while( uiBlocksFreed < uiBlocksToFree) { if( RC_BAD( rc = pDoc->getChildElement( this, ELM_BLOCK_CHAIN_TAG, (IF_DOMNode **)&pChainNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } if( RC_BAD( rc = pDoc->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pDoc->deleteNode( this))) { goto Exit; } break; } if( RC_BAD( rc = pChainNode->getAttributeValueUINT( this, ATTR_ADDRESS_TAG, &uiStartAddr, 0))) { goto Exit; } if( !uiStartAddr) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = btFreeBlockChain( this, NULL, uiStartAddr, uiBlocksToFree - uiBlocksFreed, &uiTmp, &uiEndAddr, NULL))) { goto Exit; } uiBlocksFreed += uiTmp; flmAssert( uiBlocksFreed <= uiBlocksToFree); if( RC_BAD( rc = pChainNode->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( !uiEndAddr) { if( RC_BAD( rc = pChainNode->deleteNode( this))) { goto Exit; } } else { if( RC_BAD( rc = pChainNode->getAttribute( this, ATTR_ADDRESS_TAG, (IF_DOMNode **)&pAddrNode))) { goto Exit; } if( RC_BAD( rc = pAddrNode->removeModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pAddrNode->setUINT( this, uiEndAddr))) { goto Exit; } if( RC_BAD( rc = pAddrNode->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pChainNode->addModeFlags( this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } } if( RC_BAD( rc = documentDone( XFLM_MAINT_COLLECTION, ui64MaintDocID))) { goto Exit; } } if( uiExpectedEndAddr) { if( uiBlocksToFree != uiBlocksFreed || uiEndAddr != uiExpectedEndAddr) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } if ( uiRflToken) { m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } if( RC_BAD( rc = m_pDatabase->m_pRfl->logBlockChainFree( this, ui64MaintDocID, uiStartAddr, uiEndAddr, uiBlocksFreed))) { goto Exit; } if( puiBlocksFreed) { *puiBlocksFreed = uiBlocksFreed; } Exit: if ( uiRflToken) { m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } if( pChainNode) { pChainNode->Release(); } if( pAddrNode) { pAddrNode->Release(); } if( pDoc) { pDoc->Release(); } return( rc); } libxflaim-5.1.969/src/fbtrset.cpp0000644000175000017500000003041310511001742020202 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains routines that implement a result set using // a temporary XFLAIM database. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fbtrset.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: ****************************************************************************/ F_BtResultSet::~F_BtResultSet() { // Free the collection table if it was ever created. if (m_ppCollectionTbl) { FLMUINT uiLoop; for (uiLoop = 0; uiLoop < BT_MAX_COLLECTION_TBL_SIZ; uiLoop++) { if (m_ppCollectionTbl[ uiLoop] != NULL) { BT_COLLECTION_XREF * pTmp; while (m_ppCollectionTbl[ uiLoop] != NULL) { pTmp = m_ppCollectionTbl[ uiLoop]; m_ppCollectionTbl[ uiLoop] = pTmp->pNext; if (pTmp && pTmp->pCompare) { pTmp->pCompare->Release(); } f_free( &pTmp); } } } f_free( &m_ppCollectionTbl); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::getBTree( F_Db * pSrcDb, IXD * pSrcIxd, F_Btree ** ppBTree) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollHash; BT_COLLECTION_XREF * pCollPtr = NULL; IF_RandomGenerator * pRandGen = NULL; F_Database * pDatabase; FLMUINT uiCollection; if (RC_BAD( rc = m_pBtPool->btpReserveBtree( ppBTree))) { goto Exit; } if (pSrcIxd) { if (!m_ppCollectionTbl) { if (RC_BAD( rc = f_calloc( BT_MAX_COLLECTION_TBL_SIZ * sizeof(BT_COLLECTION_XREF), &m_ppCollectionTbl))) { goto Exit; } } uiCollHash = pSrcIxd->uiIndexNum % BT_MAX_COLLECTION_TBL_SIZ; pCollPtr = m_ppCollectionTbl[ uiCollHash]; // Verify that we have the right collection while (pCollPtr && pCollPtr->uiKeyNum != pSrcIxd->uiIndexNum) { pCollPtr = pCollPtr->pNext; } if (!pCollPtr) { pDatabase = m_pResultSetDb->m_pDatabase; // Will need a random number generator. if( RC_BAD( rc = FlmAllocRandomGenerator( &pRandGen))) { goto Exit; } pRandGen->setSeed( (FLMINT32)pSrcIxd->uiIndexNum); // Allocate a new collection key context and create a new // collection for it. if (RC_BAD( rc = f_calloc( sizeof(BT_COLLECTION_XREF), &pCollPtr))) { goto Exit; } // Insert into the table at the head of the list. pCollPtr->pCompare = NULL; pCollPtr->pNext = m_ppCollectionTbl[ uiCollHash]; m_ppCollectionTbl[ uiCollHash] = pCollPtr; TryAgain: // Randomly select a collection number to use. uiCollection = pRandGen->getUINT32( 100, XFLM_MAX_COLLECTION_NUM); // Check to see if it already exists. if (RC_BAD( rc = pDatabase->lFileCreate( m_pResultSetDb, &pCollPtr->Collection.lfInfo, &pCollPtr->Collection, uiCollection, XFLM_LF_COLLECTION, FALSE, TRUE, pSrcIxd->lfInfo.uiEncId))) { if (rc != NE_XFLM_EXISTS) { goto Exit; } rc = NE_XFLM_OK; goto TryAgain; } pCollPtr->uiKeyNum = pSrcIxd->uiIndexNum; pCollPtr->uiCollection = uiCollection; // Set up the comparison object. if ((pCollPtr->pCompare = f_new IXKeyCompare) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } pCollPtr->pCompare->setIxInfo( pSrcDb, pSrcIxd); // Open the btree and use the specified collection. if (RC_BAD( rc = (*ppBTree)->btOpen( m_pResultSetDb, &pCollPtr->Collection.lfInfo, FALSE, TRUE, pCollPtr->pCompare))) { goto Exit; } } else { if (RC_BAD( rc = (*ppBTree)->btOpen( m_pResultSetDb, &m_Collection.lfInfo, FALSE, TRUE, NULL))) { goto Exit; } } Exit: if (pRandGen) { pRandGen->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::addEntry( F_Db * pSrcDb, IXD * pSrcIxd, FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } flmAssert( uiKeyLength <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btInsertEntry( pucKey, uiKeyLength, pucEntry, uiEntryLength, TRUE, TRUE))) { if (rc == NE_XFLM_NOT_UNIQUE) { rc = NE_XFLM_OK; } else { goto Exit; } } Exit: if (pBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::modifyEntry( F_Db * pSrcDb, IXD * pSrcIxd, FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; if (RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } flmAssert( uiKeyLength <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btReplaceEntry( pucKey, uiKeyLength, pucEntry, uiEntryLength, TRUE, TRUE))) { goto Exit; } Exit: if( pBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::deleteEntry( F_Db * pSrcDb, IXD * pSrcIxd, FLMBYTE * pucKey, FLMUINT uiKeyLength) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; if (RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } flmAssert( uiKeyLength <= XFLM_MAX_KEY_SIZE); if (RC_BAD( rc = pBTree->btRemoveEntry( pucKey, uiKeyLength))) { goto Exit; } Exit: if (pBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::findEntry( F_Db * pSrcDb, IXD * pSrcIxd, FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeyLen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength) { RCODE rc = NE_XFLM_OK; FLMUINT uiLengthRV; F_Btree * pBTree = NULL; if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } flmAssert( uiKeyBufLen <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btLocateEntry( pucKey, uiKeyBufLen, puiKeyLen, XFLM_EXACT, NULL, &uiLengthRV))) { goto Exit; } if( pucBuffer) { // Get the entry ... if( RC_BAD( rc = pBTree->btGetEntry( pucKey, uiKeyBufLen, *puiKeyLen, pucBuffer, uiBufferLength, puiReturnLength))) { goto Exit; } } else if( puiReturnLength) { *puiReturnLength = uiLengthRV; } Exit: if( pBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::getCurrent( F_Db * pSrcDb, IXD * pSrcIxd, FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength, FLMUINT * puiReturnLength) { RCODE rc = NE_XFLM_OK; F_Btree * pBTree = NULL; if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } flmAssert( uiKeyLength <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btGetEntry( pucKey, uiKeyLength, uiKeyLength, pucEntry, uiEntryLength, puiReturnLength))) { goto Exit; } Exit: if( pBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::getNext( F_Db * pSrcDb, IXD * pSrcIxd, F_Btree * pBTree, FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeyLen, FLMBYTE * pucEntry, FLMUINT uiEntryLength, FLMUINT * puiReturnLength) { RCODE rc = NE_XFLM_OK; FLMBOOL bFreeBTree = FALSE; if( !pBTree) { if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } bFreeBTree = TRUE; } flmAssert( uiKeyBufLen <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btNextEntry( pucKey, uiKeyBufLen, puiKeyLen, puiReturnLength))) { goto Exit; } if( pucEntry) { if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, pucEntry, uiEntryLength, puiReturnLength))) { goto Exit; } } Exit: if( bFreeBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::getPrev( F_Db * pSrcDb, IXD * pSrcIxd, F_Btree * pBTree, FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeyLen, FLMBYTE * pucEntry, FLMUINT uiEntryLength, FLMUINT * puiReturnLength) { RCODE rc = NE_XFLM_OK; FLMBOOL bFreeBTree = FALSE; if( !pBTree) { if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } bFreeBTree = TRUE; } flmAssert( uiKeyBufLen <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btPrevEntry( pucKey, uiKeyBufLen, puiKeyLen, puiReturnLength))) { goto Exit; } if( pucEntry) { if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, pucEntry, uiEntryLength, puiReturnLength))) { goto Exit; } } Exit: if( bFreeBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::getFirst( F_Db * pSrcDb, IXD * pSrcIxd, F_Btree * pBTree, FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeyLen, FLMBYTE * pucEntry, FLMUINT uiEntryLength, FLMUINT * puiReturnLength) { RCODE rc = NE_XFLM_OK; FLMBOOL bFreeBTree = FALSE; if( !pBTree) { if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } bFreeBTree = TRUE; } flmAssert( uiKeyBufLen <= XFLM_MAX_KEY_SIZE); pBTree->btResetBtree(); if( RC_BAD( rc = pBTree->btFirstEntry( pucKey, uiKeyBufLen, puiKeyLen, puiReturnLength))) { goto Exit; } if( pucEntry) { if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, pucEntry, uiEntryLength, puiReturnLength))) { goto Exit; } } Exit: if( bFreeBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_BtResultSet::getLast( F_Db * pSrcDb, IXD * pSrcIxd, F_Btree * pBTree, FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeyLen, FLMBYTE * pucEntry, FLMUINT uiEntryLength, FLMUINT * puiReturnLength) { RCODE rc = NE_XFLM_OK; FLMBOOL bFreeBTree = FALSE; if( !pBTree) { if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) { goto Exit; } bFreeBTree = TRUE; } flmAssert( uiKeyBufLen <= XFLM_MAX_KEY_SIZE); if( RC_BAD( rc = pBTree->btLastEntry( pucKey, uiKeyBufLen, puiKeyLen, puiReturnLength))) { goto Exit; } if( pucEntry) { if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, pucEntry, uiEntryLength, puiReturnLength))) { goto Exit; } } Exit: if( bFreeBTree) { m_pBtPool->btpReturnBtree( &pBTree); } return( rc); } libxflaim-5.1.969/src/f_btpool.h0000644000175000017500000000335210511001742020004 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Header file for the B-Tree pool // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: f_btpool.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef F_BTPOOL_H #define F_BTPOOL_H #include "f_btree.h" class F_BtPool : public F_Object { public: F_BtPool( void) { m_pBtreeList = NULL; m_hMutex = F_MUTEX_NULL; m_bInitialized = FALSE; } ~F_BtPool( void) { while (m_pBtreeList) { F_Btree * pBtree; pBtree = m_pBtreeList; m_pBtreeList = m_pBtreeList->m_pNext; pBtree->Release(); } if (m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } m_bInitialized = FALSE; } RCODE btpInit( void); RCODE btpReserveBtree( F_Btree ** ppBtree); void btpReturnBtree( F_Btree ** ppBtree); private: F_Btree * m_pBtreeList; F_MUTEX m_hMutex; FLMBOOL m_bInitialized; }; #endif libxflaim-5.1.969/src/fscursor.h0000644000175000017500000001605210511001742020047 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This is the header file that contains the FSIndexCursor class. // // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fscursor.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FSCURSOR_H #define FSCURSOR_H typedef struct KeyPosition { FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; } KEYPOS; /*============================================================================ Desc: File system implementation of a cursor for an index. ============================================================================*/ class FSIndexCursor : public F_Object { public: // Constructors & Destructor FSIndexCursor(); ~FSIndexCursor(); void resetCursor( void); RCODE resetTransaction( F_Db * pDb); RCODE setupKeys( F_Db * pDb, IXD * pIxd, PATH_PRED * pPred, FLMBOOL * pbDoNodeMatch, FLMBOOL * pbCanCompareOnKey, FLMUINT * puiLeafBlocksBetween, FLMUINT * puiTotalRefs, FLMBOOL * pbTotalsEstimated); RCODE currentKey( F_Db * pDb, F_DataVector * pKey); RCODE firstKey( F_Db * pDb, F_DataVector * pKey); RCODE lastKey( F_Db * pDb, F_DataVector * pKey); RCODE nextKey( F_Db * pDb, F_DataVector * pKey, FLMBOOL bSkipCurrKey); RCODE prevKey( F_Db * pDb, F_DataVector * pKey, FLMBOOL bSkipCurrKey); private: RCODE allocDupCheckSet( void); RCODE checkIfDup( FLMUINT64 ui64NodeId, FLMBOOL * pbDup); RCODE useNewDb( F_Db * pDb); RCODE openBTree( F_Db * pDb); // Does this index support native absolute positioning? FLMBOOL isAbsolutePositionable() { return (m_pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE; } RCODE getKeyData( F_Btree * pBTree, FLMUINT uiDataLen); RCODE setKeyPosition( F_Db * pDb, FLMBOOL bGoingForward, FLMBOOL bExcludeKey, F_DataVector * pExtSrchKey, KEYPOS * pSearchKey, KEYPOS * pFoundKey, FLMBOOL bGetKeyData, FLMUINT * puiDataLen, F_Btree * pBTree, FLMUINT * puiAbsolutePos); FINLINE void closeBTree( void) { if (m_bTreeOpen) { m_pbTree->btClose(); m_bTreeOpen = FALSE; m_pDb = NULL; m_eTransType = XFLM_NO_TRANS; } } FINLINE RCODE checkTransaction( F_Db * pDb) { RCODE rc = NE_XFLM_OK; if (RC_OK( rc = pDb->flushKeys())) { rc = (RCODE)((m_ui64CurrTransId != pDb->m_ui64CurrTransID || m_uiBlkChangeCnt != pDb->m_uiBlkChangeCnt) ? resetTransaction( pDb) : NE_XFLM_OK); } return( rc); } RCODE populateKey( F_DataVector * pKey); RCODE checkIfKeyInRange( FLMBOOL bPositionForward); FINLINE void getCurrKey( KEYPOS * pKey ) { f_memcpy( pKey->ucKey, m_curKey.ucKey, m_curKey.uiKeyLen); pKey->uiKeyLen = m_curKey.uiKeyLen; } // Database information FLMUINT64 m_ui64CurrTransId; FLMUINT m_uiBlkChangeCnt; FLMUINT m_uiIndexNum; LFILE * m_pLFile; IXD * m_pIxd; F_Db * m_pDb; eDbTransType m_eTransType; // Key range information FLMBOOL m_bSetup; KEYPOS m_fromKey; KEYPOS m_untilKey; // State information. FLMBOOL m_bAtBOF; // Before the first key. FLMBOOL m_bAtEOF; // After the last key. KEYPOS m_curKey; // Current key FLMBYTE * m_pucCurKeyDataBuf; FLMUINT m_uiCurKeyDataBufSize; FLMUINT m_uiCurKeyDataLen; F_Btree * m_pbTree; FLMBOOL m_bTreeOpen; F_DynSearchSet * m_pNodeIdSet; FLMBOOL m_bElimDups; FLMBOOL m_bMovingForward; IXKeyCompare m_ixCompare; F_DataVector m_fromExtKey; F_DataVector m_untilExtKey; friend class F_Query; }; /*============================================================================ Desc: File system implementation of a cursor for a collection. ============================================================================*/ class FSCollectionCursor : public F_Object { public: // Constructors & Destructor FSCollectionCursor(); ~FSCollectionCursor(); void resetCursor(); RCODE resetTransaction( F_Db * pDb); RCODE setupRange( F_Db * pDb, FLMUINT uiCollection, FLMBOOL bDocumentIds, FLMUINT64 ui64LowNodeId, FLMUINT64 ui64HighNodeId, FLMUINT * puiLeafBlocksBetween, FLMUINT * puiTotalNodes, FLMBOOL * pbTotalsEstimated); RCODE currentNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId); RCODE firstNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId); RCODE lastNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId); RCODE nextNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId); RCODE prevNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId); private: RCODE setNodePosition( F_Db * pDb, FLMBOOL bGoingForward, FLMUINT64 ui64NodeId, FLMUINT64 * pui64FoundNodeId, F_Btree * pBTree); RCODE openBTree( F_Db * pDb); FINLINE void closeBTree( void) { if (m_bTreeOpen) { m_pbTree->btClose(); m_bTreeOpen = FALSE; m_pDb = NULL; m_eTransType = XFLM_NO_TRANS; } } FINLINE RCODE checkTransaction( F_Db * pDb) { RCODE rc = NE_XFLM_OK; if (pDb->m_uiDirtyNodeCount) { if (RC_BAD( rc = pDb->flushDirtyNodes())) { goto Exit; } } rc = (RCODE)((m_pDb != pDb || m_ui64CurrTransId != pDb->m_ui64CurrTransID || m_uiBlkChangeCnt != pDb->m_uiBlkChangeCnt) ? resetTransaction( pDb) : NE_XFLM_OK); Exit: return( rc); } FINLINE RCODE populateNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId ) { if (pui64NodeId) { *pui64NodeId = m_ui64CurNodeId; } if (ppNode) { return( pDb->getNode( m_uiCollection, m_ui64CurNodeId, ppNode)); } return( NE_XFLM_OK); } RCODE checkIfNodeInRange( FLMBOOL bPositionForward); // Database Information FLMUINT64 m_ui64CurrTransId; FLMUINT m_uiBlkChangeCnt; FLMUINT m_uiCollection; F_COLLECTION * m_pCollection; FLMBOOL m_bDocumentIds; LFILE * m_pLFile; F_Db * m_pDb; eDbTransType m_eTransType; // Key range information FLMBOOL m_bSetup; FLMUINT64 m_ui64FromNodeId; FLMUINT64 m_ui64UntilNodeId; // State information. FLMBOOL m_bAtBOF; // Before the first key. FLMBOOL m_bAtEOF; // After the last key. FLMUINT64 m_ui64CurNodeId; // Current node F_Btree * m_pbTree; FLMBOOL m_bTreeOpen; }; #endif libxflaim-5.1.969/src/kybldkey.cpp0000644000175000017500000014113610511001742020354 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Build from and until keys from a predicate // // Tabs: 3 // // Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kybldkey.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC FLMUINT kyAddInclComponent( ICD * pIcd, FLMBYTE * pucKeyEnd, FLMBOOL bFromKey, FLMUINT uiMaxSpaceLeft); FINLINE void flmSetupFirstToLastKey( FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen); FSTATIC RCODE flmAddNonTextKeyPiece( PATH_PRED * pPred, IXD * pIxd, ICD * pIcd, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, F_DataVector * pUntilSearchKey, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbCanCompareOnKey); FSTATIC RCODE flmUTF8FindWildcard( const FLMBYTE * pucValue, FLMUINT * puiCharPos, FLMUINT * puiCompareRules); FSTATIC RCODE flmCountCharacters( const FLMBYTE * pucValue, FLMUINT uiValueLen, FLMUINT uiMaxToCount, FLMUINT * puiCompareRules, FLMUINT * puiCount); FSTATIC RCODE flmSelectBestSubstr( const FLMBYTE ** ppucValue, FLMUINT * puiValueLen, FLMUINT * puiCompareRules, FLMBOOL * pbTrailingWildcard, FLMBOOL * pbNotUsingFirstOfString); FSTATIC void setFromCaseByte( FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, FLMUINT uiCaseLen, FLMBOOL bIsDBCS, FLMBOOL bAscending, FLMBOOL bExcl); FSTATIC void setUntilCaseByte( FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMUINT uiCaseLen, FLMBOOL bIsDBCS, FLMBOOL bAscending, FLMBOOL bExcl); FSTATIC RCODE flmAddTextKeyPiece( PATH_PRED * pPred, IXD * pIxd, ICD * pIcd, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, F_DataVector * pUntilSearchKey, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbCanCompareOnKey); /**************************************************************************** Desc: Add what is needed to an until key so that it is greater than or equal for all possible components that come after the primary component. In other words, this key should be less than any keys whose primary component is greater than it, but greater than all keys whose primary component is less than or equal to it. ****************************************************************************/ FSTATIC FLMUINT kyAddInclComponent( ICD * pIcd, FLMBYTE * pucKeyEnd, FLMBOOL bFromKey, FLMUINT uiMaxSpaceLeft) { FLMUINT uiBytesAdded = 0; // If there is a next key component that would be expected, set it to // the highest possible value. if (pIcd->pNextKeyComponent) { // Must at least be room for a 2 byte length. if (uiMaxSpaceLeft >= 2) { // Need 2nd key component to sort lower if it is the from key // higher if it is the until key. Note that KEY_LOW_VALUE and // KEY_HIGH_VALUE always sort lower/higher no matter whether the // component is ascending or descending. if (bFromKey) { UW2FBA( (FLMUINT16)KEY_LOW_VALUE, pucKeyEnd); } else { UW2FBA( (FLMUINT16)KEY_HIGH_VALUE, pucKeyEnd); } uiBytesAdded = 2; } } else { // There are no more key components. // Output one byte of 0xFF - which should be higher than any // possible SEN that could be output for document ID and node IDs. // Only do this for until keys. For from keys, no need to add // anything, because an empty list of IDs will be be inclusive // on the from side - it will sort lower, and therefore be inclusive. if (uiMaxSpaceLeft && !bFromKey) { *pucKeyEnd = 0xFF; uiBytesAdded = 1; } } return( uiBytesAdded); } /**************************************************************************** Desc: Setup a first-to-last key for the index. ****************************************************************************/ FINLINE void flmSetupFirstToLastKey( FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen ) { UW2FBA( KEY_LOW_VALUE, pucFromKey); UW2FBA( KEY_HIGH_VALUE, pucUntilKey); *puiFromKeyLen = 2; *puiUntilKeyLen = 2; } /**************************************************************************** Desc: Add a key piece to the from and until key. Text fields are not handled in this routine because of their complexity. Notes: The goal of this code is to build a the collated compound piece for the 'from' and 'until' key only once instead of twice. ****************************************************************************/ FSTATIC RCODE flmAddNonTextKeyPiece( PATH_PRED * pPred, IXD * pIxd, ICD * pIcd, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, F_DataVector * pUntilSearchKey, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbCanCompareOnKey) { RCODE rc = NE_XFLM_OK; FLMUINT uiFromKeyLen = 0; FLMUINT uiUntilKeyLen = 0; FLMBYTE * pucFromKeyLenPos = pucFromKey; FLMBYTE * pucUntilKeyLenPos = pucUntilKey; FLMBOOL bDataTruncated; FLMBYTE * pucFromBuf; FLMUINT uiFromBufLen; FLMBYTE * pucUntilBuf; FLMUINT uiUntilBufLen; FLMBYTE ucFromNumberBuf [FLM_MAX_NUM_BUF_SIZE]; FLMBYTE ucUntilNumberBuf [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiValue; FLMINT iValue; FLMBOOL bNeg; FLMUINT64 ui64Value; FLMINT64 i64Value; FLMUINT uiFromFlags = 0; FLMUINT uiUntilFlags = 0; FQVALUE * pFromValue; FQVALUE * pUntilValue; FLMBOOL bInclFrom; FLMBOOL bInclUntil; FLMBOOL bAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE: TRUE; IF_BufferIStream * pBufferIStream = NULL; // Leave room for the component length pucFromKey += 2; pucUntilKey += 2; // Handle the presence case here - this is not done in kyCollate. if (pIcd->uiFlags & ICD_PRESENCE) { f_UINT32ToBigEndian( (FLMUINT32)pIcd->uiDictNum, pucFromKey); uiFromKeyLen = uiUntilKeyLen = 4; f_memcpy( pucUntilKey, pucFromKey, uiUntilKeyLen); } else if (pIcd->uiFlags & ICD_METAPHONE) { if (pPred->eOperator != XFLM_APPROX_EQ_OP || pPred->pFromValue->eValType != XFLM_UTF8_VAL) { flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, pucUntilKeyLenPos, puiUntilKeyLen); if (pPred->eOperator != XFLM_EXISTS_OP) { *pbCanCompareOnKey = FALSE; } goto Exit; } *pbCanCompareOnKey = FALSE; // The value type in pPred->pFromValue is XFLM_UTF8_VAL, but the // calling routine should have put the metaphone value into // pPred->pFromValue->val.uiVal. Sort of weird, but was the // only way we could evaluate the cost of multiple words in // the string. uiFromBufLen = sizeof( ucFromNumberBuf); if( RC_BAD( rc = FlmUINT2Storage( pPred->pFromValue->val.uiVal, &uiFromBufLen, ucFromNumberBuf))) { goto Exit; } pucFromBuf = &ucFromNumberBuf [0]; if( !pBufferIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)pucFromBuf, uiFromBufLen))) { goto Exit; } uiFromKeyLen = XFLM_MAX_KEY_SIZE - 2; bDataTruncated = FALSE; // Pass 0 for compare rules because it is non-text if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen, pBufferIStream, XFLM_NUMBER_TYPE, pIcd->uiFlags, 0, pIcd->uiLimit, NULL, NULL, pIxd->uiLanguage, FALSE, FALSE, &bDataTruncated, NULL))) { goto Exit; } pBufferIStream->closeStream(); if (bDataTruncated) { // This should never happen on numeric data. flmAssert( 0); *pbCanCompareOnKey = FALSE; } if (uiFromKeyLen) { f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); } uiUntilKeyLen = uiFromKeyLen; } else { if (pPred->eOperator == XFLM_EXISTS_OP || pPred->eOperator == XFLM_NE_OP || pPred->eOperator == XFLM_APPROX_EQ_OP) { // Setup a first-to-last key flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, pucUntilKeyLenPos, puiUntilKeyLen); goto Exit; } // Only other operator possible is the range operator flmAssert( pPred->eOperator == XFLM_RANGE_OP); if (bAscending) { pFromValue = pPred->pFromValue; bInclFrom = pPred->bInclFrom; pUntilValue = pPred->pUntilValue; bInclUntil = pPred->bInclUntil; } else { pFromValue = pPred->pUntilValue; bInclFrom = pPred->bInclUntil; pUntilValue = pPred->pFromValue; bInclUntil = pPred->bInclFrom; } // Set up from buffer if (!pFromValue) { pucFromBuf = NULL; uiFromBufLen = 0; } else { switch (pFromValue->eValType) { case XFLM_UINT_VAL: uiValue = pFromValue->val.uiVal; if (!bInclFrom) { if (bAscending) { uiValue++; } else { uiValue--; } } uiFromBufLen = sizeof( ucFromNumberBuf); if( RC_BAD( rc = FlmUINT2Storage( uiValue, &uiFromBufLen, ucFromNumberBuf))) { goto Exit; } pucFromBuf = &ucFromNumberBuf [0]; break; case XFLM_INT_VAL: iValue = pFromValue->val.iVal; if (!bInclFrom) { if (bAscending) { iValue++; } else { iValue--; } } uiFromBufLen = sizeof( ucFromNumberBuf); if (RC_BAD( rc = FlmINT2Storage( iValue, &uiFromBufLen, ucFromNumberBuf))) { goto Exit; } pucFromBuf = &ucFromNumberBuf [0]; break; case XFLM_UINT64_VAL: ui64Value = pFromValue->val.ui64Val; if (!bInclFrom) { if (bAscending) { ui64Value++; } else { ui64Value--; } } uiFromBufLen = sizeof( ucFromNumberBuf); if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiFromBufLen, ucFromNumberBuf, FALSE, FALSE))) { goto Exit; } pucFromBuf = &ucFromNumberBuf [0]; break; case XFLM_INT64_VAL: i64Value = pFromValue->val.i64Val; if (!bInclFrom) { if (bAscending) { i64Value++; } else { i64Value--; } } if (i64Value < 0) { bNeg = TRUE; ui64Value = (FLMUINT64)-i64Value; } else { bNeg = FALSE; ui64Value = (FLMUINT64)i64Value; } uiFromBufLen = sizeof( ucFromNumberBuf); if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiFromBufLen, ucFromNumberBuf, bNeg, FALSE))) { goto Exit; } pucFromBuf = &ucFromNumberBuf [0]; break; case XFLM_BINARY_VAL: pucFromBuf = pFromValue->val.pucBuf; uiFromBufLen = pFromValue->uiDataLen; if (!bInclFrom) { // Should use EXCLUSIVE_GT_FLAG even if in descending // order, because the comparison routines will take // that into account. uiFromFlags |= EXCLUSIVE_GT_FLAG; } break; default: // Text type should have been taken care of elsewhere. rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); goto Exit; } } // Set up until buffer. if (!pUntilValue) { pucUntilBuf = NULL; uiUntilBufLen = 0; } else if (pUntilValue == pFromValue) { pucUntilBuf = pucFromBuf; uiUntilBufLen = uiFromBufLen; } else { switch (pUntilValue->eValType) { case XFLM_UINT_VAL: uiValue = pUntilValue->val.uiVal; if (!bInclUntil) { if (bAscending) { uiValue--; } else { uiValue++; } } uiUntilBufLen = sizeof( ucUntilNumberBuf); if( RC_BAD( rc = FlmUINT2Storage( uiValue, &uiUntilBufLen, ucUntilNumberBuf))) { goto Exit; } pucUntilBuf = &ucUntilNumberBuf [0]; break; case XFLM_INT_VAL: iValue = pUntilValue->val.iVal; if (!bInclUntil) { if (bAscending) { iValue--; } else { iValue++; } } uiUntilBufLen = sizeof( ucUntilNumberBuf); if (RC_BAD( rc = FlmINT2Storage( iValue, &uiUntilBufLen, ucUntilNumberBuf))) { goto Exit; } pucUntilBuf = &ucUntilNumberBuf [0]; break; case XFLM_UINT64_VAL: ui64Value = pUntilValue->val.ui64Val; if (!bInclUntil) { if (bAscending) { ui64Value--; } else { ui64Value++; } } uiUntilBufLen = sizeof( ucUntilNumberBuf); if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiUntilBufLen, ucUntilNumberBuf, FALSE, FALSE))) { goto Exit; } pucUntilBuf = &ucUntilNumberBuf [0]; break; case XFLM_INT64_VAL: i64Value = pUntilValue->val.i64Val; if (!bInclUntil) { if (bAscending) { i64Value--; } else { i64Value++; } } if (i64Value < 0) { bNeg = TRUE; ui64Value = (FLMUINT64)-i64Value; } else { bNeg = FALSE; ui64Value = (FLMUINT64)i64Value; } uiUntilBufLen = sizeof( ucUntilNumberBuf); if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiUntilBufLen, ucUntilNumberBuf, bNeg, FALSE))) { goto Exit; } pucUntilBuf = &ucUntilNumberBuf [0]; break; case XFLM_BINARY_VAL: pucUntilBuf = pUntilValue->val.pucBuf; uiUntilBufLen = pUntilValue->uiDataLen; if (!bInclUntil) { // Should use EXCLUSIVE_LT_FLAG even if in descending // order, because the comparison routines will take // that into account. uiUntilFlags |= EXCLUSIVE_LT_FLAG; } break; default: // Text type should have been taken care of elsewhere. rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); goto Exit; } } // Generate the keys using the from and until buffers that // have been set up. if (!pucFromBuf && !pucUntilBuf) { // setup a first-to-last key flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, pucUntilKeyLenPos, puiUntilKeyLen); goto Exit; } // Set up the from key if (!pucFromBuf) { uiFromKeyLen = KEY_LOW_VALUE; } else { if( !pBufferIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)pucFromBuf, uiFromBufLen))) { goto Exit; } uiFromKeyLen = XFLM_MAX_KEY_SIZE - 2; bDataTruncated = FALSE; // Pass 0 for compare rules on non-text component. if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen, pBufferIStream, icdGetDataType( pIcd), pIcd->uiFlags, 0, pIcd->uiLimit, NULL, NULL, pIxd->uiLanguage, FALSE, FALSE, &bDataTruncated, NULL))) { goto Exit; } pBufferIStream->closeStream(); if (bDataTruncated) { *pbCanCompareOnKey = FALSE; // Save the original data into pFromSearchKey so the comparison // routines can do a comparison on the full value if // necessary. // Better only be a binary data type at this point. flmAssert( pFromValue->eValType == XFLM_BINARY_VAL); if (RC_BAD( rc = pFromSearchKey->setBinary( pIcd->uiKeyComponent - 1, pucFromBuf, uiFromBufLen))) { goto Exit; } uiFromFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); } } // Set up the until key if (!pucUntilBuf) { uiUntilKeyLen = KEY_HIGH_VALUE; } else if (pucUntilBuf == pucFromBuf) { if (uiFromKeyLen) { f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); } uiUntilKeyLen = uiFromKeyLen; // The "exclusive" flags better not have been set in this // case - because this should only be possible if the operator // was an EQ. flmAssert( !(uiFromFlags & EXCLUSIVE_GT_FLAG) && !(uiUntilFlags & EXCLUSIVE_LT_FLAG)); if (uiFromFlags & SEARCH_KEY_FLAG) { // Save the original data into pUntilSearchKey so the comparison // routines can do a comparison on the full value if // necessary. // Better only be a binary data type at this point. flmAssert( pUntilValue->eValType == XFLM_BINARY_VAL); if (RC_BAD( rc = pUntilSearchKey->setBinary( pIcd->uiKeyComponent - 1, pucUntilBuf, uiUntilBufLen))) { goto Exit; } uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); } } else { if( !pBufferIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)pucUntilBuf, uiUntilBufLen))) { goto Exit; } uiUntilKeyLen = XFLM_MAX_KEY_SIZE - 2; bDataTruncated = FALSE; // Pass 0 for compare rule because it is a non-text piece. if (RC_BAD( rc = KYCollateValue( pucUntilKey, &uiUntilKeyLen, pBufferIStream, icdGetDataType( pIcd), pIcd->uiFlags, 0, pIcd->uiLimit, NULL, NULL, pIxd->uiLanguage, FALSE, FALSE, &bDataTruncated, NULL))) { goto Exit; } pBufferIStream->closeStream(); if (bDataTruncated) { *pbCanCompareOnKey = FALSE; // Save the original data into pUntilSearchKey so the comparison // routines can do a comparison on the full value if // necessary. // Better only be a binary data type at this point. flmAssert( pUntilValue->eValType == XFLM_BINARY_VAL); if (RC_BAD( rc = pUntilSearchKey->setBinary( pIcd->uiKeyComponent - 1, pucUntilBuf, uiUntilBufLen))) { goto Exit; } uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); } } } UW2FBA( (FLMUINT16)(uiFromKeyLen | uiFromFlags), pucFromKeyLenPos); UW2FBA( (FLMUINT16)(uiUntilKeyLen | uiUntilFlags), pucUntilKeyLenPos); if (!(uiFromFlags & EXCLUSIVE_GT_FLAG) && uiFromKeyLen < XFLM_MAX_KEY_SIZE - 2) { uiFromKeyLen += kyAddInclComponent( pIcd, &pucFromKey [uiFromKeyLen], TRUE, XFLM_MAX_KEY_SIZE - 2 - uiFromKeyLen); } if (!(uiUntilFlags & EXCLUSIVE_LT_FLAG) && uiUntilKeyLen < XFLM_MAX_KEY_SIZE - 2) { uiUntilKeyLen += kyAddInclComponent( pIcd, &pucUntilKey [uiUntilKeyLen], FALSE, XFLM_MAX_KEY_SIZE - 2 - uiUntilKeyLen); } // Set the FROM and UNTIL key length return values. if (uiFromKeyLen != KEY_HIGH_VALUE && uiFromKeyLen != KEY_LOW_VALUE) { *puiFromKeyLen = uiFromKeyLen + 2; } else { *puiFromKeyLen = 2; } if (uiUntilKeyLen != KEY_HIGH_VALUE && uiUntilKeyLen != KEY_LOW_VALUE) { *puiUntilKeyLen = uiUntilKeyLen + 2; } else { *puiUntilKeyLen = 2; } Exit: if( pBufferIStream) { pBufferIStream->Release(); } return( rc); } /**************************************************************************** Desc: Finds the location of a wildcard in the internal text string, if any. ****************************************************************************/ FSTATIC RCODE flmUTF8FindWildcard( const FLMBYTE * pucValue, FLMUINT * puiCharPos, FLMUINT * puiCompareRules) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucSaveVal; const FLMBYTE * pucStart = pucValue; FLMUNICODE uzChar; FLMUINT uiCompareRules = *puiCompareRules; flmAssert( pucValue); *puiCharPos = FLM_MAX_UINT; for( ;;) { pucSaveVal = pucValue; if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, NULL, &uzChar))) { goto Exit; } if (!uzChar) { break; } if ((uzChar = f_convertChar( uzChar, uiCompareRules)) == 0) { continue; } if (uzChar == ASCII_WILDCARD) { *puiCharPos = (FLMUINT)(pucSaveVal - pucStart); goto Exit; } if (uzChar != ASCII_SPACE) { // Once we hit a non-space character - except for the wildcard, // we can remove the ignore leading space rule. uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); if (uzChar == ASCII_BACKSLASH) { // Skip the escaped character if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, NULL, &uzChar))) { goto Exit; } if (!uzChar) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } } } Exit: *puiCompareRules = uiCompareRules; return( rc); } /**************************************************************************** Desc: Count the number of characters that would be returned. ****************************************************************************/ FSTATIC RCODE flmCountCharacters( const FLMBYTE * pucValue, FLMUINT uiValueLen, FLMUINT uiMaxToCount, FLMUINT * puiCompareRules, FLMUINT * puiCharCount) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumChars = 0; FLMUINT uiCompareRules = *puiCompareRules; const FLMBYTE * pucEnd = &pucValue [uiValueLen]; FLMUNICODE uzChar; FLMBOOL bLastCharWasSpace = FALSE; FLMUINT uiNumSpaces = 0; while (uiNumChars < uiMaxToCount) { if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, pucEnd, &uzChar))) { goto Exit; } if (!uzChar) { if (bLastCharWasSpace) { // The spaces are trailing spaces, so if the ignore trailing // space flag is set, we do nothing. If the compress space // flag is set, we will increment by one. Otherwise, we will // add in a count for all of the spaces. if (!(uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE)) { if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) { uiNumChars++; } else { uiNumChars += uiNumSpaces; } } } break; } if ((uzChar = f_convertChar( uzChar, uiCompareRules)) == 0) { continue; } if (uzChar == ASCII_SPACE) { if (!bLastCharWasSpace) { bLastCharWasSpace = TRUE; uiNumSpaces = 0; } uiNumSpaces++; } else { // Once we hit a non-space character, disable the ignore // leading space flag. uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); if (bLastCharWasSpace) { bLastCharWasSpace = FALSE; if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) { // Consecutive spaces are compressed to a single space. uiNumChars++; } else { // The spaces were not trailing spaces and were not compressed // so we need to count all of them. uiNumChars += uiNumSpaces; } } if (uzChar == ASCII_BACKSLASH) { // Skip the next character, no matter what it is - only want // to count one character here. A backslash followed by any // character is only a single character. if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucValue, pucEnd, &uzChar))) { goto Exit; } } uiNumChars++; } } Exit: *puiCharCount = uiNumChars; *puiCompareRules = uiCompareRules; return( rc); } /**************************************************************************** Desc: Select the best substring for a CONTAINS or MATCH_END search. ****************************************************************************/ FSTATIC RCODE flmSelectBestSubstr( const FLMBYTE ** ppucValue, // [in/out] FLMUINT * puiValueLen, // [in/out] FLMUINT * puiCompareRules, FLMBOOL * pbTrailingWildcard, // [in] change if found a wildcard FLMBOOL * pbNotUsingFirstOfString) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucValue = *ppucValue; const FLMBYTE * pucCurValue; const FLMBYTE * pucBest; const FLMBYTE * pucEnd; const FLMBYTE * pucTmp; FLMBOOL bBestTerminatesWithWildCard = *pbTrailingWildcard; FLMUINT uiCurLen; FLMUINT uiBestNumChars; FLMUINT uiBestValueLen; FLMUINT uiWildcardPos; FLMUINT uiTargetNumChars; FLMUINT uiNumChars; FLMBOOL bNotUsingFirstOfString = FALSE; FLMUNICODE uzChar; FLMUNICODE uzDummy; #define GOOD_ENOUGH_CHARS 16 // There may not be any wildcards at all. Find the first one. if (RC_BAD( rc = flmUTF8FindWildcard( pucValue, &uiWildcardPos, puiCompareRules))) { goto Exit; } // FLM_MAX_UINT is returned if no wildcard was found. if (uiWildcardPos == FLM_MAX_UINT) { goto Exit; } pucEnd = &pucValue [*puiValueLen]; bBestTerminatesWithWildCard = TRUE; pucBest = pucValue; // Skip past the wildcard pucTmp = &pucValue [uiWildcardPos]; if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucTmp, pucEnd, &uzDummy))) { goto Exit; } uiCurLen = *puiValueLen - (FLMUINT)(pucTmp - pucValue); pucCurValue = pucTmp; uiBestValueLen = uiWildcardPos; if (RC_BAD( rc = flmCountCharacters( pucValue, uiWildcardPos, GOOD_ENOUGH_CHARS, puiCompareRules, &uiBestNumChars))) { goto Exit; } uiTargetNumChars = uiBestNumChars + uiBestNumChars; // Here is the great FindADoubleLengthThatIsBetter algorithm. // Below are the values to pick a next better contains key. // First Key Size Next Key Size that will be used // 1 * 2 2 // Single char searches are REALLY BAD // 2 * 2 4 // 3 * 2 6 // 4 * 2 8 // ... ... // At each new key piece, increment the target length by 2 so that it // will be even harder to find a better key. while (uiBestNumChars < GOOD_ENOUGH_CHARS) { pucTmp = pucCurValue; if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucTmp, pucEnd, &uzChar))) { goto Exit; } if (!uzChar) { break; } if (RC_BAD( rc = flmUTF8FindWildcard( pucCurValue, &uiWildcardPos, puiCompareRules))) { goto Exit; } // FLM_MAX_UINT is returned when no wildcard is found. if (uiWildcardPos == FLM_MAX_UINT) { // No wildcard found // Check the last section that may or may not have trailing *. if (RC_BAD( rc = flmCountCharacters( pucCurValue, uiCurLen, GOOD_ENOUGH_CHARS, puiCompareRules, &uiNumChars))) { goto Exit; } if (uiNumChars >= uiTargetNumChars) { pucBest = pucCurValue; uiBestValueLen = uiCurLen; bBestTerminatesWithWildCard = *pbTrailingWildcard; } break; } else { if (RC_BAD( rc = flmCountCharacters( pucCurValue, uiWildcardPos, GOOD_ENOUGH_CHARS, puiCompareRules, &uiNumChars))) { goto Exit; } if (uiNumChars >= uiTargetNumChars) { pucBest = pucCurValue; uiBestValueLen = uiWildcardPos; uiBestNumChars = uiNumChars; uiTargetNumChars = uiNumChars + uiNumChars; } else { uiTargetNumChars += 2; } // Skip past the wildcard pucTmp = &pucCurValue[ uiWildcardPos]; if (RC_BAD( rc = f_getCharFromUTF8Buf( &pucTmp, pucEnd, &uzDummy))) { goto Exit; } uiCurLen -= (FLMUINT)(pucTmp - pucCurValue); pucCurValue = pucTmp; } } if (pucBest != *ppucValue) { bNotUsingFirstOfString = TRUE; } *ppucValue = pucBest; *puiValueLen = uiBestValueLen; *pbTrailingWildcard = bBestTerminatesWithWildCard; Exit: *pbNotUsingFirstOfString = bNotUsingFirstOfString; return( rc); } /**************************************************************************** Desc: Set the case byte on the from key for a case-insensitive search that is using a case sensitive index. ****************************************************************************/ FSTATIC void setFromCaseByte( FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, FLMUINT uiCaseLen, FLMBOOL bIsDBCS, FLMBOOL bAscending, FLMBOOL bExcl) { // Subtract off all but the case marker. // Remember that for DBCS (Asian) the case marker is two bytes. *puiFromKeyLen -= (uiCaseLen - ((FLMUINT)(bIsDBCS ? (FLMUINT)2 : (FLMUINT)1))); if (bExcl) { if (bAscending) { // Keys are in ascending order: // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // Thus, to exclude all "abc"s on "from" side we need the // following key: // key == abc+6 (F_COLL_MARKER | F_SC_UPPER) + 1 pucFromKey[ *puiFromKeyLen - 1] = (F_COLL_MARKER | F_SC_UPPER); } else { // Keys are in descending order: // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // Thus, to exclude "abc"s on "from" side we need the // following key: // key == abc+4 (F_COLL_MARKER | F_SC_LOWER) pucFromKey[ *puiFromKeyLen - 1] = (F_COLL_MARKER | F_SC_LOWER); } } else // Inclusive { if (bAscending) { // Keys are in ascending order: // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // Thus, to include all "abc"s on "from" side, // we need the following key: // key == abc+4 (F_COLL_MARKER | F_SC_LOWER) pucFromKey [*puiFromKeyLen - 1] = F_COLL_MARKER | F_SC_LOWER; } else { // Keys are in descending order: // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // Thus, to include all "abc"s on "from" side we need the // following key: // key == abc+6 (F_COLL_MARKER | F_SC_UPPER) pucFromKey [*puiFromKeyLen - 1] = F_COLL_MARKER | F_SC_UPPER; } } } /**************************************************************************** Desc: Set the case byte on the until key for a case-insensitive search that is using a case sensitive index. ****************************************************************************/ FSTATIC void setUntilCaseByte( FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMUINT uiCaseLen, FLMBOOL bIsDBCS, FLMBOOL bAscending, FLMBOOL bExcl) { // Subtract off all but the case marker. // Remember that for DBCS (Asian) the case marker is two bytes. *puiUntilKeyLen -= (uiCaseLen - ((FLMUINT)(bIsDBCS ? (FLMUINT)2 : (FLMUINT)1))); if (bExcl) { if (bAscending) { // Keys are in ascending order: // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // Thus, to exclude all "abc"s on the "until" side we need // the following key: // key == abc+4 (F_COLL_MARKER | F_SC_LOWER) pucUntilKey[ *puiUntilKeyLen - 1] = (F_COLL_MARKER | F_SC_LOWER); } else { // Keys are in descending order: // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // Thus, to exclude all "abc"s on the "until" side we need // the following key: // key == abc+6 (F_COLL_MARKER | F_SC_UPPER) + 1 pucUntilKey[ *puiUntilKeyLen - 1] = (F_COLL_MARKER | F_SC_UPPER); } } else { if (bAscending) { // Keys are in ascending order: // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // Thus, to get include all "abc"s on the "until" side we need // the following key: // key == abc+6 (F_COLL_MARKER | F_SC_UPPER) pucUntilKey [*puiUntilKeyLen - 1] = (F_COLL_MARKER | F_SC_UPPER); } else { // Keys are in descending order: // "ABC" key == abc+6 (6 is F_COLL_MARKER | F_SC_UPPER) // "abc" key == abc+4 (4 is F_COLL_MARKER | F_SC_LOWER) // Thus, to include all "abc"s on the "until side we need // the following key: // key == abc+4 (F_COLL_MARKER | F_SC_LOWER) pucUntilKey [*puiUntilKeyLen - 1] = (F_COLL_MARKER | F_SC_LOWER); } } } /**************************************************************************** Desc: Build a text key. ****************************************************************************/ FSTATIC RCODE flmAddTextKeyPiece( PATH_PRED * pPred, IXD * pIxd, ICD * pIcd, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, F_DataVector * pUntilSearchKey, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbCanCompareOnKey) { RCODE rc = NE_XFLM_OK; FLMUINT uiFromKeyLen = 0; FLMUINT uiUntilKeyLen = 0; FLMBYTE * pucFromKeyLenPos = pucFromKey; FLMBYTE * pucUntilKeyLenPos = pucUntilKey; FLMUINT uiLanguage = pIxd->uiLanguage; FLMUINT uiCollationLen = 0; FLMUINT uiCharCount; FLMUINT uiCaseLen; FLMBOOL bOriginalCharsLost = FALSE; FLMBOOL bIsDBCS = (uiLanguage >= FLM_FIRST_DBCS_LANG && uiLanguage <= FLM_LAST_DBCS_LANG) ? TRUE : FALSE; FLMBOOL bCaseInsensitive = (FLMBOOL)((pPred->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) ? TRUE : FALSE); FLMBOOL bDoFirstSubstring = (FLMBOOL)((pIcd->uiFlags & ICD_SUBSTRING) ? TRUE : FALSE); FLMBOOL bDoMatchBegin = FALSE; FLMBOOL bTrailingWildcard = FALSE; const FLMBYTE * pucFromUTF8Buf = NULL; FLMUINT uiFromBufLen = 0; const FLMBYTE * pucUntilUTF8Buf = NULL; FLMUINT uiUntilBufLen = 0; FLMUINT uiWildcardPos; FLMBOOL bDataTruncated; FLMUINT uiFromFlags = 0; FLMUINT uiUntilFlags = 0; FLMUINT uiCompareRules; FLMBOOL bAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE: TRUE; IF_BufferIStream * pBufferIStream = NULL; switch (pPred->eOperator) { // The difference between MATCH and EQ_OP is that EQ does // not support wildcards embedded in the search key. case XFLM_MATCH_OP: flmAssert( pPred->pFromValue->eValType == XFLM_UTF8_VAL); pucFromUTF8Buf = pPred->pFromValue->val.pucBuf; uiFromBufLen = pPred->pFromValue->uiDataLen; uiCompareRules = pIcd->uiCompareRules; if (RC_BAD( rc = flmUTF8FindWildcard( pucFromUTF8Buf, &uiWildcardPos, &uiCompareRules))) { goto Exit; } // If there is no wildcard, uiWildcardPos will be FLM_MAX_UINT if (uiWildcardPos != FLM_MAX_UINT) { // If wildcard is in position 0, it is NOT // a match begin. if (uiWildcardPos) { bDoMatchBegin = TRUE; uiFromBufLen = uiWildcardPos; } } if (!(pIcd->uiFlags & ICD_SUBSTRING)) { // Index is NOT a substring index if (!bDoMatchBegin) { // Wildcard was at the beginning, will have // to search the index from first to last pucFromUTF8Buf = NULL; } else { bTrailingWildcard = TRUE; } } else { FLMBOOL bNotUsingFirstOfString; // If this is a substring index look for a // better 'contains' string to search for. // We don't like "A*BCDEFG" searches. bTrailingWildcard = bDoMatchBegin; uiCompareRules = pIcd->uiCompareRules; if (RC_BAD( rc = flmSelectBestSubstr( &pucFromUTF8Buf, &uiFromBufLen, &uiCompareRules, &bTrailingWildcard, &bNotUsingFirstOfString))) { goto Exit; } if (bNotUsingFirstOfString) { bDoMatchBegin = bTrailingWildcard; *pbCanCompareOnKey = FALSE; bDoFirstSubstring = FALSE; } else if (bTrailingWildcard) { bDoMatchBegin = TRUE; } if (RC_BAD( rc = flmCountCharacters( pucFromUTF8Buf, uiFromBufLen, 2, &uiCompareRules, &uiCharCount))) { goto Exit; } // Special case: Single character contains/MEnd in a substr ix. if (!bIsDBCS && uiCharCount < 2) { pucFromUTF8Buf = NULL; } } pucUntilUTF8Buf = pucFromUTF8Buf; uiUntilBufLen = uiFromBufLen; break; case XFLM_RANGE_OP: if (bAscending) { if (pPred->pFromValue) { flmAssert( pPred->pFromValue->eValType == XFLM_UTF8_VAL); pucFromUTF8Buf = pPred->pFromValue->val.pucBuf; uiFromBufLen = pPred->pFromValue->uiDataLen; } else { // Should have been done up above // pucFromUTF8Buf = NULL; // uiFromBufLen = 0; } if (pPred->pUntilValue) { flmAssert( pPred->pUntilValue->eValType == XFLM_UTF8_VAL); pucUntilUTF8Buf = pPred->pUntilValue->val.pucBuf; uiUntilBufLen = pPred->pUntilValue->uiDataLen; } else { // Should have been done up above. // pucUntilUTF8Buf = NULL; // uiUntilBufLen = 0; } if (!pPred->bInclFrom) { uiFromFlags |= EXCLUSIVE_GT_FLAG; } if (!pPred->bInclUntil) { uiUntilFlags |= EXCLUSIVE_LT_FLAG; } } else { if (pPred->pUntilValue) { flmAssert( pPred->pUntilValue->eValType == XFLM_UTF8_VAL); pucFromUTF8Buf = pPred->pUntilValue->val.pucBuf; uiFromBufLen = pPred->pUntilValue->uiDataLen; } else { // Should have been done up above // pucFromUTF8Buf = NULL; // uiFromBufLen = 0; } if (pPred->pFromValue) { flmAssert( pPred->pFromValue->eValType == XFLM_UTF8_VAL); pucUntilUTF8Buf = pPred->pFromValue->val.pucBuf; uiUntilBufLen = pPred->pFromValue->uiDataLen; } else { // Should have been done up above. // pucUntilUTF8Buf = NULL; // uiUntilBufLen = 0; } if (!pPred->bInclUntil) { uiFromFlags |= EXCLUSIVE_GT_FLAG; } if (!pPred->bInclFrom) { uiUntilFlags |= EXCLUSIVE_LT_FLAG; } } break; case XFLM_NE_OP: // Set up to do full index scan. // Buffers should already be NULL. // pucFromUTF8Buf = NULL; // pucUntilUTF8Buf = NULL; break; case XFLM_APPROX_EQ_OP: // Set up to do full index scan. // Buffers should already be NULL // pucFromUTF8Buf = NULL; // pucUntilUTF8Buf = NULL; // Cannot compare on the key if index is upper case, // even if the bCaseInsensitive flag is set. if (pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) { *pbCanCompareOnKey = FALSE; } break; default: // Every predicate should have been converted to one of the above // cases, or should be handled by another routine. rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); goto Exit; } // If index is case insensitive, but search is case sensitive // we must NOT do a key match - we would fail things we should // not be failing. if ((pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && !bCaseInsensitive) { *pbCanCompareOnKey = FALSE; } if (!pucFromUTF8Buf && !pucUntilUTF8Buf) { // setup a first-to-last key flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, pucUntilKeyLenPos, puiUntilKeyLen); goto Exit; } pucFromKey += 2; pucUntilKey += 2; if (!pucFromUTF8Buf) { uiFromKeyLen = KEY_LOW_VALUE; } else { if( !pBufferIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)pucFromUTF8Buf, uiFromBufLen))) { goto Exit; } // Add ICD_ESC_CHAR to the icd flags because // the search string must have BACKSLASHES and '*' escaped. uiFromKeyLen = XFLM_MAX_KEY_SIZE - 2; bDataTruncated = FALSE; if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen, pBufferIStream, XFLM_TEXT_TYPE, pIcd->uiFlags | ICD_ESC_CHAR, pIcd->uiCompareRules, pIcd->uiLimit, &uiCollationLen, &uiCaseLen, uiLanguage, bDoFirstSubstring, FALSE, &bDataTruncated, &bOriginalCharsLost))) { goto Exit; } pBufferIStream->closeStream(); if (bDataTruncated) { *pbCanCompareOnKey = FALSE; // Save the original data into pFromSearchKey so the comparison // routines can do a comparison on the full value if // necessary. if (RC_BAD( rc = pFromSearchKey->setUTF8( pIcd->uiKeyComponent - 1, pucFromUTF8Buf, uiFromBufLen))) { goto Exit; } uiFromFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); } else if (bOriginalCharsLost) { *pbCanCompareOnKey = FALSE; } if (pucFromUTF8Buf != pucUntilUTF8Buf) { // Handle scenario of a case-sensitive index, but search is // case-insensitive. if (uiFromKeyLen && (bIsDBCS || (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && bCaseInsensitive))) { setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen, bIsDBCS, bAscending, (uiFromFlags & EXCLUSIVE_GT_FLAG) ? TRUE : FALSE); } } else { // Handle case where from and until buffers are the same. // This should only be possible in the equality case or match begin // case, in which cases neither the EXCLUSIVE_LT_FLAG or the // EXCLUSIVE_GT_FLAG should be set. flmAssert( uiFromBufLen == uiUntilBufLen); flmAssert( !(uiFromFlags & (EXCLUSIVE_GT_FLAG | EXCLUSIVE_LT_FLAG))); flmAssert( !(uiUntilFlags & (EXCLUSIVE_GT_FLAG | EXCLUSIVE_LT_FLAG))); if (uiFromFlags & SEARCH_KEY_FLAG) { // Save the original data into pUntilSearchKey so the comparison // routines can do a comparison on the full value if // necessary. if (RC_BAD( rc = pUntilSearchKey->setUTF8( pIcd->uiKeyComponent - 1, pucUntilUTF8Buf, uiUntilBufLen))) { goto Exit; } uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); } if (bDoMatchBegin) { if (bAscending) { // Handle scenario of a case-sensitive index, but search is // case-insensitive. if (uiFromKeyLen && (bIsDBCS || (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && bCaseInsensitive))) { setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen, bIsDBCS, bAscending, FALSE); } // From key is set up properly, setup until key. if (uiCollationLen) { f_memcpy( pucUntilKey, pucFromKey, uiCollationLen); } // Fill the rest of the until key with high values. f_memset( &pucUntilKey[ uiCollationLen], 0xFF, XFLM_MAX_KEY_SIZE - uiCollationLen - 2); uiUntilKeyLen = XFLM_MAX_KEY_SIZE - 2; } else { // Copy from key into until key. if (uiFromKeyLen) { f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); } uiUntilKeyLen = uiFromKeyLen; if (uiUntilKeyLen && (bIsDBCS || (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && bCaseInsensitive))) { // NOTE: Always inclusive because this is a matchbegin. setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen, bIsDBCS, bAscending, FALSE); } // Fill rest of from key with high values after collation values. f_memset( &pucFromKey[ uiCollationLen], 0xFF, XFLM_MAX_KEY_SIZE - uiCollationLen - 2); uiFromKeyLen = XFLM_MAX_KEY_SIZE - 2; } } else { // Copy from key into until key. if (!uiFromKeyLen) { uiUntilKeyLen = 0; } else { if (!bDoFirstSubstring) { f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); uiUntilKeyLen = uiFromKeyLen; } else if (bAscending) { // Do two copies so that the first substring byte is gone // in the until key. f_memcpy( pucUntilKey, pucFromKey, uiCollationLen); uiUntilKeyLen = uiCollationLen; if (bIsDBCS) { uiCollationLen++; } uiCollationLen++; f_memcpy( &pucUntilKey [uiUntilKeyLen], pucFromKey + uiCollationLen, uiFromKeyLen - uiCollationLen); uiUntilKeyLen += (uiFromKeyLen - uiCollationLen); } else { // Descending order - put the string without the // first-substring-marker into the from key instead of // the until key. f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); uiUntilKeyLen = uiFromKeyLen; // Modify from key to NOT have first-substring-marker. f_memcpy( pucUntilKey, pucFromKey, uiCollationLen); uiFromKeyLen = uiCollationLen; if (bIsDBCS) { uiCollationLen++; } uiCollationLen++; f_memcpy( &pucFromKey [uiFromKeyLen], pucUntilKey + uiCollationLen, uiUntilKeyLen - uiCollationLen); uiFromKeyLen += (uiUntilKeyLen - uiCollationLen); } // Handle scenario of a case-sensitive index, but search is // case-insensitive. if (bIsDBCS || (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && bCaseInsensitive)) { setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen, bIsDBCS, bAscending, FALSE); setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen, bIsDBCS, bAscending, FALSE); } } } } } // Do the until key now if (!pucUntilUTF8Buf) { uiUntilKeyLen = KEY_HIGH_VALUE; } else if (pucFromUTF8Buf != pucUntilUTF8Buf) { if( !pBufferIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)pucUntilUTF8Buf, uiUntilBufLen))) { goto Exit; } // Add ICD_ESC_CHAR to the icd flags because // the search string must have BACKSLASHES and '*' escaped. uiUntilKeyLen = XFLM_MAX_KEY_SIZE - 2; bDataTruncated = FALSE; if (RC_BAD( rc = KYCollateValue( pucUntilKey, &uiUntilKeyLen, pBufferIStream, XFLM_TEXT_TYPE, pIcd->uiFlags | ICD_ESC_CHAR, pIcd->uiCompareRules, pIcd->uiLimit, &uiCollationLen, &uiCaseLen, uiLanguage, bDoFirstSubstring, FALSE, &bDataTruncated, &bOriginalCharsLost))) { goto Exit; } pBufferIStream->closeStream(); if (bDataTruncated) { // Save the original data into pUntilSearchKey so the comparison // routines can do a comparison on the full value if // necessary. if (RC_BAD( rc = pUntilSearchKey->setUTF8( pIcd->uiKeyComponent - 1, pucUntilUTF8Buf, uiUntilBufLen))) { goto Exit; } *pbCanCompareOnKey = FALSE; uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); } else if (bOriginalCharsLost) { *pbCanCompareOnKey = FALSE; } if (uiUntilKeyLen && (bIsDBCS || (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && bCaseInsensitive))) { setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen, bIsDBCS, bAscending, (uiUntilFlags & EXCLUSIVE_LT_FLAG) ? TRUE : FALSE); } } UW2FBA( (FLMUINT16)(uiFromKeyLen | uiFromFlags), pucFromKeyLenPos); UW2FBA( (FLMUINT16)(uiUntilKeyLen | uiUntilFlags), pucUntilKeyLenPos); if (!(uiFromFlags & EXCLUSIVE_GT_FLAG) && uiFromKeyLen < XFLM_MAX_KEY_SIZE - 2) { uiFromKeyLen += kyAddInclComponent( pIcd, &pucFromKey [uiFromKeyLen], TRUE, XFLM_MAX_KEY_SIZE - 2 - uiFromKeyLen); } if (!(uiUntilFlags & EXCLUSIVE_LT_FLAG) && uiUntilKeyLen < XFLM_MAX_KEY_SIZE - 2) { uiUntilKeyLen += kyAddInclComponent( pIcd, &pucUntilKey [uiUntilKeyLen], FALSE, XFLM_MAX_KEY_SIZE - 2 - uiUntilKeyLen); } // Set the FROM and UNTIL key lengths if (uiFromKeyLen != KEY_HIGH_VALUE && uiFromKeyLen != KEY_LOW_VALUE) { *puiFromKeyLen = uiFromKeyLen + 2; } else { *puiFromKeyLen = 2; } if (uiUntilKeyLen != KEY_HIGH_VALUE && uiUntilKeyLen != KEY_LOW_VALUE) { *puiUntilKeyLen = uiUntilKeyLen + 2; } else { *puiUntilKeyLen = 2; } Exit: if( pBufferIStream) { pBufferIStream->Release(); } return( rc); } /**************************************************************************** Desc: Build the from and until keys given a field list with operators and values and an index. Notes: The knowledge of query definitions is limited in these routines. ****************************************************************************/ RCODE flmBuildFromAndUntilKeys( IXD * pIxd, PATH_PRED * pPred, F_DataVector * pFromSearchKey, FLMBYTE * pucFromKey, FLMUINT * puiFromKeyLen, F_DataVector * pUntilSearchKey, FLMBYTE * pucUntilKey, FLMUINT * puiUntilKeyLen, FLMBOOL * pbDoNodeMatch, FLMBOOL * pbCanCompareOnKey) { RCODE rc = NE_XFLM_OK; ICD * pIcd = pIxd->pFirstKey; *puiFromKeyLen = *puiUntilKeyLen = 0; *pbDoNodeMatch = FALSE; *pbCanCompareOnKey = TRUE; if (!pPred) { // Setup a first-to-last key flmSetupFirstToLastKey( pucFromKey, puiFromKeyLen, pucUntilKey, puiUntilKeyLen); *pbDoNodeMatch = TRUE; *pbCanCompareOnKey = FALSE; } else { // Predicates we are looking at should NEVER be notted. They // will have been weeded out earlier. flmAssert( !pPred->bNotted); // Handle special cases for indexing presence and/or exists predicate. if (icdGetDataType( pIcd) == XFLM_TEXT_TYPE && !(pIcd->uiFlags & (ICD_PRESENCE | ICD_METAPHONE)) && pPred->eOperator != XFLM_EXISTS_OP) { if (RC_BAD( rc = flmAddTextKeyPiece( pPred, pIxd, pIcd, pFromSearchKey, pucFromKey, puiFromKeyLen, pUntilSearchKey, pucUntilKey, puiUntilKeyLen, pbCanCompareOnKey))) { goto Exit; } } else { if (RC_BAD( rc = flmAddNonTextKeyPiece( pPred, pIxd, pIcd, pFromSearchKey, pucFromKey, puiFromKeyLen, pUntilSearchKey, pucUntilKey, puiUntilKeyLen, pbCanCompareOnKey))) { goto Exit; } } } Exit: if (!(*pbCanCompareOnKey)) { *pbDoNodeMatch = TRUE; } return( rc); } libxflaim-5.1.969/src/flmstat.h0000644000175000017500000000754610511001742017663 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This include file contains the structure definitions and prototypes // needed to capture statistics. // // Tabs: 3 // // Copyright (c) 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flmstat.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FLMSTAT_H #define FLMSTAT_H #define INIT_DB_STAT_ARRAY_SIZE 5 #define DB_STAT_ARRAY_INCR_SIZE 5 #define INIT_LFILE_STAT_ARRAY_SIZE 5 #define LFILE_STAT_ARRAY_INCR_SIZE 5 /************************************************************************** Various function prototypes. **************************************************************************/ RCODE flmStatGetDb( // Source: flmstat.cpp XFLM_STATS * pFlmStats, F_Database * pDatabase, FLMUINT uiLowStart, XFLM_DB_STATS ** ppDbStatsRV, FLMUINT * puiDbAllocSeqRV, FLMUINT * puiDbTblPosRV); RCODE flmStatGetLFile( // Source: flmstat.cpp XFLM_DB_STATS * pDbStats, FLMUINT uiLFileNum, eLFileType eLfType, FLMUINT uiLowStart, XFLM_LFILE_STATS ** ppLFileStatsRV, FLMUINT * puiLFileAllocSeqRV, FLMUINT * puiLFileTblPosRV); void flmStatReset( // Source: flmstat.cpp XFLM_STATS * pStats, FLMBOOL bFree); FINLINE void flmStatStart( XFLM_STATS * pStats) { pStats->bCollectingStats = TRUE; flmStatReset( pStats, TRUE); } FINLINE void flmStatStop( XFLM_STATS * pStats) { if (pStats->bCollectingStats) { pStats->bCollectingStats = FALSE; f_timeGetSeconds( &pStats->uiStopTime); } } FINLINE void flmStatFree( XFLM_STATS * pStats) { pStats->bCollectingStats = FALSE; flmStatReset( pStats, TRUE); } void flmUpdateBlockIOStats( // Source: flmstat.cpp XFLM_BLOCKIO_STATS * pDest, XFLM_BLOCKIO_STATS * pSrc); RCODE flmStatUpdate( // Source: flmstat.cpp XFLM_STATS * pSrcStats); void flmFreeSavedQueries( // Source: flmstat.cpp FLMBOOL bMutexAlreadyLocked); void flmSaveQuery( // Source: flmstat.cpp F_Query * pQuery); RCODE flmStatCopy( // Source: flmstat.cpp XFLM_STATS * pDestStats, XFLM_STATS * pSrcStats); XFLM_BLOCKIO_STATS * flmGetBlockIOStatPtr(// Source: flmstat.cpp XFLM_DB_STATS * pDbStats, XFLM_LFILE_STATS * pLFileStats, FLMBYTE * pucBlk); void flmAddElapTime( // Source: flmstat.cpp F_TMSTAMP * pStartTime, FLMUINT64 * pui64ElapMilli); /**************************************************************************** Inline Functions ****************************************************************************/ /* Desc: This routine updates statistics from one DISKIO_STAT structure into another. */ FINLINE void flmUpdateDiskIOStats( XFLM_DISKIO_STAT * pDest, XFLM_DISKIO_STAT * pSrc) { pDest->ui64Count += pSrc->ui64Count; pDest->ui64TotalBytes += pSrc->ui64TotalBytes; pDest->ui64ElapMilli += pSrc->ui64ElapMilli; } FINLINE void flmUpdateCountTimeStats( F_COUNT_TIME_STAT * pDest, F_COUNT_TIME_STAT * pSrc) { pDest->ui64Count += pSrc->ui64Count; pDest->ui64ElapMilli += pSrc->ui64ElapMilli; } #endif // ifdef FLMSTAT_H libxflaim-5.1.969/src/xflaimtk.h0000644000175000017500000056524410511001742020034 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: FLAIM's cross-platform toolkit public definitions and interfaces // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id$ //------------------------------------------------------------------------------ /// \file #ifndef FTK_H #define FTK_H /// \defgroup retcodes Return Codes #ifndef FLM_PLATFORM_CONFIGURED #define FLM_PLATFORM_CONFIGURED // Determine the build platform #undef FLM_WIN #undef FLM_NLM #undef FLM_UNIX #undef FLM_AIX #undef FLM_LINUX #undef FLM_SOLARIS #undef FLM_HPUX #undef FLM_OSX #undef FLM_S390 #undef FLM_IA64 #undef FLM_PPC #undef FLM_SPARC #undef FLM_SPARC_PLUS #undef FLM_X86 #undef FLM_BIG_ENDIAN #undef FLM_STRICT_ALIGNMENT #undef FLM_GNUC #undef FLM_HAS_ASYNC_IO #undef FLM_HAS_DIRECT_IO #if defined( __GNUC__) #define FLM_GNUC #if defined( __arch64__) #if !defined( FLM_64BIT) #define FLM_64BIT #endif #endif #endif #if defined( __NETWARE__) || defined( NLM) || defined( N_PLAT_NLM) #if defined( __WATCOMC__) && defined( __386__) #define FLM_X86 #else #error Platform architecture not supported #endif #define FLM_NLM #if !defined( FLM_RING_ZERO_NLM) && !defined( FLM_LIBC_NLM) #define FLM_RING_ZERO_NLM #endif #define FLM_OSTYPE_STR "NetWare" #if defined( __WATCOMC__) #define FLM_WATCOM_NLM #elif defined( __MWERKS__) #define FLM_MWERKS_NLM #endif #if defined( FLM_RING_ZERO_NLM) #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #endif #elif defined( _WIN64) #if defined( _M_IX6) || defined( _M_X64) #define FLM_X86 #endif #define FLM_WIN #define FLM_OSTYPE_STR "Windows" #ifndef FLM_64BIT #define FLM_64BIT #endif #define FLM_STRICT_ALIGNMENT #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #elif defined( _WIN32) #if defined( _M_IX86) || defined( _M_X64) #define FLM_X86 #else #error Platform architecture not supported #endif #define FLM_WIN #define FLM_OSTYPE_STR "Windows" #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #elif defined( _AIX) #define FLM_AIX #define FLM_OSTYPE_STR "AIX" #define FLM_UNIX #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #elif defined( linux) #define FLM_LINUX #define FLM_OSTYPE_STR "Linux" #define FLM_UNIX #if defined( __PPC__) || defined( __ppc__) #define FLM_PPC #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #elif defined( __s390__) #define FLM_S390 #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #elif defined( __s390x__) #define FLM_S390 #ifndef FLM_64BIT #define FLM_64BIT #endif #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #elif defined( __ia64__) #define FLM_IA64 #ifndef FLM_64BIT #define FLM_64BIT #endif #define FLM_STRICT_ALIGNMENT #elif defined( sparc) || defined( __sparc) || defined( __sparc__) #define FLM_SPARC #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #if !defined ( FLM_SPARC_GENERIC) #if defined( __sparcv8plus) || defined( __sparcv9) || defined( __sparcv9__) || \ defined( __sparc_v8__) || defined( __sparc_v9__) || defined( __arch64__) #define FLM_SPARC_PLUS #endif #endif #elif defined( __x86__) || defined( __i386__) || defined( __x86_64__) #define FLM_X86 #else #error Platform architecture not supported #endif #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #elif defined( sun) #define FLM_SOLARIS #define FLM_OSTYPE_STR "Solaris" #define FLM_UNIX #define FLM_STRICT_ALIGNMENT #if defined( sparc) || defined( __sparc) || defined( __sparc__) #define FLM_SPARC #define FLM_BIG_ENDIAN #if !defined ( FLM_SPARC_GENERIC) #if defined( __sparcv8plus) || defined( __sparcv9) #define FLM_SPARC_PLUS #endif #endif #elif defined( i386) || defined( _i386) #define FLM_X86 #else #error Platform architecture not supported #endif #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #elif defined( __hpux) || defined( hpux) #define FLM_HPUX #define FLM_OSTYPE_STR "HPUX" #define FLM_UNIX #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #elif defined( __APPLE__) #define FLM_OSX #define FLM_OSTYPE_STR "OSX" #define FLM_UNIX #if (defined( __ppc__) || defined( __ppc64__)) #define FLM_PPC #define FLM_BIG_ENDIAN #define FLM_STRICT_ALIGNMENT #elif defined( __x86__) || defined( __x86_64__) #define FLM_X86 #else #error Platform architecture not supported #endif #define FLM_HAS_ASYNC_IO #define FLM_HAS_DIRECT_IO #else #error Platform architecture is undefined. #endif #if !defined( FLM_64BIT) && !defined( FLM_32BIT) #if defined( FLM_UNIX) #if defined( __x86_64__) || defined( _M_X64) || \ defined( _LP64) || defined( __LP64__) || \ defined ( __64BIT__) || defined( __arch64__) || \ defined( __sparcv9) || defined( __sparcv9__) #define FLM_64BIT #endif #endif #endif #if !defined( FLM_64BIT) #define FLM_32BIT #elif defined( FLM_32BIT) #error Cannot define both FLM_32BIT and FLM_64BIT #endif #if defined( __x86_64__) || defined( _M_X64) || \ defined( _LP64) || defined( __LP64__) || \ defined ( __64BIT__) || defined( __arch64__) || \ defined( __sparcv9) || defined( __sparcv9__) #if !defined( FLM_64BIT) #error Platform word size is incorrect #endif #else #if !defined( FLM_32BIT) #error Platform word size is incorrect #endif #endif #ifdef FLM_NLM #define FSTATIC #else #define FSTATIC static #endif // Debug or release build? #ifndef FLM_DEBUG #if defined( DEBUG) || (defined( PRECHECKIN) && PRECHECKIN != 0) #define FLM_DEBUG #endif #endif // Alignment #if defined( FLM_UNIX) || defined( FLM_64BIT) #define FLM_ALLOC_ALIGN 0x0007 #define FLM_ALIGN_SIZE 8 #elif defined( FLM_WIN) || defined( FLM_NLM) #define FLM_ALLOC_ALIGN 0x0003 #define FLM_ALIGN_SIZE 4 #else #error Platform not supported #endif // Basic type definitions #if defined( FLM_UNIX) typedef unsigned long FLMUINT; typedef long FLMINT; typedef unsigned char FLMBYTE; typedef unsigned short FLMUNICODE; typedef unsigned long long FLMUINT64; typedef unsigned int FLMUINT32; typedef unsigned short FLMUINT16; typedef unsigned char FLMUINT8; typedef long long FLMINT64; typedef int FLMINT32; typedef short FLMINT16; typedef signed char FLMINT8; #if defined( FLM_64BIT) || defined( FLM_OSX) || \ defined( FLM_S390) || defined( FLM_HPUX) || defined( FLM_AIX) typedef unsigned long FLMSIZET; #else typedef unsigned FLMSIZET; #endif #else #if defined( FLM_WIN) #if defined( FLM_64BIT) typedef unsigned __int64 FLMUINT; typedef __int64 FLMINT; typedef unsigned __int64 FLMSIZET; typedef unsigned int FLMUINT32; #elif _MSC_VER >= 1300 typedef unsigned long __w64 FLMUINT; typedef long __w64 FLMINT; typedef __w64 unsigned int FLMUINT32; typedef __w64 unsigned int FLMSIZET; #else typedef unsigned long FLMUINT; typedef long FLMINT; typedef unsigned int FLMUINT32; typedef unsigned int FLMSIZET; #endif #elif defined( FLM_NLM) typedef unsigned long int FLMUINT; typedef long int FLMINT; typedef unsigned long int FLMUINT32; typedef unsigned FLMSIZET; #else #error Platform not supported #endif typedef unsigned char FLMBYTE; typedef unsigned short int FLMUNICODE; typedef unsigned short int FLMUINT16; typedef unsigned char FLMUINT8; typedef signed int FLMINT32; typedef signed short int FLMINT16; typedef signed char FLMINT8; #if defined( __MWERKS__) typedef unsigned long long FLMUINT64; typedef long long FLMINT64; #else typedef unsigned __int64 FLMUINT64; typedef __int64 FLMINT64; #endif #endif #if defined( FLM_WIN) || defined( FLM_NLM) #define FLMATOMIC volatile long #else #define FLMATOMIC volatile int #endif /// \addtogroup retcodes /// @{ typedef FLMINT32 RCODE; ///< Return code /// @} typedef FLMINT32 FLMBOOL; #define F_FILENAME_SIZE 256 #define F_PATH_MAX_SIZE 256 #define F_WAITFOREVER (0xFFFFFFFF) #define FLM_MAX_UINT ((FLMUINT)(-1L)) #define FLM_MAX_INT ((FLMINT)(((FLMUINT)(-1L)) >> 1)) #define FLM_MIN_INT ((FLMINT)((((FLMUINT)(-1L)) >> 1) + 1)) #define FLM_MAX_UINT32 ((FLMUINT32)(0xFFFFFFFFL)) #define FLM_MAX_INT32 ((FLMINT32)(0x7FFFFFFFL)) #define FLM_MIN_INT32 ((FLMINT32)(0x80000000L)) #define FLM_MAX_UINT16 ((FLMUINT16)(0xFFFF)) #define FLM_MAX_INT16 ((FLMINT16)(0x7FFF)) #define FLM_MIN_INT16 ((FLMINT16)(0x8000)) #define FLM_MAX_UINT8 ((FLMUINT8)0xFF) #if ((_MSC_VER >= 1200) && (_MSC_VER < 1300)) || defined( FLM_NLM) #define FLM_MAX_UINT64 ((FLMUINT64)(0xFFFFFFFFFFFFFFFFL)) #define FLM_MAX_INT64 ((FLMINT64)(0x7FFFFFFFFFFFFFFFL)) #define FLM_MIN_INT64 ((FLMINT64)(0x8000000000000000L)) #else #define FLM_MAX_UINT64 ((FLMUINT64)(0xFFFFFFFFFFFFFFFFLL)) #define FLM_MAX_INT64 ((FLMINT64)(0x7FFFFFFFFFFFFFFFLL)) #define FLM_MIN_INT64 ((FLMINT64)(0x8000000000000000LL)) #endif #endif // xpcselany keeps MS compilers from complaining about multiple definitions #if defined(_MSC_VER) #define xpcselany __declspec(selectany) #else #define xpcselany #endif typedef struct { FLMUINT32 l; FLMUINT16 w1; FLMUINT16 w2; FLMUINT8 b[ 8]; } FLM_GUID; #define RFLMIID const FLM_GUID & #define RFLMCLSID const FLM_GUID & #define FLMGUID FLM_GUID #define FLMCLSID FLM_GUID // FLM_DEFINE_GUID may be used to define or declare a GUID // #define FLM_INIT_GUID before including this header file when // you want to define the guid, all other inclusions will only declare // the guid, not define it. #if !defined( PCOM_INIT_GUID) #define FLM_DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ extern const FLMGUID name #else #define FLM_DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ extern const xpcselany FLMGUID name \ = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } #endif #define FLMEXTC extern "C" #if defined( FLM_WIN) #define FLMAPI __stdcall #define FLMEXP __declspec(dllexport) #ifdef FLM_DEBUG #define FINLINE inline #else #define FINLINE __forceinline #endif #elif defined( FLM_NLM) #define FLMAPI __stdcall #define FLMEXP #define FINLINE inline #elif defined( FLM_UNIX) #define FLMAPI #define FLMEXP #define FINLINE inline #else #error Platform not supported #endif /**************************************************************************** Desc: Argument lists ****************************************************************************/ #define f_alignedsize(n) \ ((sizeof(n) + FLM_ALIGN_SIZE - 1) & ~(FLM_ALIGN_SIZE - 1) ) #if defined( FLM_RING_ZERO_NLM) #define f_argsize(x) \ ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int)-1)) typedef unsigned long f_va_list; #define f_va_start(ap, parmN) \ ((void)((ap) = (unsigned long)&(parmN) + f_argsize(parmN))) #define f_va_arg(ap, type) \ (*(type *)(((ap) += f_argsize(type)) - (f_argsize(type)))) #define f_va_end(ap) ((void)0) #else #include #define f_va_list va_list #define f_va_start va_start #define f_va_arg va_arg #define f_va_end va_end #endif // flmnovtbl keeps MS compilers from generating vtables for interfaces #ifdef _MSC_VER #define flmnovtbl __declspec( novtable) #else #define flmnovtbl #endif #define flminterface struct flmnovtbl /**************************************************************************** Desc: General errors ****************************************************************************/ /// \addtogroup retcodes /// @{ #define NE_FLM_OK 0 ///< 0 - Operation was successful. // Error codes that need to be the same as they were for FLAIM #define NE_FLM_BOF_HIT 0xC001 ///< 0xC001 - Beginning of results encountered. #define NE_FLM_EOF_HIT 0xC002 ///< 0xC002 - End of results encountered. #define NE_FLM_EXISTS 0xC004 ///< 0xC004 - Object already exists. #define NE_FLM_FAILURE 0xC005 ///< 0xC005 - Internal failure. #define NE_FLM_NOT_FOUND 0xC006 ///< 0xC006 - An object was not found. #define NE_FLM_BTREE_ERROR 0xC012 ///< 0xC012 - Corruption found in b-tree. #define NE_FLM_BTREE_FULL 0xC013 ///< 0xC013 - B-tree cannot grow beyond current size. #define NE_FLM_CONV_DEST_OVERFLOW 0xC01C ///< 0xC01C - Destination buffer not large enough to hold data. #define NE_FLM_CONV_ILLEGAL 0xC01D ///< 0xC01D - Attempt to convert between data types is an unsupported conversion. #define NE_FLM_CONV_NUM_OVERFLOW 0xC020 ///< 0xC020 - Numeric overflow (> upper bound) converting to numeric type. #define NE_FLM_DATA_ERROR 0xC022 ///< 0xC022 - Corruption found in b-tree. #define NE_FLM_ILLEGAL_OP 0xC026 ///< 0xC026 - Illegal operation #define NE_FLM_MEM 0xC037 ///< 0xC037 - Attempt to allocate memory failed. #define NE_FLM_NOT_UNIQUE 0xC03E ///< 0xC03E - Non-unique key. #define NE_FLM_SYNTAX 0xC045 ///< 0xC045 - Syntax error while parsing. #define NE_FLM_NOT_IMPLEMENTED 0xC05F ///< 0xC05F - Attempt was made to use a feature that is not implemented. #define NE_FLM_INVALID_PARM 0xC08B ///< 0xC08B - Invalid parameter passed into a function. // I/O Errors - Must be the same as they were for FLAIM. #define NE_FLM_IO_ACCESS_DENIED 0xC201 ///< 0xC201 - Access to file is denied.\ Caller is not allowed access to a file. #define NE_FLM_IO_BAD_FILE_HANDLE 0xC202 ///< 0xC202 - Bad file handle or file descriptor. #define NE_FLM_IO_COPY_ERR 0xC203 ///< 0xC203 - Error occurred while copying a file. #define NE_FLM_IO_DISK_FULL 0xC204 ///< 0xC204 - Disk full. #define NE_FLM_IO_END_OF_FILE 0xC205 ///< 0xC205 - End of file reached while reading from the file. #define NE_FLM_IO_OPEN_ERR 0xC206 ///< 0xC206 - Error while opening the file. #define NE_FLM_IO_SEEK_ERR 0xC207 ///< 0xC207 - Error occurred while positioning (seeking) within a file. #define NE_FLM_IO_DIRECTORY_ERR 0xC208 ///< 0xC208 - Error occurred while accessing or deleting a directory. #define NE_FLM_IO_PATH_NOT_FOUND 0xC209 ///< 0xC209 - File not found. #define NE_FLM_IO_TOO_MANY_OPEN_FILES 0xC20A ///< 0xC20A - Too many files open. #define NE_FLM_IO_PATH_TOO_LONG 0xC20B ///< 0xC20B - File name too long. #define NE_FLM_IO_NO_MORE_FILES 0xC20C ///< 0xC20C - No more files in directory. #define NE_FLM_IO_DELETING_FILE 0xC20D ///< 0xC20D - Error occurred while deleting a file. #define NE_FLM_IO_FILE_LOCK_ERR 0xC20E ///< 0xC20E - Error attempting to acquire a byte-range lock on a file. #define NE_FLM_IO_FILE_UNLOCK_ERR 0xC20F ///< 0xC20F - Error attempting to release a byte-range lock on a file. #define NE_FLM_IO_PATH_CREATE_FAILURE 0xC210 ///< 0xC210 - Error occurred while attempting to create a directory or sub-directory. #define NE_FLM_IO_RENAME_FAILURE 0xC211 ///< 0xC211 - Error occurred while renaming a file. #define NE_FLM_IO_INVALID_PASSWORD 0xC212 ///< 0xC212 - Invalid file password. #define NE_FLM_SETTING_UP_FOR_READ 0xC213 ///< 0xC213 - Error occurred while setting up to perform a file read operation. #define NE_FLM_SETTING_UP_FOR_WRITE 0xC214 ///< 0xC214 - Error occurred while setting up to perform a file write operation. #define NE_FLM_IO_CANNOT_REDUCE_PATH 0xC215 ///< 0xC215 - Cannot reduce file name into more components. #define NE_FLM_INITIALIZING_IO_SYSTEM 0xC216 ///< 0xC216 - Error occurred while setting up to access the file system. #define NE_FLM_FLUSHING_FILE 0xC217 ///< 0xC217 - Error occurred while flushing file data buffers to disk. #define NE_FLM_IO_INVALID_FILENAME 0xC218 ///< 0xC218 - Invalid file name. #define NE_FLM_IO_CONNECT_ERROR 0xC219 ///< 0xC219 - Error connecting to a remote network resource. #define NE_FLM_OPENING_FILE 0xC21A ///< 0xC21A - Unexpected error occurred while opening a file. #define NE_FLM_DIRECT_OPENING_FILE 0xC21B ///< 0xC21B - Unexpected error occurred while opening a file in direct access mode. #define NE_FLM_CREATING_FILE 0xC21C ///< 0xC21C - Unexpected error occurred while creating a file. #define NE_FLM_DIRECT_CREATING_FILE 0xC21D ///< 0xC21D - Unexpected error occurred while creating a file in direct access mode. #define NE_FLM_READING_FILE 0xC21E ///< 0xC21E - Unexpected error occurred while reading a file. #define NE_FLM_DIRECT_READING_FILE 0xC21F ///< 0xC21F - Unexpected error occurred while reading a file in direct access mode. #define NE_FLM_WRITING_FILE 0xC220 ///< 0xC220 - Unexpected error occurred while writing to a file. #define NE_FLM_DIRECT_WRITING_FILE 0xC221 ///< 0xC221 - Unexpected error occurred while writing a file in direct access mode. #define NE_FLM_POSITIONING_IN_FILE 0xC222 ///< 0xC222 - Unexpected error occurred while positioning within a file. #define NE_FLM_GETTING_FILE_SIZE 0xC223 ///< 0xC223 - Unexpected error occurred while getting a file's size. #define NE_FLM_TRUNCATING_FILE 0xC224 ///< 0xC224 - Unexpected error occurred while truncating a file. #define NE_FLM_PARSING_FILE_NAME 0xC225 ///< 0xC225 - Unexpected error occurred while parsing a file's name. #define NE_FLM_CLOSING_FILE 0xC226 ///< 0xC226 - Unexpected error occurred while closing a file. #define NE_FLM_GETTING_FILE_INFO 0xC227 ///< 0xC227 - Unexpected error occurred while getting information about a file. #define NE_FLM_EXPANDING_FILE 0xC228 ///< 0xC228 - Unexpected error occurred while expanding a file. #define NE_FLM_GETTING_FREE_BLOCKS 0xC229 ///< 0xC229 - Unexpected error getting free blocks from file system. #define NE_FLM_CHECKING_FILE_EXISTENCE 0xC22A ///< 0xC22A - Unexpected error occurred while checking to see if a file exists. #define NE_FLM_RENAMING_FILE 0xC22B ///< 0xC22B - Unexpected error occurred while renaming a file. #define NE_FLM_SETTING_FILE_INFO 0xC22C ///< 0xC22C - Unexpected error occurred while setting a file's information. #define NE_FLM_IO_PENDING 0xC22D ///< 0xC22D - I/O has not yet completed #define NE_FLM_ASYNC_FAILED 0xC22E ///< 0xC22E - An async I/O operation failed #define NE_FLM_MISALIGNED_IO 0xC22F ///< 0xC22F - Misaligned buffer or offset encountered during I/O request // Stream Errors - These are new #define NE_FLM_STREAM_DECOMPRESS_ERROR 0xC400 ///< 0xC400 - Error decompressing data stream. #define NE_FLM_STREAM_NOT_COMPRESSED 0xC401 ///< 0xC401 - Attempting to decompress a data stream that is not compressed. #define NE_FLM_STREAM_TOO_MANY_FILES 0xC402 ///< 0xC402 - Too many files in input stream. // Miscellaneous new toolkit errors #define NE_FLM_COULD_NOT_CREATE_SEMAPHORE 0xC500 ///< 0xC500 - Could not create a semaphore. #define NE_FLM_BAD_UTF8 0xC501 ///< 0xC501 - An invalid byte sequence was found in a UTF-8 string #define NE_FLM_ERROR_WAITING_ON_SEMAPHORE 0xC502 ///< 0xC502 - Error occurred while waiting on a sempahore. #define NE_FLM_BAD_SEN 0xC503 ///< 0xC503 - Invalid simple encoded number. #define NE_FLM_COULD_NOT_START_THREAD 0xC504 ///< 0xC504 - Problem starting a new thread. #define NE_FLM_BAD_BASE64_ENCODING 0xC505 ///< 0xC505 - Invalid base64 sequence encountered. #define NE_FLM_STREAM_EXISTS 0xC506 ///< 0xC506 - Stream file already exists. #define NE_FLM_MULTIPLE_MATCHES 0xC507 ///< 0xC507 - Multiple items matched but only one match was expected. #define NE_FLM_BTREE_KEY_SIZE 0xC508 ///< 0xC508 - Invalid b-tree key size. #define NE_FLM_BTREE_BAD_STATE 0xC509 ///< 0xC509 - B-tree operation cannot be completed. #define NE_FLM_COULD_NOT_CREATE_MUTEX 0xC50A ///< 0xC50A - Error occurred while creating or initializing a mutex. #define NE_FLM_BAD_PLATFORM_FORMAT 0xC50B ///< 0xC50B - In-memory alignment of disk structures is incorrect #define NE_FLM_LOCK_REQ_TIMEOUT 0xC50C ///< 0xC50C - Timeout while waiting for a lock object #define NE_FLM_WAIT_TIMEOUT 0xC50D ///< 0xC50D - Timeout while waiting on a semaphore, condition variable, or reader/writer lock // Network Errors - Must be the same as they were for FLAIM #define NE_FLM_NOIP_ADDR 0xC900 ///< 0xC900 - IP address not found #define NE_FLM_SOCKET_FAIL 0xC901 ///< 0xC901 - IP socket failure #define NE_FLM_CONNECT_FAIL 0xC902 ///< 0xC902 - TCP/IP connection failure #define NE_FLM_BIND_FAIL 0xC903 ///< 0xC903 - The TCP/IP services on your system may not be configured or installed. #define NE_FLM_LISTEN_FAIL 0xC904 ///< 0xC904 - TCP/IP listen failed #define NE_FLM_ACCEPT_FAIL 0xC905 ///< 0xC905 - TCP/IP accept failed #define NE_FLM_SELECT_ERR 0xC906 ///< 0xC906 - TCP/IP select failed #define NE_FLM_SOCKET_SET_OPT_FAIL 0xC907 ///< 0xC907 - TCP/IP socket operation failed #define NE_FLM_SOCKET_DISCONNECT 0xC908 ///< 0xC908 - TCP/IP disconnected #define NE_FLM_SOCKET_READ_FAIL 0xC909 ///< 0xC909 - TCP/IP read failed #define NE_FLM_SOCKET_WRITE_FAIL 0xC90A ///< 0xC90A - TCP/IP write failed #define NE_FLM_SOCKET_READ_TIMEOUT 0xC90B ///< 0xC90B - TCP/IP read timeout #define NE_FLM_SOCKET_WRITE_TIMEOUT 0xC90C ///< 0xC90C - TCP/IP write timeout #define NE_FLM_SOCKET_ALREADY_CLOSED 0xC90D ///< 0xC90D - Connection already closed /// @} /**************************************************************************** Desc: Return code functions and macros ****************************************************************************/ #ifndef RC_OK #define RC_OK( rc) ((rc) == NE_FLM_OK) #endif #ifndef RC_BAD #define RC_BAD( rc) ((rc) != NE_FLM_OK) #endif RCODE FLMAPI f_mapPlatformError( FLMINT iError, RCODE defaultRc); /**************************************************************************** Desc: Forward References ****************************************************************************/ flminterface IF_DirHdl; flminterface IF_FileHdl; flminterface IF_FileSystem; flminterface IF_FileHdlCache; flminterface IF_IStream; flminterface IF_PosIStream; flminterface IF_ResultSet; flminterface IF_ThreadInfo; flminterface IF_OStream; flminterface IF_IOStream; flminterface IF_LogMessageClient; flminterface IF_Thread; flminterface IF_IOBuffer; flminterface IF_AsyncClient; flminterface IF_Block; class F_Pool; class F_DynaBuf; class F_ListItem; class F_ListManager; /**************************************************************************** Desc: Cross-platform definitions ****************************************************************************/ #ifndef NULL #define NULL 0 #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define f_offsetof(s,m) \ (((FLMSIZET)&((s *)1)->m) - 1) /**************************************************************************** Desc: Language constants IMPORTANT NOTE: If langes are added or changed, the corresponding definitions in java and C# must also be updated. ****************************************************************************/ /// \addtogroup flm_languages /// @{ #define FLM_US_LANG 0 ///< English, United States #define FLM_AF_LANG 1 ///< Afrikaans #define FLM_AR_LANG 2 ///< Arabic #define FLM_CA_LANG 3 ///< Catalan #define FLM_HR_LANG 4 ///< Croatian #define FLM_CZ_LANG 5 ///< Czech #define FLM_DK_LANG 6 ///< Danish #define FLM_NL_LANG 7 ///< Dutch #define FLM_OZ_LANG 8 ///< English, Australia #define FLM_CE_LANG 9 ///< English, Canada #define FLM_UK_LANG 10 ///< English, United Kingdom #define FLM_FA_LANG 11 ///< Farsi #define FLM_SU_LANG 12 ///< Finnish #define FLM_CF_LANG 13 ///< French, Canada #define FLM_FR_LANG 14 ///< French, France #define FLM_GA_LANG 15 ///< Galician #define FLM_DE_LANG 16 ///< German, Germany #define FLM_SD_LANG 17 ///< German, Switzerland #define FLM_GR_LANG 18 ///< Greek #define FLM_HE_LANG 19 ///< Hebrew #define FLM_HU_LANG 20 ///< Hungarian #define FLM_IS_LANG 21 ///< Icelandic #define FLM_IT_LANG 22 ///< Italian #define FLM_NO_LANG 23 ///< Norwegian #define FLM_PL_LANG 24 ///< Polish #define FLM_BR_LANG 25 ///< Portuguese, Brazil #define FLM_PO_LANG 26 ///< Portuguese, Portugal #define FLM_RU_LANG 27 ///< Russian #define FLM_SL_LANG 28 ///< Slovak #define FLM_ES_LANG 29 ///< Spanish #define FLM_SV_LANG 30 ///< Swedish #define FLM_YK_LANG 31 ///< Ukrainian #define FLM_UR_LANG 32 ///< Urdu #define FLM_TK_LANG 33 ///< Turkey #define FLM_JP_LANG 34 ///< Japanese #define FLM_KO_LANG 35 ///< Korean #define FLM_CT_LANG 36 ///< Chinese-Traditional #define FLM_CS_LANG 37 ///< Chinese-Simplified #define FLM_LA_LANG 38 ///< another Asian language /// @} #define FLM_LAST_LANG (FLM_LA_LANG + 1) #define FLM_FIRST_DBCS_LANG (FLM_JP_LANG) #define FLM_LAST_DBCS_LANG (FLM_LA_LANG) /**************************************************************************** Desc: Collation flags and constants ****************************************************************************/ #define F_HAD_SUB_COLLATION 0x01 // Set if had sub-collating values-diacritics #define F_HAD_LOWER_CASE 0x02 // Set if you hit a lowercase character #define F_COLL_FIRST_SUBSTRING 0x03 // First substring marker #define F_COLL_MARKER 0x04 // Marks place of sub-collation #define F_SC_LOWER 0x00 // Only lowercase characters exist #define F_SC_MIXED 0x01 // Lower/uppercase flags follow in next byte #define F_SC_UPPER 0x02 // Only upper characters exist #define F_SC_SUB_COL 0x03 // Sub-collation follows (diacritics|extCh) #define F_COLL_TRUNCATED 0x0C // This key piece has been truncated from original #define F_MAX_COL_OPCODE F_COLL_TRUNCATED #define F_CHSASCI 0 // ASCII #define F_CHSMUL1 1 // Multinational 1 #define F_CHSMUL2 2 // Multinational 2 #define F_CHSBOXD 3 // Box drawing #define F_CHSSYM1 4 // Typographic Symbols #define F_CHSSYM2 5 // Iconic Symbols #define F_CHSMATH 6 // Math #define F_CHMATHX 7 // Math Extension #define F_CHSGREK 8 // Greek #define F_CHSHEB 9 // Hebrew #define F_CHSCYR 10 // Cyrillic #define F_CHSKANA 11 // Japanese Kana #define F_CHSUSER 12 // User-defined #define F_CHSARB1 13 // Arabic #define F_CHSARB2 14 // Arabic script #define F_NCHSETS 15 // # of character sets (excluding asian) #define F_ACHSETS 0x0E0 // maximum character set value - asian #define F_ACHSMIN 0x024 // minimum character set value - asian #define F_ACHCMAX 0x0FE // maxmimum character value in asian sets /**************************************************************************** Desc: Diacritics ****************************************************************************/ #define F_GRAVE 0 #define F_CENTERD 1 #define F_TILDE 2 #define F_CIRCUM 3 #define F_CROSSB 4 #define F_SLASH 5 #define F_ACUTE 6 #define F_UMLAUT 7 #define F_MACRON 8 #define F_APOSAB 9 #define F_APOSBES 10 #define F_APOSBA 11 #define F_RING 14 #define F_DOTA 15 #define F_DACUTE 16 #define F_CEDILLA 17 #define F_OGONEK 18 #define F_CARON 19 #define F_STROKE 20 #define F_BREVE 22 #define F_DOTLESI 239 #define F_DOTLESJ 25 #define F_GACUTE 83 // greek acute #define F_GDIA 84 // greek diaeresis #define F_GACTDIA 85 // acute diaeresis #define F_GGRVDIA 86 // grave diaeresis #define F_GGRAVE 87 // greek grave #define F_GCIRCM 88 // greek circumflex #define F_GSMOOTH 89 // smooth breathing #define F_GROUGH 90 // rough breathing #define F_GIOTA 91 // iota subscript #define F_GSMACT 92 // smooth breathing acute #define F_GRGACT 93 // rough breathing acute #define F_GSMGRV 94 // smooth breathing grave #define F_GRGGRV 95 // rough breathing grave #define F_GSMCIR 96 // smooth breathing circumflex #define F_GRGCIR 97 // rough breathing circumflex #define F_GACTIO 98 // acute iota #define F_GGRVIO 99 // grave iota #define F_GCIRIO 100 // circumflex iota #define F_GSMIO 101 // smooth iota #define F_GRGIO 102 // rough iota #define F_GSMAIO 103 // smooth acute iota #define F_GRGAIO 104 // rough acute iota #define F_GSMGVIO 105 // smooth grave iota #define F_GRGGVIO 106 // rough grave iota #define F_GSMCIO 107 // smooth circumflex iota #define F_GRGCIO 108 // rough circumflex iota #define F_GHPRIME 81 // high prime #define F_GLPRIME 82 // low prime #define F_RACUTE 200 // russian acute #define F_RGRAVE 201 // russian grave #define F_RRTDESC 204 // russian right descender #define F_ROGONEK 205 // russian ogonek #define F_RMACRON 206 // russian macron /**************************************************************************** Desc: I/O Flags ****************************************************************************/ #define FLM_IO_CURRENT_POS FLM_MAX_UINT64 #define FLM_IO_RDONLY 0x0001 #define FLM_IO_RDWR 0x0002 #define FLM_IO_EXCL 0x0004 #define FLM_IO_CREATE_DIR 0x0008 #define FLM_IO_SH_DENYRW 0x0010 #define FLM_IO_SH_DENYWR 0x0020 #define FLM_IO_SH_DENYNONE 0x0040 #define FLM_IO_DIRECT 0x0080 #define FLM_IO_DELETE_ON_RELEASE 0x0100 #define FLM_IO_NO_MISALIGNED 0x0200 // File Positioning Definitions #define FLM_IO_SEEK_SET 0 // Beginning of File #define FLM_IO_SEEK_CUR 1 // Current File Pointer Position #define FLM_IO_SEEK_END 2 // End of File // Maximum file size #define FLM_MAXIMUM_FILE_SIZE 0xFFFC0000 // Maximum SEN (compressed number) length #define FLM_MAX_SEN_LEN 9 // Retrieval flags #define FLM_INCL 0x0010 #define FLM_EXCL 0x0020 #define FLM_EXACT 0x0040 #define FLM_KEY_EXACT 0x0080 #define FLM_FIRST 0x0100 #define FLM_LAST 0x0200 /**************************************************************************** Desc: Comparison flags for strings IMPORTANT NOTE: If changes are made to these, corresponding changes need to be made in the java and C# code where they are defined. ****************************************************************************/ /// \addtogroup compare_rules /// @{ #define FLM_COMP_CASE_INSENSITIVE 0x0001 ///< 0x0001 = Do case sensitive comparison. #define FLM_COMP_COMPRESS_WHITESPACE 0x0002 ///< 0x0002 = Compare multiple whitespace characters as a single space. #define FLM_COMP_NO_WHITESPACE 0x0004 ///< 0x0004 = Ignore all whitespace during comparison. #define FLM_COMP_NO_UNDERSCORES 0x0008 ///< 0x0008 = Ignore all underscore characters during comparison. #define FLM_COMP_NO_DASHES 0x0010 ///< 0x0010 = Ignore all dash characters during comparison. #define FLM_COMP_WHITESPACE_AS_SPACE 0x0020 ///< 0x0020 = Treat newlines and tabs as spaces during comparison. #define FLM_COMP_IGNORE_LEADING_SPACE 0x0040 ///< 0x0040 = Ignore leading space characters during comparison. #define FLM_COMP_IGNORE_TRAILING_SPACE 0x0080 ///< 0x0080 = Ignore trailing space characters during comparison. #define FLM_COMP_WILD 0x0100 /// @} /**************************************************************************** Desc: Colors ****************************************************************************/ /// Colors used for logging messages typedef enum { FLM_BLACK = 0, ///< 0 = Black FLM_BLUE, ///< 1 = Blue FLM_GREEN, ///< 2 = Green FLM_CYAN, ///< 3 = Cyan FLM_RED, ///< 4 = Red FLM_MAGENTA, ///< 5 = Magenta FLM_BROWN, ///< 6 = Brown FLM_LIGHTGRAY, ///< 7 = Light Gray FLM_DARKGRAY, ///< 8 = Dark Gray FLM_LIGHTBLUE, ///< 9 = Light Blue FLM_LIGHTGREEN, ///< 10 = Light Green FLM_LIGHTCYAN, ///< 11 = Light Cyan FLM_LIGHTRED, ///< 12 = Light Red FLM_LIGHTMAGENTA, ///< 13 = Light Magenta FLM_YELLOW, ///< 14 = Light Yellow FLM_WHITE, ///< 15 = White FLM_NUM_COLORS, FLM_CURRENT_COLOR } eColorType; #define F_FOREBLACK "%0F" #define F_FOREBLUE "%1F" #define F_FOREGREEN "%2F" #define F_FORECYAN "%3F" #define F_FORERED "%4F" #define F_FOREMAGENTA "%5F" #define F_FOREBROWN "%6F" #define F_FORELIGHTGRAY "%7F" #define F_FOREDARKGRAY "%8F" #define F_FORELIGHTBLUE "%9F" #define F_FORELIGHTGREEN "%10F" #define F_FORELIGHTCYAN "%11F" #define F_FORELIGHTRED "%12F" #define F_FORELIGHTMAGENTA "%13F" #define F_FOREYELLOW "%14F" #define F_FOREWHITE "%15F" #define F_BACKBLACK "%0B" #define F_BACKBLUE "%1B" #define F_BACKGREEN "%2B" #define F_BACKCYAN "%3B" #define F_BACKRED "%4B" #define F_BACKMAGENTA "%5B" #define F_BACKBROWN "%6B" #define F_BACKLIGHTGRAY "%7B" #define F_BACKDARKGRAY "%8B" #define F_BACKLIGHTBLUE "%9B" #define F_BACKLIGHTGREEN "%10B" #define F_BACKLIGHTCYAN "%11B" #define F_BACKLIGHTRED "%12B" #define F_BACKLIGHTMAGENTA "%13B" #define F_BACKYELLOW "%14B" #define F_BACKWHITE "%15B" #define F_PUSH_FORECOLOR "%+F" #define F_POP_FORECOLOR "%-F" #define F_PUSH_BACKCOLOR "%+B" #define F_POP_BACKCOLOR "%-B" #define F_PUSHCOLOR F_PUSH_FORECOLOR F_PUSH_BACKCOLOR #define F_POPCOLOR F_POP_FORECOLOR F_POP_BACKCOLOR // IMPORTANT NOTE: This structure needs to be kept in sync with corresponding // structures and classes in java and C#. /**************************************************************************** /// Structure for reporting slab usage information in cache. ****************************************************************************/ typedef struct { FLMUINT64 ui64Slabs; ///< Total slabs currently allocated. FLMUINT64 ui64SlabBytes; ///< Total bytes currently allocated in slabs. FLMUINT64 ui64AllocatedCells; ///< Total cells allocated within slabs. FLMUINT64 ui64FreeCells; ///< Total cells that are free within slabs. } FLM_SLAB_USAGE; /**************************************************************************** /// Structure returned from FlmGetThreadInfo() - contains information about a thread. ****************************************************************************/ typedef struct { FLMUINT uiThreadId; ///< Operating system thread ID. FLMUINT uiThreadGroup; ///< Thread group this thread belongs to. FLMUINT uiAppId; ///< Application ID that was assigned to the thread when it was started. FLMUINT uiStartTime; ///< Time the thread was started. char * pszThreadName; ///< Name of the thread. char * pszThreadStatus; ///< String indicating the last action the thread reported it was performing. } F_THREAD_INFO; typedef enum { FLM_THREAD_STATUS_UNKNOWN = 0, FLM_THREAD_STATUS_INITIALIZING, FLM_THREAD_STATUS_RUNNING, FLM_THREAD_STATUS_SLEEPING, FLM_THREAD_STATUS_TERMINATING } eThreadStatus; #define F_THREAD_MIN_STACK_SIZE (16 * 1024) #define F_THREAD_DEFAULT_STACK_SIZE (16 * 1024) #define F_DEFAULT_THREAD_GROUP 0 #define F_INVALID_THREAD_GROUP 0xFFFFFFFF typedef RCODE (FLMAPI * F_THREAD_FUNC)(IF_Thread *); /**************************************************************************** Desc: Startup and shutdown ****************************************************************************/ RCODE FLMAPI ftkStartup( void); void FLMAPI ftkShutdown( void); /**************************************************************************** Desc: Global data ****************************************************************************/ extern FLMUINT16 * gv_pui16USCollationTable; /**************************************************************************** /// This is a pure virtual base class that other classes inherit from.\ It /// provides methods for reference counting (AddRef, Release). ****************************************************************************/ flminterface FLMEXP IF_Object { virtual ~IF_Object() { } virtual FLMINT FLMAPI AddRef( void) = 0; virtual FLMINT FLMAPI Release( void) = 0; virtual FLMINT FLMAPI getRefCount( void) = 0; }; /**************************************************************************** /// This is the base class that all other classes inherit from.\ It /// provides methods for reference counting (AddRef, Release) as well as /// methods for overloading new and delete operators. ****************************************************************************/ class FLMEXP F_Object : public IF_Object { public: F_Object() { m_refCnt = 1; } virtual ~F_Object() { } /// Increment the reference count for this object. /// The reference count is the number of pointers that are referencing this object. /// Return value is the incremented reference count. virtual FLMINT FLMAPI AddRef( void); /// Decrement the reference count for this object. /// The reference count is the number of pointers that are referencing this object. /// Return value is the decremented reference count. If the reference count goes to /// zero, the object will be deleted. virtual FLMINT FLMAPI Release( void); /// Return the current reference count on the object. virtual FLMINT FLMAPI getRefCount( void); /// Overloaded new operator for objects of this class. void * FLMAPI operator new( FLMSIZET uiSize, ///< Number of bytes to allocate - should be sizeof( ThisClass). const char * pszFile, ///< Name of source file where this allocation is made. int iLine) ///< Line number in source file where this allocation request is made. #ifndef FLM_WATCOM_NLM throw() #endif ; /// Overloaded new operator for objects of this class. void * FLMAPI operator new( FLMSIZET uiSize) ///< Number of bytes to allocate - should be sizeof( ThisClass). #ifndef FLM_WATCOM_NLM throw() #endif ; /// Overloaded new operator (array) for objects of this class (with source file and line number). /// This new operator is called when an array of objects of this class are allocated. /// This new operator passes in the current file and line number. This information is /// useful in tracking memory allocations to determine where memory leaks are coming from. void * FLMAPI operator new[]( FLMSIZET uiSize, ///< Number of bytes to allocate - should be sizeof( ThisClass). const char * pszFile, ///< Name of source file where this allocation is made. int iLine) ///< Line number in source file where this allocation request is made. #ifndef FLM_WATCOM_NLM throw() #endif ; /// Overloaded new operator (array) for objects of this class. /// This new operator is called when an array of objects of this class are allocated. void * FLMAPI operator new[]( FLMSIZET uiSize) ///< Number of bytes to allocate - should be sizeof( ThisClass). #ifndef FLM_WATCOM_NLM throw() #endif ; /// Overloaded delete operator for objects of this class. void FLMAPI operator delete( void * ptr); ///< Pointer to object being freed. /// Overloaded delete operator (array) for objects of this class. void FLMAPI operator delete[]( void * ptr); ///< Pointer to array of objects being freed. #ifndef FLM_WATCOM_NLM /// Overloaded delete operator for objects of this class (with source file and line number). /// This delete operator passes in the current file and line number. This information is /// useful in tracking memory allocations to determine where memory leaks are coming from. void FLMAPI operator delete( void * ptr, ///< Pointer to object being freed. const char * file, ///< Name of source file where this delete occurs. int line); ///< Line number in source file where this delete occurs. #endif #ifndef FLM_WATCOM_NLM /// Overloaded delete operator (array) for objects of this class (with source file and line number). /// This delete operator is called when an array of objects of this class is freed. /// This delete operator passes in the current file and line number. This information is /// useful in tracking memory allocations to determine where memory leaks are coming from. void FLMAPI operator delete[]( void * ptr, ///< Pointer to object being freed. const char * file, ///< Name of source file where this delete occurs. int line); ///< Line number in source file where this delete occurs. #endif protected: FLMATOMIC m_refCnt; }; /**************************************************************************** Desc: Internal base class ****************************************************************************/ class FLMEXP F_OSBase { public: F_OSBase() { m_refCnt = 1; } virtual ~F_OSBase() { } void * operator new( FLMSIZET uiSize, const char * pszFile, int iLine) #ifndef FLM_WATCOM_NLM throw() #endif ; void * operator new[]( FLMSIZET uiSize, const char * pszFile, int iLine) #ifndef FLM_WATCOM_NLM throw() #endif ; void operator delete( void * ptr); void operator delete[]( void * ptr); #ifndef FLM_WATCOM_NLM void operator delete( void * ptr, const char * file, int line); #endif #ifndef FLM_WATCOM_NLM void operator delete[]( void * ptr, const char * file, int line); #endif virtual FINLINE FLMINT FLMAPI AddRef( void) { return( ++m_refCnt); } virtual FINLINE FLMINT FLMAPI Release( void) { FLMINT iRefCnt = --m_refCnt; if( !iRefCnt) { delete this; } return( iRefCnt); } virtual FINLINE FLMINT FLMAPI getRefCount( void) { return( m_refCnt); } protected: FLMATOMIC m_refCnt; }; /**************************************************************************** Desc: Errors ****************************************************************************/ #ifdef FLM_DEBUG RCODE FLMAPI f_makeErr( RCODE rc, const char * pszFile, int iLine, FLMBOOL bAssert); FLMINT FLMAPI f_enterDebugger( const char * pszFile, int iLine); #define RC_SET( rc) \ f_makeErr( rc, __FILE__, __LINE__, FALSE) #define RC_SET_AND_ASSERT( rc) \ f_makeErr( rc, __FILE__, __LINE__, TRUE) #define RC_UNEXPECTED_ASSERT( rc) \ f_makeErr( rc, __FILE__, __LINE__, TRUE) #define f_assert( c) \ (void)((c) ? 0 : f_enterDebugger( __FILE__, __LINE__)) #define flmAssert( c) \ f_assert( c) #else #define RC_SET( rc) (rc) #define RC_SET_AND_ASSERT( rc) (rc) #define RC_UNEXPECTED_ASSERT( rc) #define f_assert(c) #define flmAssert(c) #endif /**************************************************************************** Desc: Memory ****************************************************************************/ RCODE FLMAPI f_allocImp( FLMUINT uiSize, void ** ppvPtr, FLMBOOL bFromNewOp, const char * pszFile, int iLine); #define f_alloc(s,p) \ f_allocImp( (s), (void **)(p), FALSE, __FILE__, __LINE__) RCODE FLMAPI f_callocImp( FLMUINT uiSize, void ** ppvPtr, const char * pszFile, int iLine); #define f_calloc(s,p) \ f_callocImp( (s), (void **)(p), __FILE__, __LINE__) RCODE FLMAPI f_reallocImp( FLMUINT uiSize, void ** ppvPtr, const char * pszFile, int iLine); #define f_realloc(s,p) \ f_reallocImp( (s), (void **)(p), __FILE__, __LINE__) RCODE FLMAPI f_recallocImp( FLMUINT uiSize, void ** ppvPtr, const char * pszFile, int iLine); #define f_recalloc(s,p) \ f_recallocImp( (s), (void **)(p), __FILE__, __LINE__) #define f_new \ new( __FILE__, __LINE__) void FLMAPI f_freeImp( void ** ppvPtr, FLMBOOL bFromDelOp); #define f_free(p) \ f_freeImp( (void **)(p), FALSE) void f_resetStackInfoImp( void * pvPtr, const char * pszFileName, int iLineNumber); #define f_resetStackInfo(p) \ f_resetStackInfoImp( (p), __FILE__, __LINE__) FLMUINT f_msize( void * pvPtr); RCODE FLMAPI f_allocAlignedBufferImp( FLMUINT uiMinSize, void ** ppvAlloc); #define f_allocAlignedBuffer(s,p) \ f_allocAlignedBufferImp( (s), (void **)(p)) void FLMAPI f_freeAlignedBufferImp( void ** ppvAlloc); #define f_freeAlignedBuffer(p) \ f_freeAlignedBufferImp( (void **)(p)) RCODE FLMAPI f_getMemoryInfo( FLMUINT64 * pui64TotalPhysMem, FLMUINT64 * pui64AvailPhysMem); FLMBOOL FLMAPI f_canGetMemoryInfo( void); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_ThreadInfo : public F_Object { virtual FLMUINT FLMAPI getNumThreads( void) = 0; virtual void FLMAPI getThreadInfo( FLMUINT uiThreadNum, FLMUINT * puiThreadId, FLMUINT * puiThreadGroup, FLMUINT * puiAppId, FLMUINT * puiStartTime, const char ** ppszThreadName, const char ** ppszThreadStatus) = 0; }; RCODE FLMAPI FlmGetThreadInfo( IF_ThreadInfo ** ppThreadInfo); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_IStream : public F_Object { virtual RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead = NULL) = 0; virtual RCODE FLMAPI closeStream( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_PosIStream : public IF_IStream { virtual FLMUINT64 FLMAPI totalSize( void) = 0; virtual FLMUINT64 FLMAPI remainingSize( void) = 0; virtual RCODE FLMAPI positionTo( FLMUINT64 ui64Position) = 0; virtual FLMUINT64 FLMAPI getCurrPosition( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_BufferIStream : public IF_PosIStream { virtual RCODE FLMAPI openStream( const char * pucBuffer, FLMUINT uiLength, char ** ppucAllocatedBuffer = NULL) = 0; virtual FLMUINT64 FLMAPI totalSize( void) = 0; virtual FLMUINT64 FLMAPI remainingSize( void) = 0; virtual RCODE FLMAPI closeStream( void) = 0; virtual RCODE FLMAPI positionTo( FLMUINT64 ui64Position) = 0; virtual FLMUINT64 FLMAPI getCurrPosition( void) = 0; virtual void FLMAPI truncateStream( FLMUINT64 ui64Offset = 0) = 0; virtual RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) = 0; virtual const FLMBYTE * FLMAPI getBufferAtCurrentOffset( void) = 0; }; RCODE FLMAPI FlmAllocBufferIStream( IF_BufferIStream ** ppIStream); RCODE FLMAPI FlmOpenBufferIStream( const char * pucBuffer, FLMUINT uiLength, IF_PosIStream ** ppIStream); RCODE FLMAPI FlmOpenBase64EncoderIStream( IF_IStream * pSourceIStream, FLMBOOL bLineBreaks, IF_IStream ** ppIStream); RCODE FLMAPI FlmOpenBase64DecoderIStream( IF_IStream * pSourceIStream, IF_IStream ** ppIStream); RCODE FLMAPI FlmOpenFileIStream( const char * pszPath, IF_PosIStream ** ppIStream); RCODE FLMAPI FlmOpenMultiFileIStream( const char * pszDirectory, const char * pszBaseName, IF_IStream ** ppIStream); RCODE FLMAPI FlmOpenBufferedIStream( IF_IStream * pSourceIStream, FLMUINT uiBufferSize, IF_IStream ** ppIStream); RCODE FLMAPI FlmOpenUncompressingIStream( IF_IStream * pIStream, IF_IStream ** ppIStream); RCODE FLMAPI FlmOpenFileOStream( const char * pszFileName, FLMBOOL bTruncateIfExists, IF_OStream ** ppOStream); RCODE FLMAPI FlmOpenMultiFileOStream( const char * pszDirectory, const char * pszBaseName, FLMUINT uiMaxFileSize, FLMBOOL bOkToOverwrite, IF_OStream ** ppStream); RCODE FLMAPI FlmOpenBufferedOStream( IF_OStream * pOStream, FLMUINT uiBufferSize, IF_OStream ** ppOStream); RCODE FLMAPI FlmOpenCompressingOStream( IF_OStream * pOStream, IF_OStream ** ppOStream); RCODE FLMAPI FlmRemoveMultiFileStream( const char * pszDirectory, const char * pszBaseName); RCODE FLMAPI FlmWriteToOStream( IF_IStream * pIStream, IF_OStream * pOStream); /**************************************************************************** Desc: ****************************************************************************/ typedef struct { FLMUINT64 ui64Position; FLMUNICODE uNextChar; } F_CollStreamPos; flminterface FLMEXP IF_CollIStream : public IF_PosIStream { virtual RCODE FLMAPI openStream( IF_PosIStream * pIStream, FLMBOOL bUnicodeStream, FLMUINT uiLanguage, FLMUINT uiCompareRules, FLMBOOL bMayHaveWildCards) = 0; virtual RCODE FLMAPI closeStream( void) = 0; virtual RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) = 0; virtual RCODE FLMAPI read( FLMBOOL bAllowTwoIntoOne, FLMUNICODE * puChar, FLMBOOL * pbCharIsWild, FLMUINT16 * pui16Col, FLMUINT16 * pui16SubCol, FLMBYTE * pucCase) = 0; virtual FLMUINT64 FLMAPI totalSize( void) = 0; virtual FLMUINT64 FLMAPI remainingSize( void) = 0; virtual RCODE FLMAPI positionTo( FLMUINT64 ui64Position) = 0; virtual FLMUINT64 FLMAPI getCurrPosition( void) = 0; virtual RCODE FLMAPI positionTo( F_CollStreamPos * pPos) = 0; virtual void FLMAPI getCurrPosition( F_CollStreamPos * pPos) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_OStream : public F_Object { virtual RCODE FLMAPI write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten = NULL) = 0; virtual RCODE FLMAPI closeStream( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_IOStream : public IF_IStream, public IF_OStream { #if defined( FLM_WIN) && _MSC_VER < 1300 using IF_IStream::operator delete; #endif }; /**************************************************************************** ****************************************************************************/ /// Message severity. typedef enum { F_FATAL_MESSAGE = 0, ///< Indicates that a fatal error occurred - the kind that would normally ///< require a shutdown or other corrective action by an administrator. F_WARN_MESSAGE, ///< Warning message. F_ERR_MESSAGE, ///< Non-fatal error message. F_INFO_MESSAGE, ///< Information-only message. F_DEBUG_MESSAGE ///< Debug message. } eLogMessageSeverity; /**************************************************************************** Desc: Logging ****************************************************************************/ IF_LogMessageClient * FLMAPI f_beginLogMessage( FLMUINT uiMsgType, eLogMessageSeverity eMsgSeverity); void FLMAPI f_logPrintf( IF_LogMessageClient * pLogMessage, const char * pszFormatStr, ...); void FLMAPI f_logVPrintf( IF_LogMessageClient * pLogMessage, const char * szFormatStr, f_va_list * args); void FLMAPI f_endLogMessage( IF_LogMessageClient ** ppLogMessage); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_LoggerClient : public F_Object { virtual IF_LogMessageClient * FLMAPI beginMessage( FLMUINT uiMsgType, eLogMessageSeverity eMsgSeverity = F_DEBUG_MESSAGE) = 0; }; void f_setLoggerClient( IF_LoggerClient * pLogger); /**************************************************************************** /// This is an abstract base class that allows an application to catch /// messages. The application must create an implementation for this class /// and then return an object of that class when the /// IF_LoggerClient::beginMessage() method is called. ****************************************************************************/ flminterface FLMEXP IF_LogMessageClient : public F_Object { /// Set the current foreground and background colors for the message. virtual void FLMAPI changeColor( eColorType eForeColor, eColorType eBackColor) = 0; /// Append a string to the message. This method may be called /// multiple times by to format a complete message. The message is not /// complete until the ::endMessage() method is called. virtual void FLMAPI appendString( const char * pszStr) = 0; /// Append a newline to the message. This method is called when a /// multi-line message is being created. Rather than embedding a /// newline character, this method is used. This allows an application /// to recognize the fact that there are multiple lines in the message /// and to log, display, store, etc. (whatever) them accordingly. virtual void FLMAPI newline( void) = 0; /// End the current message. The application should finish logging, /// displaying, storing, etc. (whatever) the message. The object /// should be reset in case a new message is subsequently started. virtual void FLMAPI endMessage( void) = 0; virtual void FLMAPI pushForegroundColor( void) = 0; virtual void FLMAPI popForegroundColor( void) = 0; virtual void FLMAPI pushBackgroundColor( void) = 0; virtual void FLMAPI popBackgroundColor( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_FileSystem : public F_Object { virtual RCODE FLMAPI createFile( const char * pszFileName, FLMUINT uiIoFlags, IF_FileHdl ** ppFile) = 0; virtual RCODE FLMAPI createUniqueFile( char * pszPath, const char * pszFileExtension, FLMUINT uiIoFlags, IF_FileHdl ** ppFile) = 0; virtual RCODE FLMAPI createLockFile( const char * pszPath, IF_FileHdl ** ppLockFileHdl) = 0; virtual RCODE FLMAPI openFile( const char * pszFileName, FLMUINT uiIoFlags, IF_FileHdl ** ppFile) = 0; virtual RCODE FLMAPI openDir( const char * pszDirName, const char * pszPattern, IF_DirHdl ** ppDir) = 0; virtual RCODE FLMAPI createDir( const char * pszDirName) = 0; virtual RCODE FLMAPI removeDir( const char * pszDirName, FLMBOOL bClear = FALSE) = 0; virtual RCODE FLMAPI doesFileExist( const char * pszFileName) = 0; virtual FLMBOOL FLMAPI isDir( const char * pszFileName) = 0; virtual RCODE FLMAPI getFileTimeStamp( const char * pszFileName, FLMUINT * puiTimeStamp) = 0; virtual RCODE FLMAPI getFileSize( const char * pszFileName, FLMUINT64 * pui64FileSize) = 0; virtual RCODE FLMAPI deleteFile( const char * pszFileName) = 0; virtual RCODE FLMAPI deleteMultiFileStream( const char * pszDirectory, const char * pszBaseName) = 0; virtual RCODE FLMAPI copyFile( const char * pszSrcFileName, const char * pszDestFileName, FLMBOOL bOverwrite, FLMUINT64 * pui64BytesCopied) = 0; virtual RCODE FLMAPI copyPartialFile( IF_FileHdl * pSrcFileHdl, FLMUINT64 ui64SrcOffset, FLMUINT64 ui64SrcSize, IF_FileHdl * pDestFileHdl, FLMUINT64 ui64DestOffset, FLMUINT64 * pui64BytesCopiedRV) = 0; virtual RCODE FLMAPI renameFile( const char * pszFileName, const char * pszNewFileName) = 0; virtual RCODE FLMAPI setReadOnly( const char * pszFileName, FLMBOOL bReadOnly) = 0; virtual RCODE FLMAPI getSectorSize( const char * pszFileName, FLMUINT * puiSectorSize) = 0; virtual void FLMAPI pathCreateUniqueName( FLMUINT * puiTime, char * pFileName, const char * pFileExt, FLMBYTE * pHighChars, FLMBOOL bModext) = 0; virtual void FLMAPI pathParse( const char * pszPath, char * pszServer, char * pszVolume, char * pszDirPath, char * pszFileName) = 0; virtual RCODE FLMAPI pathReduce( const char * pszSourcePath, char * pszDestPath, char * pszString) = 0; virtual RCODE FLMAPI pathAppend( char * pszPath, const char * pszPathComponent) = 0; virtual RCODE FLMAPI pathToStorageString( const char * pPath, char * pszString) = 0; virtual FLMBOOL FLMAPI doesFileMatch( const char * pszFileName, const char * pszTemplate) = 0; virtual FLMBOOL FLMAPI canDoAsync( void) = 0; virtual RCODE FLMAPI allocIOBuffer( FLMUINT uiMinSize, IF_IOBuffer ** ppIOBuffer) = 0; virtual RCODE FLMAPI allocFileHandleCache( FLMUINT uiMaxCachedFiles, FLMUINT uiIdleTimeoutSecs, IF_FileHdlCache ** ppFileHdlCache) = 0; }; RCODE FLMAPI FlmGetFileSystem( IF_FileSystem ** ppFileSystem); IF_FileSystem * FLMAPI f_getFileSysPtr( void); FLMUINT FLMAPI f_getOpenFileCount( void); RCODE FLMAPI f_chdir( const char * pszDir); RCODE FLMAPI f_getcwd( char * pszDir); RCODE FLMAPI f_pathReduce( const char * pszSourcePath, char * pszDestPath, char * pszString); RCODE FLMAPI f_pathAppend( char * pszPath, const char * pszPathComponent); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_FileHdl : virtual public F_Object { virtual RCODE FLMAPI flush( void) = 0; virtual RCODE FLMAPI read( FLMUINT64 ui64Offset, FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesRead = NULL) = 0; virtual RCODE FLMAPI read( FLMUINT64 ui64ReadOffset, FLMUINT uiBytesToRead, IF_IOBuffer * pIOBuffer) = 0; virtual RCODE FLMAPI write( FLMUINT64 ui64Offset, FLMUINT uiLength, const void * pvBuffer, FLMUINT * puiBytesWritten = NULL) = 0; virtual RCODE FLMAPI write( FLMUINT64 ui64WriteOffset, FLMUINT uiBytesToWrite, IF_IOBuffer * pIOBuffer) = 0; virtual RCODE FLMAPI seek( FLMUINT64 ui64Offset, FLMINT iWhence, FLMUINT64 * pui64NewOffset = NULL) = 0; virtual RCODE FLMAPI size( FLMUINT64 * pui64Size) = 0; virtual RCODE FLMAPI tell( FLMUINT64 * pui64Offset) = 0; virtual RCODE FLMAPI extendFile( FLMUINT64 ui64FileSize) = 0; virtual RCODE FLMAPI truncateFile( FLMUINT64 ui64FileSize = 0) = 0; virtual RCODE FLMAPI closeFile( void) = 0; virtual FLMBOOL FLMAPI canDoAsync( void) = 0; virtual FLMBOOL FLMAPI canDoDirectIO( void) = 0; virtual void FLMAPI setExtendSize( FLMUINT uiExtendSize) = 0; virtual void FLMAPI setMaxAutoExtendSize( FLMUINT uiMaxAutoExtendSize) = 0; virtual FLMUINT FLMAPI getSectorSize( void) = 0; virtual FLMBOOL FLMAPI isReadOnly( void) = 0; virtual FLMBOOL FLMAPI isOpen( void) = 0; virtual RCODE FLMAPI lock( void) = 0; virtual RCODE FLMAPI unlock( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_FileHdlCache : public F_Object { virtual RCODE FLMAPI openFile( const char * pszFileName, FLMUINT uiIoFlags, IF_FileHdl ** ppFile) = 0; virtual RCODE FLMAPI createFile( const char * pszFileName, FLMUINT uiIoFlags, IF_FileHdl ** ppFile) = 0; virtual void FLMAPI closeUnusedFiles( FLMUINT uiUnusedTime = 0) = 0; virtual FLMUINT FLMAPI getOpenThreshold( void) = 0; virtual RCODE FLMAPI setOpenThreshold( FLMUINT uiMaxOpenFiles) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_MultiFileHdl : public F_Object { virtual RCODE FLMAPI createFile( const char * pszPath) = 0; virtual RCODE FLMAPI createUniqueFile( const char * pszPath, const char * pszFileExtension) = 0; virtual RCODE FLMAPI openFile( const char * pszPath) = 0; virtual RCODE FLMAPI deleteMultiFile( const char * pszPath) = 0; virtual RCODE FLMAPI flush( void) = 0; virtual RCODE FLMAPI read( FLMUINT64 ui64Offset, FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesRead = NULL) = 0; virtual RCODE FLMAPI write( FLMUINT64 ui64Offset, FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesWritten = NULL) = 0; virtual RCODE FLMAPI getPath( char * pszFilePath) = 0; virtual RCODE FLMAPI size( FLMUINT64 * pui64FileSize) = 0; virtual RCODE FLMAPI truncateFile( FLMUINT64 ui64Offset = 0) = 0; virtual void FLMAPI closeFile( FLMBOOL bDelete = FALSE) = 0; }; RCODE FLMAPI FlmAllocMultiFileHdl( IF_MultiFileHdl ** ppFileHdl); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_SuperFileClient : public F_Object { virtual FLMUINT FLMAPI getFileNumber( FLMUINT uiBlockAddr) = 0; virtual FLMUINT FLMAPI getFileOffset( FLMUINT uiBlockAddr) = 0; virtual FLMUINT FLMAPI getBlockAddress( FLMUINT uiFileNumber, FLMUINT uiFileOffset) = 0; virtual RCODE FLMAPI getFilePath( FLMUINT uiFileNumber, char * pszPath) = 0; virtual FLMUINT64 FLMAPI getMaxFileSize( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ class F_SuperFileHdl : public F_Object { public: F_SuperFileHdl(); virtual ~F_SuperFileHdl(); RCODE FLMAPI setup( IF_SuperFileClient * pSuperFileClient, IF_FileHdlCache * pFileHdlCache, FLMUINT uiFileOpenFlags, FLMUINT uiFileCreateFlags); RCODE FLMAPI createFile( FLMUINT uiFileNumber, IF_FileHdl ** ppFileHdl = NULL); RCODE FLMAPI getFileHdl( FLMUINT uiFileNumber, FLMBOOL bGetForUpdate, IF_FileHdl ** ppFileHdlRV); RCODE FLMAPI readBlock( FLMUINT uiBlkAddress, FLMUINT uiBytesToRead, void * pvBuffer, FLMUINT * puiBytesRead); RCODE FLMAPI writeBlock( FLMUINT uiBlkAddress, FLMUINT uiBytesToWrite, const void * pvBuffer, FLMUINT * puiBytesWritten); RCODE FLMAPI writeBlock( FLMUINT uiBlkAddress, FLMUINT uiBytesToWrite, IF_IOBuffer * pIOBuffer); RCODE FLMAPI getFilePath( FLMUINT uiFileNumber, char * pszPath); RCODE FLMAPI getFileSize( FLMUINT uiFileNumber, FLMUINT64 * pui64FileSize); RCODE FLMAPI releaseFiles( void); RCODE FLMAPI allocateBlocks( FLMUINT uiStartAddress, FLMUINT uiEndAddress); RCODE FLMAPI truncateFile( FLMUINT uiEOFBlkAddress); RCODE FLMAPI truncateFile( FLMUINT uiFileNumber, FLMUINT uiOffset); void FLMAPI truncateFiles( FLMUINT uiStartFileNum, FLMUINT uiEndFileNum); RCODE FLMAPI flush( void); FINLINE void FLMAPI setExtendSize( FLMUINT uiExtendSize) { m_uiExtendSize = uiExtendSize; } FINLINE void FLMAPI setMaxAutoExtendSize( FLMUINT uiMaxAutoExtendSize) { m_uiMaxAutoExtendSize = uiMaxAutoExtendSize; } FLMBOOL FLMAPI canDoAsync( void); FLMBOOL FLMAPI canDoDirectIO( void); private: IF_SuperFileClient * m_pSuperFileClient; IF_FileHdlCache * m_pFileHdlCache; IF_FileHdl * m_pCFileHdl; IF_FileHdl * m_pBlockFileHdl; FLMBOOL m_bCFileDirty; FLMBOOL m_bBlockFileDirty; FLMUINT m_uiBlockFileNum; FLMUINT m_uiMaxFileSize; FLMUINT m_uiExtendSize; FLMUINT m_uiMaxAutoExtendSize; FLMUINT m_uiFileOpenFlags; FLMUINT m_uiFileCreateFlags; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_AsyncClient : virtual public F_Object { virtual RCODE FLMAPI waitToComplete( void) = 0; virtual RCODE FLMAPI getCompletionCode( void) = 0; virtual FLMUINT FLMAPI getElapsedTime( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ typedef void (FLMAPI * F_BUFFER_COMPLETION_FUNC)(IF_IOBuffer *, void *); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_IOBuffer : virtual public F_Object { virtual FLMBYTE * FLMAPI getBufferPtr( void) = 0; virtual FLMUINT FLMAPI getBufferSize( void) = 0; virtual void FLMAPI setCompletionCallback( F_BUFFER_COMPLETION_FUNC fnCompletion, void * pvData) = 0; virtual RCODE FLMAPI addCallbackData( void * pvData) = 0; virtual void * FLMAPI getCallbackData( FLMUINT uiSlot) = 0; virtual FLMUINT FLMAPI getCallbackDataCount( void) = 0; virtual void FLMAPI setAsyncClient( IF_AsyncClient * pAsyncClient) = 0; virtual void FLMAPI notifyComplete( RCODE completionRc) = 0; virtual void FLMAPI setPending( void) = 0; virtual void FLMAPI clearPending( void) = 0; virtual FLMBOOL FLMAPI isPending( void) = 0; virtual FLMBOOL FLMAPI isComplete( void) = 0; virtual RCODE FLMAPI waitToComplete( void) = 0; virtual RCODE FLMAPI getCompletionCode( void) = 0; virtual FLMUINT FLMAPI getElapsedTime( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_IOBufferMgr : public F_Object { virtual RCODE FLMAPI getBuffer( FLMUINT uiBufferSize, IF_IOBuffer ** ppIOBuffer) = 0; virtual FLMBOOL FLMAPI isIOPending( void) = 0; virtual RCODE FLMAPI waitForAllPendingIO( void) = 0; }; RCODE FLMAPI FlmAllocIOBufferMgr( FLMUINT uiMaxBuffers, FLMUINT uiMaxBytes, FLMBOOL bReuseBuffers, IF_IOBufferMgr ** ppIOBufferMgr); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_DirHdl : public F_Object { virtual RCODE FLMAPI next( void) = 0; virtual const char * FLMAPI currentItemName( void) = 0; virtual void FLMAPI currentItemPath( char * pszPath) = 0; virtual FLMUINT64 FLMAPI currentItemSize( void) = 0; virtual FLMBOOL FLMAPI currentItemIsDir( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_ResultSetCompare : public F_Object { virtual RCODE FLMAPI compare( const void * pvData1, FLMUINT uiLength1, const void * pvData2, FLMUINT uiLength2, FLMINT * piCompare) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_ResultSetSortStatus : public F_Object { virtual RCODE FLMAPI reportSortStatus( FLMUINT64 ui64EstTotalUnits, FLMUINT64 ui64UnitsDone) = 0; }; #define RS_POSITION_NOT_SET FLM_MAX_UINT64 /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_ResultSet : public F_Object { virtual RCODE FLMAPI setupResultSet( const char * pszPath, IF_ResultSetCompare * pCompare, FLMUINT uiEntrySize, FLMBOOL bDropDuplicates = TRUE, FLMBOOL bEntriesInOrder = FALSE, const char * pszFileName = NULL) = 0; virtual FLMUINT64 FLMAPI getTotalEntries( void) = 0; virtual RCODE FLMAPI addEntry( const void * pvEntry, FLMUINT uiEntryLength = 0) = 0; virtual RCODE FLMAPI finalizeResultSet( IF_ResultSetSortStatus * pSortStatus = NULL, FLMUINT64 * pui64TotalEntries = NULL) = 0; virtual RCODE FLMAPI getFirst( void * pvEntryBuffer, FLMUINT uiBufferLength = 0, FLMUINT * puiEntryLength = NULL) = 0; virtual RCODE FLMAPI getNext( void * pvEntryBuffer, FLMUINT uiBufferLength = 0, FLMUINT * puiEntryLength = NULL) = 0; virtual RCODE FLMAPI getLast( void * pvEntryBuffer, FLMUINT uiBufferLength = 0, FLMUINT * puiEntryLength = NULL) = 0; virtual RCODE FLMAPI getPrev( void * pvEntryBuffer, FLMUINT uiBufferLength = 0, FLMUINT * puiEntryLength = NULL) = 0; virtual RCODE FLMAPI getCurrent( void * pvEntryBuffer, FLMUINT uiBufferLength = 0, FLMUINT * puiEntryLength = NULL) = 0; virtual RCODE FLMAPI findMatch( const void * pvMatchEntry, void * pvFoundEntry) = 0; virtual RCODE FLMAPI findMatch( const void * pvMatchEntry, FLMUINT uiMatchEntryLength, void * pvFoundEntry, FLMUINT * puiFoundEntryLength) = 0; virtual RCODE FLMAPI modifyCurrent( const void * pvEntry, FLMUINT uiEntryLength = 0) = 0; virtual FLMUINT64 FLMAPI getPosition( void) = 0; virtual RCODE FLMAPI setPosition( FLMUINT64 ui64Position) = 0; virtual RCODE FLMAPI resetResultSet( FLMBOOL bDelete = TRUE) = 0; virtual RCODE FLMAPI flushToFile( void) = 0; }; RCODE FLMAPI FlmAllocResultSet( IF_ResultSet ** ppResultSet); /***************************************************************************** Desc: *****************************************************************************/ flminterface FLMEXP IF_BTreeResultSet : public F_Object { virtual RCODE FLMAPI addEntry( FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength) = 0; virtual RCODE FLMAPI modifyEntry( FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength) = 0; virtual RCODE FLMAPI getCurrent( FLMBYTE * pucKey, FLMUINT uiKeyLength, FLMBYTE * pucEntry, FLMUINT uiEntryLength, FLMUINT * puiReturnLength) = 0; virtual RCODE FLMAPI getNext( FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength) = 0; virtual RCODE FLMAPI getPrev( FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength) = 0; virtual RCODE FLMAPI getFirst( FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength) = 0; virtual RCODE FLMAPI getLast( FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength) = 0; virtual RCODE FLMAPI findEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufLen, FLMUINT * puiKeylen, FLMBYTE * pucBuffer, FLMUINT uiBufferLength, FLMUINT * puiReturnLength) = 0; virtual RCODE FLMAPI deleteEntry( FLMBYTE * pucKey, FLMUINT uiKeyLength) = 0; }; /**************************************************************************** Desc: Random numbers ****************************************************************************/ #define FLM_MAX_RANDOM ((FLMUINT32)2147483646) flminterface FLMEXP IF_RandomGenerator : public F_Object { virtual void FLMAPI randomize( void) = 0; virtual void FLMAPI setSeed( FLMUINT32 ui32seed) = 0; virtual FLMUINT32 FLMAPI getSeed( void) = 0; virtual FLMUINT32 FLMAPI getUINT32( FLMUINT32 ui32Low = 0, FLMUINT32 ui32High = FLM_MAX_RANDOM) = 0; virtual FLMBOOL FLMAPI getBoolean( void) = 0; }; RCODE FLMAPI FlmAllocRandomGenerator( IF_RandomGenerator ** ppRandomGenerator); FLMUINT32 FLMAPI f_getRandomUINT32( FLMUINT32 ui32Low = 0, FLMUINT32 ui32High = FLM_MAX_RANDOM); FLMBYTE FLMAPI f_getRandomByte( void); /********************************************************************** Desc: Atomic operations **********************************************************************/ FLMINT32 FLMAPI f_atomicInc( FLMATOMIC * piTarget); FLMINT32 FLMAPI f_atomicDec( FLMATOMIC * piTarget); FLMINT32 FLMAPI f_atomicExchange( FLMATOMIC * piTarget, FLMINT32 i32NewVal); /**************************************************************************** Desc: Mutexes ****************************************************************************/ typedef void * F_MUTEX; #define F_MUTEX_NULL NULL RCODE FLMAPI f_mutexCreate( F_MUTEX * phMutex); void FLMAPI f_mutexDestroy( F_MUTEX * phMutex); void FLMAPI f_mutexLock( F_MUTEX hMutex); void FLMAPI f_mutexUnlock( F_MUTEX hMutex); #ifdef FLM_DEBUG void FLMAPI f_assertMutexLocked( F_MUTEX hMutex); #else #define f_assertMutexLocked( h) (void)(h) #endif #ifdef FLM_DEBUG void FLMAPI f_assertMutexNotLocked( F_MUTEX hMutex); #else #define f_assertMutexNotLocked( h) (void)(h) #endif /**************************************************************************** Desc: Semaphores ****************************************************************************/ typedef void * F_SEM; #define F_SEM_NULL NULL RCODE FLMAPI f_semCreate( F_SEM * phSem); void FLMAPI f_semDestroy( F_SEM * phSem); RCODE FLMAPI f_semWait( F_SEM hSem, FLMUINT uiTimeout); void FLMAPI f_semSignal( F_SEM hSem); FLMUINT FLMAPI f_semGetSignalCount( F_SEM hSem); /**************************************************************************** Desc: Notify Lists ****************************************************************************/ typedef struct F_NOTIFY_LIST_ITEM { F_NOTIFY_LIST_ITEM * pNext; ///< Pointer to next F_NOTIFY_LIST_ITEM structure in list. FLMUINT uiThreadId; ///< ID of thread requesting the notify RCODE * pRc; ///< Pointer to a return code variable that is to ///< be filled in when the operation is completed. ///< The thread requesting notification supplies ///< the return code variable to be filled in. void * pvData; ///< Data that is passed through to a custom ///< notify routine F_SEM hSem; ///< Semaphore that will be signaled when the ///< operation is complete. } F_NOTIFY_LIST_ITEM; RCODE FLMAPI f_notifyWait( F_MUTEX hMutex, F_SEM hSem, void * pvData, F_NOTIFY_LIST_ITEM ** ppNotifyList); void FLMAPI f_notifySignal( F_NOTIFY_LIST_ITEM * pNotifyList, RCODE notifyRc); /**************************************************************************** Desc: Reader / Writer Locks ****************************************************************************/ typedef void * F_RWLOCK; #define F_RWLOCK_NULL NULL RCODE FLMAPI f_rwlockCreate( F_RWLOCK * phReadWriteLock); void FLMAPI f_rwlockDestroy( F_RWLOCK * phReadWriteLock); RCODE FLMAPI f_rwlockAcquire( F_RWLOCK hReadWriteLock, F_SEM hSem, FLMBOOL bWriter); RCODE FLMAPI f_rwlockPromote( F_RWLOCK hReadWriteLock, F_SEM hSem); RCODE FLMAPI f_rwlockRelease( F_RWLOCK hReadWriteLock); /**************************************************************************** Desc: Thread manager ****************************************************************************/ flminterface FLMEXP IF_ThreadMgr : public F_Object { virtual RCODE FLMAPI setupThreadMgr( void) = 0; virtual RCODE FLMAPI createThread( IF_Thread ** ppThread, F_THREAD_FUNC fnThread, const char * pszThreadName = NULL, FLMUINT uiThreadGroup = 0, FLMUINT uiAppId = 0, void * pvParm1 = NULL, void * pvParm2 = NULL, FLMUINT uiStackSize = F_THREAD_DEFAULT_STACK_SIZE) = 0; virtual void FLMAPI shutdownThreadGroup( FLMUINT uiThreadGroup) = 0; virtual void FLMAPI setThreadShutdownFlag( FLMUINT uiThreadId) = 0; virtual RCODE FLMAPI findThread( IF_Thread ** ppThread, FLMUINT uiThreadGroup, FLMUINT uiAppId = 0, FLMBOOL bOkToFindMe = TRUE) = 0; virtual RCODE FLMAPI getNextGroupThread( IF_Thread ** ppThread, FLMUINT uiThreadGroup, FLMUINT * puiThreadId) = 0; virtual RCODE FLMAPI getThreadInfo( F_Pool * pPool, F_THREAD_INFO ** ppThreadInfo, FLMUINT * puiNumThreads) = 0; virtual RCODE FLMAPI getThreadName( FLMUINT uiThreadId, char * pszThreadName, FLMUINT * puiLength) = 0; virtual FLMUINT FLMAPI getThreadGroupCount( FLMUINT uiThreadGroup) = 0; virtual FLMUINT FLMAPI allocGroupId( void) = 0; }; RCODE FLMAPI FlmGetThreadMgr( IF_ThreadMgr ** ppThreadMgr); /**************************************************************************** Desc: Thread ****************************************************************************/ flminterface FLMEXP IF_Thread : public F_Object { virtual RCODE FLMAPI startThread( F_THREAD_FUNC fnThread, const char * pszThreadName = NULL, FLMUINT uiThreadGroup = F_DEFAULT_THREAD_GROUP, FLMUINT uiAppId = 0, void * pvParm1 = NULL, void * pvParm2 = NULL, FLMUINT uiStackSize = F_THREAD_DEFAULT_STACK_SIZE) = 0; virtual void FLMAPI stopThread( void) = 0; virtual FLMUINT FLMAPI getThreadId( void) = 0; virtual FLMBOOL FLMAPI getShutdownFlag( void) = 0; virtual RCODE FLMAPI getExitCode( void) = 0; virtual void * FLMAPI getParm1( void) = 0; virtual void FLMAPI setParm1( void * pvParm) = 0; virtual void * FLMAPI getParm2( void) = 0; virtual void FLMAPI setParm2( void * pvParm) = 0; virtual void FLMAPI setShutdownFlag( void) = 0; virtual FLMBOOL FLMAPI isThreadRunning( void) = 0; virtual void FLMAPI setThreadStatusStr( const char * pszStatus) = 0; virtual void FLMAPI setThreadStatus( const char * pszBuffer, ...) = 0; virtual void FLMAPI setThreadStatus( eThreadStatus genericStatus) = 0; virtual void FLMAPI setThreadAppId( FLMUINT uiAppId) = 0; virtual FLMUINT FLMAPI getThreadAppId( void) = 0; virtual FLMUINT FLMAPI getThreadGroup( void) = 0; virtual void FLMAPI cleanupThread( void) = 0; virtual void FLMAPI sleep( FLMUINT uiMilliseconds) = 0; virtual void FLMAPI waitToComplete( void) = 0; }; RCODE FLMAPI f_threadCreate( IF_Thread ** ppThread, F_THREAD_FUNC fnThread, const char * pszThreadName = NULL, FLMUINT uiThreadGroup = F_DEFAULT_THREAD_GROUP, FLMUINT uiAppId = 0, void * pvParm1 = NULL, void * pvParm2 = NULL, FLMUINT uiStackSize = F_THREAD_DEFAULT_STACK_SIZE); void FLMAPI f_threadDestroy( IF_Thread ** ppThread); FLMUINT FLMAPI f_threadId( void); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_IniFile : public F_Object { virtual RCODE FLMAPI read( const char * pszFileName) = 0; virtual RCODE FLMAPI write( void) = 0; virtual FLMBOOL FLMAPI getParam( const char * pszParamName, FLMUINT * puiParamVal) = 0; virtual FLMBOOL FLMAPI getParam( const char * pszParamName, FLMBOOL * pbParamVal) = 0; virtual FLMBOOL FLMAPI getParam( const char * pszParamName, char ** ppszParamVal) = 0; virtual RCODE FLMAPI setParam( const char * pszParamName, FLMUINT uiParamVal) = 0; virtual RCODE FLMAPI setParam( const char * pszParamName, FLMBOOL bParamVal) = 0; virtual RCODE FLMAPI setParam( const char * pszParamName, const char * pszParamVal) = 0; virtual FLMBOOL FLMAPI testParam( const char * pszParamName) = 0; }; RCODE FLMAPI FlmAllocIniFile( IF_IniFile ** ppIniFile); /**************************************************************************** Desc: Serial numbers ****************************************************************************/ RCODE FLMAPI f_createSerialNumber( FLMBYTE * pszSerialNumber); #define F_SERIAL_NUM_SIZE 16 /**************************************************************************** Desc: Checksum ****************************************************************************/ void FLMAPI f_updateCRC( const void * pvBuffer, FLMUINT uiLength, FLMUINT32 * pui32CRC); FLMUINT32 FLMAPI f_calcFastChecksum( const void * pvBuffer, FLMUINT uiLength, FLMUINT * puiSum = NULL, FLMUINT * puiXOR = NULL); FLMBYTE FLMAPI f_calcPacketChecksum( const void * pvPacket, FLMUINT uiBytesToChecksum); /**************************************************************************** Desc: ****************************************************************************/ char * FLMAPI f_uwtoa( FLMUINT16 value, char * ptr); char * FLMAPI f_udtoa( FLMUINT value, char * ptr); char * FLMAPI f_wtoa( FLMINT16 value, char * ptr); char * FLMAPI f_dtoa( FLMINT value, char * ptr); char * FLMAPI f_ui64toa( FLMUINT64 value, char * ptr); char * FLMAPI f_i64toa( FLMINT64 value, char * ptr); FLMINT FLMAPI f_atoi( const char * ptr); FLMINT FLMAPI f_atol( const char * ptr); FLMINT FLMAPI f_atod( const char * ptr); FLMUINT FLMAPI f_atoud( const char * ptr, FLMBOOL bAllowUnprefixedHex = FALSE); FLMUINT64 FLMAPI f_atou64( const char * pszBuf); FLMUINT FLMAPI f_unilen( const FLMUNICODE * puzStr); FLMUNICODE * FLMAPI f_unicpy( FLMUNICODE * puzDestStr, const FLMUNICODE * puzSrcStr); FLMBOOL FLMAPI f_uniIsUpper( FLMUNICODE uChar); FLMBOOL FLMAPI f_uniIsLower( FLMUNICODE uChar); FLMBOOL FLMAPI f_uniIsAlpha( FLMUNICODE uChar); FLMBOOL FLMAPI f_uniIsDecimalDigit( FLMUNICODE uChar); FLMUNICODE FLMAPI f_uniToLower( FLMUNICODE uChar); FLMINT FLMAPI f_unicmp( const FLMUNICODE * puzStr1, const FLMUNICODE * puzStr2); FLMINT FLMAPI f_uniicmp( const FLMUNICODE * puzStr1, const FLMUNICODE * puzStr2); FLMINT FLMAPI f_uninativecmp( const FLMUNICODE * puzStr1, const char * pszStr2); FLMINT FLMAPI f_uninativencmp( const FLMUNICODE * puzStr1, const char * pszStr2, FLMUINT uiCount); RCODE FLMAPI f_nextUCS2Char( const FLMBYTE ** ppszUTF8, const FLMBYTE * pszEndOfUTF8String, FLMUNICODE * puzChar); RCODE FLMAPI f_numUCS2Chars( const FLMBYTE * pszUTF8, FLMUINT * puiNumChars); FLMBOOL FLMAPI f_isWhitespace( FLMUNICODE ucChar); FLMUNICODE FLMAPI f_convertChar( FLMUNICODE uzChar, FLMUINT uiCompareRules); RCODE FLMAPI f_wpToUnicode( FLMUINT16 ui16WPChar, FLMUNICODE * puUniChar); FLMBOOL FLMAPI f_unicodeToWP( FLMUNICODE uUniChar, FLMUINT16 * pui16WPChar); FLMBOOL FLMAPI f_depricatedUnicodeToWP( FLMUNICODE uUniChar, FLMUINT16 * pui16WPChar); FLMUINT16 FLMAPI f_wpUpper( FLMUINT16 ui16WpChar); FLMBOOL FLMAPI f_wpIsUpper( FLMUINT16 ui16WpChar); FLMUINT16 FLMAPI f_wpLower( FLMUINT16 ui16WpChar); FLMBOOL FLMAPI f_breakWPChar( FLMUINT16 ui16WpChar, FLMUINT16 * pui16BaseChar, FLMUINT16 * pui16DiacriticChar); FLMBOOL FLMAPI f_combineWPChar( FLMUINT16 * pui16WpChar, FLMUINT16 ui16BaseChar, FLMINT16 ui16DiacriticChar); FLMUINT16 FLMAPI f_wpGetCollationImp( FLMUINT16 ui16WpChar, FLMUINT uiLanguage); FINLINE FLMUINT16 FLMAPI f_wpGetCollation( FLMUINT16 ui16WpChar, FLMUINT uiLanguage) { if( uiLanguage == FLM_US_LANG) { return( gv_pui16USCollationTable[ ui16WpChar]); } return( f_wpGetCollationImp( ui16WpChar, uiLanguage)); } RCODE FLMAPI f_wpCheckDoubleCollation( IF_PosIStream * pIStream, FLMBOOL bUnicodeStream, FLMBOOL bAllowTwoIntoOne, FLMUNICODE * puzChar, FLMUNICODE * puzChar2, FLMBOOL * pbTwoIntoOne, FLMUINT uiLanguage); FLMUINT16 FLMAPI f_wpCheckDoubleCollation( FLMUINT16 * pui16WpChar, FLMBOOL * pbTwoIntoOne, const FLMBYTE ** ppucInputStr, FLMUINT uiLanguage); FLMUINT16 FLMAPI f_wpHanToZenkaku( FLMUINT16 ui16WpChar, FLMUINT16 ui16NextWpChar, FLMUINT16 * pui16Zenkaku); FLMUINT16 FLMAPI f_wpZenToHankaku( FLMUINT16 ui16WpChar, FLMUINT16 * pui16DakutenOrHandakuten); FLMUINT FLMAPI f_wpToMixed( FLMBYTE * pucWPStr, FLMUINT uiWPStrLen, const FLMBYTE * pucLowUpBitStr, FLMUINT uiLang); RCODE FLMAPI f_asiaParseSubCol( FLMBYTE * pucWPStr, FLMUINT * puiWPStrLen, FLMUINT uiMaxWPBytes, const FLMBYTE * pucSubColBuf, FLMUINT * puiSubColBitPos); RCODE FLMAPI f_asiaColStr2WPStr( const FLMBYTE * pucColStr, FLMUINT uiColStrLen, FLMBYTE * pucWPStr, FLMUINT * puiWPStrLen, FLMUINT * puiUnconvChars, FLMBOOL * pbDataTruncated, FLMBOOL * pbFirstSubstring); RCODE FLMAPI f_colStr2WPStr( const FLMBYTE * pucColStr, FLMUINT uiColStrLen, FLMBYTE * pucWPStr, FLMUINT * puiWPStrLen, FLMUINT uiLang, FLMUINT * puiUnconvChars, FLMBOOL * pbDataTruncated, FLMBOOL * pbFirstSubstring); RCODE FLMAPI f_asiaUTF8ToColText( IF_PosIStream * pIStream, FLMBYTE * pucColStr, FLMUINT * puiColStrLen, FLMBOOL bCaseInsensitive, FLMUINT * puiCollationLen, FLMUINT * puiCaseLen, FLMUINT uiCharLimit, FLMBOOL bFirstSubstring, FLMBOOL bDataTruncated, FLMBOOL * pbDataTruncated); RCODE FLMAPI f_compareUTF8Strings( const FLMBYTE * pucLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMBYTE * pucRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI f_compareUTF8Streams( IF_PosIStream * pLStream, FLMBOOL bLeftWild, IF_PosIStream * pRStream, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI f_compareUnicodeStrings( const FLMUNICODE * puzLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMUNICODE * puzRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI f_compareUnicodeStreams( IF_PosIStream * pLStream, FLMBOOL bLeftWild, IF_PosIStream * pRStream, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI f_compareCollStreams( IF_CollIStream * pLStream, IF_CollIStream * pRStream, FLMBOOL bOpIsMatch, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI f_utf8IsSubStr( const FLMBYTE * pszString, const FLMBYTE * pszSubString, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMBOOL * pbExists); RCODE FLMAPI f_readUTF8CharAsUnicode( IF_IStream * pStream, FLMUNICODE * puChar); RCODE FLMAPI f_readUTF8CharAsUTF8( IF_IStream * pIStream, FLMBYTE * pucBuf, FLMUINT * puiLen); RCODE FLMAPI f_formatUTF8Text( IF_PosIStream * pIStream, FLMBOOL bAllowEscapes, FLMUINT uiCompareRules, F_DynaBuf * pDynaBuf); RCODE FLMAPI f_getNextMetaphone( IF_IStream * pIStream, FLMUINT * puiMetaphone, FLMUINT * puiAltMetaphone = NULL); FLMUINT FLMAPI f_getSENLength( FLMBYTE ucByte); FLMUINT FLMAPI f_getSENByteCount( FLMUINT64 ui64Num); FLMUINT FLMAPI f_encodeSEN( FLMUINT64 ui64Value, FLMBYTE ** ppucBuffer, FLMUINT uiBytesWanted = 0); RCODE FLMAPI f_encodeSEN( FLMUINT64 ui64Value, FLMBYTE ** ppucBuffer, FLMBYTE * pucEnd); FLMUINT FLMAPI f_encodeSENKnownLength( FLMUINT64 ui64Value, FLMUINT uiSenLen, FLMBYTE ** ppucBuffer); RCODE FLMAPI f_decodeSEN( const FLMBYTE ** ppucBuffer, const FLMBYTE * pucEnd, FLMUINT * puiValue); RCODE FLMAPI f_decodeSEN64( const FLMBYTE ** ppucBuffer, const FLMBYTE * pucEnd, FLMUINT64 * pui64Value); RCODE FLMAPI f_readSEN( IF_IStream * pIStream, FLMUINT * puiValue, FLMUINT * puiLength = NULL); RCODE FLMAPI f_readSEN64( IF_IStream * pIStream, FLMUINT64 * pui64Value, FLMUINT * puiLength = NULL); /// Get the language string from a language code /// \ingroup language FLMUINT FLMAPI f_languageToNum( const char * pszLanguage); /// Convert a language string to the appropriate language code. /// \ingroup language void FLMAPI f_languageToStr( FLMINT iLangNum, char * pszLanguage ///< Language string that is to be converted to a code. ); /**************************************************************************** Desc: ASCII character constants and macros ****************************************************************************/ #define ASCII_TAB 0x09 #define ASCII_NEWLINE 0x0A #define ASCII_CR 0x0D #define ASCII_CTRLZ 0x1A #define ASCII_SPACE 0x20 #define ASCII_DQUOTE 0x22 #define ASCII_POUND 0x23 #define ASCII_DOLLAR 0x24 #define ASCII_SQUOTE 0x27 #define ASCII_WILDCARD 0x2A #define ASCII_PLUS 0x2B #define ASCII_COMMA 0x2C #define ASCII_DASH 0x2D #define ASCII_MINUS 0x2D #define ASCII_DOT 0x2E #define ASCII_SLASH 0x2F #define ASCII_COLON 0x3A #define ASCII_SEMICOLON 0x3B #define ASCII_EQUAL 0x3D #define ASCII_QUESTIONMARK 0x3F #define ASCII_AT 0x40 #define ASCII_BACKSLASH 0x5C #define ASCII_CARAT 0x5E #define ASCII_UNDERSCORE 0x5F #define ASCII_TILDE 0x7E #define ASCII_AMP 0x26 #define ASCII_UPPER_A 0x41 #define ASCII_UPPER_B 0x42 #define ASCII_UPPER_C 0x43 #define ASCII_UPPER_D 0x44 #define ASCII_UPPER_E 0x45 #define ASCII_UPPER_F 0x46 #define ASCII_UPPER_G 0x47 #define ASCII_UPPER_H 0x48 #define ASCII_UPPER_I 0x49 #define ASCII_UPPER_J 0x4A #define ASCII_UPPER_K 0x4B #define ASCII_UPPER_L 0x4C #define ASCII_UPPER_M 0x4D #define ASCII_UPPER_N 0x4E #define ASCII_UPPER_O 0x4F #define ASCII_UPPER_P 0x50 #define ASCII_UPPER_Q 0x51 #define ASCII_UPPER_R 0x52 #define ASCII_UPPER_S 0x53 #define ASCII_UPPER_T 0x54 #define ASCII_UPPER_U 0x55 #define ASCII_UPPER_V 0x56 #define ASCII_UPPER_W 0x57 #define ASCII_UPPER_X 0x58 #define ASCII_UPPER_Y 0x59 #define ASCII_UPPER_Z 0x5A #define ASCII_LOWER_A 0x61 #define ASCII_LOWER_B 0x62 #define ASCII_LOWER_C 0x63 #define ASCII_LOWER_D 0x64 #define ASCII_LOWER_E 0x65 #define ASCII_LOWER_F 0x66 #define ASCII_LOWER_G 0x67 #define ASCII_LOWER_H 0x68 #define ASCII_LOWER_I 0x69 #define ASCII_LOWER_J 0x6A #define ASCII_LOWER_K 0x6B #define ASCII_LOWER_L 0x6C #define ASCII_LOWER_M 0x6D #define ASCII_LOWER_N 0x6E #define ASCII_LOWER_O 0x6F #define ASCII_LOWER_P 0x70 #define ASCII_LOWER_Q 0x71 #define ASCII_LOWER_R 0x72 #define ASCII_LOWER_S 0x73 #define ASCII_LOWER_T 0x74 #define ASCII_LOWER_U 0x75 #define ASCII_LOWER_V 0x76 #define ASCII_LOWER_W 0x77 #define ASCII_LOWER_X 0x78 #define ASCII_LOWER_Y 0x79 #define ASCII_LOWER_Z 0x7A #define ASCII_ZERO 0x30 #define ASCII_ONE 0x31 #define ASCII_TWO 0x32 #define ASCII_THREE 0x33 #define ASCII_FOUR 0x34 #define ASCII_FIVE 0x35 #define ASCII_SIX 0x36 #define ASCII_SEVEN 0x37 #define ASCII_EIGHT 0x38 #define ASCII_NINE 0x39 /**************************************************************************** Desc: Native character constants and macros ****************************************************************************/ #define NATIVE_SPACE ' ' #define NATIVE_DOT '.' #define NATIVE_PLUS '+' #define NATIVE_MINUS '-' #define NATIVE_WILDCARD '*' #define NATIVE_QUESTIONMARK '?' #define NATIVE_UPPER_A 'A' #define NATIVE_UPPER_F 'F' #define NATIVE_UPPER_X 'X' #define NATIVE_UPPER_Z 'Z' #define NATIVE_LOWER_A 'a' #define NATIVE_LOWER_F 'f' #define NATIVE_LOWER_X 'x' #define NATIVE_LOWER_Z 'z' #define NATIVE_ZERO '0' #define NATIVE_NINE '9' #define f_stringToAscii( str) #define f_toascii( native) \ (native) #define f_tonative( ascii) \ (ascii) #define f_toupper( native) \ (((native) >= 'a' && (native) <= 'z') \ ? (native) - 'a' + 'A' \ : (native)) #define f_tolower( native) \ (((native) >= 'A' && (native) <= 'Z') \ ? (native) - 'A' + 'a' \ : (native)) #define f_islower( native) \ ((native) >= 'a' && (native) <= 'z') #ifndef FLM_ASCII_PLATFORM #define FLM_ASCII_PLATFORM #endif /**************************************************************************** Desc: Unicode character constants and macros ****************************************************************************/ #define FLM_UNICODE_LINEFEED ((FLMUNICODE)10) #define FLM_UNICODE_SPACE ((FLMUNICODE)32) #define FLM_UNICODE_BANG ((FLMUNICODE)33) #define FLM_UNICODE_QUOTE ((FLMUNICODE)34) #define FLM_UNICODE_POUND ((FLMUNICODE)35) #define FLM_UNICODE_DOLLAR ((FLMUNICODE)36) #define FLM_UNICODE_PERCENT ((FLMUNICODE)37) #define FLM_UNICODE_AMP ((FLMUNICODE)38) #define FLM_UNICODE_APOS ((FLMUNICODE)39) #define FLM_UNICODE_LPAREN ((FLMUNICODE)40) #define FLM_UNICODE_RPAREN ((FLMUNICODE)41) #define FLM_UNICODE_ASTERISK ((FLMUNICODE)42) #define FLM_UNICODE_PLUS ((FLMUNICODE)43) #define FLM_UNICODE_COMMA ((FLMUNICODE)44) #define FLM_UNICODE_HYPHEN ((FLMUNICODE)45) #define FLM_UNICODE_PERIOD ((FLMUNICODE)46) #define FLM_UNICODE_FSLASH ((FLMUNICODE)47) #define FLM_UNICODE_0 ((FLMUNICODE)48) #define FLM_UNICODE_1 ((FLMUNICODE)49) #define FLM_UNICODE_2 ((FLMUNICODE)50) #define FLM_UNICODE_3 ((FLMUNICODE)51) #define FLM_UNICODE_4 ((FLMUNICODE)52) #define FLM_UNICODE_5 ((FLMUNICODE)53) #define FLM_UNICODE_6 ((FLMUNICODE)54) #define FLM_UNICODE_7 ((FLMUNICODE)55) #define FLM_UNICODE_8 ((FLMUNICODE)56) #define FLM_UNICODE_9 ((FLMUNICODE)57) #define FLM_UNICODE_COLON ((FLMUNICODE)58) #define FLM_UNICODE_SEMI ((FLMUNICODE)59) #define FLM_UNICODE_LT ((FLMUNICODE)60) #define FLM_UNICODE_EQ ((FLMUNICODE)61) #define FLM_UNICODE_GT ((FLMUNICODE)62) #define FLM_UNICODE_QUEST ((FLMUNICODE)63) #define FLM_UNICODE_ATSIGN ((FLMUNICODE)64) #define FLM_UNICODE_A ((FLMUNICODE)65) #define FLM_UNICODE_B ((FLMUNICODE)66) #define FLM_UNICODE_C ((FLMUNICODE)67) #define FLM_UNICODE_D ((FLMUNICODE)68) #define FLM_UNICODE_E ((FLMUNICODE)69) #define FLM_UNICODE_F ((FLMUNICODE)70) #define FLM_UNICODE_G ((FLMUNICODE)71) #define FLM_UNICODE_H ((FLMUNICODE)72) #define FLM_UNICODE_I ((FLMUNICODE)73) #define FLM_UNICODE_J ((FLMUNICODE)74) #define FLM_UNICODE_K ((FLMUNICODE)75) #define FLM_UNICODE_L ((FLMUNICODE)76) #define FLM_UNICODE_M ((FLMUNICODE)77) #define FLM_UNICODE_N ((FLMUNICODE)78) #define FLM_UNICODE_O ((FLMUNICODE)79) #define FLM_UNICODE_P ((FLMUNICODE)80) #define FLM_UNICODE_Q ((FLMUNICODE)81) #define FLM_UNICODE_R ((FLMUNICODE)82) #define FLM_UNICODE_S ((FLMUNICODE)83) #define FLM_UNICODE_T ((FLMUNICODE)84) #define FLM_UNICODE_U ((FLMUNICODE)85) #define FLM_UNICODE_V ((FLMUNICODE)86) #define FLM_UNICODE_W ((FLMUNICODE)87) #define FLM_UNICODE_X ((FLMUNICODE)88) #define FLM_UNICODE_Y ((FLMUNICODE)89) #define FLM_UNICODE_Z ((FLMUNICODE)90) #define FLM_UNICODE_LBRACKET ((FLMUNICODE)91) #define FLM_UNICODE_BACKSLASH ((FLMUNICODE)92) #define FLM_UNICODE_RBRACKET ((FLMUNICODE)93) #define FLM_UNICODE_UNDERSCORE ((FLMUNICODE)95) #define FLM_UNICODE_a ((FLMUNICODE)97) #define FLM_UNICODE_b ((FLMUNICODE)98) #define FLM_UNICODE_c ((FLMUNICODE)99) #define FLM_UNICODE_d ((FLMUNICODE)100) #define FLM_UNICODE_e ((FLMUNICODE)101) #define FLM_UNICODE_f ((FLMUNICODE)102) #define FLM_UNICODE_g ((FLMUNICODE)103) #define FLM_UNICODE_h ((FLMUNICODE)104) #define FLM_UNICODE_i ((FLMUNICODE)105) #define FLM_UNICODE_j ((FLMUNICODE)106) #define FLM_UNICODE_k ((FLMUNICODE)107) #define FLM_UNICODE_l ((FLMUNICODE)108) #define FLM_UNICODE_m ((FLMUNICODE)109) #define FLM_UNICODE_n ((FLMUNICODE)110) #define FLM_UNICODE_o ((FLMUNICODE)111) #define FLM_UNICODE_p ((FLMUNICODE)112) #define FLM_UNICODE_q ((FLMUNICODE)113) #define FLM_UNICODE_r ((FLMUNICODE)114) #define FLM_UNICODE_s ((FLMUNICODE)115) #define FLM_UNICODE_t ((FLMUNICODE)116) #define FLM_UNICODE_u ((FLMUNICODE)117) #define FLM_UNICODE_v ((FLMUNICODE)118) #define FLM_UNICODE_w ((FLMUNICODE)119) #define FLM_UNICODE_x ((FLMUNICODE)120) #define FLM_UNICODE_y ((FLMUNICODE)121) #define FLM_UNICODE_z ((FLMUNICODE)122) #define FLM_UNICODE_LBRACE ((FLMUNICODE)123) #define FLM_UNICODE_PIPE ((FLMUNICODE)124) #define FLM_UNICODE_RBRACE ((FLMUNICODE)125) #define FLM_UNICODE_TILDE ((FLMUNICODE)126) #define FLM_UNICODE_C_CEDILLA ((FLMUNICODE)199) #define FLM_UNICODE_N_TILDE ((FLMUNICODE)209) #define FLM_UNICODE_c_CEDILLA ((FLMUNICODE)231) #define FLM_UNICODE_n_TILDE ((FLMUNICODE)241) FINLINE FLMBOOL f_isvowel( FLMUNICODE uChar) { uChar = f_uniToLower( uChar); if( uChar == FLM_UNICODE_a || uChar == FLM_UNICODE_e || uChar == FLM_UNICODE_i || uChar == FLM_UNICODE_o || uChar == FLM_UNICODE_u || uChar == FLM_UNICODE_y) { return( TRUE); } return( FALSE); } /**************************************************************************** Desc: Endian macros ****************************************************************************/ FINLINE FLMUINT16 f_littleEndianToUINT16( const FLMBYTE * pucBuf) { FLMUINT16 ui16Val = 0; ui16Val |= ((FLMUINT16)pucBuf[ 1]) << 8; ui16Val |= ((FLMUINT16)pucBuf[ 0]); return( ui16Val); } FINLINE FLMUINT32 f_littleEndianToUINT32( const FLMBYTE * pucBuf) { FLMUINT32 ui32Val = 0; ui32Val |= ((FLMUINT32)pucBuf[ 3]) << 24; ui32Val |= ((FLMUINT32)pucBuf[ 2]) << 16; ui32Val |= ((FLMUINT32)pucBuf[ 1]) << 8; ui32Val |= ((FLMUINT32)pucBuf[ 0]); return( ui32Val); } FINLINE FLMUINT64 f_littleEndianToUINT64( const FLMBYTE * pucBuf) { FLMUINT64 ui64Val = 0; ui64Val |= ((FLMUINT64)pucBuf[ 7]) << 56; ui64Val |= ((FLMUINT64)pucBuf[ 6]) << 48; ui64Val |= ((FLMUINT64)pucBuf[ 5]) << 40; ui64Val |= ((FLMUINT64)pucBuf[ 4]) << 32; ui64Val |= ((FLMUINT64)pucBuf[ 3]) << 24; ui64Val |= ((FLMUINT64)pucBuf[ 2]) << 16; ui64Val |= ((FLMUINT64)pucBuf[ 1]) << 8; ui64Val |= ((FLMUINT64)pucBuf[ 0]); return( ui64Val); } FINLINE void f_UINT16ToLittleEndian( FLMUINT16 ui16Num, FLMBYTE * pucBuf) { pucBuf[ 1] = (FLMBYTE) (ui16Num >> 8); pucBuf[ 0] = (FLMBYTE) (ui16Num); } FINLINE void f_UINT32ToLittleEndian( FLMUINT32 ui32Num, FLMBYTE * pucBuf) { pucBuf[ 3] = (FLMBYTE) (ui32Num >> 24); pucBuf[ 2] = (FLMBYTE) (ui32Num >> 16); pucBuf[ 1] = (FLMBYTE) (ui32Num >> 8); pucBuf[ 0] = (FLMBYTE) (ui32Num); } FINLINE void f_UINT64ToLittleEndian( FLMUINT64 ui64Num, FLMBYTE * pucBuf) { pucBuf[ 7] = (FLMBYTE) (ui64Num >> 56); pucBuf[ 6] = (FLMBYTE) (ui64Num >> 48); pucBuf[ 5] = (FLMBYTE) (ui64Num >> 40); pucBuf[ 4] = (FLMBYTE) (ui64Num >> 32); pucBuf[ 3] = (FLMBYTE) (ui64Num >> 24); pucBuf[ 2] = (FLMBYTE) (ui64Num >> 16); pucBuf[ 1] = (FLMBYTE) (ui64Num >> 8); pucBuf[ 0] = (FLMBYTE) (ui64Num); } FINLINE FLMUINT16 f_bigEndianToUINT16( const FLMBYTE * pucBuf) { FLMUINT16 ui16Val = 0; ui16Val |= ((FLMUINT16)pucBuf[ 0]) << 8; ui16Val |= ((FLMUINT16)pucBuf[ 1]); return( ui16Val); } FINLINE FLMUINT32 f_bigEndianToUINT32( const FLMBYTE * pucBuf) { FLMUINT32 ui32Val = 0; ui32Val |= ((FLMUINT32)pucBuf[ 0]) << 24; ui32Val |= ((FLMUINT32)pucBuf[ 1]) << 16; ui32Val |= ((FLMUINT32)pucBuf[ 2]) << 8; ui32Val |= ((FLMUINT32)pucBuf[ 3]); return( ui32Val); } FINLINE FLMUINT64 f_bigEndianToUINT64( const FLMBYTE * pucBuf) { FLMUINT64 ui64Val = 0; ui64Val |= ((FLMUINT64)pucBuf[ 0]) << 56; ui64Val |= ((FLMUINT64)pucBuf[ 1]) << 48; ui64Val |= ((FLMUINT64)pucBuf[ 2]) << 40; ui64Val |= ((FLMUINT64)pucBuf[ 3]) << 32; ui64Val |= ((FLMUINT64)pucBuf[ 4]) << 24; ui64Val |= ((FLMUINT64)pucBuf[ 5]) << 16; ui64Val |= ((FLMUINT64)pucBuf[ 6]) << 8; ui64Val |= ((FLMUINT64)pucBuf[ 7]); return( ui64Val); } FINLINE FLMINT16 f_bigEndianToINT16( const FLMBYTE * pucBuf) { FLMINT16 i16Val = 0; i16Val |= ((FLMINT16)pucBuf[ 0]) << 8; i16Val |= ((FLMINT16)pucBuf[ 1]); return( i16Val); } FINLINE FLMINT32 f_bigEndianToINT32( const FLMBYTE * pucBuf) { FLMINT32 i32Val = 0; i32Val |= ((FLMINT32)pucBuf[ 0]) << 24; i32Val |= ((FLMINT32)pucBuf[ 1]) << 16; i32Val |= ((FLMINT32)pucBuf[ 2]) << 8; i32Val |= ((FLMINT32)pucBuf[ 3]); return( i32Val); } FINLINE FLMINT64 f_bigEndianToINT64( const FLMBYTE * pucBuf) { FLMINT64 i64Val = 0; i64Val |= ((FLMINT64)pucBuf[ 0]) << 56; i64Val |= ((FLMINT64)pucBuf[ 1]) << 48; i64Val |= ((FLMINT64)pucBuf[ 2]) << 40; i64Val |= ((FLMINT64)pucBuf[ 3]) << 32; i64Val |= ((FLMINT64)pucBuf[ 4]) << 24; i64Val |= ((FLMINT64)pucBuf[ 5]) << 16; i64Val |= ((FLMINT64)pucBuf[ 6]) << 8; i64Val |= ((FLMINT64)pucBuf[ 7]); return( i64Val); } FINLINE void f_UINT32ToBigEndian( FLMUINT32 ui32Num, FLMBYTE * pucBuf) { pucBuf[ 0] = (FLMBYTE) (ui32Num >> 24); pucBuf[ 1] = (FLMBYTE) (ui32Num >> 16); pucBuf[ 2] = (FLMBYTE) (ui32Num >> 8); pucBuf[ 3] = (FLMBYTE) (ui32Num); } FINLINE void f_INT32ToBigEndian( FLMINT32 i32Num, FLMBYTE * pucBuf) { pucBuf[ 0] = (FLMBYTE) (i32Num >> 24); pucBuf[ 1] = (FLMBYTE) (i32Num >> 16); pucBuf[ 2] = (FLMBYTE) (i32Num >> 8); pucBuf[ 3] = (FLMBYTE) (i32Num); } FINLINE void f_INT64ToBigEndian( FLMINT64 i64Num, FLMBYTE * pucBuf) { pucBuf[ 0] = (FLMBYTE) (i64Num >> 56); pucBuf[ 1] = (FLMBYTE) (i64Num >> 48); pucBuf[ 2] = (FLMBYTE) (i64Num >> 40); pucBuf[ 3] = (FLMBYTE) (i64Num >> 32); pucBuf[ 4] = (FLMBYTE) (i64Num >> 24); pucBuf[ 5] = (FLMBYTE) (i64Num >> 16); pucBuf[ 6] = (FLMBYTE) (i64Num >> 8); pucBuf[ 7] = (FLMBYTE) (i64Num); } FINLINE void f_UINT64ToBigEndian( FLMUINT64 ui64Num, FLMBYTE * pucBuf) { pucBuf[ 0] = (FLMBYTE) (ui64Num >> 56); pucBuf[ 1] = (FLMBYTE) (ui64Num >> 48); pucBuf[ 2] = (FLMBYTE) (ui64Num >> 40); pucBuf[ 3] = (FLMBYTE) (ui64Num >> 32); pucBuf[ 4] = (FLMBYTE) (ui64Num >> 24); pucBuf[ 5] = (FLMBYTE) (ui64Num >> 16); pucBuf[ 6] = (FLMBYTE) (ui64Num >> 8); pucBuf[ 7] = (FLMBYTE) (ui64Num); } FINLINE void f_INT16ToBigEndian( FLMINT16 i16Num, FLMBYTE * pucBuf) { pucBuf[ 0] = (FLMBYTE) (i16Num >> 8); pucBuf[ 1] = (FLMBYTE) (i16Num); } FINLINE void f_UINT16ToBigEndian( FLMUINT16 ui16Num, FLMBYTE * pucBuf) { pucBuf[ 0] = (FLMBYTE) (ui16Num >> 8); pucBuf[ 1] = (FLMBYTE) (ui16Num); } #if defined( FLM_STRICT_ALIGNMENT) || defined( FLM_BIG_ENDIAN) FINLINE FLMUINT16 FB2UW( const FLMBYTE * pucBuf) { FLMUINT16 ui16Val = 0; ui16Val |= ((FLMUINT16)pucBuf[ 1]) << 8; ui16Val |= ((FLMUINT16)pucBuf[ 0]); return( ui16Val); } FINLINE FLMUINT32 FB2UD( const FLMBYTE * pucBuf) { FLMUINT32 ui32Val = 0; ui32Val |= ((FLMUINT32)pucBuf[ 3]) << 24; ui32Val |= ((FLMUINT32)pucBuf[ 2]) << 16; ui32Val |= ((FLMUINT32)pucBuf[ 1]) << 8; ui32Val |= ((FLMUINT32)pucBuf[ 0]); return( ui32Val); } FINLINE FLMUINT64 FB2U64( const FLMBYTE * pucBuf) { FLMUINT64 ui64Val = 0; ui64Val |= ((FLMUINT64)pucBuf[ 7]) << 56; ui64Val |= ((FLMUINT64)pucBuf[ 6]) << 48; ui64Val |= ((FLMUINT64)pucBuf[ 5]) << 40; ui64Val |= ((FLMUINT64)pucBuf[ 4]) << 32; ui64Val |= ((FLMUINT64)pucBuf[ 3]) << 24; ui64Val |= ((FLMUINT64)pucBuf[ 2]) << 16; ui64Val |= ((FLMUINT64)pucBuf[ 1]) << 8; ui64Val |= ((FLMUINT64)pucBuf[ 0]); return( ui64Val); } FINLINE void UW2FBA( FLMUINT uiNum, FLMBYTE * pucBuf) { f_assert( uiNum <= FLM_MAX_UINT16); pucBuf[ 1] = (FLMBYTE) (uiNum >> 8); pucBuf[ 0] = (FLMBYTE) (uiNum); } FINLINE void UD2FBA( FLMUINT uiNum, FLMBYTE * pucBuf) { f_assert( uiNum <= FLM_MAX_UINT32); pucBuf[ 3] = (FLMBYTE) (uiNum >> 24); pucBuf[ 2] = (FLMBYTE) (uiNum >> 16); pucBuf[ 1] = (FLMBYTE) (uiNum >> 8); pucBuf[ 0] = (FLMBYTE) (uiNum); } FINLINE void U642FBA( FLMUINT64 ui64Num, FLMBYTE * pucBuf) { pucBuf[ 7] = (FLMBYTE) (ui64Num >> 56); pucBuf[ 6] = (FLMBYTE) (ui64Num >> 48); pucBuf[ 5] = (FLMBYTE) (ui64Num >> 40); pucBuf[ 4] = (FLMBYTE) (ui64Num >> 32); pucBuf[ 3] = (FLMBYTE) (ui64Num >> 24); pucBuf[ 2] = (FLMBYTE) (ui64Num >> 16); pucBuf[ 1] = (FLMBYTE) (ui64Num >> 8); pucBuf[ 0] = (FLMBYTE) (ui64Num); } #else #define FB2UW( fbp) \ (*((FLMUINT16 *)(fbp))) #define FB2UD( fbp) \ (*((FLMUINT32 *)(fbp))) #define FB2U64( fbp) \ (*((FLMUINT64 *)(fbp))) #define UW2FBA( uw, fbp) \ (*((FLMUINT16 *)(fbp)) = ((FLMUINT16) (uw))) #define UD2FBA( uw, fbp) \ (*((FLMUINT32 *)(fbp)) = ((FLMUINT32) (uw))) #define U642FBA( uw, fbp) \ (*((FLMUINT64 *)(fbp)) = ((FLMUINT64) (uw))) #endif #ifdef FLM_BIG_ENDIAN #define LO( wrd) \ (*((FLMUINT8 *)&wrd + 1)) #define HI( wrd) \ (*(FLMUINT8 *)&wrd) #else #define LO(wrd) \ (*(FLMUINT8 *)&wrd) #define HI(wrd) \ (*((FLMUINT8 *)&wrd + 1)) #endif /**************************************************************************** Desc: File path functions and macros ****************************************************************************/ #if defined( FLM_WIN) || defined( FLM_NLM) #define FWSLASH '/' #define SLASH '\\' #define SSLASH "\\" #define COLON ':' #define PERIOD '.' #define PARENT_DIR ".." #define CURRENT_DIR "." #else #ifndef FWSLASH #define FWSLASH '/' #endif #ifndef SLASH #define SLASH '/' #endif #ifndef SSLASH #define SSLASH "/" #endif #ifndef COLON #define COLON ':' #endif #ifndef PERIOD #define PERIOD '.' #endif #ifndef PARENT_DIR #define PARENT_DIR ".." #endif #ifndef CURRENT_DIR #define CURRENT_DIR "." #endif #endif FLMUINT FLMAPI f_getMaxFileSize( void); /**************************************************************************** Desc: CPU release and sleep functions ****************************************************************************/ void FLMAPI f_yieldCPU( void); void FLMAPI f_sleep( FLMUINT uiMilliseconds); /**************************************************************************** Desc: Time, date, timestamp functions ****************************************************************************/ typedef struct { FLMUINT16 year; FLMBYTE month; FLMBYTE day; } F_DATE; typedef struct { FLMBYTE hour; FLMBYTE minute; FLMBYTE second; FLMBYTE hundredth; } F_TIME; typedef struct { FLMUINT16 year; FLMBYTE month; FLMBYTE day; FLMBYTE hour; FLMBYTE minute; FLMBYTE second; FLMBYTE hundredth; } F_TMSTAMP; #define f_timeIsLeapYear(year) \ ((((year) & 0x03) == 0) && (((year) % 100) != 0) || (((year) % 400) == 0)) void f_timeGetSeconds( FLMUINT * puiSeconds); void f_timeGetTimeStamp( F_TMSTAMP * pTimeStamp); FLMINT f_timeGetLocalOffset( void); void f_timeSecondsToDate( FLMUINT uiSeconds, F_TMSTAMP * pTimeStamp); void f_timeDateToSeconds( F_TMSTAMP * pTimeStamp, FLMUINT * puiSeconds); FLMINT f_timeCompareTimeStamps( F_TMSTAMP * pTimeStamp1, F_TMSTAMP * pTimeStamp2, FLMUINT flag); FINLINE FLMUINT f_localTimeToUTC( FLMUINT uiSeconds) { return( uiSeconds + f_timeGetLocalOffset()); } FLMUINT FLMAPI FLM_GET_TIMER( void); FLMUINT FLMAPI FLM_ELAPSED_TIME( FLMUINT uiLaterTime, FLMUINT uiEarlierTime); FLMUINT FLMAPI FLM_SECS_TO_TIMER_UNITS( FLMUINT uiSeconds); FLMUINT FLMAPI FLM_TIMER_UNITS_TO_SECS( FLMUINT uiTU); FLMUINT FLM_TIMER_UNITS_TO_MILLI( FLMUINT uiTU); FLMUINT FLM_MILLI_TO_TIMER_UNITS( FLMUINT uiMilliSeconds); void FLMAPI f_addElapsedTime( F_TMSTAMP * pStartTime, FLMUINT64 * pui64ElapMilli); /**************************************************************************** Desc: Quick sort ****************************************************************************/ typedef FLMINT (FLMAPI * F_SORT_COMPARE_FUNC)( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2); typedef void (FLMAPI * F_SORT_SWAP_FUNC)( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2); FLMINT FLMAPI f_qsortUINTCompare( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2); void FLMAPI f_qsortUINTSwap( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2); void FLMAPI f_qsort( void * pvBuffer, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds, F_SORT_COMPARE_FUNC fnCompare, F_SORT_SWAP_FUNC fnSwap); /**************************************************************************** Desc: Environment ****************************************************************************/ void FLMAPI f_getenv( const char * pszKey, FLMBYTE * pszBuffer, FLMUINT uiBufferSize, FLMUINT * puiValueLen = NULL); /**************************************************************************** Desc: f_sprintf ****************************************************************************/ FLMINT FLMAPI f_vsprintf( char * pszDestStr, const char * pszFormat, f_va_list * args); FLMINT FLMAPI f_sprintf( char * pszDestStr, const char * pszFormat, ...); FLMINT FLMAPI f_printf( const char * pszFormat, ...); /**************************************************************************** Desc: Memory copying, moving, setting ****************************************************************************/ void * FLMAPI f_memcpy( void * pvDest, const void * pvSrc, FLMSIZET uiLength); void * FLMAPI f_memmove( void * pvDest, const void * pvSrc, FLMSIZET uiLength); void * FLMAPI f_memset( void * pvDest, unsigned char ucByte, FLMSIZET uiLength); FLMINT FLMAPI f_memcmp( const void * pvStr1, const void * pvStr2, FLMSIZET uiLength); char * FLMAPI f_strcat( char * pszDest, const char * pszSrc); char * FLMAPI f_strncat( char * pszDest, const char * pszSrc, FLMSIZET uiLength); char * FLMAPI f_strchr( const char * pszStr, unsigned char ucByte); char * FLMAPI f_strrchr( const char * pszStr, unsigned char ucByte); char * FLMAPI f_strstr( const char * pszStr, const char * pszSearch); char * FLMAPI f_strupr( char * pszStr); FLMINT FLMAPI f_strcmp( const char * pszStr1, const char * pszStr2); FLMINT FLMAPI f_strncmp( const char * pszStr1, const char * pszStr2, FLMSIZET uiLength); FLMINT FLMAPI f_stricmp( const char * pszStr1, const char * pszStr2); FLMINT FLMAPI f_strnicmp( const char * pszStr1, const char * pszStr2, FLMSIZET uiLength); char * FLMAPI f_strcpy( char * pszDest, const char * pszSrc); char * FLMAPI f_strncpy( char * pszDest, const char * pszSrc, FLMSIZET uiLength); FLMUINT FLMAPI f_strlen( const char * pszStr); RCODE FLMAPI f_strdup( const char * pszSrc, char ** ppszDup); RCODE FLMAPI f_getCharFromUTF8Buf( const FLMBYTE ** ppucBuf, const FLMBYTE * pucEnd, FLMUNICODE * puChar); RCODE FLMAPI f_uni2UTF8( FLMUNICODE uChar, FLMBYTE * pucBuf, FLMUINT * puiBufSize); RCODE FLMAPI f_getUTF8Length( const FLMBYTE * pucBuf, FLMUINT uiBufLen, FLMUINT * puiBytes, FLMUINT * puiChars); RCODE FLMAPI f_getUTF8CharFromUTF8Buf( FLMBYTE ** ppucBuf, FLMBYTE * pucEnd, FLMBYTE * pucDestBuf, FLMUINT * puiLen); RCODE FLMAPI f_unicode2UTF8( FLMUNICODE * puzStr, FLMUINT uiStrLen, FLMBYTE * pucBuf, FLMUINT * puiBufLength); FLMBYTE FLMAPI f_getBase24DigitChar( FLMBYTE ucValue); #define shiftN(data,size,distance) \ f_memmove((FLMBYTE *)(data) + (FLMINT)(distance), \ (FLMBYTE *)(data), (unsigned)(size)) /*************************************************************************** Desc: ***************************************************************************/ class FLMEXP F_Printf : public F_Object { public: #define MAX_LOG_BUF_CHARS 255 F_Printf() { } virtual ~F_Printf() { } FLMINT FLMAPI strvPrintf( char * pszDestStr, const char * pszFormat, f_va_list * args); FLMINT FLMAPI strPrintf( char * pszDestStr, const char * pszFormat, ...); FLMINT FLMAPI logvPrintf( IF_LogMessageClient * pLogMsg, const char * pszFormat, f_va_list * args); FLMINT FLMAPI logPrintf( IF_LogMessageClient * pLogMsg, const char * pszFormat, ...); private: void processFieldInfo( const char ** ppszFormat, FLMUINT * puiWidth, FLMUINT * puiPrecision, FLMUINT * puiFlags, f_va_list * args); void stringFormatter( char cFormatChar, FLMUINT uiWidth, FLMUINT uiPrecision, FLMUINT uiFlags, f_va_list * args); void colorFormatter( char cFormatChar, eColorType eColor, FLMUINT uiFlags); void charFormatter( char cFormatChar, f_va_list * args); void errorFormatter( f_va_list * args); void notHandledFormatter( void); void numberFormatter( char cFormatChar, FLMUINT uiWidth, FLMUINT uiPrecision, FLMUINT uiFlags, f_va_list * args); void parseArgs( const char * pszFormat, f_va_list * args); void processFormatString( FLMUINT uiLen, ...); FLMUINT printNumber( FLMUINT64 ui64Val, FLMUINT uiBase, FLMBOOL bUpperCase, FLMBOOL bCommas, char * pszBuf); void outputLogBuffer( void); FINLINE void outputChar( char cChar) { if (!m_pLogMsg) { *m_pszDestStr++ = cChar; } else { m_szLogBuf [m_uiCharOffset++] = cChar; m_uiNumLogChars++; if (m_uiCharOffset == MAX_LOG_BUF_CHARS) { outputLogBuffer(); } } } FINLINE void memsetChar( char cChar, FLMUINT uiCount) { if (!m_pLogMsg) { f_memset( m_pszDestStr, cChar, uiCount); m_pszDestStr += uiCount; } else { FLMUINT uiTmpCount; while (uiCount) { uiTmpCount = uiCount; if (m_uiCharOffset + uiTmpCount > MAX_LOG_BUF_CHARS) { uiTmpCount = MAX_LOG_BUF_CHARS - m_uiCharOffset; } f_memset( &m_szLogBuf [m_uiCharOffset], cChar, uiTmpCount); m_uiCharOffset += uiTmpCount; m_uiNumLogChars += uiTmpCount; uiCount -= uiTmpCount; if (m_uiCharOffset == MAX_LOG_BUF_CHARS) { outputLogBuffer(); } } } } FINLINE void outputStr( const char * pszStr, FLMUINT uiLen) { if (!m_pLogMsg) { f_memcpy( m_pszDestStr, pszStr, uiLen); m_pszDestStr += uiLen; } else { FLMUINT uiTmpLen; while (uiLen) { uiTmpLen = uiLen; if (m_uiCharOffset + uiTmpLen > MAX_LOG_BUF_CHARS) { uiTmpLen = MAX_LOG_BUF_CHARS - m_uiCharOffset; } f_memcpy( &m_szLogBuf [m_uiCharOffset], pszStr, uiTmpLen); m_uiCharOffset += uiTmpLen; m_uiNumLogChars += uiTmpLen; uiLen -= uiTmpLen; pszStr += uiTmpLen; if (m_uiCharOffset == MAX_LOG_BUF_CHARS) { outputLogBuffer(); } } } } // Variables used to do the printf stuff char m_szLogBuf [MAX_LOG_BUF_CHARS + 1]; FLMUINT m_uiNumLogChars; FLMUINT m_uiCharOffset; char * m_pszDestStr; IF_LogMessageClient * m_pLogMsg; eColorType m_eCurrentForeColor; eColorType m_eCurrentBackColor; }; /**************************************************************************** Desc: XML ****************************************************************************/ flminterface FLMEXP IF_XML : public F_Object { public: virtual FLMBOOL FLMAPI isPubidChar( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isQuoteChar( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isWhitespace( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isExtender( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isCombiningChar( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isNameChar( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isNCNameChar( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isIdeographic( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isBaseChar( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isDigit( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isLetter( FLMUNICODE uChar) = 0; virtual FLMBOOL FLMAPI isNameValid( FLMUNICODE * puzName, FLMBYTE * pszName) = 0; }; RCODE FLMAPI FlmGetXMLObject( IF_XML ** ppXmlObject); /**************************************************************************** Desc: Name table ****************************************************************************/ flminterface FLMEXP IF_NameTable : public F_Object { virtual void FLMAPI clearTable( FLMUINT uiPoolBlockSize) = 0; virtual RCODE FLMAPI getNextTagTypeAndNumOrder( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName = NULL, char * pszTagName = NULL, FLMUINT uiNameBufSize = 0, FLMUINT * puiTagNum = NULL, FLMUINT * puiDataType = NULL, FLMUNICODE * puzNamespace = NULL, FLMUINT uiNamespaceBufSize = 0, FLMBOOL bTruncatedNamesOk = TRUE) = 0; virtual RCODE FLMAPI getNextTagTypeAndNameOrder( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName = NULL, char * pszTagName = NULL, FLMUINT uiNameBufSize = 0, FLMUINT * puiTagNum = NULL, FLMUINT * puiDataType = NULL, FLMUNICODE * puzNamespace = NULL, FLMUINT uiNamespaceBufSize = 0, FLMBOOL bTruncatedNamesOk = TRUE) = 0; virtual RCODE FLMAPI getFromTagTypeAndName( FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, FLMBOOL bMatchNamespace, const FLMUNICODE * puzNamespace = NULL, FLMUINT * puiTagNum = NULL, FLMUINT * puiDataType = NULL) = 0; virtual RCODE FLMAPI getFromTagTypeAndNum( FLMUINT uiType, FLMUINT uiTagNum, FLMUNICODE * puzTagName = NULL, char * pszTagName = NULL, FLMUINT * puiNameBufSize = NULL, FLMUINT * puiDataType = NULL, FLMUNICODE * puzNamespace = NULL, char * pszNamespace = NULL, FLMUINT * puiNamespaceBufSize = NULL, FLMBOOL bTruncatedNamesOk = TRUE) = 0; virtual RCODE FLMAPI addTag( FLMUINT uiType, FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiDataType = 0, FLMUNICODE * puzNamespace = NULL, FLMBOOL bCheckDuplicates = TRUE) = 0; virtual void FLMAPI removeTag( FLMUINT uiType, FLMUINT uiTagNum) = 0; virtual RCODE FLMAPI cloneNameTable( IF_NameTable ** ppNewNameTable) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_DeleteStatus : public F_Object { virtual RCODE FLMAPI reportDelete( FLMUINT uiBlocksDeleted, FLMUINT uiBlockSize) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_Relocator : public F_Object { virtual void FLMAPI relocate( void * pvOldAlloc, void * pvNewAlloc) = 0; virtual FLMBOOL FLMAPI canRelocate( void * pvOldAlloc) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ typedef void (FLMAPI * F_ALLOC_INIT_FUNC)( void * pvAlloc, FLMUINT uiSize); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_SlabManager : public F_Object { virtual RCODE FLMAPI setup( FLMUINT uiPreallocSize) = 0; virtual RCODE FLMAPI allocSlab( void ** ppSlab) = 0; virtual void FLMAPI freeSlab( void ** ppSlab) = 0; virtual RCODE FLMAPI resize( FLMUINT uiNumBytes, FLMBOOL bPreallocate, FLMUINT * puiActualSize = NULL) = 0; virtual void FLMAPI incrementTotalBytesAllocated( FLMUINT uiCount) = 0; virtual void FLMAPI decrementTotalBytesAllocated( FLMUINT uiCount) = 0; virtual FLMUINT FLMAPI getSlabSize( void) = 0; virtual FLMUINT FLMAPI getTotalSlabs( void) = 0; virtual FLMUINT FLMAPI totalBytesAllocated( void) = 0; virtual FLMUINT FLMAPI getTotalSlabBytesAllocated( void) = 0; virtual FLMUINT FLMAPI availSlabs( void) = 0; }; RCODE FLMAPI FlmAllocSlabManager( IF_SlabManager ** ppSlabManager); /**************************************************************************** Desc: Class to provide an efficient means of providing many allocations of a fixed size. ****************************************************************************/ flminterface FLMEXP IF_FixedAlloc : public F_Object { virtual RCODE FLMAPI setup( FLMBOOL bMultiThreaded, IF_SlabManager * pSlabManager, IF_Relocator * pDefaultRelocator, FLMUINT uiCellSize, FLM_SLAB_USAGE * pUsageStats, FLMUINT * puiTotalBytesAllocated) = 0; virtual void * FLMAPI allocCell( IF_Relocator * pRelocator, void * pvInitialData, FLMUINT uiDataSize) = 0; virtual void * FLMAPI allocCell( IF_Relocator * pRelocator, F_ALLOC_INIT_FUNC fnAllocInit) = 0; virtual void FLMAPI freeCell( void * ptr) = 0; virtual void FLMAPI freeUnused( void) = 0; virtual void FLMAPI freeAll( void) = 0; virtual FLMUINT FLMAPI getCellSize( void) = 0; virtual void FLMAPI defragmentMemory( void) = 0; }; RCODE FLMAPI FlmAllocFixedAllocator( IF_FixedAlloc ** ppFixedAllocator); /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_BlockAlloc : public F_Object { virtual RCODE FLMAPI setup( FLMBOOL bMultiThreaded, IF_SlabManager * pSlabManager, IF_Relocator * pRelocator, FLMUINT uiBlockSize, FLM_SLAB_USAGE * pUsageStats, FLMUINT * puiTotalBytesAllocated) = 0; virtual RCODE FLMAPI allocBlock( void ** ppvBlock) = 0; virtual void FLMAPI freeBlock( void ** ppvBlock) = 0; virtual void FLMAPI freeUnused( void) = 0; virtual void FLMAPI freeAll( void) = 0; virtual void FLMAPI defragmentMemory( void) = 0; }; RCODE FLMAPI FlmAllocBlockAllocator( IF_BlockAlloc ** ppBlockAllocator); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_BufferAlloc : public F_Object { virtual RCODE FLMAPI setup( FLMBOOL bMultiThreaded, IF_SlabManager * pSlabManager, IF_Relocator * pDefaultRelocator, FLM_SLAB_USAGE * pUsageStats, FLMUINT * puiTotalBytesAllocated) = 0; virtual RCODE FLMAPI allocBuf( IF_Relocator * pRelocator, FLMUINT uiSize, void * pvInitialData, FLMUINT uiDataSize, FLMBYTE ** ppucBuffer, FLMBOOL * pbAllocatedOnHeap = NULL) = 0; virtual RCODE FLMAPI allocBuf( IF_Relocator * pRelocator, FLMUINT uiSize, F_ALLOC_INIT_FUNC fnAllocInit, FLMBYTE ** ppucBuffer, FLMBOOL * pbAllocatedOnHeap = NULL) = 0; virtual RCODE FLMAPI reallocBuf( IF_Relocator * pRelocator, FLMUINT uiOldSize, FLMUINT uiNewSize, void * pvInitialData, FLMUINT uiDataSize, FLMBYTE ** ppucBuffer, FLMBOOL * pbAllocatedOnHeap = NULL) = 0; virtual void FLMAPI freeBuf( FLMUINT uiSize, FLMBYTE ** ppucBuffer) = 0; virtual FLMUINT FLMAPI getTrueSize( FLMUINT uiSize, FLMBYTE * pucBuffer) = 0; virtual FLMUINT FLMAPI getMaxCellSize( void) = 0; virtual void FLMAPI defragmentMemory( void) = 0; }; RCODE FLMAPI FlmAllocBufferAllocator( IF_BufferAlloc ** ppBufferAllocator); /**************************************************************************** Desc: ****************************************************************************/ flminterface FLMEXP IF_MultiAlloc : public F_Object { virtual RCODE FLMAPI setup( FLMBOOL bMultiThreaded, IF_SlabManager * pSlabManager, IF_Relocator * pDefaultRelocator, FLMUINT * puiCellSizes, FLM_SLAB_USAGE * pUsageStats, FLMUINT * puiTotalBytesAllocated) = 0; virtual RCODE FLMAPI allocBuf( IF_Relocator * pRelocator, FLMUINT uiSize, FLMBYTE ** ppucBuffer) = 0; virtual RCODE FLMAPI allocBuf( IF_Relocator * pRelocator, FLMUINT uiSize, F_ALLOC_INIT_FUNC fnAllocInit, FLMBYTE ** ppucBuffer) = 0; virtual RCODE FLMAPI reallocBuf( IF_Relocator * pRelocator, FLMUINT uiNewSize, FLMBYTE ** ppucBuffer) = 0; virtual void FLMAPI freeBuf( FLMBYTE ** ppucBuffer) = 0; virtual void FLMAPI defragmentMemory( void) = 0; virtual FLMUINT FLMAPI getTrueSize( FLMBYTE * pucBuffer) = 0; }; RCODE FLMAPI FlmAllocMultiAllocator( IF_MultiAlloc ** ppMultiAllocator); /**************************************************************************** Desc: Block ****************************************************************************/ flminterface FLMEXP IF_Block : public F_Object { }; /**************************************************************************** Desc: Block manager ****************************************************************************/ flminterface FLMEXP IF_BlockMgr : public F_Object { virtual FLMUINT FLMAPI getBlockSize( void) = 0; virtual RCODE FLMAPI getBlock( FLMUINT32 ui32BlockId, IF_Block ** ppBlock, FLMBYTE ** ppucBlock) = 0; virtual RCODE FLMAPI createBlock( IF_Block ** ppBlock, FLMBYTE ** ppucBlock, FLMUINT32 * pui32BlockId) = 0; virtual RCODE FLMAPI freeBlock( IF_Block ** ppBlock, FLMBYTE ** ppucBlock) = 0; virtual RCODE FLMAPI prepareForUpdate( IF_Block ** ppBlock, FLMBYTE ** ppucBlock) = 0; }; RCODE FLMAPI FlmAllocBlockMgr( FLMUINT uiBlockSize, IF_BlockMgr ** ppBlockMgr); /**************************************************************************** Desc: B-Tree ****************************************************************************/ flminterface FLMEXP IF_BTree : public F_Object { virtual RCODE FLMAPI btCreate( FLMUINT16 ui16BtreeId, FLMBOOL bCounts, FLMBOOL bData, FLMUINT32 * pui32RootBlockId) = 0; virtual RCODE FLMAPI btOpen( FLMUINT32 ui32RootBlockId, FLMBOOL bCounts, FLMBOOL bData, IF_ResultSetCompare * pCompare = NULL) = 0; virtual void FLMAPI btClose( void) = 0; virtual RCODE FLMAPI btDeleteTree( IF_DeleteStatus * ifpDeleteStatus = NULL) = 0; virtual RCODE FLMAPI btGetBlockChains( FLMUINT * puiBlockChains, FLMUINT * puiNumLevels) = 0; virtual RCODE FLMAPI btRemoveEntry( const FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT uiKeyLen) = 0; virtual RCODE FLMAPI btInsertEntry( const FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bFirst, FLMBOOL bLast, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btReplaceEntry( const FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bFirst, FLMBOOL bLast, FLMBOOL bTruncate = TRUE, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btLocateEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT uiMatch, FLMUINT * puiPosition = NULL, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btGetEntry( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBYTE * pucData, FLMUINT uiDataBufSize, FLMUINT * puiDataLen) = 0; virtual RCODE FLMAPI btNextEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btPrevEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btFirstEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btLastEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlockId = NULL, FLMUINT * puiOffsetIndex = NULL) = 0; virtual RCODE FLMAPI btSetReadPosition( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiPosition) = 0; virtual RCODE FLMAPI btGetReadPosition( FLMUINT * puiPosition) = 0; virtual RCODE FLMAPI btPositionTo( FLMUINT uiPosition, FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen) = 0; virtual RCODE FLMAPI btGetPosition( FLMUINT * puiPosition) = 0; virtual RCODE FLMAPI btRewind( void) = 0; // virtual RCODE FLMAPI btComputeCounts( // IF_BTree * pUntilBtree, // FLMUINT * puiBlockCount, // FLMUINT * puiKeyCount, // FLMBOOL * pbTotalsEstimated, // FLMUINT uiAvgBlockFullness) = 0; virtual FLMBOOL FLMAPI btHasCounts( void) = 0; virtual FLMBOOL FLMAPI btHasData( void) = 0; virtual void FLMAPI btResetBtree( void) = 0; virtual FLMUINT32 FLMAPI getRootBlockId( void) = 0; }; RCODE FLMAPI FlmAllocBTree( IF_BlockMgr * pBlockMgr, IF_BTree ** ppBtree); /**************************************************************************** Desc: This class is used to do pool memory allocations. ****************************************************************************/ /// Header for blocks in a memory pool. This structure is at the head of each block that belongs to a pool of /// memory. typedef struct PoolMemoryBlock { PoolMemoryBlock * pPrevBlock; ///< Points to the previous memory block in the memory pool. FLMUINT uiBlockSize; ///< Total size of the memory block. FLMUINT uiFreeOffset; ///< Offset in block where next allocation should be made. FLMUINT uiFreeSize; ///< Amount of free memory left in block - from uiFreeOfs. } PoolMemoryBlock; /// Pool memory manager. This structure is used to keep track of a pool /// of memory blocks that are used for pool memory allocation. typedef struct { FLMUINT uiAllocBytes; ///< Total number of bytes requested from ///< GedPoolAlloc and GedPoolCalloc calls FLMUINT uiCount; ///< Number of frees and resets performed on ///< the pool } POOL_STATS; class FLMEXP F_Pool : public F_Object { public: F_Pool() { m_uiBytesAllocated = 0; m_pLastBlock = NULL; m_pPoolStats = NULL; m_uiBlockSize = 0; } virtual ~F_Pool(); /// Initialize memory pool. /// \ingroup pool FINLINE void FLMAPI poolInit( FLMUINT uiBlockSize ///< Default block size for the memory pool. ) { m_uiBlockSize = uiBlockSize; } void smartPoolInit( POOL_STATS * pPoolStats); /// Allocate memory from a memory pool. /// \ingroup pool RCODE FLMAPI poolAlloc( FLMUINT uiSize, ///< Requested allocation size (in bytes). void ** ppvPtr ///< Pointer to the allocation ); /// Allocate memory from a memory pool and initialize memory to zeroes. /// \ingroup pool RCODE FLMAPI poolCalloc( FLMUINT uiSize, ///< Requested allocation size (in bytes). void ** ppvPtr); ///< Pointer to the allocation /// Free all memory blocks in a memory pool. /// \ingroup pool void FLMAPI poolFree( void); /// Reset a memory pool back to a mark.\ Free all memory blocks allocated after the mark. /// \ingroup pool void FLMAPI poolReset( void * pvMark = NULL, ///< Mark that was obtained from GedPoolMark(). FLMBOOL bReduceFirstBlock = FALSE); /// Obtain a mark in a memory pool.\ Returned mark remembers a location in the /// pool which can later be passed to poolReset() to free all memory that was /// allocated after the mark. /// \ingroup pool FINLINE void * FLMAPI poolMark( void) { return (void *)(m_pLastBlock ? (FLMBYTE *)m_pLastBlock + m_pLastBlock->uiFreeOffset : NULL); } FINLINE FLMUINT FLMAPI getBlockSize( void) { return( m_uiBlockSize); } FINLINE FLMUINT FLMAPI getBytesAllocated( void) { return( m_uiBytesAllocated); } private: FINLINE void updateSmartPoolStats( void) { if (m_uiBytesAllocated) { if( (m_pPoolStats->uiAllocBytes + m_uiBytesAllocated) >= 0xFFFF0000) { m_pPoolStats->uiAllocBytes = (m_pPoolStats->uiAllocBytes / m_pPoolStats->uiCount) * 100; m_pPoolStats->uiCount = 100; } else { m_pPoolStats->uiAllocBytes += m_uiBytesAllocated; m_pPoolStats->uiCount++; } m_uiBytesAllocated = 0; } } FINLINE void setInitialSmartPoolBlockSize( void) { // Determine starting block size: // 1) average of bytes allocated / # of frees/resets (average size needed) // 2) add 10% - to minimize extra allocs m_uiBlockSize = (m_pPoolStats->uiAllocBytes / m_pPoolStats->uiCount); m_uiBlockSize += (m_uiBlockSize / 10); if (m_uiBlockSize < 512) { m_uiBlockSize = 512; } } void freeToMark( void * pvMark); PoolMemoryBlock * m_pLastBlock; FLMUINT m_uiBlockSize; FLMUINT m_uiBytesAllocated; POOL_STATS * m_pPoolStats; }; /**************************************************************************** Desc: *****************************************************************************/ class FLMEXP F_DynaBuf : public F_Object { public: F_DynaBuf( FLMBYTE * pucBuffer = NULL, FLMUINT uiBufferSize = 0) { m_pucBuffer = pucBuffer; m_uiBufferSize = uiBufferSize; m_uiOffset = 0; m_bAllocatedBuffer = FALSE; } virtual ~F_DynaBuf() { if( m_bAllocatedBuffer) { f_free( &m_pucBuffer); } } FINLINE void FLMAPI truncateData( FLMUINT uiSize) { if( uiSize < m_uiOffset) { m_uiOffset = uiSize; } } FINLINE RCODE FLMAPI allocSpace( FLMUINT uiSize, void ** ppvPtr) { RCODE rc = NE_FLM_OK; if( m_uiOffset + uiSize >= m_uiBufferSize) { if( RC_BAD( rc = resizeBuffer( m_uiOffset + uiSize + 512))) { goto Exit; } } *ppvPtr = &m_pucBuffer[ m_uiOffset]; m_uiOffset += uiSize; Exit: return( rc); } FINLINE RCODE FLMAPI appendByte( FLMBYTE ucChar) { RCODE rc = NE_FLM_OK; FLMBYTE * pucTmp = NULL; if( RC_BAD( rc = allocSpace( 1, (void **)&pucTmp))) { goto Exit; } *pucTmp = ucChar; Exit: return( rc); } FINLINE RCODE FLMAPI appendUniChar( FLMUNICODE uChar) { RCODE rc = NE_FLM_OK; FLMUNICODE * puTmp = NULL; if( RC_BAD( rc = allocSpace( sizeof( FLMUNICODE), (void **)&puTmp))) { goto Exit; } *puTmp = uChar; Exit: return( rc); } FINLINE RCODE FLMAPI appendData( const void * pvData, FLMUINT uiSize) { RCODE rc = NE_FLM_OK; void * pvTmp = NULL; if( RC_BAD( rc = allocSpace( uiSize, &pvTmp))) { goto Exit; } if( uiSize == 1) { *((FLMBYTE *)pvTmp) = *((FLMBYTE *)pvData); } else { f_memcpy( pvTmp, pvData, uiSize); } Exit: return( rc); } FINLINE RCODE FLMAPI appendString( const char * pszString) { RCODE rc = NE_FLM_OK; void * pvTmp = NULL; FLMUINT uiSize = f_strlen( pszString); if( RC_BAD( rc = allocSpace( uiSize, &pvTmp))) { goto Exit; } f_memcpy( pvTmp, pszString, uiSize); Exit: return( rc); } FINLINE FLMBYTE * FLMAPI getBufferPtr( void) { return( m_pucBuffer); } FINLINE FLMUNICODE * FLMAPI getUnicodePtr( void) { if( m_uiOffset >= sizeof( FLMUNICODE)) { return( (FLMUNICODE *)m_pucBuffer); } return( NULL); } FINLINE FLMUINT FLMAPI getUnicodeLength( void) { if( m_uiOffset <= sizeof( FLMUNICODE)) { return( 0); } return( (m_uiOffset >> 1) - 1); } FINLINE FLMUINT FLMAPI getDataLength( void) { return( m_uiOffset); } FINLINE RCODE FLMAPI copyFromBuffer( F_DynaBuf * pSource) { RCODE rc = NE_FLM_OK; if( RC_BAD( rc = resizeBuffer( pSource->m_uiBufferSize))) { goto Exit; } if( (m_uiOffset = pSource->m_uiOffset) != 0) { f_memcpy( m_pucBuffer, pSource->m_pucBuffer, pSource->m_uiOffset); } Exit: return( rc); } private: FINLINE RCODE resizeBuffer( FLMUINT uiNewSize) { RCODE rc = NE_FLM_OK; if( !m_bAllocatedBuffer) { if( uiNewSize > m_uiBufferSize) { FLMBYTE * pucOriginalBuf = m_pucBuffer; if( RC_BAD( rc = f_alloc( uiNewSize, &m_pucBuffer))) { m_pucBuffer = pucOriginalBuf; goto Exit; } m_bAllocatedBuffer = TRUE; if( m_uiOffset) { f_memcpy( m_pucBuffer, pucOriginalBuf, m_uiOffset); } } } else { if( RC_BAD( rc = f_realloc( uiNewSize, &m_pucBuffer))) { goto Exit; } if( uiNewSize < m_uiOffset) { m_uiOffset = uiNewSize; } } m_uiBufferSize = uiNewSize; Exit: return( rc); } FLMBOOL m_bAllocatedBuffer; FLMBYTE * m_pucBuffer; FLMUINT m_uiBufferSize; FLMUINT m_uiOffset; }; /**************************************************************************** Desc: ****************************************************************************/ typedef struct { F_ListItem * pPrevItem; F_ListItem * pNextItem; FLMUINT uiListCount; } F_ListNode; #define FLM_ALL_LISTS 0xFFFF /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_ListItem : public F_Object { public: F_ListItem() { m_pListManager = NULL; m_pListNodes = NULL; m_uiListNodeCnt = 0; m_bInList = FALSE; } virtual ~F_ListItem(); void setup( F_ListManager * pListMgr, F_ListNode * pListNodes, FLMUINT uiListNodeCnt); void removeFromList( FLMUINT uiList = 0); FINLINE F_ListItem * getNextListItem( FLMUINT uiList = 0) { return( m_pListNodes[ uiList].pNextItem); } FINLINE F_ListItem * getPrevListItem( FLMUINT uiList = 0) { return( m_pListNodes[ uiList].pPrevItem); } FINLINE F_ListItem * setNextListItem( FLMUINT uiList, F_ListItem * pNewNext) { F_ListNode * pListNode; pListNode = &m_pListNodes[ uiList]; pListNode->pNextItem = pNewNext; return( pNewNext); } FINLINE F_ListItem * setPrevListItem( FLMUINT uiList, F_ListItem * pNewPrev) { F_ListNode * pListNode; pListNode = &m_pListNodes[ uiList]; pListNode->pPrevItem = pNewPrev; return( pNewPrev); } private: F_ListManager * m_pListManager; FLMUINT m_uiListNodeCnt; F_ListNode * m_pListNodes; FLMBOOL m_bInList; friend class F_ListManager; }; /**************************************************************************** Desc: ****************************************************************************/ class F_ListManager : public F_Object { public: F_ListManager( F_ListNode * pListNodes, FLMUINT uiListNodeCnt) { flmAssert( pListNodes && uiListNodeCnt ); m_uiListNodeCnt = uiListNodeCnt; m_pListNodes = pListNodes; f_memset( pListNodes, 0, sizeof( F_ListNode) * uiListNodeCnt ); } virtual ~F_ListManager() { clearList( FLM_ALL_LISTS); } void insertFirst( FLMUINT uiList, F_ListItem * pNewFirstItem); void insertLast( FLMUINT uiList, F_ListItem * pNewLastItem); F_ListItem * getItem( FLMUINT uiList, FLMUINT nth); void removeItem( FLMUINT uiList, F_ListItem * pItem); FINLINE FLMUINT getListCount( void) { return( m_uiListNodeCnt); } FLMUINT getItemCount( FLMUINT uiList); void clearList( FLMUINT uiList = 0); private: FLMUINT m_uiListNodeCnt; F_ListNode * m_pListNodes; }; // IMPORTANT NOTE: If these are changed, corresonding changes // should be made in java and C# code as well. /// Types of locks that may be requested. typedef enum { FLM_LOCK_NONE = 0, FLM_LOCK_EXCLUSIVE, ///< 1 = Exclusive lock. FLM_LOCK_SHARED ///< 2 = Shared lock. } eLockType; /**************************************************************************** /// Abstract base class to get lock information. The application must /// implement this class. A pointer to an object of this class is passed /// into IF_LockObject::getLockInfo(). ****************************************************************************/ flminterface IF_LockInfoClient : public F_Object { /// Return the lock count. This method is called by to tell the /// application how many lock holders plus lock waiters there are. This /// gives the application an opportunity to allocate memory to hold the /// information that will be returned via the /// IF_LockInfoClient::addLockInfo() method. The application should /// return TRUE from this method in order to continue, FALSE if it wants /// to stop and return from the IF_LockObject::getLockInfo() function. virtual FLMBOOL FLMAPI setLockCount( FLMUINT uiTotalLocks ///< Total number of lock holders plus lock waiters. ) = 0; /// Return lock information for a lock holder or waiter. This method /// is called for each thread that is either holding the lock or waiting /// to obtain the lock. The application should return TRUE from this /// method in order to continue, FALSE if it wants to stop and return /// from the IF_LockObject::getLockInfo() function. virtual FLMBOOL FLMAPI addLockInfo( FLMUINT uiLockNum, ///< Position in queue (0 = lock holder, 1..n = lock waiter). FLMUINT uiThreadID, ///< Thread ID of the lock holder/waiter. FLMUINT uiTime ///< For the lock holder, this is the amount of time the lock has been ///< held.\ For a lock waiter, this is the amount of time the thread ///< has been waiting to obtain the lock.\ Both times are milliseconds. ) = 0; }; // IMPORTANT NOTE: This structure needs to stay in sync with // corresponding structures in java and C# code. /************************************************************************** /// Structure used in gathering statistics to hold an operation count and an elapsed time. **************************************************************************/ typedef struct { FLMUINT64 ui64Count; ///< Number of times operation was performed FLMUINT64 ui64ElapMilli; ///< Total elapsed time (milliseconds) for the operations. } F_COUNT_TIME_STAT; // IMPORTANT NOTE: This structure needs to stay in sync with // corresponding structures in java and C# code. /************************************************************************** /// Structure for returning lock statistics. **************************************************************************/ typedef struct { F_COUNT_TIME_STAT NoLocks; ///< Statistics on times when nobody was holding a lock on the database. F_COUNT_TIME_STAT WaitingForLock; ///< Statistics on times threads were waiting to obtain a database lock. F_COUNT_TIME_STAT HeldLock; ///< Statistics on times when a thread was holding a lock on the database. } F_LOCK_STATS; /**************************************************************************** /// Structure that gives information on threads that are either waiting to /// obtain a lock or have obtained a lock. ****************************************************************************/ typedef struct { FLMUINT uiThreadId; ///< Thread id of thread that is waiting to obtain a lock or holds a lock. FLMUINT uiTime; ///< For lock holder, this is the time the lock was obtained. ///< For the lock waiter, this is the time he started waiting for the lock. } F_LOCK_USER; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_LockObject : public F_Object { virtual RCODE FLMAPI lock( F_SEM hWaitSem, FLMBOOL bExclLock, FLMUINT uiMaxWaitSecs, FLMINT iPriority, F_LOCK_STATS * pLockStats = NULL) = 0; virtual RCODE FLMAPI unlock( F_LOCK_STATS * pLockStats = NULL) = 0; virtual FLMUINT FLMAPI getLockCount( void) = 0; virtual FLMUINT FLMAPI getWaiterCount( void) = 0; virtual RCODE FLMAPI getLockInfo( FLMINT iPriority, eLockType * peCurrLockType, FLMUINT * puiThreadId, FLMUINT * puiLockHeldTime = NULL, FLMUINT * puiNumExclQueued = NULL, FLMUINT * puiNumSharedQueued = NULL, FLMUINT * puiPriorityCount = NULL) = 0; virtual RCODE FLMAPI getLockInfo( IF_LockInfoClient * pLockInfo) = 0; virtual RCODE FLMAPI getLockQueue( F_LOCK_USER ** ppLockUsers) = 0; virtual FLMBOOL FLMAPI haveHigherPriorityWaiter( FLMINT iPriority) = 0; virtual void FLMAPI timeoutLockWaiter( FLMUINT uiThreadId) = 0; virtual void FLMAPI timeoutAllWaiters( void) = 0; }; RCODE FLMAPI FlmAllocLockObject( IF_LockObject ** ppLockObject); /**************************************************************************** Desc: Misc. ****************************************************************************/ #define F_UNREFERENCED_PARM( parm) \ (void)parm #define f_min(a, b) \ ((a) < (b) ? (a) : (b)) #define f_max(a, b) \ ((a) < (b) ? (b) : (a)) #define f_swap( a, b, tmp) \ ((tmp) = (a), (a) = (b), (b) = (tmp)) FINLINE FLMBOOL f_isHexChar( FLMBYTE ucChar) { if( (ucChar >= '0' && ucChar <= '9') || (ucChar >= 'A' && ucChar <= 'F') || (ucChar >= 'a' && ucChar <= 'f')) { return( TRUE); } return( FALSE); } FINLINE FLMBOOL f_isHexChar( FLMUNICODE uChar) { if( uChar > 127) { return( FALSE); } return( f_isHexChar( f_tonative( (FLMBYTE)uChar))); } FINLINE FLMBYTE f_getHexVal( FLMBYTE ucChar) { if( ucChar >= '0' && ucChar <= '9') { return( (FLMBYTE)(ucChar - '0')); } else if( ucChar >= 'A' && ucChar <= 'F') { return( (FLMBYTE)((ucChar - 'A') + 10)); } else if( ucChar >= 'a' && ucChar <= 'f') { return( (FLMBYTE)((ucChar - 'a') + 10)); } return( 0); } FINLINE FLMBYTE f_getHexVal( FLMUNICODE uChar) { return( f_getHexVal( f_tonative( (FLMBYTE)uChar))); } FINLINE FLMBOOL f_isValidHexNum( const FLMBYTE * pszString) { if( *pszString == 0) { return( FALSE); } while( *pszString) { if( !f_isHexChar( *pszString)) { return( TRUE); } pszString++; } return( TRUE); } FINLINE FLMUINT64 f_roundUp( FLMUINT64 ui64ValueToRound, FLMUINT64 ui64Boundary) { FLMUINT64 ui64RetVal; ui64RetVal = ((ui64ValueToRound / ui64Boundary) * ui64Boundary); if( ui64RetVal < ui64ValueToRound) { ui64RetVal += ui64Boundary; } return( ui64RetVal); } FINLINE void f_setBit( FLMBYTE * pucBuffer, FLMUINT uiBit) { pucBuffer[ uiBit >> 3] |= (FLMBYTE)(0x01 << (uiBit & 0x07)); } FINLINE void f_clearBit( FLMBYTE * pucBuffer, FLMUINT uiBit) { pucBuffer[ uiBit >> 3] &= ~((FLMBYTE)(0x01 << (uiBit & 0x07))); } FINLINE FLMBOOL f_isBitSet( FLMBYTE * pucBuffer, FLMUINT uiBit) { return( (pucBuffer[ uiBit >> 3] & (FLMBYTE)(0x01 << (uiBit & 0x07))) ? TRUE : FALSE); } RCODE FLMAPI f_filecpy( const char * pszSourceFile, const char * pszData); RCODE FLMAPI f_filecat( const char * pszSourceFile, const char * pszData); RCODE FLMAPI f_filetobuf( const char * pszSourceFile, char ** ppszBuffer); /**************************************************************************** Desc: FTX ****************************************************************************/ #define FKB_ESCAPE 0xE01B #define FKB_ESC FKB_ESCAPE #define FKB_SPACE 0x20 #define FKB_HOME 0xE008 #define FKB_UP 0xE017 #define FKB_PGUP 0xE059 #define FKB_LEFT 0xE019 #define FKB_RIGHT 0xE018 #define FKB_END 0xE055 #define FKB_DOWN 0xE01A #define FKB_PGDN 0xE05A #define FKB_PLUS 0x002B #define FKB_MINUS 0x002D #define FKB_INSERT 0xE05D #define FKB_DELETE 0xE051 #define FKB_BACKSPACE 0xE050 #define FKB_TAB 0xE009 #define FKB_ENTER 0xE00A #define FKB_F1 0xE020 #define FKB_F2 0xE021 #define FKB_F3 0xE022 #define FKB_F4 0xE023 #define FKB_F5 0xE024 #define FKB_F6 0xE025 #define FKB_F7 0xE026 #define FKB_F8 0xE027 #define FKB_F9 0xE028 #define FKB_F10 0xE029 #define FKB_F11 0xE03A #define FKB_F12 0xE03B #define FKB_STAB 0xE05E #define FKB_SF1 0xE02C #define FKB_SF2 0xE02D #define FKB_SF3 0xE02E #define FKB_SF4 0xE02F #define FKB_SF5 0xE030 #define FKB_SF6 0xE031 #define FKB_SF7 0xE032 #define FKB_SF8 0xE033 #define FKB_SF9 0xE034 #define FKB_SF10 0xE035 #define FKB_SF11 0xE036 #define FKB_SF12 0xE037 #define FKB_ALT_A 0xFDDC #define FKB_ALT_B 0xFDDD #define FKB_ALT_C 0xFDDE #define FKB_ALT_D 0xFDDF #define FKB_ALT_E 0xFDE0 #define FKB_ALT_F 0xFDE1 #define FKB_ALT_G 0xFDE2 #define FKB_ALT_H 0xFDE3 #define FKB_ALT_I 0xFDE4 #define FKB_ALT_J 0xFDE5 #define FKB_ALT_K 0xFDE6 #define FKB_ALT_L 0xFDE7 #define FKB_ALT_M 0xFDE8 #define FKB_ALT_N 0xFDE9 #define FKB_ALT_O 0xFDEA #define FKB_ALT_P 0xFDEB #define FKB_ALT_Q 0xFDEC #define FKB_ALT_R 0xFDED #define FKB_ALT_S 0xFDEE #define FKB_ALT_T 0xFDEF #define FKB_ALT_U 0xFDF0 #define FKB_ALT_V 0xFDF1 #define FKB_ALT_W 0xFDF2 #define FKB_ALT_X 0xFDF3 #define FKB_ALT_Y 0xFDF4 #define FKB_ALT_Z 0xFDF5 #define FKB_ALT_1 0xFDF7 #define FKB_ALT_2 0xFDF8 #define FKB_ALT_3 0xFDF9 #define FKB_ALT_4 0xFDFA #define FKB_ALT_5 0xFDFB #define FKB_ALT_6 0xFDFC #define FKB_ALT_7 0xFDFD #define FKB_ALT_8 0xFDFE #define FKB_ALT_9 0xFDFF #define FKB_ALT_0 0xFDF6 #define FKB_ALT_MINUS 0xE061 #define FKB_ALT_EQUAL 0xE06B #define FKB_ALT_F1 0xE038 #define FKB_ALT_F2 0xE039 #define FKB_ALT_F3 0xE03A #define FKB_ALT_F4 0xE03B #define FKB_ALT_F5 0xE03C #define FKB_ALT_F6 0xE03D #define FKB_ALT_F7 0xE03E #define FKB_ALT_F8 0xE03F #define FKB_ALT_F9 0xE040 #define FKB_ALT_F10 0xE041 #define FKB_GOTO 0xE058 #define FKB_CTRL_HOME 0xE058 #define FKB_CTRL_UP 0xE063 #define FKB_CTRL_PGUP 0xE057 #define FKB_CTRL_LEFT 0xE054 #define FKB_CTRL_RIGHT 0xE053 #define FKB_CTRL_END 0xE00B #define FKB_CTRL_DOWN 0xE064 #define FKB_CTRL_PGDN 0xE00C #define FKB_CTRL_INSERT 0xE06E #define FKB_CTRL_DELETE 0xE06D #define FKB_CTRL_ENTER 0xE05F #define FKB_CTRL_A 0xE07C #define FKB_CTRL_B 0xE07D #define FKB_CTRL_C 0xE07E #define FKB_CTRL_D 0xE07F #define FKB_CTRL_E 0xE080 #define FKB_CTRL_F 0xE081 #define FKB_CTRL_G 0xE082 #define FKB_CTRL_H 0xE083 #define FKB_CTRL_I 0xE084 #define FKB_CTRL_J 0xE085 #define FKB_CTRL_K 0xE086 #define FKB_CTRL_L 0xE087 #define FKB_CTRL_M 0xE088 #define FKB_CTRL_N 0xE089 #define FKB_CTRL_O 0xE08A #define FKB_CTRL_P 0xE08B #define FKB_CTRL_Q 0xE08C #define FKB_CTRL_R 0xE08D #define FKB_CTRL_S 0xE08E #define FKB_CTRL_T 0xE08F #define FKB_CTRL_U 0xE090 #define FKB_CTRL_V 0xE091 #define FKB_CTRL_W 0xE092 #define FKB_CTRL_X 0xE093 #define FKB_CTRL_Y 0xE094 #define FKB_CTRL_Z 0xE095 #define FKB_CTRL_1 0xE06B #define FKB_CTRL_2 0xE06C #define FKB_CTRL_3 0xE06D #define FKB_CTRL_4 0xE06E #define FKB_CTRL_5 0xE06F #define FKB_CTRL_6 0xE070 #define FKB_CTRL_7 0xE071 #define FKB_CTRL_8 0xE072 #define FKB_CTRL_9 0xE073 #define FKB_CTRL_0 0xE074 #define FKB_CTRL_MINUS 0xE060 #define FKB_CTRL_EQUAL 0xE061 #define FKB_CTRL_F1 0xE038 #define FKB_CTRL_F2 0xE039 #define FKB_CTRL_F3 0xE03A #define FKB_CTRL_F4 0xE03B #define FKB_CTRL_F5 0xE03C #define FKB_CTRL_F6 0xE03D #define FKB_CTRL_F7 0xE03E #define FKB_CTRL_F8 0xE03F #define FKB_CTRL_F9 0xE040 #define FKB_CTRL_F10 0xE041 #define FLM_CURSOR_BLOCK 0x01 #define FLM_CURSOR_UNDERLINE 0x02 #define FLM_CURSOR_INVISIBLE 0x04 #define FLM_CURSOR_VISIBLE 0x08 typedef struct FTX_SCREEN FTX_SCREEN; typedef struct FTX_WINDOW FTX_WINDOW; typedef FLMBOOL (FLMAPI * KEY_HANDLER)( FLMUINT uiKeyIn, FLMUINT * puiKeyOut, void * pvAppData); RCODE FLMAPI FTXInit( const char * pszAppName = NULL, FLMUINT uiCols = 0, FLMUINT uiRows = 0, eColorType backgroundColor = FLM_BLUE, eColorType foregroundColor = FLM_WHITE, KEY_HANDLER pKeyHandler = NULL, void * pvKeyHandlerData = NULL); void FLMAPI FTXExit( void); void FLMAPI FTXCycleScreensNext( void); void FLMAPI FTXCycleScreensPrev( void); void FLMAPI FTXRefreshCursor( void); void FLMAPI FTXInvalidate( void); void FLMAPI FTXSetShutdownFlag( FLMBOOL * pbShutdownFlag); RCODE FLMAPI FTXScreenInit( const char * pszName, FTX_SCREEN ** ppScreen); RCODE FLMAPI FTXCaptureScreen( FLMBYTE * pText, FLMBYTE * pForeAttrib, FLMBYTE * pBackAttrib); void FLMAPI FTXRefresh( void); void FLMAPI FTXSetRefreshState( FLMBOOL bDisable); FLMBOOL FLMAPI FTXRefreshDisabled( void); RCODE FLMAPI FTXAddKey( FLMUINT uiKey); RCODE FLMAPI FTXWinInit( FTX_SCREEN * pScreen, FLMUINT uiCols, FLMUINT uiRows, FTX_WINDOW ** ppWindow); void FLMAPI FTXWinFree( FTX_WINDOW ** ppWindow); RCODE FLMAPI FTXWinOpen( FTX_WINDOW * pWindow); RCODE FLMAPI FTXWinSetName( FTX_WINDOW * pWindow, char * pszName); void FLMAPI FTXWinClose( FTX_WINDOW * pWindow); void FLMAPI FTXWinSetFocus( FTX_WINDOW * pWindow); void FLMAPI FTXWinPrintChar( FTX_WINDOW * pWindow, FLMUINT uiChar); void FLMAPI FTXWinPrintStr( FTX_WINDOW * pWindow, const char * pszString); void FLMAPI FTXWinPrintf( FTX_WINDOW * pWindow, const char * pszFormat, ...); void FLMAPI FTXWinCPrintf( FTX_WINDOW * pWindow, eColorType backgroundColor, eColorType foregroundColor, const char * pszFormat, ...); void FLMAPI FTXWinPrintStrXY( FTX_WINDOW * pWindow, const char * pszString, FLMUINT uiCol, FLMUINT uiRow); void FLMAPI FTXWinMove( FTX_WINDOW * pWindow, FLMUINT uiCol, FLMUINT uiRow); void FLMAPI FTXWinPaintBackground( FTX_WINDOW * pWindow, eColorType backgroundColor); void FLMAPI FTXWinPaintForeground( FTX_WINDOW * pWindow, eColorType foregroundColor); void FLMAPI FTXWinPaintRow( FTX_WINDOW * pWindow, eColorType * pBackgroundColor, eColorType * pForegroundColor, FLMUINT uiRow); void FLMAPI FTXWinSetChar( FTX_WINDOW * pWindow, FLMUINT uiChar); void FLMAPI FTXWinPaintRowForeground( FTX_WINDOW * pWindow, eColorType foregroundColor, FLMUINT uiRow); void FLMAPI FTXWinPaintRowBackground( FTX_WINDOW * pWindow, eColorType backgroundColor, FLMUINT uiRow); void FLMAPI FTXWinSetBackFore( FTX_WINDOW * pWindow, eColorType backgroundColor, eColorType foregroundColor); void FLMAPI FTXWinGetCanvasSize( FTX_WINDOW * pWindow, FLMUINT * puiNumCols, FLMUINT * puiNumRows); void FLMAPI FTXWinGetSize( FTX_WINDOW * pWindow, FLMUINT * puiNumCols, FLMUINT * puiNumRows); FLMUINT FLMAPI FTXWinGetCurrRow( FTX_WINDOW * pWindow); FLMUINT FLMAPI FTXWinGetCurrCol( FTX_WINDOW * pWindow); void FLMAPI FTXWinGetBackFore( FTX_WINDOW * pWindow, eColorType * pBackgroundColor, eColorType * pForegroundColor); void FLMAPI FTXWinDrawBorder( FTX_WINDOW * pWindow); void FLMAPI FTXWinSetTitle( FTX_WINDOW * pWindow, const char * pszTitle, eColorType backgroundColor, eColorType foregroundColor); void FLMAPI FTXWinSetHelp( FTX_WINDOW * pWindow, const char * pszHelp, eColorType backgroundColor, eColorType foregroundColor); RCODE FLMAPI FTXLineEdit( FTX_WINDOW * pWindow, char * pszBuffer, FLMUINT uiBufSize, FLMUINT uiMaxWidth, FLMUINT * puiCharCount, FLMUINT * puiTermChar); FLMUINT FLMAPI FTXLineEd( FTX_WINDOW * pWindow, char * pszBuffer, FLMUINT uiBufSize); void FLMAPI FTXWinSetCursorPos( FTX_WINDOW * pWindow, FLMUINT uiCol, FLMUINT uiRow); void FLMAPI FTXWinGetCursorPos( FTX_WINDOW * pWindow, FLMUINT * puiCol, FLMUINT * puiRow); void FLMAPI FTXWinClear( FTX_WINDOW * pWindow); void FLMAPI FTXWinClearXY( FTX_WINDOW * pWindow, FLMUINT uiCol, FLMUINT uiRow); void FLMAPI FTXWinClearLine( FTX_WINDOW * pWindow, FLMUINT uiCol, FLMUINT uiRow); void FLMAPI FTXWinClearToEOL( FTX_WINDOW * pWindow); void FLMAPI FTXWinSetCursorType( FTX_WINDOW * pWindow, FLMUINT uiType); FLMUINT FLMAPI FTXWinGetCursorType( FTX_WINDOW * pWindow); RCODE FLMAPI FTXWinInputChar( FTX_WINDOW * pWindow, FLMUINT * puiChar); RCODE FLMAPI FTXWinTestKB( FTX_WINDOW * pWindow); void FLMAPI FTXWinSetScroll( FTX_WINDOW * pWindow, FLMBOOL bScroll); void FLMAPI FTXWinSetLineWrap( FTX_WINDOW * pWindow, FLMBOOL bLineWrap); void FLMAPI FTXWinGetScroll( FTX_WINDOW * pWindow, FLMBOOL * pbScroll); RCODE FLMAPI FTXWinGetScreen( FTX_WINDOW * pWindow, FTX_SCREEN ** ppScreen); RCODE FLMAPI FTXWinGetPosition( FTX_WINDOW * pWindow, FLMUINT * puiCol, FLMUINT * puiRow); void FLMAPI FTXScreenFree( FTX_SCREEN ** ppScreen); RCODE FLMAPI FTXScreenInitStandardWindows( FTX_SCREEN * pScreen, eColorType titleBackColor, eColorType titleForeColor, eColorType mainBackColor, eColorType mainForeColor, FLMBOOL bBorder, FLMBOOL bBackFill, const char * pszTitle, FTX_WINDOW ** ppTitleWin, FTX_WINDOW ** ppMainWin); void FLMAPI FTXScreenSetShutdownFlag( FTX_SCREEN * pScreen, FLMBOOL * pbShutdownFlag); RCODE FLMAPI FTXScreenDisplay( FTX_SCREEN * pScreen); RCODE FLMAPI FTXScreenGetSize( FTX_SCREEN * pScreen, FLMUINT * puiNumCols, FLMUINT * puiNumRows); RCODE FLMAPI FTXMessageWindow( FTX_SCREEN * pScreen, eColorType backgroundColor, eColorType foregroundColor, const char * pszMessage1, const char * pszMessage2, FTX_WINDOW ** ppWindow); RCODE FLMAPI FTXDisplayMessage( FTX_SCREEN * pScreen, eColorType backgroundColor, eColorType foregroundColor, const char * pszMessage1, const char * pszMessage2, FLMUINT * puiTermChar); RCODE FLMAPI FTXDisplayScrollWindow( FTX_SCREEN * pScreen, const char * pszTitle, const char * pszMessage, FLMUINT uiCols, FLMUINT uiRows); RCODE FLMAPI FTXGetInput( FTX_SCREEN * pScreen, const char * pszMessage, char * pszResponse, FLMUINT uiMaxRespLen, FLMUINT * puiTermChar); void FLMAPI FTXBeep( void); RCODE FLMAPI f_conInit( FLMUINT uiRows, FLMUINT uiCols, const char * pszTitle); void FLMAPI f_conExit( void); void FLMAPI f_conGetScreenSize( FLMUINT * puiNumColsRV, FLMUINT * puiNumRowsRV); void FLMAPI f_conDrawBorder( void); void FLMAPI f_conStrOut( const char * pszString); void FLMAPI f_conStrOutXY( const char * pszString, FLMUINT uiCol, FLMUINT uiRow); void FLMAPI f_conPrintf( const char * pszFormat, ...); void FLMAPI f_conCPrintf( eColorType back, eColorType fore, const char * pszFormat, ...); void FLMAPI f_conClearScreen( FLMUINT uiCol, FLMUINT uiRow); void FLMAPI f_conClearLine( FLMUINT uiCol, FLMUINT uiRow); void FLMAPI f_conSetBackFore( eColorType backColor, eColorType foreColor); FLMUINT FLMAPI f_conGetCursorColumn( void); FLMUINT FLMAPI f_conGetCursorRow( void); void FLMAPI f_conSetCursorType( FLMUINT uiType); void FLMAPI f_conSetCursorPos( FLMUINT uiCol, FLMUINT uiRow); FLMUINT FLMAPI f_conGetKey( void); FLMBOOL FLMAPI f_conHaveKey( void); FLMUINT FLMAPI f_conLineEdit( char * pszString, FLMUINT uiMaxLen); /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_BufferIStream : public IF_BufferIStream { public: F_BufferIStream() { m_pucBuffer = NULL; m_uiBufferLen = 0; m_uiOffset = 0; m_bAllocatedBuffer = FALSE; m_bIsOpen = FALSE; } virtual ~F_BufferIStream(); RCODE FLMAPI openStream( const char * pucBuffer, FLMUINT uiLength, char ** ppucAllocatedBuffer = NULL); FINLINE FLMUINT64 FLMAPI totalSize( void) { f_assert( m_bIsOpen); return( m_uiBufferLen); } FINLINE FLMUINT64 FLMAPI remainingSize( void) { f_assert( m_bIsOpen); return( m_uiBufferLen - m_uiOffset); } RCODE FLMAPI closeStream( void); FINLINE RCODE FLMAPI positionTo( FLMUINT64 ui64Position) { f_assert( m_bIsOpen); if( ui64Position < m_uiBufferLen) { m_uiOffset = (FLMUINT)ui64Position; } else { m_uiOffset = m_uiBufferLen; } return( NE_FLM_OK); } FINLINE FLMUINT64 FLMAPI getCurrPosition( void) { f_assert( m_bIsOpen); return( m_uiOffset); } FINLINE void FLMAPI truncateStream( FLMUINT64 ui64Offset = 0) { f_assert( m_bIsOpen); f_assert( ui64Offset >= m_uiOffset); f_assert( ui64Offset <= m_uiBufferLen); m_uiBufferLen = (FLMUINT)ui64Offset; } RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FINLINE const FLMBYTE * getBuffer( void) { f_assert( m_bIsOpen); return( m_pucBuffer); } FINLINE const FLMBYTE * FLMAPI getBufferAtCurrentOffset( void) { f_assert( m_bIsOpen); return( m_pucBuffer ? &m_pucBuffer[ m_uiOffset] : NULL); } FINLINE FLMBOOL isOpen( void) { return( m_bIsOpen); } private: const FLMBYTE * m_pucBuffer; FLMUINT m_uiBufferLen; FLMUINT m_uiOffset; FLMBOOL m_bAllocatedBuffer; FLMBOOL m_bIsOpen; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_FileIStream : public IF_PosIStream { public: F_FileIStream() { m_pFileHdl = NULL; m_ui64FileOffset = 0; } virtual ~F_FileIStream() { if( m_pFileHdl) { m_pFileHdl->Release(); } } RCODE FLMAPI openStream( const char * pszPath); RCODE FLMAPI closeStream( void); RCODE FLMAPI positionTo( FLMUINT64 ui64Position); FLMUINT64 FLMAPI totalSize( void); FLMUINT64 FLMAPI remainingSize( void); FLMUINT64 FLMAPI getCurrPosition( void); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); private: IF_FileHdl * m_pFileHdl; FLMUINT64 m_ui64FileOffset; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_BufferedIStream : public IF_PosIStream { public: F_BufferedIStream() { m_pIStream = NULL; m_pucBuffer = NULL; } virtual ~F_BufferedIStream() { closeStream(); } RCODE FLMAPI openStream( IF_IStream * pIStream, FLMUINT uiBufferSize); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); RCODE FLMAPI closeStream( void); FINLINE FLMUINT64 FLMAPI totalSize( void) { if (!m_pIStream) { f_assert( 0); return( 0); } return( m_uiBytesAvail); } FINLINE FLMUINT64 FLMAPI remainingSize( void) { if( !m_pIStream) { f_assert( 0); return( 0); } return( m_uiBytesAvail - m_uiBufferOffset); } FINLINE RCODE FLMAPI positionTo( FLMUINT64 ui64Position) { if( !m_pIStream) { f_assert( 0); return( RC_SET( NE_FLM_ILLEGAL_OP)); } if( ui64Position < m_uiBytesAvail) { m_uiBufferOffset = (FLMUINT)ui64Position; } else { m_uiBufferOffset = m_uiBytesAvail; } return( NE_FLM_OK); } FINLINE FLMUINT64 FLMAPI getCurrPosition( void) { if( !m_pIStream) { f_assert( 0); return( 0); } return( m_uiBufferOffset); } private: IF_IStream * m_pIStream; FLMBYTE * m_pucBuffer; FLMUINT m_uiBufferSize; FLMUINT m_uiBufferOffset; FLMUINT m_uiBytesAvail; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_BufferedOStream : public IF_OStream { public: F_BufferedOStream() { m_pOStream = NULL; m_pucBuffer = NULL; } virtual ~F_BufferedOStream() { closeStream(); } RCODE FLMAPI openStream( IF_OStream * pOStream, FLMUINT uiBufferSize); RCODE FLMAPI write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten); RCODE FLMAPI closeStream( void); RCODE FLMAPI flush( void); private: IF_OStream * m_pOStream; FLMBYTE * m_pucBuffer; FLMUINT m_uiBufferSize; FLMUINT m_uiBufferOffset; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_FileOStream : public IF_OStream { public: F_FileOStream() { m_pFileHdl = NULL; } virtual ~F_FileOStream() { closeStream(); } RCODE FLMAPI openStream( const char * pszFilePath, FLMBOOL bTruncateIfExists); RCODE FLMAPI write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten); RCODE FLMAPI closeStream( void); private: IF_FileHdl * m_pFileHdl; FLMUINT64 m_ui64FileOffset; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_MultiFileIStream : public IF_IStream { public: F_MultiFileIStream() { m_pIStream = NULL; m_bOpen = FALSE; } virtual ~F_MultiFileIStream() { closeStream(); } RCODE FLMAPI openStream( const char * pszDirectory, const char * pszBaseName); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); RCODE FLMAPI closeStream( void); private: RCODE rollToNextFile( void); IF_IStream * m_pIStream; FLMBOOL m_bOpen; FLMBOOL m_bEndOfStream; FLMUINT m_uiFileNum; FLMUINT64 m_ui64FileOffset; char m_szDirectory[ F_PATH_MAX_SIZE + 1]; char m_szBaseName[ F_PATH_MAX_SIZE + 1]; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_MultiFileOStream : public IF_OStream { public: F_MultiFileOStream() { m_pOStream = NULL; m_bOpen = FALSE; } virtual ~F_MultiFileOStream() { closeStream(); } RCODE createStream( const char * pszDirectory, const char * pszBaseName, FLMUINT uiMaxFileSize, FLMBOOL bOkToOverwrite); RCODE FLMAPI write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten); RCODE FLMAPI closeStream( void); RCODE processDirectory( const char * pszDirectory, const char * pszBaseName, FLMBOOL bOkToDelete); private: RCODE rollToNextFile( void); IF_OStream * m_pOStream; FLMBOOL m_bOpen; FLMUINT m_uiFileNum; FLMUINT64 m_ui64MaxFileSize; FLMUINT64 m_ui64FileOffset; char m_szDirectory[ F_PATH_MAX_SIZE + 1]; char m_szBaseName[ F_PATH_MAX_SIZE + 1]; }; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_CollIStream : public IF_CollIStream { public: F_CollIStream() { m_pIStream = NULL; m_uiLanguage = 0; m_bMayHaveWildCards = FALSE; m_bUnicodeStream = FALSE; m_uNextChar = 0; } virtual ~F_CollIStream() { if( m_pIStream) { m_pIStream->Release(); } } RCODE FLMAPI openStream( IF_PosIStream * pIStream, FLMBOOL bUnicodeStream, FLMUINT uiLanguage, FLMUINT uiCompareRules, FLMBOOL bMayHaveWildCards) { if( m_pIStream) { m_pIStream->Release(); } m_pIStream = pIStream; m_pIStream->AddRef(); m_uiLanguage = uiLanguage; m_uiCompareRules = uiCompareRules; m_bCaseSensitive = (uiCompareRules & FLM_COMP_CASE_INSENSITIVE) ? FALSE : TRUE; m_bMayHaveWildCards = bMayHaveWildCards; m_bUnicodeStream = bUnicodeStream; m_ui64EndOfLeadingSpacesPos = 0; return( NE_FLM_OK); } RCODE FLMAPI closeStream( void) { if( m_pIStream) { m_pIStream->Release(); m_pIStream = NULL; } return( NE_FLM_OK); } RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { RCODE rc = NE_FLM_OK; if( RC_BAD( rc = m_pIStream->read( pvBuffer, uiBytesToRead, puiBytesRead))) { goto Exit; } Exit: return( rc); } RCODE FLMAPI read( FLMBOOL bAllowTwoIntoOne, FLMUNICODE * puChar, FLMBOOL * pbCharIsWild, FLMUINT16 * pui16Col, FLMUINT16 * pui16SubCol, FLMBYTE * pucCase); FINLINE FLMUINT64 FLMAPI totalSize( void) { if( m_pIStream) { return( m_pIStream->totalSize()); } return( 0); } FINLINE FLMUINT64 FLMAPI remainingSize( void) { if( m_pIStream) { return( m_pIStream->remainingSize()); } return( 0); } FINLINE RCODE FLMAPI positionTo( FLMUINT64) { return( RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED)); } FINLINE RCODE FLMAPI positionTo( F_CollStreamPos * pPos) { // Should never be able to position back to before the // leading spaces. m_uNextChar = pPos->uNextChar; flmAssert( pPos->ui64Position >= m_ui64EndOfLeadingSpacesPos); return( m_pIStream->positionTo( pPos->ui64Position)); } FINLINE FLMUINT64 FLMAPI getCurrPosition( void) { flmAssert( 0); return( 0); } void FLMAPI getCurrPosition( F_CollStreamPos * pPos); private: FINLINE RCODE readCharFromStream( FLMUNICODE * puChar) { RCODE rc = NE_FLM_OK; if( m_bUnicodeStream) { if( RC_BAD( rc = m_pIStream->read( puChar, sizeof( FLMUNICODE), NULL))) { goto Exit; } } else { if( RC_BAD( rc = f_readUTF8CharAsUnicode( m_pIStream, puChar))) { goto Exit; } } Exit: return( rc); } IF_PosIStream * m_pIStream; FLMUINT m_uiLanguage; FLMBOOL m_bCaseSensitive; FLMUINT m_uiCompareRules; FLMUINT64 m_ui64EndOfLeadingSpacesPos; FLMBOOL m_bMayHaveWildCards; FLMBOOL m_bUnicodeStream; FLMUNICODE m_uNextChar; }; /**************************************************************************** Desc: Decodes an ASCII base64 stream to binary ****************************************************************************/ class FLMEXP F_Base64DecoderIStream : public IF_IStream { public: F_Base64DecoderIStream() { m_pIStream = NULL; m_uiBufOffset = 0; m_uiAvailBytes = 0; } virtual ~F_Base64DecoderIStream() { closeStream(); } RCODE FLMAPI openStream( IF_IStream * pIStream); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FINLINE RCODE FLMAPI closeStream( void) { RCODE rc = NE_FLM_OK; if( m_pIStream) { if( m_pIStream->getRefCount() == 1) { rc = m_pIStream->closeStream(); } m_pIStream->Release(); m_pIStream = NULL; } m_uiAvailBytes = 0; m_uiBufOffset = 0; return( rc); } private: IF_IStream * m_pIStream; FLMUINT m_uiBufOffset; FLMUINT m_uiAvailBytes; FLMBYTE m_ucBuffer[ 8]; }; /**************************************************************************** Desc: Encodes a binary input stream into ASCII base64. ****************************************************************************/ class FLMEXP F_Base64EncoderIStream : public IF_IStream { public: F_Base64EncoderIStream() { m_pIStream = NULL; } virtual ~F_Base64EncoderIStream() { closeStream(); } RCODE FLMAPI openStream( IF_IStream * pIStream, FLMBOOL bLineBreaks); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FINLINE RCODE FLMAPI closeStream( void) { RCODE rc = NE_FLM_OK; if( m_pIStream) { if( m_pIStream->getRefCount() == 1) { rc = m_pIStream->closeStream(); } m_pIStream->Release(); m_pIStream = NULL; } return( rc); } private: IF_IStream * m_pIStream; FLMBOOL m_bInputExhausted; FLMBOOL m_bLineBreaks; FLMBOOL m_bPriorLineEnd; FLMUINT m_uiBase64Count; FLMUINT m_uiBufOffset; FLMUINT m_uiAvailBytes; FLMBYTE m_ucBuffer[ 8]; }; /**************************************************************************** Desc: ****************************************************************************/ typedef struct LZWODictItem { LZWODictItem * pNext; FLMUINT16 ui16Code; FLMUINT16 ui16ParentCode; FLMBYTE ucChar; } LZWODictItem; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_CompressingOStream : public IF_OStream { public: F_CompressingOStream() { m_pool.poolInit( 64 * 1024); m_pOStream = NULL; m_ppHashTbl = NULL; } virtual ~F_CompressingOStream() { closeStream(); } RCODE FLMAPI openStream( IF_OStream * pOStream); RCODE FLMAPI write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten); RCODE FLMAPI closeStream( void); private: FINLINE FLMUINT getHashBucket( FLMUINT16 ui16CurrentCode, FLMBYTE ucChar) { return( ((((FLMUINT)ui16CurrentCode) << 8) | ((FLMUINT)ucChar)) % m_uiHashTblSize); } LZWODictItem * findDictEntry( FLMUINT16 ui16CurrentCode, FLMBYTE ucChar); F_Pool m_pool; IF_OStream * m_pOStream; LZWODictItem ** m_ppHashTbl; FLMUINT m_uiHashTblSize; FLMUINT m_uiLastRatio; FLMUINT m_uiBestRatio; FLMUINT m_uiCurrentBytesIn; FLMUINT m_uiTotalBytesIn; FLMUINT m_uiCurrentBytesOut; FLMUINT m_uiTotalBytesOut; FLMBOOL m_bStopCompression; FLMUINT16 m_ui16CurrentCode; FLMUINT16 m_ui16FreeCode; }; typedef struct LZWIDictItem { LZWODictItem * pNext; FLMUINT16 ui16ParentCode; FLMBYTE ucChar; } LZWIDictItem; /**************************************************************************** Desc: ****************************************************************************/ class FLMEXP F_UncompressingIStream : public IF_IStream { public: F_UncompressingIStream() { m_pIStream = NULL; m_pDict = NULL; m_pucDecodeBuffer = NULL; } virtual ~F_UncompressingIStream() { closeStream(); } RCODE FLMAPI openStream( IF_IStream * pIStream); RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); RCODE FLMAPI closeStream( void); private: RCODE readCode( FLMUINT16 * pui16Code); RCODE decodeToBuffer( FLMUINT16 ui16Code); IF_IStream * m_pIStream; LZWIDictItem * m_pDict; FLMBYTE * m_pucDecodeBuffer; FLMUINT m_uiDecodeBufferSize; FLMUINT m_uiDecodeBufferOffset; FLMUINT16 m_ui16FreeCode; FLMUINT16 m_ui16LastCode; FLMBOOL m_bStopCompression; FLMBOOL m_bEndOfStream; }; /*************************************************************************** Desc: Dynamic result sets ***************************************************************************/ typedef int (* F_DYNSET_COMPARE_FUNC)( void * pvData1, void * pvData2, void * pvUserData); typedef enum { ACCESS_HASH, ACCESS_BTREE_LEAF, ACCESS_BTREE_ROOT, ACCESS_BTREE_NON_LEAF } eDynRSetBlkTypes; class F_FixedBlk : public F_Object { public: F_FixedBlk(); virtual ~F_FixedBlk() { } eDynRSetBlkTypes blkType() { return m_eBlkType; } virtual RCODE getCurrent( void * pvEntryBuffer) = 0; virtual RCODE getFirst( void * pvEntryBuffer) = 0; virtual RCODE getLast( void * pvEntryBuffer) = 0; virtual RCODE getNext( void * pvEntryBuffer) = 0; virtual FLMUINT getTotalEntries() = 0; virtual RCODE insert( void * pvEntry) = 0; FINLINE FLMBOOL isDirty( void) { return( m_bDirty); } virtual RCODE search( void * pvEntry, void * pvFoundEntry = NULL) = 0; void setCompareFunc( F_DYNSET_COMPARE_FUNC fnCompare, void * pvUserData) { m_fnCompare = fnCompare; m_pvUserData = pvUserData; } protected: F_DYNSET_COMPARE_FUNC m_fnCompare; void * m_pvUserData; eDynRSetBlkTypes m_eBlkType; FLMUINT m_uiEntrySize; FLMUINT m_uiNumSlots; FLMUINT m_uiPosition; FLMBOOL m_bDirty; FLMBYTE * m_pucBlkBuf; }; class FLMEXP F_DynSearchSet : public F_Object { public: F_DynSearchSet() { m_fnCompare = NULL; m_pvUserData = NULL; m_uiEntrySize = 0; m_pAccess = NULL; } virtual ~F_DynSearchSet() { if( m_pAccess) { m_pAccess->Release(); } } RCODE FLMAPI setup( char * pszTmpDir, FLMUINT uiEntrySize); FINLINE void FLMAPI setCompareFunc( F_DYNSET_COMPARE_FUNC fnCompare, void * pvUserData) { m_fnCompare = fnCompare; m_pvUserData = pvUserData; m_pAccess->setCompareFunc( fnCompare, pvUserData); } RCODE FLMAPI addEntry( void * pvEntry); FINLINE RCODE FLMAPI findMatch( void * pvMatchEntry, void * pvFoundEntry) { return m_pAccess->search( pvMatchEntry, pvFoundEntry); } FINLINE FLMUINT FLMAPI getEntrySize( void) { return m_uiEntrySize; } FINLINE FLMUINT FLMAPI getTotalEntries( void) { return( m_pAccess->getTotalEntries()); } private: F_DYNSET_COMPARE_FUNC m_fnCompare; void * m_pvUserData; FLMUINT m_uiEntrySize; F_FixedBlk * m_pAccess; char m_szFileName[ F_PATH_MAX_SIZE]; }; /*************************************************************************** Desc: Hash tables ***************************************************************************/ typedef struct { void * pFirstInBucket; FLMUINT uiHashValue; } F_BUCKET; RCODE FLMAPI f_allocHashTable( FLMUINT uiHashTblSize, F_BUCKET ** ppHashTblRV); FLMUINT FLMAPI f_strHashBucket( char * pszStr, F_BUCKET * pHashTbl, FLMUINT uiNumBuckets); FLMUINT FLMAPI f_binHashBucket( void * pBuf, FLMUINT uiBufLen, F_BUCKET * pHashTbl, FLMUINT uiNumBuckets); /*************************************************************************** Desc: ***************************************************************************/ class F_HashObject : virtual public F_Object { public: #define F_INVALID_HASH_BUCKET (~((FLMUINT)0)) F_HashObject() { m_pNextInBucket = NULL; m_pPrevInBucket = NULL; m_pNextInGlobal = NULL; m_pPrevInGlobal = NULL; m_uiHashBucket = F_INVALID_HASH_BUCKET; m_ui32KeyCRC = 0; m_uiTimeAdded = 0; } virtual ~F_HashObject() { flmAssert( !m_pNextInBucket); flmAssert( !m_pPrevInBucket); flmAssert( !m_pNextInGlobal); flmAssert( !m_pPrevInGlobal); flmAssert( !getRefCount()); } virtual const void * FLMAPI getKey( void) = 0; virtual FLMUINT FLMAPI getKeyLength( void) = 0; FINLINE FLMUINT FLMAPI getKeyCRC( void) { return( m_ui32KeyCRC); } FINLINE FLMUINT FLMAPI getHashBucket( void) { return( m_uiHashBucket); } FINLINE F_HashObject * FLMAPI getNextInGlobal( void) { return( m_pNextInGlobal); } FINLINE F_HashObject * FLMAPI getNextInBucket( void) { return( m_pNextInBucket); } virtual FLMUINT FLMAPI getObjectType( void) = 0; protected: void setHashBucket( FLMUINT uiHashBucket) { m_uiHashBucket = uiHashBucket; } F_HashObject * m_pNextInBucket; F_HashObject * m_pPrevInBucket; F_HashObject * m_pNextInGlobal; F_HashObject * m_pPrevInGlobal; FLMUINT m_uiHashBucket; FLMUINT m_uiTimeAdded; FLMUINT32 m_ui32KeyCRC; friend class F_HashTable; }; /*************************************************************************** Desc: Hash tables ***************************************************************************/ class F_HashTable : public F_Object { public: F_HashTable(); virtual ~F_HashTable(); RCODE FLMAPI setupHashTable( FLMBOOL bMultithreaded, FLMUINT uiNumBuckets, FLMUINT uiMaxObjects); RCODE FLMAPI addObject( F_HashObject * pObject, FLMBOOL bAllowDuplicates = FALSE); RCODE FLMAPI getNextObjectInGlobal( F_HashObject ** ppObject); RCODE FLMAPI getNextObjectInBucket( F_HashObject ** ppObject); RCODE FLMAPI getObject( const void * pvKey, FLMUINT uiKeyLen, F_HashObject ** ppObject, FLMBOOL bRemove = FALSE); RCODE FLMAPI removeObject( void * pvKey, FLMUINT uiKeyLen); RCODE FLMAPI removeObject( F_HashObject * pObject); void FLMAPI removeAllObjects( void); void FLMAPI removeAgedObjects( FLMUINT uiMaxAge); FLMUINT FLMAPI getMaxObjects( void); RCODE FLMAPI setMaxObjects( FLMUINT uiMaxObjects); private: FLMUINT getHashBucket( const void * pvKey, FLMUINT uiLen, FLMUINT32 * pui32KeyCRC = NULL); void linkObject( F_HashObject * pObject, FLMUINT uiBucket); void unlinkObject( F_HashObject * pObject); RCODE findObject( const void * pvKey, FLMUINT uiKeyLen, F_HashObject ** ppObject); F_MUTEX m_hMutex; F_HashObject * m_pMRUObject; F_HashObject * m_pLRUObject; F_HashObject ** m_ppHashTable; FLMUINT m_uiBuckets; FLMUINT m_uiObjects; FLMUINT m_uiMaxObjects; }; /**************************************************************************** Desc: Process ID Functions ****************************************************************************/ FLMUINT f_getpid( void); #endif // FTK_H libxflaim-5.1.969/src/fsblk_u.cpp0000644000175000017500000001330010511001742020152 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains routines for handling blocks in the avail list - putting // blocks into the avail list and removing them from the avail list. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsblk_u.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /*************************************************************************** Desc: Take current avail block off of free list. Have db header point to next block in the chain. *****************************************************************************/ RCODE F_Database::blockUseNextAvail( F_Db * pDb, F_CachedBlock ** ppSCache // Returns pointer to cache structure. ) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = NULL; F_CachedBlock * pNextSCache = NULL; XFLM_DB_HDR * pDbHdr; pDbHdr = &m_uncommittedDbHdr; if (RC_BAD( rc = getBlock( pDb, NULL, pDb->m_uiFirstAvailBlkAddr, NULL, &pSCache))) { goto Exit; } // A corruption we have seen a couple of times is where a free // block points to itself in the free list. This will hang the machine // so this check has been added to verify that the block is a free block. if (pSCache->m_pBlkHdr->ui8BlkType != BT_FREE || isEncryptedBlk( pSCache->m_pBlkHdr)) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Log the block because we are changing it! if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } *ppSCache = pSCache; pDb->m_uiFirstAvailBlkAddr = (FLMUINT)pSCache->m_pBlkHdr->ui32NextBlkInChain; pDbHdr->ui32FirstAvailBlkAddr = (FLMUINT32)pDb->m_uiFirstAvailBlkAddr; pSCache->m_pBlkHdr->ui32NextBlkInChain = 0; // Set the next block's previous to zero. if (pDb->m_uiFirstAvailBlkAddr) { if (RC_BAD( rc = getBlock( pDb, NULL, pDb->m_uiFirstAvailBlkAddr, NULL, &pNextSCache))) { goto Exit; } if (pNextSCache->m_pBlkHdr->ui8BlkType != BT_FREE) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // Log the block because we are changing it if (RC_BAD( rc = logPhysBlk( pDb, &pNextSCache))) { goto Exit; } pNextSCache->m_pBlkHdr->ui32PrevBlkInChain = 0; ScaReleaseCache( pNextSCache, FALSE); pNextSCache = NULL; } Exit: if (RC_BAD( rc)) { if (pSCache) { ScaReleaseCache( pSCache, FALSE); } if (pNextSCache) { ScaReleaseCache( pNextSCache, FALSE); } } return( rc); } /*************************************************************************** Desc: Put a block into the avail list. Notes: This routine assumes that the block pointed to by pSCache has been locked into memory. Regardless of whether or not the block is actually freed on disk, its cache will be released. The cached block should NOT be accessed after a call to FSBlockFree. *****************************************************************************/ RCODE F_Database::blockFree( F_Db * pDb, F_CachedBlock * pSCache // Pointer to pointer of cache block // that is to be freed. NOTE: Regardless of whether // or not the block is actually freed, it will be // released. ) { RCODE rc = NE_XFLM_OK; F_BLK_HDR * pBlkHdr; F_CachedBlock * pFirstAvailSCache; XFLM_DB_HDR * pDbHdr; pDbHdr = &m_uncommittedDbHdr; // Log the block before modifying it. if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } pBlkHdr = pSCache->m_pBlkHdr; // Modify header to be an avail block. if (isEncryptedBlk( pBlkHdr)) { // If block was previously encrypted, need to zero it // out so that clear data will not be written to disk // when this avail block is written out. unsetBlockEncrypted( pBlkHdr); f_memset( ((FLMBYTE *)pBlkHdr) + SIZEOF_STD_BLK_HDR, 0, m_uiBlockSize - SIZEOF_STD_BLK_HDR); } pBlkHdr->ui8BlkType = BT_FREE; pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - SIZEOF_STD_BLK_HDR); // Modify back chain of first block in avail list, if any, // to point back to this block. if (pDbHdr->ui32FirstAvailBlkAddr) { // Read the block at head of avail list. if (RC_BAD( rc = getBlock( pDb, NULL, (FLMUINT)pDbHdr->ui32FirstAvailBlkAddr, NULL, &pFirstAvailSCache))) { goto Exit; } // Log the block if (RC_OK( rc = logPhysBlk( pDb, &pFirstAvailSCache))) { // Previous block pointer better be zero at this point. flmAssert( pFirstAvailSCache->m_pBlkHdr->ui32PrevBlkInChain == 0); pFirstAvailSCache->m_pBlkHdr->ui32PrevBlkInChain = pBlkHdr->ui32BlkAddr; } ScaReleaseCache( pFirstAvailSCache, FALSE); if (RC_BAD( rc)) { goto Exit; } } // Link block at head of avail list. pBlkHdr->ui32PrevBlkInChain = 0; pBlkHdr->ui32NextBlkInChain = pDbHdr->ui32FirstAvailBlkAddr; pDbHdr->ui32FirstAvailBlkAddr = pBlkHdr->ui32BlkAddr; pDb->m_uiFirstAvailBlkAddr = (FLMUINT)pBlkHdr->ui32BlkAddr; Exit: ScaReleaseCache( pSCache, FALSE); return( rc); } libxflaim-5.1.969/src/scache.cpp0000644000175000017500000061410510511001742017765 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Block Cache routines // // Tabs: 3 // // Copyright (c) 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: scache.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define MAX_BLOCKS_TO_SORT 500 #define FLM_MAX_IO_BUFFER_BLOCKS 16 FSTATIC void ScaNotify( F_NOTIFY_LIST_ITEM * pNotify, F_CachedBlock * pUseSCache, RCODE NotifyRc); #ifdef SCACHE_LINK_CHECKING FSTATIC void scaVerify( int iPlace); #else #define scaVerify(iPlace) #endif /*************************************************************************** Desc: Compare two cache blocks to determine which one has lower address. *****************************************************************************/ FINLINE FLMINT scaCompare( F_CachedBlock * pSCache1, F_CachedBlock * pSCache2) { if (FSAddrIsAtOrBelow( pSCache1->blkAddress(), pSCache2->blkAddress())) { flmAssert( pSCache1->blkAddress() != pSCache2->blkAddress()); return( -1); } else { return( 1); } } /*************************************************************************** Desc: Compare two cache blocks during a sort to determine which one has lower address. *****************************************************************************/ FINLINE FLMINT FLMAPI scaSortCompare( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2) { return( scaCompare( ((F_CachedBlock **)pvBuffer)[ uiPos1], ((F_CachedBlock **)pvBuffer)[ uiPos2])); } /*************************************************************************** Desc: Swap two entries in cache table during sort. *****************************************************************************/ FINLINE void FLMAPI scaSortSwap( void * pvBuffer, FLMUINT uiPos1, FLMUINT uiPos2) { F_CachedBlock ** ppSCacheTbl = (F_CachedBlock **)pvBuffer; F_CachedBlock * pTmpSCache = ppSCacheTbl[ uiPos1]; ppSCacheTbl[ uiPos1] = ppSCacheTbl[ uiPos2]; ppSCacheTbl[ uiPos2] = pTmpSCache; } /**************************************************************************** Desc: Link a cache block into the list of F_Database blocks that need one or more versions of the block to be logged. This routine assumes that the block cache mutex is locked. *****************************************************************************/ void F_CachedBlock::linkToLogList( void) { FLMUINT uiPrevBlkAddress; flmAssert( m_pDatabase); flmAssert( m_ui16Flags & CA_DIRTY); flmAssert( !(m_ui16Flags & (CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); flmAssert( !m_pPrevInReplaceList); flmAssert( !m_pNextInReplaceList); uiPrevBlkAddress = getPriorImageAddress(); if( uiPrevBlkAddress || !m_pNextInVersionList) { goto Exit; } if ((m_pNextInReplaceList = m_pDatabase->m_pFirstInLogList) != NULL) { m_pNextInReplaceList->m_pPrevInReplaceList = this; } else { m_pDatabase->m_pLastInLogList = this; } setFlags( CA_IN_FILE_LOG_LIST); m_pPrevInReplaceList = NULL; m_pDatabase->m_pFirstInLogList = this; m_pDatabase->m_uiLogListCount++; Exit: return; } /**************************************************************************** Desc: Unlinks a cache block from the F_Database's log list NOTE: This function assumes that the block cache mutex is locked. ****************************************************************************/ void F_CachedBlock::unlinkFromLogList( void) { flmAssert( m_pDatabase); flmAssert( m_ui16Flags & CA_IN_FILE_LOG_LIST); flmAssert( m_pDatabase->m_uiLogListCount); if (m_pNextInReplaceList) { m_pNextInReplaceList->m_pPrevInReplaceList = m_pPrevInReplaceList; } else { m_pDatabase->m_pLastInLogList = m_pPrevInReplaceList; } if (m_pPrevInReplaceList) { m_pPrevInReplaceList->m_pNextInReplaceList = m_pNextInReplaceList; } else { m_pDatabase->m_pFirstInLogList = m_pNextInReplaceList; } m_pNextInReplaceList = NULL; m_pPrevInReplaceList = NULL; clearFlags( CA_IN_FILE_LOG_LIST); m_pDatabase->m_uiLogListCount--; } /**************************************************************************** Desc: Link a cache block into the list of F_Database blocks that are beyond the EOF. The blocks are linked to the end of the list so that they are kept in ascending order. NOTE: This function assumes that the block cache mutex is locked. *****************************************************************************/ void F_CachedBlock::linkToNewList( void) { flmAssert( m_pDatabase); flmAssert( m_ui64HighTransID == ~((FLMUINT64)0)); flmAssert( m_ui16Flags & CA_DIRTY); flmAssert( !(m_ui16Flags & (CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); flmAssert( !m_pPrevInReplaceList); flmAssert( !m_pNextInReplaceList); if ((m_pPrevInReplaceList = m_pDatabase->m_pLastInNewList) != NULL) { flmAssert( scaCompare( m_pDatabase->m_pLastInNewList, this) < 0); m_pPrevInReplaceList->m_pNextInReplaceList = this; } else { m_pDatabase->m_pFirstInNewList = this; } m_pNextInReplaceList = NULL; m_pDatabase->m_pLastInNewList = this; setFlags( CA_IN_NEW_LIST); m_pDatabase->m_uiNewCount++; } /**************************************************************************** Desc: Unlinks a cache block from the F_Database's new block list NOTE: This function assumes that the block cache mutex is locked. ****************************************************************************/ void F_CachedBlock::unlinkFromNewList( void) { flmAssert( m_pDatabase); flmAssert( m_ui16Flags & CA_IN_NEW_LIST); flmAssert( m_pDatabase->m_uiNewCount); if (m_pNextInReplaceList) { m_pNextInReplaceList->m_pPrevInReplaceList = m_pPrevInReplaceList; } else { m_pDatabase->m_pLastInNewList = m_pPrevInReplaceList; } if (m_pPrevInReplaceList) { m_pPrevInReplaceList->m_pNextInReplaceList = m_pNextInReplaceList; } else { m_pDatabase->m_pFirstInNewList = m_pNextInReplaceList; } m_pNextInReplaceList = NULL; m_pPrevInReplaceList = NULL; clearFlags( CA_IN_NEW_LIST); m_pDatabase->m_uiNewCount--; } /**************************************************************************** Desc: Unlinks a cache block from the replace list NOTE: This function assumes that the block cache mutex is locked. ****************************************************************************/ void F_CachedBlock::unlinkFromReplaceList( void) { FLMUINT uiSize = memSize(); flmAssert( !m_ui16Flags); if( m_pNextInReplaceList) { m_pNextInReplaceList->m_pPrevInReplaceList = m_pPrevInReplaceList; } else { gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = m_pPrevInReplaceList; } if( m_pPrevInReplaceList) { m_pPrevInReplaceList->m_pNextInReplaceList = m_pNextInReplaceList; } else { gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = m_pNextInReplaceList; } m_pNextInReplaceList = NULL; m_pPrevInReplaceList = NULL; flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount); gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount--; flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes >= uiSize); gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes -= uiSize; } /**************************************************************************** Desc: Check hash links. This routine assumes that the block cache mutex has already been locked. ****************************************************************************/ #ifdef SCACHE_LINK_CHECKING void F_CachedBlock::checkHashLinks( F_CachedBlock ** ppSCacheBucket) { F_CachedBlock * pBlock; if (!m_pDatabase) { f_breakpoint( 1); } if (m_pPrevInVersionList) { f_breakpoint( 2); } if (m_pNextInVersionList == this) { f_breakpoint( 3); } if (m_pPrevInVersionList == this) { f_breakpoint( 4); } // Make sure that the block isn't added into the list a second time. for (pBlock = *ppSCacheBucket; pBlock; pBlock = pBlock->m_pNextInHashBucket) { if (this == pBlock) { f_breakpoint( 5); } } // Make sure the block is not in the transaction // log list. for (pBlock = m_pDatabase->getTransLogList(); pBlock; pBlock = pBlock->m_pNextInHashBucket) { if (this == pBlock) { f_breakpoint( 6); } } } #endif /**************************************************************************** Desc: Unlink a cache block from its hash bucket. This routine assumes that the block cache mutex has already been locked. ****************************************************************************/ #ifdef SCACHE_LINK_CHECKING void F_CachedBlock::checkHashUnlinks( F_CachedBlock ** ppSCacheBucket) { F_CachedBlock * pTmpSCache; // Make sure the cache is actually in this bucket pTmpSCache = *ppSCacheBucket; while (pTmpSCache && pTmpSCache != this) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (!pTmpSCache) { f_breakpoint( 333); } for (pTmpSCache = m_pDatabase->getTransLogList(); pTmpSCache; pTmpSCache = pTmpSCache->m_pNextInHashBucket) { if (this == pTmpSCache) { f_breakpoint( 334); } } } #endif /**************************************************************************** Desc: Link a cache block to its F_Database structure. This routine assumes that the block cache mutex has already been locked. ****************************************************************************/ void F_CachedBlock::linkToDatabase( F_Database * pDatabase) { flmAssert( !m_pDatabase); if (m_ui16Flags & CA_WRITE_PENDING) { if ((m_pNextInDatabase = pDatabase->m_pPendingWriteList) != NULL) { m_pNextInDatabase->m_pPrevInDatabase = this; } pDatabase->m_pPendingWriteList = this; setFlags( CA_IN_WRITE_PENDING_LIST); } else { F_CachedBlock * pPrevSCache; F_CachedBlock * pNextSCache; // Link at end of dirty blocks. if (pDatabase->m_pLastDirtyBlk) { pPrevSCache = pDatabase->m_pLastDirtyBlk; pNextSCache = pPrevSCache->m_pNextInDatabase; } else { // No dirty blocks, so link to head of list. pPrevSCache = NULL; pNextSCache = pDatabase->m_pSCacheList; } // If the block is dirty, change the last dirty block pointer. if (m_ui16Flags & CA_DIRTY) { pDatabase->m_pLastDirtyBlk = this; } if ((m_pNextInDatabase = pNextSCache) != NULL) { pNextSCache->m_pPrevInDatabase = this; } if ((m_pPrevInDatabase = pPrevSCache) != NULL) { pPrevSCache->m_pNextInDatabase = this; } else { pDatabase->m_pSCacheList = this; } } m_pDatabase = pDatabase; } /**************************************************************************** Desc: Unlink a cache block from its F_Database object. This routine assumes that the block cache mutex has already been locked. ****************************************************************************/ void F_CachedBlock::unlinkFromDatabase( void) { flmAssert( m_pDatabase); if (m_ui16Flags & CA_IN_WRITE_PENDING_LIST) { if (m_pPrevInDatabase) { m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; } else { m_pDatabase->m_pPendingWriteList = m_pNextInDatabase; } if (m_pNextInDatabase) { m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; } clearFlags( CA_IN_WRITE_PENDING_LIST); } else { if (this == m_pDatabase->m_pLastDirtyBlk) { m_pDatabase->m_pLastDirtyBlk = m_pDatabase->m_pLastDirtyBlk->m_pPrevInDatabase; #ifdef FLM_DEBUG // If m_pLastDirtyBlk is non-NULL, it had better be pointing // to a dirty block. if (m_pDatabase->m_pLastDirtyBlk) { flmAssert( m_pDatabase->m_pLastDirtyBlk->m_ui16Flags & CA_DIRTY); } #endif } if (m_pNextInDatabase) { m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; } if (m_pPrevInDatabase) { m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; } else { m_pDatabase->m_pSCacheList = m_pNextInDatabase; } m_pNextInDatabase = NULL; m_pPrevInDatabase = NULL; } m_pDatabase = NULL; } /**************************************************************************** Desc: Link a cache block to the free list. This routine assumes that the block cache mutex is locked. ****************************************************************************/ void F_CachedBlock::linkToFreeList( FLMUINT uiFreeTime) { flmAssert( !m_ui16Flags); flmAssert( !m_pDatabase); flmAssert( !m_pPrevInReplaceList); flmAssert( !m_pNextInReplaceList); if (m_ui64HighTransID != ~((FLMUINT64)0)) { // Set the transaction ID to ~((FLMUINT64)0) so that the old version // counts will be decremented if this is an old version // of the block. Also, we want the transaction ID to be // ~((FLMUINT64)0) so that when the block is re-used in allocBlock() // the old version counts won't be decremented again. setTransID( ~((FLMUINT64)0)); } if ((m_pNextInDatabase = gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree) != NULL) { m_pNextInDatabase->m_pPrevInDatabase = this; } else { gv_XFlmSysData.pBlockCacheMgr->m_pLastFree = this; } m_pPrevInDatabase = NULL; m_uiBlkAddress = uiFreeTime; m_ui16Flags = CA_FREE; gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree = this; gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes += memSize(); gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount++; } /**************************************************************************** Desc: Unlink a cache block from the free list. This routine assumes that the block cache mutex has already been locked. ****************************************************************************/ void F_CachedBlock::unlinkFromFreeList( void) { FLMUINT uiSize = memSize(); flmAssert( !m_uiUseCount); flmAssert( m_ui16Flags == CA_FREE); if( m_pNextInDatabase) { m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; } else { gv_XFlmSysData.pBlockCacheMgr->m_pLastFree = m_pPrevInDatabase; } if( m_pPrevInDatabase) { m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; } else { gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree = m_pNextInDatabase; } m_pNextInDatabase = NULL; m_pPrevInDatabase = NULL; m_ui16Flags &= ~CA_FREE; flmAssert( !m_ui16Flags); flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes >= uiSize); gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes -= uiSize; flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount); gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount--; } /**************************************************************************** Desc: This routine notifies threads waiting for a pending read or write to complete. NOTE: This routine assumes that the block cache mutex is already locked. ****************************************************************************/ FSTATIC void ScaNotify( F_NOTIFY_LIST_ITEM * pNotify, F_CachedBlock * pUseSCache, RCODE NotifyRc) { while( pNotify) { F_SEM hSem; *(pNotify->pRc) = NotifyRc; if( RC_OK( NotifyRc)) { if( pNotify->pvData) { *((F_CachedBlock **)pNotify->pvData) = pUseSCache; } if( pUseSCache) { pUseSCache->useForThread( pNotify->uiThreadId); } } hSem = pNotify->hSem; pNotify = pNotify->pNext; f_semSignal( hSem); } } /**************************************************************************** Desc: This routine logs information about changes to a cache block's flags ****************************************************************************/ #ifdef FLM_DBG_LOG void F_CachedBlock::logFlgChange( FLMUINT16 ui16OldFlags, char cPlace) { char szBuf [60]; char * pszTmp; FLMUINT16 ui16NewFlags = m_ui16Flags; szBuf [0] = cPlace; szBuf [1] = '-'; f_strcpy( &szBuf[2], "FLG:"); pszTmp = &szBuf [6]; if (ui16OldFlags & CA_DIRTY) { *pszTmp++ = ' '; if (!(ui16NewFlags & CA_DIRTY)) { *pszTmp++ = '-'; } *pszTmp++ = 'D'; *pszTmp = 0; } else if (ui16NewFlags & CA_DIRTY) { *pszTmp++ = ' '; f_strcpy( pszTmp, "+D"); pszTmp += 2; } if (ui16OldFlags & CA_WRITE_INHIBIT) { *pszTmp++ = ' '; if (!(ui16NewFlags & CA_WRITE_INHIBIT)) { *pszTmp++ = '-'; } f_strcpy( pszTmp, "WI"); pszTmp += 2; } else if (ui16NewFlags & CA_WRITE_INHIBIT) { *pszTmp++ = ' '; f_strcpy( pszTmp, "+WI"); pszTmp += 3; } if (ui16OldFlags & CA_READ_PENDING) { *pszTmp++ = ' '; if (!(ui16NewFlags & CA_READ_PENDING)) { *pszTmp++ = '-'; } f_strcpy( pszTmp, "RD"); pszTmp += 2; } else if (ui16NewFlags & CA_READ_PENDING) { *pszTmp++ = ' '; f_strcpy( pszTmp, "+RD"); pszTmp += 3; } if (ui16OldFlags & CA_WRITE_TO_LOG) { *pszTmp++ = ' '; if (!(ui16NewFlags & CA_WRITE_TO_LOG)) { *pszTmp++ = '-'; } f_strcpy( pszTmp, "WL"); pszTmp += 2; } else if (ui16NewFlags & CA_WRITE_TO_LOG) { *pszTmp++ = ' '; f_strcpy( pszTmp, "+WL"); pszTmp += 3; } if (ui16OldFlags & CA_LOG_FOR_CP) { *pszTmp++ = ' '; if (!(ui16NewFlags & CA_LOG_FOR_CP)) { *pszTmp++ = '-'; } f_strcpy( pszTmp, "CP"); pszTmp += 2; } else if (ui16NewFlags & CA_LOG_FOR_CP) { *pszTmp++ = ' '; f_strcpy( pszTmp, "+CP"); pszTmp += 3; } if (ui16OldFlags & CA_WAS_DIRTY) { *pszTmp++ = ' '; if (!(ui16NewFlags & CA_WAS_DIRTY)) { *pszTmp++ = '-'; } f_strcpy( pszTmp, "WD"); pszTmp += 2; } else if (ui16NewFlags & CA_WAS_DIRTY) { *pszTmp++ = ' '; f_strcpy( pszTmp, "+WD"); pszTmp += 3; } if (pszTmp != &szBuf [6]) { flmDbgLogWrite( m_pDatabase, m_uiBlkAddress, 0, getLowTransID(), szBuf); } } #endif /**************************************************************************** Desc: This routine frees the memory for a cache block and decrements the necessary counters in the cache manager. NOTE: This routine assumes that the block cache mutex is already locked. ****************************************************************************/ F_CachedBlock::~F_CachedBlock() { FLMUINT uiSize = memSize(); if (m_ui64HighTransID != ~((FLMUINT64)0)) { flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes >= uiSize); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes -= uiSize; flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount--; } flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiByteCount >= uiSize); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiByteCount -= uiSize; flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCount); gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCount--; if (shouldRehash( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCount, gv_XFlmSysData.pBlockCacheMgr->m_uiNumBuckets)) { if (checkHashFailTime( &gv_XFlmSysData.pBlockCacheMgr->m_uiHashFailTime)) { (void)gv_XFlmSysData.pBlockCacheMgr->rehash(); } } } /**************************************************************************** Desc: This routine unlinks a cache block from all of its lists and then optionally frees it. NOTE: This routine assumes that the block cache mutex is already locked. ****************************************************************************/ void F_CachedBlock::unlinkCache( FLMBOOL bFreeIt, RCODE NotifyRc) { #ifdef FLM_DEBUG SCACHE_USE * pUse; #endif // Cache block better not be dirty and better not need to be written // to the log. #ifdef FLM_DEBUG if( RC_OK( NotifyRc)) { flmAssert (!(m_ui16Flags & (CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); } #endif unlinkFromGlobalList(); #ifdef FLM_DBG_LOG flmDbgLogWrite( m_pDatabase, m_uiBlkAddress, 0, getLowTransID(), "UNLINK"); #endif // If cache block has no previous versions linked to it, it // is in the hash bucket and needs to be unlinked from it. // Otherwise, it only needs to be unlinked from the version list. if (m_pDatabase) { if (!m_pPrevInVersionList) { F_CachedBlock ** ppSCacheBucket; ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_pDatabase->getSigBitsInBlkSize(), (FLMUINT)m_uiBlkAddress); unlinkFromHashBucket( ppSCacheBucket); if (m_pNextInVersionList) { // Older version better not be needing to be logged #ifdef FLM_DEBUG if( RC_OK( NotifyRc)) { flmAssert( !(m_pNextInVersionList->m_ui16Flags & (CA_WRITE_TO_LOG | CA_DIRTY | CA_WAS_DIRTY | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); } #endif m_pNextInVersionList->m_pPrevInVersionList = NULL; m_pNextInVersionList->linkToHashBucket( ppSCacheBucket); m_pNextInVersionList->verifyCache( 2100); m_pNextInVersionList = NULL; } } else { verifyCache( 2000); savePrevBlkAddress(); m_pPrevInVersionList->m_pNextInVersionList = m_pNextInVersionList; m_pPrevInVersionList->verifyCache( 2200); if (m_pNextInVersionList) { // Older version better not be dirty or not yet logged. #ifdef FLM_DEBUG if( RC_OK( NotifyRc)) { flmAssert( !(m_pNextInVersionList->m_ui16Flags & (CA_WRITE_TO_LOG | CA_DIRTY | CA_WAS_DIRTY))); } #endif m_pNextInVersionList->m_pPrevInVersionList = m_pPrevInVersionList; m_pNextInVersionList->verifyCache( 2300); } m_pNextInVersionList = NULL; m_pPrevInVersionList = NULL; } #ifdef SCACHE_LINK_CHECKING // Verify that the thing is not in a hash bucket. { F_CachedBlock ** ppSCacheBucket; F_CachedBlock * pTmpSCache; ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_pDatabase->getSigBitsInBlkSize(), m_uiBlkAddress); pTmpSCache = *ppSCacheBucket; while (pTmpSCache && this != pTmpSCache) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (pTmpSCache) { f_breakpoint( 4); } } #endif unlinkFromDatabase(); } if (bFreeIt) { // Free the notify list associated with the cache block. // NOTE: If there is actually a notify list, NotifyRc WILL ALWAYS // be something other than NE_XFLM_OK. If there is a notify list, // the notified threads will thus get a non-OK return code // in every case. #ifdef FLM_DEBUG if( NotifyRc == NE_XFLM_OK) { flmAssert( m_pNotifyList == NULL); } #endif ScaNotify( m_pNotifyList, NULL, NotifyRc); m_pNotifyList = NULL; #ifdef FLM_DEBUG // Free the use list associated with the cache block pUse = m_pUseList; while (pUse) { SCACHE_USE * pTmp; pTmp = pUse; pUse = pUse->pNext; f_free( &pTmp); } #endif delete this; } } /**************************************************************************** Desc: Unlink all log blocks for a file that were logged during the transaction. NOTE: This is only called when a transaction is aborted. WHEN A TRANSACTION IS ABORTED, THIS FUNCTION SHOULD BE CALLED BEFORE FREEING DIRTY BLOCKS. OTHERWISE, THE m_pPrevInVersionList POINTER WILL BE NULL AND WILL CAUSE AN ABEND WHEN IT IS ACCESSED. NOTE: This routine assumes that the block cache mutex has been locked. ****************************************************************************/ void F_Database::unlinkTransLogBlocks( void) { F_CachedBlock * pSCache; F_CachedBlock * pNextSCache; pSCache = m_pTransLogList; while (pSCache) { #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags = pSCache->m_ui16Flags; #endif if (pSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) { flmAssert( m_uiLogCacheCount); m_uiLogCacheCount--; } pSCache->clearFlags( CA_WRITE_TO_LOG | CA_LOG_FOR_CP); pNextSCache = pSCache->m_pNextInHashBucket; if (pSCache->m_ui16Flags & CA_WAS_DIRTY) { flmAssert( this == pSCache->m_pDatabase); pSCache->setDirtyFlag( this); pSCache->clearFlags( CA_WAS_DIRTY); // Move the block into the dirty blocks. pSCache->unlinkFromDatabase(); pSCache->linkToDatabase( this); } #ifdef FLM_DBG_LOG pSCache->logFlgChange( ui16OldFlags, 'A'); #endif // Perhaps we don't really need to set these pointers to NULL, // but it helps keep things clean. pSCache->m_pNextInHashBucket = NULL; pSCache->m_pPrevInHashBucket = NULL; pSCache = pNextSCache; } m_pTransLogList = NULL; } /**************************************************************************** Desc: Unlink a cache block from the list of cache blocks that are in the log list for the current transaction. ****************************************************************************/ void F_CachedBlock::unlinkFromTransLogList( void) { #ifdef SCACHE_LINK_CHECKING // Make sure the block is not in a hash bucket { F_CachedBlock ** ppSCacheBucket; F_CachedBlock * pTmpSCache; ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_pDatabase->m_uiSigBitsInBlkSize, m_uiBlkAddress); pTmpSCache = *ppSCacheBucket; while (pTmpSCache && pTmpSCache != this) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (pTmpSCache) { f_breakpoint( 1001); } // Make sure the block is in the log list. pTmpSCache = m_pDatabase->m_pTransLogList; while (pTmpSCache && pTmpSCache != this) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (!pTmpSCache) { f_breakpoint( 1002); } } #endif if (m_pPrevInHashBucket) { m_pPrevInHashBucket->m_pNextInHashBucket = m_pNextInHashBucket; } else { m_pDatabase->m_pTransLogList = m_pNextInHashBucket; } if (m_pNextInHashBucket) { m_pNextInHashBucket->m_pPrevInHashBucket = m_pPrevInHashBucket; } m_pNextInHashBucket = NULL; m_pPrevInHashBucket = NULL; } /**************************************************************************** Desc: The block pointed to by pSCache is about to be removed from from the version list for a particular block address because it is no longer needed. Before doing that, the previous block address should be moved to the next newer version's block header so that it will not be lost, but only if the next newer version's block header is not already pointing to a prior version of the block. This method assumes the block cache mutex is locked. ****************************************************************************/ void F_CachedBlock::savePrevBlkAddress( void) { FLMUINT uiPrevBlkAddress = getPriorImageAddress(); F_CachedBlock * pNewerSCache; FLMUINT uiNewerBlkPrevAddress; // NOTE: If a block is being read in from disk, it has to have a // previous block address in its header. Otherwise, it could never // have been written out to disk and removed from cache in the first // place. This is obvious for versions being read from the rollback // log - it would be impossible to retrieve them from the rollback // log if they weren't already part of a version chain! It is also // true for the most current version of a block. The most current // version of a block can never be written out and removed from // cache without having a pointer to the chain of older versions that // may still be needed by read transactions - or to rollback the // transaction - or to recover a checkpoint. if ((uiPrevBlkAddress) && ((pNewerSCache = m_pPrevInVersionList) != NULL) && (!(pNewerSCache->m_ui16Flags & CA_READ_PENDING))) { // Only move the older version's previous block address to the // newer version, if the newer version doesn't already have a // previous block address. Also need to set the previous // transaction ID. // // NOTE: The newer block may or may not be dirty. It is OK // to change the prior version address in the header of a // non-dirty block in this case. This is because the block // may or may not be written out to the roll-back log. If it // is, we want to make sure it has the correct prior version // address. If it isn't ever written out to the log, it // will eventually fall out of cache because it is no longer // needed. uiNewerBlkPrevAddress = pNewerSCache->getPriorImageAddress(); if (!uiNewerBlkPrevAddress) { // Need to temporarily use the newer version of the block // before changing its prior image block address. pNewerSCache->useForThread( 0); flmAssert( uiPrevBlkAddress); pNewerSCache->m_pBlkHdr->ui32PriorBlkImgAddr = (FLMUINT32)uiPrevBlkAddress; // Need to remove the newer block from the file log // list, since it no longer needs to be logged if( pNewerSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) { pNewerSCache->unlinkFromLogList(); } pNewerSCache->releaseForThread(); } } } /**************************************************************************** Desc: See if we should force a checkpoint. ****************************************************************************/ FINLINE FLMBOOL scaSeeIfForceCheckpoint( FLMUINT uiCurrTime, FLMUINT uiLastCheckpointTime, CP_INFO * pCPInfo) { if (FLM_ELAPSED_TIME( uiCurrTime, uiLastCheckpointTime) >= gv_XFlmSysData.uiMaxCPInterval) { if (pCPInfo) { pCPInfo->bForcingCheckpoint = TRUE; pCPInfo->iForceCheckpointReason = XFLM_CP_TIME_INTERVAL_REASON; pCPInfo->uiForceCheckpointStartTime = (FLMUINT)FLM_GET_TIMER(); } return( TRUE); } return( FALSE); } /**************************************************************************** Desc: Allocate the array that keeps track of blocks written or logged. ****************************************************************************/ RCODE F_Database::allocBlocksArray( FLMUINT uiNewSize, FLMBOOL bOneArray) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldSize = m_uiBlocksDoneArraySize; if (!uiNewSize) { uiNewSize = uiOldSize + 500; } // Re-alloc the array if (RC_BAD( rc = f_realloc( (FLMUINT)(uiNewSize * (sizeof( F_CachedBlock *) + sizeof( F_CachedBlock *))), &m_ppBlocksDone))) { goto Exit; } // Copy the old stuff into the two new areas of the new array. if (uiOldSize && !bOneArray) { f_memmove( &m_ppBlocksDone [uiNewSize], &m_ppBlocksDone [uiOldSize], uiOldSize * sizeof( F_CachedBlock *)); } // Set the new array size m_uiBlocksDoneArraySize = uiNewSize; Exit: return( rc); } /**************************************************************************** Desc: Write out log blocks to the rollback log for a database. ****************************************************************************/ RCODE F_Database::flushLogBlocks( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMBOOL bIsCPThread, FLMUINT uiMaxDirtyCache, FLMBOOL * pbForceCheckpoint, FLMBOOL * pbWroteAll) { RCODE rc = NE_XFLM_OK; FLMUINT uiLogEof; XFLM_DB_HDR * pDbHdr; F_CachedBlock * pTmpSCache; F_CachedBlock * pLastBlockToLog; F_CachedBlock * pFirstBlockToLog; F_CachedBlock * pDirtySCache; F_CachedBlock * pSavedSCache = NULL; FLMUINT uiDirtyCacheLeft; FLMUINT uiPrevBlkAddress; FLMBOOL bMutexLocked = TRUE; FLMBOOL bLoggedFirstBlk = FALSE; FLMBOOL bLoggedFirstCPBlk = FALSE; FLMUINT uiCurrTime; FLMUINT uiSaveEOFAddr; FLMUINT uiSaveFirstCPBlkAddr = 0; FLMBOOL bDone = FALSE; F_CachedBlock * pUsedSCache; F_CachedBlock * pNextSCache = NULL; F_CachedBlock ** ppUsedBlocks = (F_CachedBlock **)((m_ppBlocksDone) ? &m_ppBlocksDone [m_uiBlocksDoneArraySize] : (F_CachedBlock **)NULL); FLMUINT uiTotalLoggedBlocks = 0; FLMBOOL bForceCheckpoint = *pbForceCheckpoint; #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags; #endif m_uiCurrLogWriteOffset = 0; // Get the correct log header. If we are in an update transaction, // need to use the uncommitted log header. Otherwise, use the last // committed log header. pDbHdr = bIsCPThread ? &m_lastCommittedDbHdr : &m_uncommittedDbHdr; uiLogEof = (FLMUINT)pDbHdr->ui32RblEOF; f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); pDirtySCache = m_pFirstInLogList; uiCurrTime = (FLMUINT)FLM_GET_TIMER(); flmAssert( m_pCurrLogBuffer == NULL); uiDirtyCacheLeft = (m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize; for (;;) { if( !pDirtySCache) { bDone = TRUE; goto Write_Log_Blocks; } flmAssert( pDirtySCache->m_ui16Flags & CA_DIRTY); flmAssert( pDirtySCache->m_ui16Flags & CA_IN_FILE_LOG_LIST); // See if we should give up our write lock. Will do so if we // are not forcing a checkpoint and we have not exceeded the // maximum time since the last checkpoint AND the dirty cache // left is below the maximum. if (!bForceCheckpoint && bIsCPThread) { if (scaSeeIfForceCheckpoint( uiCurrTime, m_uiLastCheckpointTime, m_pCPInfo)) { bForceCheckpoint = TRUE; } else { if (m_pWriteLockObj->getWaiterCount() && uiDirtyCacheLeft <= uiMaxDirtyCache) { bDone = TRUE; *pbWroteAll = FALSE; goto Write_Log_Blocks; } } } uiPrevBlkAddress = pDirtySCache->getPriorImageAddress(); if (uiPrevBlkAddress) { // We shouldn't find anything in the log list that has // already been logged. However, if we do find something, // we will deal with it rather than returning an error. flmAssert( 0); pTmpSCache = pDirtySCache->m_pNextInReplaceList; pDirtySCache->unlinkFromLogList(); pDirtySCache = pTmpSCache; continue; } // The replace list pointers are used to maintain links // between items in the file log list pTmpSCache = pDirtySCache->m_pNextInVersionList; pLastBlockToLog = NULL; pFirstBlockToLog = NULL; // Grab the next block in the chain and see if we are done. // NOTE: pDirtySCache should not be accessed in the loop // below, because it has been changed to point to the // next cache block in the log list. If you need to access // the current block, use pSavedSCache. pSavedSCache = pDirtySCache; if ((pDirtySCache = pDirtySCache->m_pNextInReplaceList) == NULL) { bDone = TRUE; } #ifdef FLM_DEBUG else { flmAssert( pDirtySCache->m_ui16Flags & CA_DIRTY); flmAssert( pDirtySCache->m_ui16Flags & CA_IN_FILE_LOG_LIST); } #endif // Traverse down the list of prior versions of the block until // we hit one that has a prior version on disk. Throw out // any not marked as CA_WRITE_TO_LOG, CA_LOG_FOR_CP, and // not needed by a read transaction. while (pTmpSCache) { pNextSCache = pTmpSCache->m_pNextInVersionList; FLMBOOL bWillLog; uiPrevBlkAddress = pTmpSCache->getPriorImageAddress(); // If we determine that we need to log a block, put a use on the // newer version of the block to prevent other threads from verifying // their checksums while we are writing the older versions to // the log. This is because lgOutputBlock may modify information // in the newer block's header area. if (pTmpSCache->m_ui16Flags & CA_READ_PENDING) { // No need to go further down the list if this block is // being read in. If it is being read in, every older // version has a path to it - otherwise, it would never // have been written out so that it would need to be // read back in. break; } else if (pTmpSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) { bWillLog = TRUE; } // Even if the block is not needed by a read transaction, if it // has a use count, we need to log it so that all blocks between // pFirstBlockToLog and pLastBlockToLog are logged. This is // necessary to ensure that previous block addresses carry all // the way up the version chain. Also, the loop that does the // actual logging below assumes that the links from pLastBlockToLog // to pFirstBlockToLog will NOT be altered - even though the mutex // is not locked. This can only be ensured if every block between // the two points is guaranteed to be logged - which also guarantees // that it will not be moved out of the list - because of the fact // that some sort of logging bit has been set. // Note that a block can have a use count even though it is no // longer needed by a read transaction because another thread // may have temporarily put a use on it while traversing down // the chain - or for any number of other reasons. else if (pTmpSCache->neededByReadTrans() || pTmpSCache->m_uiUseCount) { bWillLog = TRUE; } else { bWillLog = FALSE; // Since the block is no longer needed by a read transaction, // and it is not in use, free it pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); } // Add this block to the list of those we will be logging if the // bWillLog flag got set above. if (bWillLog) { if (uiTotalLoggedBlocks >= m_uiBlocksDoneArraySize) { if (RC_BAD( rc = allocBlocksArray( 0, FALSE))) { goto Exit; } ppUsedBlocks = &m_ppBlocksDone [m_uiBlocksDoneArraySize]; } pLastBlockToLog = pTmpSCache; if (!pFirstBlockToLog) { pFirstBlockToLog = pLastBlockToLog; } pTmpSCache->m_pPrevInVersionList->useForThread( 0); pTmpSCache->useForThread( 0); m_ppBlocksDone [uiTotalLoggedBlocks] = pTmpSCache; ppUsedBlocks [uiTotalLoggedBlocks] = pTmpSCache->m_pPrevInVersionList; uiTotalLoggedBlocks++; } // No need to go further down the list if this block has // has a previous block address. if (uiPrevBlkAddress) { break; } pTmpSCache = pNextSCache; } #ifdef FLM_DEBUG while (pNextSCache) { flmAssert( !(pNextSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))); pNextSCache = pNextSCache->m_pNextInVersionList; } #endif // If nothing to log for the block, unlink it from the // log list. We check CA_IN_FILE_LOG_LIST again, because // savePrevBlkAddress may have been called during an // unlink above. savePrevBlkAddress will remove // the dirty cache block from the log list if it determines // that there is no need to log prior versions if (!pLastBlockToLog) { if (pSavedSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) { pSavedSCache->unlinkFromLogList(); } continue; } // Don't want the mutex locked while we do the I/O f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; // Write the log blocks to the rollback log. // Do all of the blocks from oldest to most current. Stop when we // hit the first log block. while (pLastBlockToLog) { FLMUINT uiLogPos = uiLogEof; if (RC_BAD( rc = lgOutputBlock( pDbStats, pSFileHdl, pLastBlockToLog, pLastBlockToLog->m_pPrevInVersionList->m_pBlkHdr, &uiLogEof))) { goto Exit; } if (pLastBlockToLog->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) { flmAssert( uiDirtyCacheLeft >= m_uiBlockSize); uiDirtyCacheLeft -= m_uiBlockSize; } // If we are logging a block for the current update // transaction, and this is the first block we have logged, // remember the block address where we logged it. if ((pLastBlockToLog->m_ui16Flags & CA_WRITE_TO_LOG) && !m_uiFirstLogBlkAddress) { // This better not EVER happen in the CP thread. flmAssert( !bIsCPThread); bLoggedFirstBlk = TRUE; m_uiFirstLogBlkAddress = uiLogPos; } // If we are logging the checkpoint version of the // block, and this is the first block we have logged // since the last checkpoint, remember its position so // that we can write it out to the log header when we // complete the checkpoint. if ((pLastBlockToLog->m_ui16Flags & CA_LOG_FOR_CP) && !m_uiFirstLogCPBlkAddress) { bLoggedFirstCPBlk = TRUE; m_uiFirstLogCPBlkAddress = uiLogPos; } // Break when we hit the first log block. if (pLastBlockToLog == pFirstBlockToLog) { break; } pLastBlockToLog = pLastBlockToLog->m_pPrevInVersionList; } // If we have logged some blocks, force the log header to be // updated on one of the following conditions: // 1. We have logged over 2000 blocks. We do this to keep // our array of logged blocks from growing too big. // 2. We are done logging. Write_Log_Blocks: if (uiTotalLoggedBlocks && // Must be at least one logged block (uiTotalLoggedBlocks >= 2000 || bDone)) { if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Flush the last log buffer, if not already flushed. if (m_uiCurrLogWriteOffset) { if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl))) { goto Exit; } } // If doing async, wait for pending writes to complete before writing // the log header. if (RC_BAD( rc = m_pBufferMgr->waitForAllPendingIO())) { goto Exit; } // Must wait for all RFL writes before writing out log header. if (!bIsCPThread) { (void)m_pRfl->seeIfRflWritesDone( hWaitSem, TRUE); } // Save the EOF address so we can restore it if // the write fails. uiSaveEOFAddr = (FLMUINT)pDbHdr->ui32RblEOF; pDbHdr->ui32RblEOF = (FLMUINT32)uiLogEof; if (bLoggedFirstCPBlk) { uiSaveFirstCPBlkAddr = pDbHdr->ui32RblFirstCPBlkAddr; pDbHdr->ui32RblFirstCPBlkAddr = (FLMUINT32)m_uiFirstLogCPBlkAddress; } if (RC_BAD( rc = writeDbHdr( pDbStats, pSFileHdl, pDbHdr, &m_checkpointDbHdr, FALSE))) { // If the write of the log header fails, // we want to restore the log header to what it was before // because we always use the log header from memory instead // of reading it from disk. The one on disk is only // current for many fields as of the last checkpoint. pDbHdr->ui32RblEOF = (FLMUINT32)uiSaveEOFAddr; if (bLoggedFirstCPBlk) { pDbHdr->ui32RblFirstCPBlkAddr = (FLMUINT32)uiSaveFirstCPBlkAddr; } goto Exit; } // Need to update the committed log header when we are operating in // an uncommitted transaction so that if the transaction turns out // to be empty, we will have the correct values in the committed // log header for subsequent transactions or the checkpoint thread // itself. if (!bIsCPThread) { m_lastCommittedDbHdr.ui32RblEOF = pDbHdr->ui32RblEOF; if (bLoggedFirstCPBlk) { m_lastCommittedDbHdr.ui32RblFirstCPBlkAddr = pDbHdr->ui32RblFirstCPBlkAddr; } } // Once the write is safe, we can reset things to start over. bLoggedFirstBlk = FALSE; bLoggedFirstCPBlk = FALSE; // Clean up the log blocks array - releasing blocks, etc. if (m_pCPInfo) { lockMutex(); m_pCPInfo->uiLogBlocksWritten += uiTotalLoggedBlocks; unlockMutex(); } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; while (uiTotalLoggedBlocks) { uiTotalLoggedBlocks--; pTmpSCache = m_ppBlocksDone [uiTotalLoggedBlocks]; #ifdef FLM_DBG_LOG ui16OldFlags = pTmpSCache->m_ui16Flags; #endif pUsedSCache = ppUsedBlocks [uiTotalLoggedBlocks]; // Newer block should be released, whether we succeeded // or not - because it will always have been used. pUsedSCache->releaseForThread(); pTmpSCache->releaseForThread(); // The current version of the block may have already been removed from // the file log list if more than one block in the version chain // needed to be logged. If the block is still in the file log list, // it will be removed. Otherwise, the prior image address better // be a non-zero value. if( pUsedSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) { pUsedSCache->unlinkFromLogList(); } flmAssert( pUsedSCache->getPriorImageAddress()); // Unlink from list of transaction log blocks if (pTmpSCache->m_ui16Flags & CA_WRITE_TO_LOG) { pTmpSCache->unlinkFromTransLogList(); } // Unset logging flags on logged block. if (pTmpSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) { flmAssert( m_uiLogCacheCount); m_uiLogCacheCount--; } pTmpSCache->clearFlags( CA_LOG_FOR_CP | CA_WRITE_TO_LOG | CA_WAS_DIRTY); #ifdef FLM_DBG_LOG pTmpSCache->logFlgChange( ui16OldFlags, 'D'); #endif if (!pTmpSCache->m_uiUseCount && !pTmpSCache->m_ui16Flags && !pTmpSCache->neededByReadTrans()) { flmAssert( pTmpSCache->m_ui64HighTransID != ~((FLMUINT64)0)); pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); } } uiDirtyCacheLeft = (m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize; // When the current set of log blocks were flushed, they were // also unlinked from the file log list. So, we need to // start at the beginning of the log list to pick up // where we left off. pDirtySCache = m_pFirstInLogList; } else if (!bDone) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } // Need to reset pDirtySCache here because the background cache // cleanup thread may have unlinked it from the log list and // cleaned up any prior versions if it determined that the blocks // were no longer needed. if( (pDirtySCache = pSavedSCache->m_pNextInReplaceList) == NULL) { bDone = TRUE; goto Write_Log_Blocks; } } if (bDone) { break; } flmAssert( bMutexLocked); } #ifdef FLM_DEBUG if( bForceCheckpoint || !bIsCPThread || (!bForceCheckpoint && bIsCPThread && *pbWroteAll)) { flmAssert( !m_uiLogListCount); flmAssert( !m_uiLogCacheCount); } #endif Exit: if (RC_BAD( rc)) { // Flush the last log buffer, if not already flushed. if (m_uiCurrLogWriteOffset) { if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Don't care what rc is at this point. Just calling // lgFlushLogBuffer to clear the buffer. (void)lgFlushLogBuffer( pDbStats, pSFileHdl); } // Need to wait for any async writes to complete. if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Don't care about rc here, but we don't want to leave // this routine until all pending IO is taken care of. (void)m_pBufferMgr->waitForAllPendingIO(); if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } // Clean up the log blocks array - releasing blocks, etc. while (uiTotalLoggedBlocks) { uiTotalLoggedBlocks--; pTmpSCache = m_ppBlocksDone [uiTotalLoggedBlocks]; pUsedSCache = ppUsedBlocks [uiTotalLoggedBlocks]; #ifdef FLM_DEBUG // If this is the most current version of the block, it // should still be in the file log list. if( !pUsedSCache->m_pPrevInVersionList) { flmAssert( pUsedSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST); } #endif // Used blocks should be released, whether we succeeded // or not. pUsedSCache->releaseForThread(); pTmpSCache->releaseForThread(); // If we quit before logging the blocks, we don't really // want to change anything on the block, but we do want // to set the previous block address back to zero on the // block that is just newer than this one. // Must put a USE on the block so that the memory cache // verifying code will not barf when we change the // data in the block - checksum is calculated and set when // the use count goes from one to zero, and then verified // when it goes from zero to one. pTmpSCache->m_pPrevInVersionList->useForThread( 0); pTmpSCache->m_pPrevInVersionList->m_pBlkHdr->ui32PriorBlkImgAddr = 0; pTmpSCache->m_pPrevInVersionList->releaseForThread(); } #ifdef SCACHE_LINK_CHECKING // If above logic changes where mutex might not be locked at // this point, be sure to modify this code to re-lock it. flmAssert( bMutexLocked); scaVerify( 100); #endif // Things to restore to their original state if we had an error. if (bLoggedFirstBlk) { m_uiFirstLogBlkAddress = 0; } if (bLoggedFirstCPBlk) { m_uiFirstLogCPBlkAddress = 0; } } if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Better not be any incomplete writes at this point. flmAssert( !m_pBufferMgr->isIOPending()); flmAssert( m_pCurrLogBuffer == NULL); *pbForceCheckpoint = bForceCheckpoint; return( rc); } /**************************************************************************** Desc: This routine is called whenever a write of a dirty block completes. ****************************************************************************/ FSTATIC void FLMAPI scaWriteComplete( IF_IOBuffer * pIOBuffer, void * pvData) { RCODE rc; FLMUINT uiNumBlocks = 0; F_CachedBlock * pSCache = NULL; F_Database * pDatabase; XFLM_DB_STATS * pDbStats = (XFLM_DB_STATS *)pvData; FLMUINT uiMilliPerBlock = 0; FLMUINT uiExtraMilli = 0; #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags; #endif f_assert( pIOBuffer->isComplete()); rc = pIOBuffer->getCompletionCode(); uiNumBlocks = pIOBuffer->getCallbackDataCount(); if( pDbStats) { FLMUINT64 ui64ElapMilli = pIOBuffer->getElapsedTime(); uiMilliPerBlock = (FLMUINT)(ui64ElapMilli / (FLMUINT64)uiNumBlocks); uiExtraMilli = (FLMUINT)(ui64ElapMilli % (FLMUINT64)uiNumBlocks); } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); while( uiNumBlocks) { uiNumBlocks--; pSCache = (F_CachedBlock *)pIOBuffer->getCallbackData( uiNumBlocks); pDatabase = pSCache->getDatabase(); if (pDbStats) { F_BLK_HDR * pBlkHdr = pSCache->getBlockPtr(); XFLM_LFILE_STATS * pLFileStats; XFLM_BLOCKIO_STATS * pBlockIOStats; if (!blkIsBTree( pBlkHdr)) { pLFileStats = NULL; } else { if (RC_BAD( flmStatGetLFile( pDbStats, (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile, getBlkLfType( (F_BTREE_BLK_HDR *)pBlkHdr), 0, &pLFileStats, NULL, NULL))) { pLFileStats = NULL; } } if ((pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, pLFileStats, (FLMBYTE *)pBlkHdr)) != NULL) { pBlockIOStats->BlockWrites.ui64Count++; pBlockIOStats->BlockWrites.ui64TotalBytes += pDatabase->getBlockSize(); if (uiExtraMilli) { pBlockIOStats->BlockWrites.ui64ElapMilli += (uiMilliPerBlock + 1); uiExtraMilli--; } else { pBlockIOStats->BlockWrites.ui64ElapMilli += uiMilliPerBlock; } } } pSCache->releaseForThread(); if (pSCache->getModeFlags() & CA_DIRTY) { flmAssert( pSCache->getModeFlags() & CA_WRITE_PENDING); #ifdef FLM_DBG_LOG ui16OldFlags = pSCache->getModeFlags(); #endif pSCache->clearFlags( CA_WRITE_PENDING); if (RC_OK( rc)) { pSCache->unsetDirtyFlag(); } #ifdef FLM_DBG_LOG pSCache->logFlgChange( ui16OldFlags, 'H'); #endif // If there are more dirty blocks after this // one, move this one out of the dirty // blocks. pSCache->unlinkFromDatabase(); pSCache->linkToDatabase( pDatabase); } else { flmAssert( !(pSCache->getModeFlags() & CA_WRITE_PENDING)); } } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: ****************************************************************************/ void F_BlockCacheMgr::cleanupLRUCache( void) { FLMUINT uiByteThreshold; FLMUINT uiSlabThreshold; FLMUINT uiSlabSize; F_CachedBlock * pPrevSCache; F_CachedBlock * pTmpSCache; FLMBOOL bDefragNeeded = FALSE; // Remove non-dirty blocks from the LRU end of the cache uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); // If the cache isn't over its slab threshold, we are done if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } uiByteThreshold = m_Usage.uiByteCount > uiSlabSize ? m_Usage.uiByteCount - uiSlabSize : 0; pTmpSCache = (F_CachedBlock *)m_MRUList.m_pLRUItem; while( pTmpSCache) { // Save the pointer to the previous entry in the list because // we may end up unlinking pTmpSCache below, in which case we would // have lost the next entry. pPrevSCache = (F_CachedBlock *)pTmpSCache->m_pPrevInGlobal; // Block must not currently be in use, cannot be dirty in any way, // cannot be in the process of being read in from disk, // and must not be needed by a read transaction. if( !pTmpSCache->m_uiUseCount && !pTmpSCache->m_ui16Flags && (!pTmpSCache->m_pDatabase || !pTmpSCache->neededByReadTrans())) { pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); bDefragNeeded = TRUE; if( m_Usage.uiByteCount <= uiByteThreshold) { if( pPrevSCache) { pPrevSCache->useForThread( 0); } gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); bDefragNeeded = FALSE; if( !pPrevSCache) { break; } pPrevSCache->releaseForThread(); // We're going to quit when we get under 50 percent for block cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving block // cache because node cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } uiByteThreshold = m_Usage.uiByteCount > uiSlabSize ? m_Usage.uiByteCount - uiSlabSize : 0; } } pTmpSCache = pPrevSCache; } if( bDefragNeeded) { gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); } Exit: return; } /**************************************************************************** Desc: Cleanup old blocks in cache that are no longer needed by any transaction. This routine assumes that the block cache mutex has been locked. ****************************************************************************/ void F_BlockCacheMgr::cleanupReplaceList( void) { F_CachedBlock * pTmpSCache; F_CachedBlock * pPrevSCache; pTmpSCache = m_pLRUReplace; for (;;) { // Stop when we reach end of list or all old blocks have // been freed. if (!pTmpSCache || !m_Usage.uiOldVerBytes) { break; } // Shouldn't encounter anything with CA_FREE set flmAssert( !(pTmpSCache->m_ui16Flags & CA_FREE)); // Save the pointer to the previous entry in the list because // we may end up unlinking pTmpSCache below, in which case we would // have lost the next entry. pPrevSCache = pTmpSCache->m_pPrevInReplaceList; // Block must not currently be in use, // Must not be the most current version of a block, // Cannot be dirty in any way, // Cannot be in the process of being read in from disk, // And must not be needed by a read transaction. if (!pTmpSCache->m_uiUseCount && pTmpSCache->m_ui64HighTransID != ~((FLMUINT64)0) && !pTmpSCache->m_ui16Flags && (!pTmpSCache->m_pDatabase || !pTmpSCache->neededByReadTrans())) { pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); } pTmpSCache = pPrevSCache; } } /**************************************************************************** Desc: ****************************************************************************/ void F_BlockCacheMgr::cleanupFreeCache( void) { F_CachedBlock * pSCache = m_pLastFree; F_CachedBlock * pPrevSCache; while( pSCache) { pPrevSCache = pSCache->m_pPrevInDatabase; if( !pSCache->m_uiUseCount) { pSCache->unlinkFromFreeList(); delete pSCache; } pSCache = pPrevSCache; } } /**************************************************************************** Desc: This routine will reduce the number of blocks in the reuse list until cache is below its limit. NOTE: This routine assumes that the block cache mutex is already locked. ****************************************************************************/ void F_BlockCacheMgr::reduceReuseList( void) { F_CachedBlock * pTmpSCache; F_CachedBlock * pPrevSCache; FLMUINT uiSlabThreshold; FLMUINT uiSlabSize; FLMUINT uiByteThreshold; // Determine if the block limit for block cache been exceeded uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) { goto Exit; } // Remove items from cache starting from the LRU pTmpSCache = m_pLRUReplace; uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); uiByteThreshold = m_Usage.uiByteCount > uiSlabSize ? m_Usage.uiByteCount - uiSlabSize : 0; while( pTmpSCache) { // Need to save the pointer to the previous entry in the list because // we may end up freeing pTmpNode below. pPrevSCache = pTmpSCache->m_pPrevInReplaceList; // See if the item can be freed. if( pTmpSCache->canBeFreed()) { pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); if( m_Usage.uiByteCount <= uiByteThreshold) { if( pPrevSCache) { pPrevSCache->useForThread( 0); } gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); if( !pPrevSCache) { break; } pPrevSCache->releaseForThread(); if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) { goto Exit; } uiByteThreshold = m_Usage.uiByteCount > uiSlabSize ? m_Usage.uiByteCount - uiSlabSize : 0; } } pTmpSCache = pPrevSCache; } Exit: return; } /**************************************************************************** Desc: Reduce cache to below the cache limit. NOTE: This routine assumes that the block cache mutex is locked. It may temporarily unlock the mutex to write out dirty blocks, but it will always return with the mutex still locked. ****************************************************************************/ RCODE F_BlockCacheMgr::reduceCache( F_Db * pDb) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb ? pDb->m_pDatabase : NULL; FLMBOOL bForceCheckpoint; FLMBOOL bWroteAll; FLMUINT uiSlabSize; FLMUINT uiSlabThreshold; FLMBOOL bDoingReduce = FALSE; // If cache is not full, we are done. if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || m_bReduceInProgress) { goto Exit; } m_bReduceInProgress = TRUE; bDoingReduce = TRUE; // Determine the cache threshold uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); // Are we over the threshold? if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) { goto Exit; } // Try cleaning up the replace list if( m_Usage.uiOldVerBytes) { cleanupReplaceList(); gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); // We're going to quit when we get under 50 percent for block cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving block // cache because node cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } } // Clean up the free list if( m_uiFreeBytes) { cleanupFreeCache(); gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); // We're going to quit when we get under 50 percent for block cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving block // cache because node cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } } // Clean up the LRU list cleanupLRUCache(); if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } // If this isn't an update transaction, there isn't anything else // that can be done to reduce cache. if( !pDb || (pDb->m_eTransType != XFLM_UPDATE_TRANS && !pDatabase->m_bTempDb)) { goto Exit; } // Flush log blocks if( pDatabase->m_pFirstInLogList) { bForceCheckpoint = FALSE; bWroteAll = TRUE; f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); if( RC_BAD( rc = pDatabase->flushLogBlocks( pDb->m_hWaitSem, pDb->m_pDbStats, pDb->m_pSFileHdl, FALSE, 0, &bForceCheckpoint, &bWroteAll))) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); goto Exit; } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); cleanupFreeCache(); reduceReuseList(); gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); // We're going to quit when we get under 50 percent for block cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving block // cache because node cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } } // Flush new blocks for( ;;) { FLMUINT uiNewBlocks = 0; f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); if( RC_BAD( rc = pDatabase->reduceNewBlocks( pDb->m_pDbStats, pDb->m_pSFileHdl, &uiNewBlocks))) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); goto Exit; } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); if( !uiNewBlocks) { break; } cleanupFreeCache(); reduceReuseList(); gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); // We're going to quit when we get under 50 percent for block cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving block // cache because node cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } } // Flush dirty blocks flmAssert( !pDatabase->m_pFirstInLogList); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); if( RC_BAD( rc = pDatabase->reduceDirtyCache( pDb->m_pDbStats, pDb->m_pSFileHdl))) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); goto Exit; } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); cleanupFreeCache(); reduceReuseList(); gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); // We're going to quit when we get under 50 percent for block cache // or we aren't over the global limit. Note that this means we // may quit reducing before we get under the global limit. We // don't want to get into a situation where we are starving block // cache because node cache is over its limit. if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { goto Exit; } // Try cleaning up the LRU again cleanupLRUCache(); Exit: if( RC_BAD( rc) && pDb) { pDb->setMustAbortTrans( rc); } if( bDoingReduce) { m_bReduceInProgress = FALSE; } return( rc); } /**************************************************************************** Desc: Constructor for cached block. ****************************************************************************/ F_CachedBlock::F_CachedBlock( FLMUINT uiBlockSize) { m_pPrevInDatabase = NULL; m_pNextInDatabase = NULL; m_pBlkHdr = (F_BLK_HDR *)((FLMBYTE *)this + sizeof( F_CachedBlock)); m_pDatabase = NULL; m_uiBlkAddress = 0; m_pNextInReplaceList = NULL; m_pPrevInReplaceList = NULL; m_pPrevInHashBucket = NULL; m_pNextInHashBucket = NULL; m_pPrevInVersionList = NULL; m_pNextInVersionList = NULL; m_pNotifyList = NULL; // Need to set high transaction ID to 0xFFFFFFFF. This indicates that // the block is not currently counted in the Usage.uiOldVerBytes tally - // seeing as how it was just allocated. // DO NOT USE setTransID routine here because that routine // will adjust the tally. The caller of this routine should call // setTransID to ensure that the tally is set appropriately. // This is the only place in the code where it is legal to set // ui64HighTransID without calling setTransID. m_ui64HighTransID = ~((FLMUINT64)0); m_uiUseCount = 0; m_ui16Flags = 0; m_ui16BlkSize = (FLMUINT16)uiBlockSize; m_bCanRelocate = FALSE; #ifdef FLM_DEBUG m_uiChecksum = 0; m_pUseList = NULL; #endif } /**************************************************************************** Desc: Allocate a cache block. If we are at the cache limit, unused cache blocks will be replaced. NOTE: This routine assumes that the block cache mutex is locked. ****************************************************************************/ RCODE F_BlockCacheMgr::allocBlock( F_Db * pDb, F_CachedBlock ** ppSCacheRV) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = pDb->getDatabase(); FLMUINT uiBlockSize = pDatabase->getBlockSize(); F_CachedBlock * pSCache; F_CachedBlock * pTmpSCache; F_CachedBlock * pPrevSCache; // Quick check to see if there is a block in the free list that can be // re-used. Start at the MRU end of the list so that if items in the // free list are only being used periodically, the items at the LRU end // will age out and the size of the list will be reduced. pSCache = m_pFirstFree; while (pSCache) { if (!pSCache->m_uiUseCount && pSCache->getBlkSize() == uiBlockSize) { pSCache->unlinkFromFreeList(); goto Reuse_Block; } pSCache = pSCache->m_pNextInDatabase; } // The intent of this little loop is to be optimistic and hope that // there is a block we can cannibalize or free without having to write // it. If not, we will still allocate a new block and allow ourselves // to be temporarily over the cache limit. In this case, the cache size // will be reduced only AFTER this new block is safely linked into cache. // This is necessary because we don't want two different threads allocating // memory for the same block. pTmpSCache = m_pLRUReplace; while( pTmpSCache && gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { // Need to save the pointer to the previous entry in the list because // we may end up unlinking it below, in which case we would have lost // the previous entry. pPrevSCache = pTmpSCache->m_pPrevInReplaceList; // See if the cache block can be replaced or freed. flmAssert( !pTmpSCache->m_ui16Flags); if (pTmpSCache->canBeFreed()) { if (pTmpSCache->getBlkSize() == uiBlockSize) { pSCache = pTmpSCache; flmAssert( !pSCache->m_ui16Flags); pTmpSCache->unlinkCache( FALSE, NE_XFLM_OK); // We use a goto instead of a break because then // we don't have to do the additional test // down below. We already know that pSCache // will be non-NULL. goto Reuse_Block; } else { // NOTE: This call will free the memory pointed to by // pTmpSCache. Hence, pTmpSCache should NOT be used after // this point. pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); } } pTmpSCache = pPrevSCache; } // If we were not able to cannibalize an F_CachedBlock object, // allocate one. if (pSCache) { Reuse_Block: flmAssert( !pSCache->m_pPrevInReplaceList); flmAssert( !pSCache->m_pNextInReplaceList); flmAssert( !pSCache->m_ui16Flags); flmAssert( !pSCache->m_uiUseCount); // If block is an old version, need to decrement the // Usage.uiOldVerBytes tally. if (pSCache->m_ui64HighTransID != ~((FLMUINT64)0)) { FLMUINT uiSize = pSCache->memSize(); flmAssert( m_Usage.uiOldVerBytes >= uiSize); m_Usage.uiOldVerBytes -= uiSize; flmAssert( m_Usage.uiOldVerCount); m_Usage.uiOldVerCount--; } // If we are cannibalizing, be sure to reset certain fields. pSCache->m_ui16Flags = 0; pSCache->m_uiUseCount = 0; #ifdef FLM_DEBUG pSCache->m_uiChecksum = 0; #endif // Need to set high transaction ID to 0xFFFFFFFF. This indicates that // the block is not currently counted in the Usage.uiOldVerBytes tally - // seeing as how it was just allocated. // DO NOT USE setTransID routine here because that routine // will adjust the tally. The caller of this routine should call // setTransID to ensure that the tally is set appropriately. // This is the only place in the code where it is legal to set // ui64HighTransID without calling setTransID. pSCache->m_ui64HighTransID = ~((FLMUINT64)0); } else { if ((pSCache = new( uiBlockSize) F_CachedBlock( uiBlockSize)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } m_Usage.uiCount++; m_Usage.uiByteCount += pSCache->memSize(); pSCache->m_bCanRelocate = TRUE; if (shouldRehash( m_Usage.uiCount, m_uiNumBuckets)) { if (checkHashFailTime( &m_uiHashFailTime)) { if (RC_BAD( rc = rehash())) { goto Exit; } } } } *ppSCacheRV = pSCache; // Set use count to one so the block cannot be replaced. pSCache->useForThread( 0); Exit: if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /******************************************************************** Desc: This converts a block header to native format. *********************************************************************/ void convertBlkHdr( F_BLK_HDR * pBlkHdr ) { // This routine should only be called on blocks that are NOT // currently in native format. flmAssert( blkIsNonNativeFormat( pBlkHdr)); convert32( &pBlkHdr->ui32BlkAddr); convert32( &pBlkHdr->ui32PrevBlkInChain); convert32( &pBlkHdr->ui32NextBlkInChain); convert32( &pBlkHdr->ui32PriorBlkImgAddr); convert64( &pBlkHdr->ui64TransID); convert32( &pBlkHdr->ui32BlkCRC); convert16( &pBlkHdr->ui16BlkBytesAvail); if (blkIsBTree( pBlkHdr)) { convert16( &(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile)); convert16( &(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys)); } blkSetNativeFormat( pBlkHdr); } /******************************************************************** Desc: This converts a logical file header structure *********************************************************************/ void convertLfHdr( F_LF_HDR * pLfHdr) { convert32( &pLfHdr->ui32LfNumber); convert32( &pLfHdr->ui32LfType); convert32( &pLfHdr->ui32RootBlkAddr); convert32( &pLfHdr->ui32EncId); convert64( &pLfHdr->ui64NextNodeId); convert64( &pLfHdr->ui64FirstDocId); convert64( &pLfHdr->ui64LastDocId); } /******************************************************************** Desc: This converts a block header to native format. *********************************************************************/ void convertBlk( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr) { // This routine should only be called on blocks that are NOT // currently in native format. convertBlkHdr( pBlkHdr); if (pBlkHdr->ui8BlkType == BT_LFH_BLK) { FLMUINT uiPos = SIZEOF_STD_BLK_HDR; FLMUINT uiEnd = blkGetEnd( uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR); // Only one block type requires further conversion. while (uiPos + sizeof( F_LF_HDR) <= uiEnd) { convertLfHdr( pLfHdr); pLfHdr++; uiPos += sizeof( F_LF_HDR); } } } /******************************************************************** Desc: This routine prepares a block for use after reading it in from disk. It will convert the block to native format if necessary, and will also verify the CRC on the block. We always want to convert the block if we can, so even if the CRC is bad or the block end is bad, we will attempt to do a convert if it is in non-native format. *********************************************************************/ RCODE flmPrepareBlockForUse( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr) { RCODE rc = NE_XFLM_OK; FLMUINT32 ui32CRC; FLMUINT16 ui16BlkBytesAvail; FLMUINT uiBlkEnd; FLMBOOL bBadBlkEnd; // Determine if we should convert the block here. // Calculation of CRC should be on unconverted block. ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; if (blkIsNonNativeFormat( pBlkHdr)) { convert16( &ui16BlkBytesAvail); } if( (FLMUINT)ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pBlkHdr)) { uiBlkEnd = blkHdrSize( pBlkHdr); bBadBlkEnd = TRUE; } else { uiBlkEnd = (blkIsNewBTree( pBlkHdr) ? uiBlockSize : uiBlockSize - (FLMUINT)ui16BlkBytesAvail); bBadBlkEnd = FALSE; } // CRC must be calculated BEFORE converting the block. ui32CRC = calcBlkCRC( pBlkHdr, uiBlkEnd); if( blkIsNonNativeFormat( pBlkHdr)) { convertBlk( uiBlockSize, pBlkHdr); } if( ui32CRC != pBlkHdr->ui32BlkCRC || bBadBlkEnd) { rc = RC_SET( NE_XFLM_BLOCK_CRC); goto Exit; } Exit: return( rc); } /******************************************************************** Desc: This routine attempts to read a block from disk. It will attempt the specified number of times. *********************************************************************/ RCODE F_Database::readTheBlock( F_Db * pDb, TMP_READ_STATS * pTmpReadStats, // READ statistics. F_BLK_HDR * pBlkHdr, // Pointer to buffer where block is // to be read into. FLMUINT uiFilePos, // File position to be read from. If // file position != block address, we // are reading from the log. FLMUINT uiBlkAddress // Block address that is to be read. ) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesRead; F_TMSTAMP StartTime; FLMUINT64 ui64ElapMilli; XFLM_DB_STATS * pDbStats = pDb->m_pDbStats; flmAssert( this == pDb->m_pDatabase); // We should NEVER be attempting to read a block address that is // beyond the current logical end of file. if (!FSAddrIsBelow( uiBlkAddress, pDb->m_uiLogicalEOF)) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Read the block if (pDb->m_uiKilledTime) { rc = RC_SET( NE_XFLM_OLD_VIEW); goto Exit; } if (pTmpReadStats) { if (uiFilePos != uiBlkAddress) { pTmpReadStats->OldViewBlockReads.ui64Count++; pTmpReadStats->OldViewBlockReads.ui64TotalBytes += m_uiBlockSize; } else { pTmpReadStats->BlockReads.ui64Count++; pTmpReadStats->BlockReads.ui64TotalBytes += m_uiBlockSize; } ui64ElapMilli = 0; f_timeGetTimeStamp( &StartTime); } if (RC_BAD( rc = pDb->m_pSFileHdl->readBlock( uiFilePos, m_uiBlockSize, pBlkHdr, &uiBytesRead))) { if (pDbStats) { pDbStats->uiReadErrors++; } if (rc == NE_FLM_IO_END_OF_FILE) { // Should only be possible when reading a root block, // because the root block address in the LFILE may be // a block that was just created by an update // transaction. flmAssert( pDb->m_uiKilledTime); rc = RC_SET( NE_XFLM_OLD_VIEW); } goto Exit; } if (pTmpReadStats) { flmAddElapTime( &StartTime, &ui64ElapMilli); if (uiFilePos != uiBlkAddress) { pTmpReadStats->OldViewBlockReads.ui64ElapMilli += ui64ElapMilli; } else { pTmpReadStats->BlockReads.ui64ElapMilli += ui64ElapMilli; } } if (uiBytesRead < m_uiBlockSize) { // Should only be possible when reading a root block, // because the root block address in the LFILE may be // a block that was just created by an update // transaction. flmAssert( pDb->m_uiKilledTime); rc = RC_SET( NE_XFLM_OLD_VIEW); #ifdef FLM_DBG_LOG // Must make this call so we can be ensured that the // transaction ID in the block header has been // converted if need be. (void)flmPrepareBlockForUse( m_uiBlockSize, pBlkHdr); #endif } else { rc = flmPrepareBlockForUse( m_uiBlockSize, pBlkHdr); } // Decrypt the block if it was encrypted if (RC_BAD( rc = decryptBlock( pDb->m_pDict, (FLMBYTE *)pBlkHdr))) { goto Exit; } #ifdef FLM_DBG_LOG if (uiFilePos != uiBlkAddress) { flmDbgLogWrite( this, uiBlkAddress, uiFilePos, (FLMUINT)pBlkHdr->ui64TransID, "LGRD"); } else { flmDbgLogWrite( this, uiBlkAddress, 0, pBlkHdr->ui64TransID, "READ"); } #endif if (RC_BAD( rc)) { if (pTmpReadStats && (rc == NE_XFLM_BLOCK_CRC || rc == NE_XFLM_OLD_VIEW)) { if (uiFilePos != uiBlkAddress) { pTmpReadStats->uiOldViewBlockChkErrs++; } else { pTmpReadStats->uiBlockChkErrs++; } } goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Read a data block into cache. This routine reads the requested version of a block into memory. It follows links to previous versions of the block if necessary in order to do this. ****************************************************************************/ RCODE F_Database::readBlock( F_Db * pDb, LFILE * pLFile, // Pointer to logical file structure // We are retrieving the block for. // NULL if there is no logical file. FLMUINT uiFilePos, // File position where we are to // start reading from. FLMUINT uiBlkAddress, // Address of block that is to // be read into cache. FLMUINT64 ui64NewerBlkLowTransID, // Low transaction ID of the last newer // version of the block. // NOTE: This has no meaning // when uiFilePos == uiBlkAddress. F_CachedBlock * pSCache, // Cache block to read the data // into. FLMBOOL * pbFoundVerRV, // Returns a flag to the caller to // tell it whether it found any // versions of the requested block // starting at the file position // that was passed in. FLMBOOL * pbDiscardRV // Returns a flag which, if TRUE, // tells the caller to discard // the block that was just read // in and set the high transaction ID // on the block that comes just // after it - because they are // the same version. ) { RCODE rc = NE_XFLM_OK; F_BLK_HDR * pBlkHdr = pSCache->m_pBlkHdr; F_CachedBlock * pNextSCache; FLMBOOL bMutexLocked = FALSE; XFLM_LFILE_STATS * pLFileStats; XFLM_BLOCKIO_STATS * pBlockIOStats; FLMBOOL bIncrPriorImageCnt = FALSE; FLMBOOL bIncrOldViewCnt = FALSE; TMP_READ_STATS TmpReadStats; TMP_READ_STATS * pTmpReadStats; XFLM_DB_STATS * pDbStats = pDb->m_pDbStats; flmAssert( this == pDb->m_pDatabase); *pbFoundVerRV = FALSE; *pbDiscardRV = FALSE; if (pDbStats) { f_memset( &TmpReadStats, 0, sizeof( TmpReadStats)); pTmpReadStats = &TmpReadStats; } else { pTmpReadStats = NULL; } // Read in the block from the database // Stay in a loop reading until we get an error or get the block for (;;) { if (pDbStats) { if (uiFilePos != uiBlkAddress) { bIncrPriorImageCnt = TRUE; } bIncrOldViewCnt = FALSE; } // Read and verify the block. if (RC_BAD( rc = readTheBlock( pDb, pTmpReadStats, pBlkHdr, uiFilePos, uiBlkAddress))) { goto Exit; } pBlkHdr->ui8BlkFlags &= ~(BLK_IS_BEFORE_IMAGE); // See if we can use the current version of the block, or if we // must go get a previous version. // See if we even got the block we thought we wanted. if ((FLMUINT)pBlkHdr->ui32BlkAddr != uiBlkAddress) { if (uiFilePos == uiBlkAddress) { rc = RC_SET( NE_XFLM_DATA_ERROR); } else { // Should only be possible when reading a root block, // because the root block address in the LFILE may be // a block that was just created by an update // transaction. flmAssert( pDb->m_uiKilledTime); rc = RC_SET( NE_XFLM_OLD_VIEW); } goto Exit; } // This flag is set to true to indicate that we found at least one // version of the requested block. NOTE: This flag does NOT mean // that we found the specific version requested, only that we // found some version starting at the given address. *pbFoundVerRV = TRUE; // Check to see if the transaction range for the block we just read // overlaps the transaction range for next older version of the block // in the version list. If the ranges overlap, the transaction ID on // each block had better be the same or we have a corruption. If the // transaction IDs are the same, they are the same version of the block, // and we can discard the one we just read. f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; Get_Next_Block: if ((pNextSCache = pSCache->m_pNextInVersionList) != NULL) { FLMUINT64 ui64TmpTransID1; FLMUINT64 ui64TmpTransID2; // If next block is still being read in, we must wait for // it to complete before looking at its transaction IDs. if (pNextSCache->m_ui16Flags & CA_READ_PENDING) { gv_XFlmSysData.pBlockCacheMgr->m_uiIoWaits++; if (RC_BAD( rc = f_notifyWait( gv_XFlmSysData.hBlockCacheMutex, pDb->m_hWaitSem, (void *)&pNextSCache, &pNextSCache->m_pNotifyList))) { goto Exit; } // The thread doing the notify "uses" the cache block // on behalf of this thread to prevent the cache block // from being flushed after it unlocks the mutex. // At this point, since we have locked the mutex, // we need to release the cache block - because we // will put a "use" on it below. pNextSCache->releaseForThread(); // See if we still have the same next block. goto Get_Next_Block; } // Check for overlapping trans ID ranges. NOTE: At this // point, if we have an overlap, we know we have the version // of the block we need (see comment above). Hence, we will // either break out of the loop at this point or goto exit // and return an error. ui64TmpTransID1 = pBlkHdr->ui64TransID; if (ui64TmpTransID1 <= pNextSCache->m_ui64HighTransID) { ui64TmpTransID2 = pNextSCache->getLowTransID(); // If the low trans IDs on the two blocks are not equal // we have a corruption. if (ui64TmpTransID1 != ui64TmpTransID2) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // The blocks are the same, discard one of them. *pbDiscardRV = TRUE; // Set the high trans ID on the block we are NOT discarding. // To find the version of the block we want, we have been // reading through a chain of blocks, from newer versions to // progressively older versions. If uiFilePos == uiBlkAddress, // we are positioned on the most current version of the block. // In this case, the high trans ID for the block should be // set to the highest possible value. // If uiFilePos != uiBlkAddress, we are positioned on an older // version of the block. The variable ui64NewerBlkLowTransID // contains the low transaction ID for a newer version of the // block we read just prior to reading this block. if (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb || uiFilePos == uiBlkAddress) { pNextSCache->setTransID( ~((FLMUINT64)0)); } else { pNextSCache->setTransID( (ui64NewerBlkLowTransID - 1)); } // When discard flag is TRUE, we need to go right to // exit, because we don't want to decrypt, do sanity // check, etc. NOTE: mutex is still locked, and // we want it to remain locked - see code at Exit. goto Exit; } } // See if this version of the block is what we want if (pBlkHdr->ui64TransID <= pDb->m_ui64CurrTransID) { // Set the high trans ID on the block. If we are in an // update transaction, or uiFilePos == uiBlkAddress, we // are positioned on the most current version of the block. // In this case, the high trans ID for the block should be // set to ~((FLMUINT64)0). Otherwise we are positioned on an older // version of the block, and the block's high transaction ID // should be set to the newer block's low transaction ID minus one. if (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb || uiFilePos == uiBlkAddress) { pSCache->setTransID( ~((FLMUINT64)0)); } else { pSCache->setTransID( (ui64NewerBlkLowTransID - 1)); } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; break; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; // At this point, we know we are going to have to get a prior // version of the block. In an update transaction, this is // indicative of a file corruption. if (pDb->m_eTransType != XFLM_READ_TRANS) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // At this point, we know we are in a read transaction. Save the // block's low trans ID. ui64NewerBlkLowTransID = pBlkHdr->ui64TransID; // See if there is a prior version of the block and determine whether // it's expected trans ID is in the range we need. // NOTE: If the prior version address is zero or is the same as our // current file position, there is no previous version of the block. if ((FLMUINT)pBlkHdr->ui32PriorBlkImgAddr == uiFilePos) { // Should only be possible when reading a root block, // because the root block address in the LFILE may be // a block that was just created by an update // transaction. flmAssert( pDb->m_uiKilledTime); rc = RC_SET( NE_XFLM_OLD_VIEW); goto Exit; } uiFilePos = (FLMUINT)pBlkHdr->ui32PriorBlkImgAddr; if (!uiFilePos) { // Should only be possible when reading a root block, // because the root block address in the LFILE may be // a block that was just created by an update // transaction. flmAssert( pDb->m_uiKilledTime); rc = RC_SET( NE_XFLM_OLD_VIEW); goto Exit; } } // Perform a sanity check on the block header. if ((FLMUINT)pBlkHdr->ui16BlkBytesAvail > m_uiBlockSize - blkHdrSize( pBlkHdr)) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } Exit: // NOTE: When we are discarding the block, we CANNOT unlock // the mutex, because we have to take care of it on // the outside. Mutex better be locked if we are discarding. if (*pbDiscardRV) { flmAssert( bMutexLocked); } else if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } // If we got an old view error, it has to be a corruption, unless we // were killed. if (rc == NE_XFLM_OLD_VIEW) { if (!pDb->m_uiKilledTime || pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb) { rc = RC_SET( NE_XFLM_DATA_ERROR); } } // Increment cache fault statistics if (pDbStats) { if ((pLFileStats = pDb->getLFileStatPtr( pLFile)) == NULL) { pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, NULL, (FLMBYTE *)pBlkHdr); } else if (RC_BAD( rc)) { // Didn't really get a valid block, assign all statistics // gathered to the leaf block statistics. pBlockIOStats = &pLFileStats->LeafBlockStats; } else { pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, pLFileStats, (FLMBYTE *)pBlkHdr); } if (pBlockIOStats) { pDbStats->bHaveStats = TRUE; if (pLFileStats) { pLFileStats->bHaveStats = TRUE; } flmUpdateDiskIOStats( &pBlockIOStats->BlockReads, &TmpReadStats.BlockReads); flmUpdateDiskIOStats( &pBlockIOStats->OldViewBlockReads, &TmpReadStats.OldViewBlockReads); pBlockIOStats->ui32BlockChkErrs += (FLMUINT32)TmpReadStats.uiBlockChkErrs; pBlockIOStats->ui32OldViewBlockChkErrs += (FLMUINT32)TmpReadStats.uiOldViewBlockChkErrs; if (rc == NE_XFLM_OLD_VIEW || bIncrOldViewCnt) { pBlockIOStats->ui32OldViewErrors++; } } } return( rc); } /**************************************************************************** Desc: Increment the use count on a cache block for a particular thread. NOTE: This routine assumes that the block cache mutex is locked. ****************************************************************************/ #ifdef FLM_DEBUG void F_CachedBlock::dbgUseForThread( FLMUINT uiThreadId) { SCACHE_USE * pUse; FLMUINT uiMyThreadId = (FLMUINT)(!uiThreadId ? (FLMUINT)f_threadId() : uiThreadId); // If the use count is 0, make sure there are not entries // in the use list. if (!m_uiUseCount && m_pUseList != NULL) { return; } // First increment the overall use count for the block m_uiUseCount++; if (m_uiUseCount == 1) { gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed++; if (m_uiChecksum) { flmAssert( m_uiChecksum == computeChecksum()); } } gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses++; // Now add the thread's usage record - or increment it if there // is already one there for the thread. // See if we already have this thread in the use list pUse = m_pUseList; while (pUse && pUse->uiThreadId != uiMyThreadId) { pUse = pUse->pNext; } if (!pUse) { if (RC_BAD( f_calloc( (FLMUINT)sizeof( SCACHE_USE), &pUse))) { return; } f_memset( pUse, 0, sizeof( SCACHE_USE)); pUse->uiThreadId = uiMyThreadId; pUse->pNext = m_pUseList; m_pUseList = pUse; } pUse->uiUseCount++; } #endif /**************************************************************************** Desc: Decrement the use count on a cache block for a particular thread. NOTE: This routine assumes that the block cache mutex is locked. ****************************************************************************/ #ifdef FLM_DEBUG void F_CachedBlock::dbgReleaseForThread( void) { SCACHE_USE * pUse; SCACHE_USE * pPrevUse; FLMUINT uiMyThreadId = (FLMUINT)f_threadId(); // Find the thread's use pUse = m_pUseList; pPrevUse = NULL; while (pUse && pUse->uiThreadId != uiMyThreadId) { pPrevUse = pUse; pUse = pUse->pNext; } if (!pUse) { return; } m_uiUseCount--; gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses--; if (!m_uiUseCount) { m_uiChecksum = computeChecksum(); gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed--; flmAssert( pUse->uiUseCount == 1); } // Free the use record if its count goes to zero pUse->uiUseCount--; if (!pUse->uiUseCount) { if (!pPrevUse) { m_pUseList = pUse->pNext; } else { pPrevUse->pNext = pUse->pNext; } f_free( &pUse); } } #endif /**************************************************************************** Desc: Read a data block into cache. This routine takes care of allocating a cache block and reading the block from disk into memory. NOTE: This routine assumes that the block cache mutex is locked. It may unlock the block cache mutex long enough to do the read, but the mutex will still be locked when it exits. ****************************************************************************/ RCODE F_Database::readIntoCache( F_Db * pDb, LFILE * pLFile, // Pointer to logical file structure // We are retrieving the block for. // NULL if there is no logical file. FLMUINT uiBlkAddress, // Address of block that is to // be read into cache. F_CachedBlock * pPrevInVerList, // Previous block in version list to // link the block to. F_CachedBlock * pNextInVerList, // Next block in version list to link // the block to. F_CachedBlock ** ppSCacheRV, // Returns allocated cache block. FLMBOOL * pbGotFromDisk) // Returns TRUE if block was read // from disk { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache; F_CachedBlock * pTmpSCache; F_NOTIFY_LIST_ITEM * pNotify; FLMUINT uiFilePos; FLMUINT64 ui64NewerBlkLowTransID = 0; FLMBOOL bFoundVer; FLMBOOL bDiscard; flmAssert( this == pDb->m_pDatabase); *pbGotFromDisk = FALSE; // Lock the prev and next in place by incrementing their use // count. We don't want allocBlock to use them. if (pPrevInVerList) { pPrevInVerList->useForThread( 0); } if (pNextInVerList) { pNextInVerList->useForThread( 0); } // Allocate a cache block - either a new one or by replacing // an existing one. rc = gv_XFlmSysData.pBlockCacheMgr->allocBlock( pDb, &pSCache); if (pPrevInVerList) { pPrevInVerList->releaseForThread(); } if (pNextInVerList) { pNextInVerList->releaseForThread(); } if (RC_BAD( rc)) { goto Exit; } pSCache->m_uiBlkAddress = uiBlkAddress; // Set the "dummy" flag so that we won't incur the overhead of // linking the block into the replace list. It would be removed // from the replace list almost immediately anyway, when the // "read pending" flag is set below. pSCache->m_ui16Flags |= CA_DUMMY_FLAG; // Link block into various lists if( pDb->m_uiFlags & FDB_DONT_POISON_CACHE) { if( !(pDb->m_uiFlags & FDB_BACKGROUND_INDEXING) || (pLFile && pLFile->eLfType != XFLM_LF_INDEX)) { pSCache->linkToGlobalListAsLRU(); } else { pSCache->linkToGlobalListAsMRU(); } } else { pSCache->linkToGlobalListAsMRU(); } pSCache->linkToDatabase( this); if (!pPrevInVerList) { F_CachedBlock ** ppSCacheBucket; ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_uiSigBitsInBlkSize, uiBlkAddress); uiFilePos = uiBlkAddress; if (pNextInVerList) { pNextInVerList->unlinkFromHashBucket( ppSCacheBucket); } pSCache->linkToHashBucket( ppSCacheBucket); } else { uiFilePos = pPrevInVerList->getPriorImageAddress(); ui64NewerBlkLowTransID = pPrevInVerList->getLowTransID(); pPrevInVerList->m_pNextInVersionList = pSCache; pPrevInVerList->verifyCache( 2400); } if (pNextInVerList) { pNextInVerList->m_pPrevInVersionList = pSCache; pNextInVerList->verifyCache( 2500); } pSCache->m_pPrevInVersionList = pPrevInVerList; pSCache->m_pNextInVersionList = pNextInVerList; pSCache->verifyCache( 2600); // Set the read-pending flag for this block. This will force other // threads that need to read this block to wait for the I/O to // complete. pSCache->setFlags( CA_READ_PENDING); pSCache->m_ui16Flags &= ~CA_DUMMY_FLAG; gv_XFlmSysData.pBlockCacheMgr->m_uiPendingReads++; // Unlock the mutex and attempt to read the block into memory f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); rc = readBlock( pDb, pLFile, uiFilePos, uiBlkAddress, ui64NewerBlkLowTransID, pSCache, &bFoundVer, &bDiscard); // NOTE: If the bDiscard flag is TRUE, the mutex will still be // locked. If FALSE, we need to relock it. if (!bDiscard) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); } // Get a pointer to the notify list BEFORE discarding the cache // block - if we are going to discard - because pSCache can // change if we discard. pNotify = pSCache->m_pNotifyList; pSCache->m_pNotifyList = NULL; // Unset the read pending flag and reset the use count to zero. // Both of these actions should be done before doing a discard, // if a discard is going to be done. pSCache->clearFlags( CA_READ_PENDING); gv_XFlmSysData.pBlockCacheMgr->m_uiPendingReads--; pSCache->releaseForThread(); // If we had no errors, take care of some other things if (RC_OK( rc)) { // The bDiscard flag tells us that we should discard the // block that we just read and use the next block in the // version list - because they are the same version. if (bDiscard) { // NOTE: We are guaranteed that pSCache->m_pNextInVersionList // is non-NULL at this point, because when we set the // bDiscard flag to TRUE, it was non-NULL, and we know that // the mutex was NOT unlocked in that case. pTmpSCache = pSCache->m_pNextInVersionList; pSCache->unlinkCache( TRUE, NE_XFLM_OK); pSCache = pTmpSCache; } else { *pbGotFromDisk = TRUE; } } // Notify all of the waiters of the read result. // IMPORTANT NOTE: This should be the LAST thing that is // done except for unlink the block below in the case of // an error having occurred. ScaNotify( pNotify, pSCache, rc); // If we had a BAD rc, unlink the block from the lists it is in and // free the memory. if (RC_BAD( rc)) { pSCache->unlinkCache( TRUE, NE_XFLM_OK); goto Exit; } *ppSCacheRV = pSCache; Exit: return( rc); } /**************************************************************************** Desc: This routine frees all cache blocks that have been modified by the update transaction. This routine is called whenever a transaction is to be aborted. ****************************************************************************/ void F_Database::freeModifiedBlocks( FLMUINT64 ui64CurrTransId) { F_CachedBlock * pSCache; F_CachedBlock * pNextSCache; FLMBOOL bFirstPass = TRUE; FLMBOOL bFreedAll; f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); // Unlink all log blocks and reset their flags so they // won't be marked as needing to be written to disk. unlinkTransLogBlocks(); Do_Free_Pass: pSCache = m_pSCacheList; flmAssert( !m_pPendingWriteList); bFreedAll = TRUE; while (pSCache) { // If the high transaction ID on the block is one less than this // transaction's ID, the block is the most current block. Therefore, // its high transaction ID should be reset to ~((FLMUINT64)0). if (pSCache->m_ui64HighTransID == ui64CurrTransId - 1) { pSCache->setTransID( ~((FLMUINT64)0)); // Need to link blocks that become the current version again // into the file log list if they are dirty. linkToLogList // will check to see if the block has already been logged. If it has, // it won't be linked into the list. // NOTE: If the blocks were in the "new" list originally, we don't take // the time to put them back into that list because they would have to // be inserted in order. They will still get written out eventually, but // they won't be written out by the reduceNewBlocks call. if (pSCache->m_ui16Flags & CA_DIRTY) { pSCache->linkToLogList(); } } else if (pSCache->m_ui64HighTransID == ~((FLMUINT64)0) && pSCache->getLowTransID() >= ui64CurrTransId && !(pSCache->m_ui16Flags & CA_READ_PENDING)) { pNextSCache = pSCache->m_pNextInDatabase; // Another thread might have a temporary "use" on this // block. Unlock the mutex long enough to allow the // other thread(s) to get rid of their "uses". Then start // from the top of the list again. if (pSCache->m_uiUseCount) { // Don't want to unlock the mutex during the first pass // because it opens the door to the prior version of one of // these modified blocks being removed from cache before we // have a chance to reset its ui64HighTransID back to // ~((FLMUINT64)0). // During the first pass, we want to get through all of the // blocks so that the code up above will get exercised for // each such block. if (bFirstPass) { bFreedAll = FALSE; pSCache = pNextSCache; continue; } else { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); f_sleep( 10); f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); pSCache = m_pSCacheList; continue; } } else { #ifdef FLM_DEBUG F_CachedBlock * pResetDirty = NULL; #endif // Unset dirty flag so we don't get an assert in unlinkCache. if (pSCache->m_ui16Flags & CA_DIRTY) { #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags = pSCache->m_ui16Flags; #endif flmAssert( this == pSCache->m_pDatabase); pSCache->unsetDirtyFlag(); #ifdef FLM_DBG_LOG pSCache->logFlgChange( ui16OldFlags, 'G'); #endif } #ifdef FLM_DEBUG // If m_pNextInVersionList is dirty it is because // ScaUnlinkTransLogBlocks changed the WAS_DIRTY flag to // DIRTY. If we don't temporarily clear the DIRTY flag, // unlinkCache will assert. if( pSCache->m_pNextInVersionList && (pSCache->m_pNextInVersionList->m_ui16Flags & CA_DIRTY)) { pResetDirty = pSCache->m_pNextInVersionList; pResetDirty->m_ui16Flags &= ~CA_DIRTY; } #endif pSCache->unlinkCache( TRUE, NE_XFLM_OK); #ifdef FLM_DEBUG if( pResetDirty) { pResetDirty->m_ui16Flags |= CA_DIRTY; } #endif pSCache = pNextSCache; continue; } } pSCache = pSCache->m_pNextInDatabase; } if (!bFreedAll && bFirstPass) { bFirstPass = FALSE; goto Do_Free_Pass; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: Prepares a block to be written out. Calculates the checksum and converts the block to native format if not currently in native format. ****************************************************************************/ RCODE flmPrepareBlockToWrite( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkLen; if ((FLMUINT)pBlkHdr->ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pBlkHdr)) { rc = RC_SET_AND_ASSERT( NE_XFLM_BLOCK_CRC); goto Exit; } uiBlkLen = (blkIsNewBTree( pBlkHdr) ? uiBlockSize : uiBlockSize - (FLMUINT)pBlkHdr->ui16BlkBytesAvail); // Block should already be in native format. flmAssert( !blkIsNonNativeFormat( pBlkHdr)); // Calculate and set the block CRC. pBlkHdr->ui32BlkCRC = calcBlkCRC( pBlkHdr, uiBlkLen); Exit: return( rc); } /**************************************************************************** Desc: This routine writes all blocks in the sorted list, or releases them. It attempts to write as many as it can that are currently contiguous. NOTE: This routine assumes that the block cache mutex is NOT locked. ****************************************************************************/ RCODE F_Database::writeSortedBlocks( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMUINT uiMaxDirtyCache, FLMUINT * puiDirtyCacheLeft, FLMBOOL * pbForceCheckpoint, FLMBOOL bIsCPThread, FLMUINT uiNumSortedBlocks, FLMBOOL * pbWroteAll) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; FLMUINT uiStartBlkAddr = 0; FLMUINT uiLastBlkAddr = 0; FLMUINT uiContiguousBlocks = 0; FLMUINT uiNumSortedBlocksProcessed; FLMUINT uiBlockCount; F_CachedBlock * ppContiguousBlocks[ FLM_MAX_IO_BUFFER_BLOCKS]; FLMBOOL bBlockDirty[ FLM_MAX_IO_BUFFER_BLOCKS]; FLMUINT uiOffset; FLMUINT uiTmpOffset; FLMUINT uiLoop; FLMUINT uiStartOffset; FLMUINT uiCopyLen; FLMBOOL bForceCheckpoint = *pbForceCheckpoint; F_CachedBlock * pSCache; IF_IOBuffer * pIOBuffer = NULL; FLMBYTE * pucBuffer; uiOffset = 0; for (;;) { // Mutex must be locked to test dirty flags. if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } // See how many we have that are contiguous uiContiguousBlocks = 0; uiNumSortedBlocksProcessed = 0; uiStartOffset = uiTmpOffset = uiOffset; while (uiTmpOffset < uiNumSortedBlocks) { pSCache = m_ppBlocksDone [uiTmpOffset]; // See if this block is still eligible for writing out. // If so, mark it as write pending and add to list. flmAssert( pSCache->m_ui16Flags & CA_DIRTY); // Is it contiguous with last block or the first block? if (!uiContiguousBlocks || (FSGetFileNumber( uiLastBlkAddr) == FSGetFileNumber( pSCache->m_uiBlkAddress) && uiLastBlkAddr + m_uiBlockSize == pSCache->m_uiBlkAddress)) { // Block is either first block or contiguous with // last block. Add_Contiguous_Block: uiLastBlkAddr = pSCache->m_uiBlkAddress; // Set first block address if this is the first one. if (!uiContiguousBlocks) { uiStartBlkAddr = pSCache->m_uiBlkAddress; } ppContiguousBlocks [uiContiguousBlocks] = pSCache; bBlockDirty [uiContiguousBlocks++] = TRUE; uiNumSortedBlocksProcessed++; if (uiContiguousBlocks == FLM_MAX_IO_BUFFER_BLOCKS) { break; } uiTmpOffset++; } else { FLMUINT uiGap; FLMUINT uiSaveContiguousBlocks; FLMUINT uiBlkAddress; // Ran into a non-contiguous block. If we are not forcing // a checkpoint, take what we have and write it out. // If we are forcing a checkpoint, see if we can fill the // gap with other blocks in cache. if (!bForceCheckpoint) { break; } // See if the gap is worth trying to fill. // If blocks are in different files, cannot fill gap. if (FSGetFileNumber( uiLastBlkAddr) != FSGetFileNumber( pSCache->m_uiBlkAddress)) { break; } // If 32K won't encompass both blocks, not worth it to try // and fill the gap. uiGap = FSGetFileOffset( pSCache->m_uiBlkAddress) - FSGetFileOffset( uiLastBlkAddr) - m_uiBlockSize; if (uiGap > 32 * 1024 - (m_uiBlockSize * 2)) { break; } // If the gap would run us off the maximum blocks to // request, don't try to fill it. if (uiContiguousBlocks + uiGap / m_uiBlockSize + 1 > FLM_MAX_IO_BUFFER_BLOCKS) { break; } uiSaveContiguousBlocks = uiContiguousBlocks; uiBlkAddress = uiLastBlkAddr + m_uiBlockSize; while (uiBlkAddress != pSCache->m_uiBlkAddress) { F_CachedBlock ** ppSCacheBucket; F_CachedBlock * pTmpSCache; ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_uiSigBitsInBlkSize, uiBlkAddress); pTmpSCache = *ppSCacheBucket; while (pTmpSCache && (pTmpSCache->m_uiBlkAddress != uiBlkAddress || pTmpSCache->m_pDatabase != this)) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (!pTmpSCache || (pTmpSCache->m_ui16Flags & (CA_READ_PENDING | CA_WRITE_PENDING | CA_WRITE_INHIBIT)) || pTmpSCache->m_ui64HighTransID != ~((FLMUINT64)0)) { break; } ppContiguousBlocks [uiContiguousBlocks] = pTmpSCache; bBlockDirty [uiContiguousBlocks++] = (pTmpSCache->m_ui16Flags & CA_DIRTY) ? TRUE : FALSE; pTmpSCache->useForThread( 0); uiBlkAddress += m_uiBlockSize; } // If we couldn't fill in the entire gap, we are done. if (uiBlkAddress != pSCache->m_uiBlkAddress) { // Release the blocks we obtained in the above loop. while (uiContiguousBlocks > uiSaveContiguousBlocks) { uiContiguousBlocks--; ppContiguousBlocks [uiContiguousBlocks]->releaseForThread(); } break; } else { goto Add_Contiguous_Block; } } } // At this point, we know how many are contiguous. if (!uiContiguousBlocks) { flmAssert( uiOffset == uiNumSortedBlocks); break; } if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Ask for a buffer of the size needed. flmAssert( pIOBuffer == NULL); if (RC_BAD( rc = m_pBufferMgr->getBuffer( uiContiguousBlocks * m_uiBlockSize, &pIOBuffer))) { goto Exit; } pIOBuffer->setCompletionCallback( scaWriteComplete, pDbStats); // Callback will now take care of everything between // uiStartOffset and uiStartOffset + uiNumSortedBlocksProcessed - 1 // inclusive, as well as any non-dirty blocks that were // put in for filler. flmAssert( uiNumSortedBlocksProcessed); uiOffset = uiStartOffset + uiNumSortedBlocksProcessed; uiBlockCount = uiContiguousBlocks; // Must set to zero so we don't process ppContiguousBlocks // at exit. uiContiguousBlocks = 0; // Set write pending on all of the blocks before unlocking // the mutex. // Then unlock the mutex and come out and copy // the blocks. if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } for (uiLoop = 0; uiLoop < uiBlockCount; uiLoop++) { pSCache = ppContiguousBlocks [uiLoop]; if (bBlockDirty [uiLoop]) { flmAssert( pSCache->m_ui16Flags & CA_DIRTY); flmAssert( !(pSCache->m_ui16Flags & CA_WRITE_INHIBIT)); pSCache->setFlags( CA_WRITE_PENDING); flmAssert( *puiDirtyCacheLeft >= m_uiBlockSize); (*puiDirtyCacheLeft) -= m_uiBlockSize; pSCache->unlinkFromDatabase(); pSCache->linkToDatabase( this); } else { flmAssert( !(pSCache->m_ui16Flags & CA_DIRTY)); } // Set callback data so we will release these and clear // the pending flag if we don't do the I/O. pIOBuffer->addCallbackData( pSCache); } if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Copy blocks into the IO buffer. pucBuffer = pIOBuffer->getBufferPtr(); for (uiLoop = 0; uiLoop < uiBlockCount; uiLoop++, pucBuffer += m_uiBlockSize) { pSCache = ppContiguousBlocks [uiLoop]; // Copy data from block to the write buffer uiCopyLen = blkGetEnd( m_uiBlockSize, blkHdrSize( pSCache->m_pBlkHdr), pSCache->m_pBlkHdr); f_memcpy( pucBuffer, pSCache->m_pBlkHdr, uiCopyLen); // Encrypt the block if needed if (RC_BAD( rc = encryptBlock( m_pDictList, pucBuffer))) { goto Exit; } if (RC_BAD( rc = flmPrepareBlockToWrite( m_uiBlockSize, (F_BLK_HDR *)pucBuffer))) { goto Exit; } } pSFileHdl->setMaxAutoExtendSize( m_uiMaxFileSize); pSFileHdl->setExtendSize( m_uiFileExtendSize); rc = pSFileHdl->writeBlock( uiStartBlkAddr, pIOBuffer->getBufferSize(), pIOBuffer); pIOBuffer->Release(); pIOBuffer = NULL; if( RC_BAD( rc)) { if (pDbStats) { pDbStats->bHaveStats = TRUE; pDbStats->uiWriteErrors++; } goto Exit; } // See if we should give up our write lock. Will do so if we // are not forcing a checkpoint and we have not exceeded the // maximum time since the last checkpoint AND we have gotten // below the maximum dirty blocks allowed. if (!bForceCheckpoint && bIsCPThread) { FLMUINT uiCurrTime = (FLMUINT)FLM_GET_TIMER(); if (scaSeeIfForceCheckpoint( uiCurrTime, m_uiLastCheckpointTime, m_pCPInfo)) { bForceCheckpoint = TRUE; } else { if (m_pWriteLockObj->getWaiterCount() && *puiDirtyCacheLeft <= uiMaxDirtyCache) { // Break out of loop and finish writing whatever // we have pending. *pbWroteAll = FALSE; goto Exit; } } } } Exit: // Unuse any blocks that did not get processed. while (uiOffset < uiNumSortedBlocks) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } m_ppBlocksDone[ uiOffset]->releaseForThread(); uiOffset++; } while (uiContiguousBlocks) { uiContiguousBlocks--; // Only release the non-dirty blocks, because dirty blocks // will have been taken care of in the loop above. if (!bBlockDirty [uiContiguousBlocks]) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } ppContiguousBlocks [uiContiguousBlocks]->releaseForThread(); } } if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } // If we allocated a write buffer, but did not do a write with it, // still need to call notifyComplete so that our callback function // will be called and the F_CachedBlock objects will be released. if (pIOBuffer) { flmAssert( RC_BAD( rc)); pIOBuffer->notifyComplete( rc); } *pbForceCheckpoint = bForceCheckpoint; return( rc); } /**************************************************************************** Desc: This routine writes all dirty cache blocks to disk. This routine is called when a transaction is committed. ****************************************************************************/ RCODE F_Database::flushDirtyBlocks( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMUINT uiMaxDirtyCache, FLMBOOL bForceCheckpoint, FLMBOOL bIsCPThread, FLMBOOL * pbWroteAll) { RCODE rc = NE_XFLM_OK; RCODE rc2; F_CachedBlock * pSCache; FLMBOOL bMutexLocked = FALSE; FLMUINT uiSortedBlocks = 0; FLMUINT uiBlockCount = 0; FLMBOOL bWasForcing; FLMBOOL bWriteInhibited; FLMUINT uiDirtyCacheLeft; FLMBOOL bAllocatedAll = FALSE; flmAssert( !m_uiLogCacheCount); if (m_pCPInfo) { lockMutex(); m_pCPInfo->bWritingDataBlocks = TRUE; unlockMutex(); } flmAssert( !m_pPendingWriteList); uiDirtyCacheLeft = m_uiDirtyCacheCount * m_uiBlockSize; // If we are forcing a checkpoint, pre-allocate an array big enough // to hold all of the dirty blocks. We do this so we won't end up // continually re-allocating the array in the loop below. Force_Checkpoint: if (bForceCheckpoint) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); pSCache = m_pSCacheList; uiBlockCount = 0; while (pSCache && (pSCache->m_ui16Flags & CA_DIRTY)) { uiBlockCount++; pSCache = pSCache->m_pNextInDatabase; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bAllocatedAll = TRUE; if (uiBlockCount > m_uiBlocksDoneArraySize * 2) { if (RC_BAD( rc = allocBlocksArray( (uiBlockCount + 1) / 2, TRUE))) { if (rc == NE_XFLM_MEM) { bAllocatedAll = FALSE; rc = NE_XFLM_OK; } else { goto Exit; } } } } for (;;) { flmAssert( !bMutexLocked); f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; // Create a list of blocks to write out - MAX_BLOCKS_TO_SORT at most. pSCache = m_pSCacheList; uiSortedBlocks = 0; for (;;) { FLMUINT uiPrevBlkAddress; if (bAllocatedAll) { if (uiSortedBlocks == uiBlockCount) { #ifdef FLM_DEBUG // Better not be any dirty blocks after the last one. if (uiSortedBlocks) { pSCache = m_ppBlocksDone [uiSortedBlocks - 1]; flmAssert( !pSCache->m_pNextInDatabase || !(pSCache->m_pNextInDatabase->m_ui16Flags & CA_DIRTY)); } #endif break; } flmAssert( pSCache && (pSCache->m_ui16Flags & CA_DIRTY)); } else { if (!pSCache || !(pSCache->m_ui16Flags & CA_DIRTY) || uiSortedBlocks == MAX_BLOCKS_TO_SORT) { break; } } flmAssert( !(pSCache->m_ui16Flags & CA_WRITE_PENDING)); uiPrevBlkAddress = pSCache->getPriorImageAddress(); bWriteInhibited = FALSE; if (pSCache->m_ui16Flags & CA_WRITE_INHIBIT) { // When the checkpoint thread is running there is no need to // inhibit writes - because it is not possible for an updater // to be making any changes at this point. However, // the inhibit writes bit may still be set because the // thread that originally did the update transaction never // got the use count to go to zero (due to a reader that // simultaneously had a use) and hence, the inhibit bit // has never been unset. It is only unset when we see // the use count go to zero. if (bIsCPThread) { pSCache->clearFlags( CA_WRITE_INHIBIT); } else { bWriteInhibited = TRUE; } } // Skip blocks that are write inhibited or that have // not been properly logged yet. if (bWriteInhibited || (!uiPrevBlkAddress && pSCache->m_pNextInVersionList)) { flmAssert( !bForceCheckpoint); } else { if (uiSortedBlocks == m_uiBlocksDoneArraySize * 2) { if (RC_BAD( rc = allocBlocksArray( 0, TRUE))) { goto Exit; } } // Keep list of blocks to process m_ppBlocksDone [uiSortedBlocks++] = pSCache; // Must use to keep from going away. pSCache->useForThread( 0); } pSCache = pSCache->m_pNextInDatabase; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; // Sort the list of blocks by block address. if (uiSortedBlocks) { if (uiSortedBlocks > 1) { f_qsort( m_ppBlocksDone, 0, uiSortedBlocks - 1, scaSortCompare, scaSortSwap); } bWasForcing = bForceCheckpoint; rc = writeSortedBlocks( pDbStats, pSFileHdl, uiMaxDirtyCache, &uiDirtyCacheLeft, &bForceCheckpoint, bIsCPThread, uiSortedBlocks, pbWroteAll); } else { goto Exit; } // Set to zero so won't get released at exit. uiSortedBlocks = 0; if (!bIsCPThread || RC_BAD( rc) || !(*pbWroteAll)) { goto Exit; } if (bForceCheckpoint) { if (!bWasForcing) { // Needs to be the checkpoint thread that does this // because all of the log blocks have to have been // written out - which the checkpoint thread does // before calling this routine. flmAssert( bIsCPThread); goto Force_Checkpoint; } else if (bAllocatedAll) { // We did all of the blocks in one pass, so // break out of the loop. goto Exit; } } } Exit: // Release any blocks that are still used. while (uiSortedBlocks) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } uiSortedBlocks--; // Release any blocks we didn't process through. pSCache = m_ppBlocksDone [uiSortedBlocks]; pSCache->releaseForThread(); } // Need to finish up any async writes. if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Wait for writes to complete. if (RC_BAD( rc2 = m_pBufferMgr->waitForAllPendingIO())) { if (RC_OK( rc)) { rc = rc2; } } flmAssert( !m_pPendingWriteList); // Better not be any incomplete writes at this point. flmAssert( !m_pBufferMgr->isIOPending()); // Don't keep around a large block array if we happened to // allocate one that is bigger than our normal size. It may // be huge because we were forcing a checkpoint. if (m_uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) { f_free( &m_ppBlocksDone); m_uiBlocksDoneArraySize = 0; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_Database::reduceDirtyCache( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl) { RCODE rc = NE_XFLM_OK; RCODE rc2; F_CachedBlock * pSCache; FLMBOOL bMutexLocked = FALSE; FLMUINT uiDirtyCacheLeft; FLMUINT uiSortedBlocks = 0; FLMUINT uiInhibitCount; FLMBOOL bForceCheckpoint; FLMBOOL bWroteAll; flmAssert( !m_uiLogCacheCount); flmAssert( !m_pPendingWriteList); if( m_uiDirtyCacheCount > m_uiBlocksDoneArraySize * 2) { if( RC_BAD( rc = allocBlocksArray( (m_uiDirtyCacheCount + 1) / 2, TRUE))) { goto Exit; } } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; pSCache = m_pSCacheList; uiSortedBlocks = 0; uiInhibitCount = 0; while( pSCache && (pSCache->m_ui16Flags & CA_DIRTY)) { if( (pSCache->m_ui16Flags & CA_WRITE_INHIBIT) != 0) { uiInhibitCount++; } else { flmAssert( uiSortedBlocks < m_uiDirtyCacheCount); m_ppBlocksDone[ uiSortedBlocks++] = pSCache; pSCache->useForThread( 0); } pSCache = pSCache->m_pNextInDatabase; } flmAssert( uiSortedBlocks + uiInhibitCount == m_uiDirtyCacheCount); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; if( !uiSortedBlocks) { goto Exit; } if( uiSortedBlocks > 1) { f_qsort( m_ppBlocksDone, 0, uiSortedBlocks - 1, scaSortCompare, scaSortSwap); } uiDirtyCacheLeft = m_uiDirtyCacheCount * m_uiBlockSize; bForceCheckpoint = FALSE; bWroteAll = TRUE; rc = writeSortedBlocks( pDbStats, pSFileHdl, 0, &uiDirtyCacheLeft, &bForceCheckpoint, FALSE, uiSortedBlocks, &bWroteAll); uiSortedBlocks = 0; if( RC_BAD( rc)) { goto Exit; } Exit: while( uiSortedBlocks) { if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } uiSortedBlocks--; // Release any blocks we didn't process through. pSCache = m_ppBlocksDone[ uiSortedBlocks]; pSCache->releaseForThread(); } // Need to finish up any async writes. if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Wait for writes to complete. if( RC_BAD( rc2 = m_pBufferMgr->waitForAllPendingIO())) { if( RC_OK( rc)) { rc = rc2; } } flmAssert( !m_pPendingWriteList); // Better not be any incomplete writes at this point. flmAssert( !m_pBufferMgr->isIOPending()); // Don't keep around a large block array if we happened to // allocate one that is bigger than our normal size. It may // be huge because we were forcing a checkpoint. if( m_uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) { f_free( &m_ppBlocksDone); m_uiBlocksDoneArraySize = 0; } return( rc); } /**************************************************************************** Desc: This routine writes new cache blocks to disk. The purpose of this routine is to allow cache to be reduced as quickly as possible. This is best accomplished by flushing blocks that are contiguous before resorting to writing out non-contiguous blocks. The "new" block list attempts to accomplish this by keeping an ordered list of most of the blocks that have been created since the last checkpoint. The list may not contain all of the new blocks if a transaction, which modified blocks, was aborted since the last checkpoint completed. In this case, the blocks would have been removed from the new list when new versions of the blocks were created. Upon aborting, the blocks that were originally in the new list are not put back into the list because of the cost associated with finding their correct places in the list. Even though these blocks aren't in the new list anymore, they are still marked as being dirty and will written out eventually. ****************************************************************************/ RCODE F_Database::reduceNewBlocks( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMUINT * puiBlocksFlushed) { RCODE rc = NE_XFLM_OK; RCODE rc2; F_CachedBlock * pSCache; FLMBOOL bMutexLocked = FALSE; FLMUINT uiSortedBlocks = 0; FLMUINT uiDirtyCacheLeft; FLMUINT uiBlocksFlushed = 0; flmAssert( !m_uiLogCacheCount); if (m_pCPInfo) { lockMutex(); m_pCPInfo->bWritingDataBlocks = TRUE; unlockMutex(); } flmAssert( !m_pPendingWriteList); uiDirtyCacheLeft = m_uiDirtyCacheCount * m_uiBlockSize; if (m_uiBlocksDoneArraySize < MAX_BLOCKS_TO_SORT) { if (RC_BAD( rc = allocBlocksArray( MAX_BLOCKS_TO_SORT, TRUE))) { // If the array size is non-zero, but we were unable to allocate // the size we wanted, we'll just be content to output as many // blocks as possible with the existing size of the array if( rc == NE_XFLM_MEM && m_uiBlocksDoneArraySize) { rc = NE_XFLM_OK; } else { goto Exit; } } } // Create a list of blocks to write out f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; pSCache = m_pFirstInNewList; uiSortedBlocks = 0; for (;;) { FLMUINT uiPrevBlkAddress; if (!pSCache || uiSortedBlocks == m_uiBlocksDoneArraySize) { break; } flmAssert( !(pSCache->m_ui16Flags & CA_WRITE_PENDING)); flmAssert( pSCache->m_ui16Flags & (CA_DIRTY | CA_IN_NEW_LIST)); uiPrevBlkAddress = pSCache->getPriorImageAddress(); // Skip blocks that are write inhibited if( pSCache->m_ui16Flags & CA_WRITE_INHIBIT) { pSCache = pSCache->m_pNextInReplaceList; continue; } // Keep list of blocks to process m_ppBlocksDone [uiSortedBlocks++] = pSCache; // Must use to keep from going away. pSCache->useForThread( 0); pSCache = pSCache->m_pNextInReplaceList; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; if (uiSortedBlocks) { FLMBOOL bForceCheckpoint = FALSE; FLMBOOL bDummy; rc = writeSortedBlocks( pDbStats, pSFileHdl, ~((FLMUINT)0), &uiDirtyCacheLeft, &bForceCheckpoint, FALSE, uiSortedBlocks, &bDummy); if( RC_OK( rc)) { uiBlocksFlushed += uiSortedBlocks; } } else { goto Exit; } // Set to zero so won't get released at exit. uiSortedBlocks = 0; Exit: // Release any blocks that are still used. while (uiSortedBlocks) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } uiSortedBlocks--; // Release any blocks we didn't process through. pSCache = m_ppBlocksDone [uiSortedBlocks]; #ifdef FLM_DEBUG if( RC_OK( rc)) { flmAssert( !(pSCache->m_ui16Flags & CA_IN_NEW_LIST)); } #endif pSCache->releaseForThread(); } // Need to finish up any async writes. if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; } // Wait for writes to complete. if (RC_BAD( rc2 = m_pBufferMgr->waitForAllPendingIO())) { if (RC_OK( rc)) { rc = rc2; } } flmAssert( !m_pPendingWriteList); // Better not be any incomplete writes at this point. flmAssert( !m_pBufferMgr->isIOPending()); // Don't keep around a large block array if we happened to // allocate one that is bigger than our normal size. It may // be huge because we were forcing a checkpoint. if (m_uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) { f_free( &m_ppBlocksDone); m_uiBlocksDoneArraySize = 0; } if (puiBlocksFlushed) { *puiBlocksFlushed = uiBlocksFlushed; } return( rc); } /**************************************************************************** Desc: This routine is called to determine if a cache block or cache record is still needed. ****************************************************************************/ FLMBOOL F_Database::neededByReadTrans( FLMUINT64 ui64LowTransId, FLMUINT64 ui64HighTransId) { FLMBOOL bNeeded = FALSE; F_Db * pReadTrans; lockMutex(); // Quick check - so we don't have to traverse all read transactions. if (!m_pFirstReadTrans || ui64HighTransId < m_pFirstReadTrans->m_ui64CurrTransID || ui64LowTransId > m_pLastReadTrans->m_ui64CurrTransID) { goto Exit; } // Traverse all read transactions - this loop assumes that the // read transactions are in order of when they started - meaning // that the ui64CurrTransID on each will be ascending order. The // loop will quit early once it can detect that the block is // too old for all remaining transactions. pReadTrans = m_pFirstReadTrans; while (pReadTrans) { if (pReadTrans->m_ui64CurrTransID >= ui64LowTransId && pReadTrans->m_ui64CurrTransID <= ui64HighTransId) { bNeeded = TRUE; goto Exit; } else if (pReadTrans->m_ui64CurrTransID > ui64HighTransId) { // All remaining transaction's transaction IDs will // also be greater than the block's high trans ID // Therefore, we can quit here. goto Exit; } pReadTrans = pReadTrans->m_pNextReadTrans; } Exit: unlockMutex(); return( bNeeded); } /**************************************************************************** Desc: This routine is called just after a transaction has successfully committed. It will unset the flags on log blocks that would cause them to be written to disk. If the block is no longer needed by a read transaction, it will also put the block in the LRU list so it will be selected for replacement first. ****************************************************************************/ void F_Database::releaseLogBlocks( void) { F_CachedBlock * pSCache; F_CachedBlock * pNextSCache; f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); pSCache = m_pTransLogList; while (pSCache) { #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags = pSCache->m_ui16Flags; #endif // A block in this list should never be dirty. flmAssert( !(pSCache->m_ui16Flags & CA_DIRTY)); if ((pSCache->m_ui16Flags & CA_WRITE_TO_LOG) && !(pSCache->m_ui16Flags & CA_LOG_FOR_CP)) { flmAssert( m_uiLogCacheCount); m_uiLogCacheCount--; } pSCache->clearFlags( CA_WRITE_TO_LOG | CA_WAS_DIRTY); #ifdef FLM_DBG_LOG pSCache->logFlgChange( ui16OldFlags, 'I'); #endif pNextSCache = pSCache->m_pNextInHashBucket; // Perhaps we don't really need to set these pointers to NULL, // but it helps keep things clean. pSCache->m_pNextInHashBucket = NULL; pSCache->m_pPrevInHashBucket = NULL; // If the block is no longer needed by a read transaction, // and it does not need to be logged for the checkpoint, // move it to the free list. if ((!pSCache->m_uiUseCount) && (!pSCache->neededByReadTrans()) && (!(pSCache->m_ui16Flags & CA_LOG_FOR_CP))) { F_CachedBlock * pNewerVer = pSCache->m_pPrevInVersionList; if( !pSCache->m_pNextInVersionList && pNewerVer && pNewerVer->m_ui64HighTransID == ~((FLMUINT64)0) && pNewerVer->m_ui16Flags & CA_IN_FILE_LOG_LIST) { pNewerVer->unlinkFromLogList(); } pSCache->unlinkCache( TRUE, NE_XFLM_OK); } pSCache = pNextSCache; } m_pTransLogList = NULL; f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: Retrieve a data block. Shared cache is searched first. If the block is not in shared cache, it will be retrieved from disk and put into cache. The use count on the block will be incremented. ****************************************************************************/ RCODE F_Database::getBlock( F_Db * pDb, LFILE * pLFile, // Pointer to logical file structure // We are retrieving the block for. // NULL if there is no logical file. FLMUINT uiBlkAddress, // Address of requested block. FLMUINT * puiNumLooksRV, // Pointer to FLMUINT where number of // cache lookups is to be returned. // If pointer is non-NULL it indicates // that we only want to find the block // if it is in cache. If it is NOT // in cache, do NOT read it in from // disk. -- This capability is needed // by the FlmDbReduceSize function. F_CachedBlock ** ppSCacheRV) // Returns pointer to cache block. { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; FLMUINT64 ui64BlkVersion; FLMUINT uiNumLooks; F_CachedBlock ** ppSCacheBucket; F_CachedBlock * pSBlkVerCache; F_CachedBlock * pSMoreRecentVerCache; F_CachedBlock * pSCache; FLMBOOL bGotFromDisk = FALSE; flmAssert( this == pDb->m_pDatabase); flmAssert( uiBlkAddress != 0); *ppSCacheRV = NULL; // We should NEVER be attempting to read a block address that is // beyond the current logical end of file. if (!FSAddrIsBelow( uiBlkAddress, pDb->m_uiLogicalEOF)) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } // Release CPU to prevent CPU hog f_yieldCPU(); // Lock the mutex f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; pDb->m_uiInactiveTime = 0; // Search shared cache for the desired version of the block. // First, determine the hash bucket. ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_uiSigBitsInBlkSize, uiBlkAddress); // Search down the linked list of F_CachedBlock objects off of the bucket // looking for the correct cache block. pSCache = *ppSCacheBucket; uiNumLooks = 1; while ((pSCache) && (pSCache->m_uiBlkAddress != uiBlkAddress || pSCache->m_pDatabase != this)) { if ((pSCache = pSCache->m_pNextInHashBucket) != NULL) { uiNumLooks++; } } // If there was no block found with the appropriate file/address we need to // create a dummy block and attempt to read it in. if (!pSCache) { if (puiNumLooksRV) { *puiNumLooksRV = uiNumLooks; *ppSCacheRV = NULL; goto Exit; } gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaults++; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaultLooks += uiNumLooks; if (RC_BAD( rc = readIntoCache( pDb, pLFile, uiBlkAddress, NULL, NULL, &pSCache, &bGotFromDisk))) { goto Exit; } } else { // A block with the appropriate file/address was found. We now // need to follow the chain until we find the version of the // block that we need. pSMoreRecentVerCache = NULL; // Save pointer to block that is newest version. pSBlkVerCache = pSCache; ui64BlkVersion = pDb->m_ui64CurrTransID; for (;;) { // If the block is being read into memory, wait for the read // to complete so we can see what it is. if (pSCache && (pSCache->m_ui16Flags & CA_READ_PENDING)) { gv_XFlmSysData.pBlockCacheMgr->m_uiIoWaits++; if (RC_BAD( rc = f_notifyWait( gv_XFlmSysData.hBlockCacheMutex, pDb->m_hWaitSem, (void *)&pSCache, &pSCache->m_pNotifyList))) { goto Exit; } // The thread doing the notify "uses" the cache block // on behalf of this thread to prevent the cache block // from being flushed after it unlocks the mutex. // At this point, since we have locked the mutex, // we need to release the cache block. pSCache->releaseForThread(); // Start over at the top of the list. pSBlkVerCache = pSCache; while (pSBlkVerCache->m_pPrevInVersionList) { pSBlkVerCache = pSBlkVerCache->m_pPrevInVersionList; } pSCache = pSBlkVerCache; pSMoreRecentVerCache = NULL; continue; } if (!pSCache || ui64BlkVersion > pSCache->m_ui64HighTransID) { if (puiNumLooksRV) { *puiNumLooksRV = uiNumLooks; *ppSCacheRV = NULL; goto Exit; } // The version of the block we want is not in the list, // either because we are at the end of the list (!pSCache), // or because the block version we want is higher than // the high trans ID on the cache block we are looking // at. See if there is anything on disk that comes after // that block. If not, simply return an OLD_VIEW // error. if (pSMoreRecentVerCache && pSMoreRecentVerCache->getPriorImageAddress() == 0) { // Should only be possible when reading a root block, // because the root block address in the LFILE may be // a block that was just created by an update // transaction. flmAssert( pDb->m_uiKilledTime); rc = RC_SET( NE_XFLM_OLD_VIEW); goto Exit; } gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaults++; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaultLooks += uiNumLooks; if (pSMoreRecentVerCache) { if( RC_BAD( rc = readIntoCache( pDb, pLFile, uiBlkAddress, pSMoreRecentVerCache, pSMoreRecentVerCache->m_pNextInVersionList, &pSCache, &bGotFromDisk))) { goto Exit; } } else { if( RC_BAD( rc = readIntoCache( pDb, pLFile, uiBlkAddress, NULL, pSBlkVerCache, &pSCache, &bGotFromDisk))) { goto Exit; } } // At this point, if the read was successful, we should // have the block we want. break; } else if (ui64BlkVersion >= pSCache->getLowTransID()) { // This is the version of the block that we need. gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHits++; gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHitLooks += uiNumLooks; break; } else { // If we are in an update transaction, the version of the // block we want should ALWAYS be at the top of the list. // If not, we have a serious problem! flmAssert( pDb->m_eTransType != XFLM_UPDATE_TRANS); pSMoreRecentVerCache = pSCache; pSCache = pSCache->m_pNextInVersionList; if (pSCache) { uiNumLooks++; } } } } // Increment the use count on the block. pSCache->useForThread( 0); // Block was found, make it the MRU block or bump it up in the MRU list, // if it is not already at the top. if( pDb->m_uiFlags & FDB_DONT_POISON_CACHE) { if (!(pDb->m_uiFlags & FDB_BACKGROUND_INDEXING) || (pLFile && pLFile->eLfType != XFLM_LF_INDEX)) { if (!bGotFromDisk) { pSCache->stepUpInGlobalList(); } // If the block was read from disk and FDB_DONT_POISION_CACHE is // set, we don't need to do anything because the block is // already linked at the LRU position. } else if (pSCache->m_pPrevInGlobal) { pSCache->unlinkFromGlobalList(); pSCache->linkToGlobalListAsMRU(); } } else if (pSCache->m_pPrevInGlobal) { pSCache->unlinkFromGlobalList(); pSCache->linkToGlobalListAsMRU(); } *ppSCacheRV = pSCache; Exit: #ifdef SCACHE_LINK_CHECKING if (RC_BAD( rc)) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } scaVerify( 300); } #endif if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } return( rc); } /**************************************************************************** Desc: Create a data block. ****************************************************************************/ RCODE F_Database::createBlock( F_Db * pDb, F_CachedBlock ** ppSCacheRV) { RCODE rc = NE_XFLM_OK; FLMUINT uiBlkAddress; F_BLK_HDR * pBlkHdr; F_CachedBlock * pSCache = NULL; F_CachedBlock * pOldSCache = NULL; FLMBOOL bMutexLocked = FALSE; FLMBOOL bLocalCacheAllocation = FALSE; FLMUINT uiOldLogicalEOF; F_CachedBlock ** ppSCacheBucket; FLMUINT uiBlockSize = pDb->m_pDatabase->getBlockSize(); pDb->m_bHadUpdOper = TRUE; // First see if there is a free block in the avail list. if (pDb->m_uiFirstAvailBlkAddr) { rc = blockUseNextAvail( pDb, ppSCacheRV); goto Exit; } // See if we need to free any cache or write dirty cache f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( pDb))) { goto Exit; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = FALSE; // Create a new block at EOF uiBlkAddress = pDb->m_uiLogicalEOF; // Time for a new block file? if (FSGetFileOffset(uiBlkAddress) >= m_uiMaxFileSize) { FLMUINT uiFileNumber = FSGetFileNumber( uiBlkAddress) + 1; if (uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER) { rc = RC_SET( NE_XFLM_DB_FULL); goto Exit; } if (RC_BAD( rc = pDb->m_pSFileHdl->createFile( uiFileNumber))) { goto Exit; } uiBlkAddress = FSBlkAddress( uiFileNumber, 0 ); } // Allocate a cache block for this new block. If we have older // versions of this block already in cache, we need to link the // new block above the older version. If reasonable, try to // allocate the new cache block without locking the mutex. if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { if( (pSCache = new( uiBlockSize) F_CachedBlock( uiBlockSize)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } bLocalCacheAllocation = TRUE; } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; // Determine the hash bucket the new block should be put into. ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_uiSigBitsInBlkSize, uiBlkAddress); // Search down the linked list of F_CachedBlock objects off of the bucket // looking for an older version of the block. If there are older // versions, we should get rid of them. pOldSCache = *ppSCacheBucket; while (pOldSCache && (pOldSCache->m_uiBlkAddress != uiBlkAddress || pOldSCache->m_pDatabase != this)) { pOldSCache = pOldSCache->m_pNextInHashBucket; } while (pOldSCache) { F_CachedBlock * pNextSCache = pOldSCache->m_pNextInVersionList; // Older versions of blocks should not be in use or needed // by anyone because the only we we would have an older // version of a block beyond the logical EOF is if // FlmDbReduceSize had been called. But it forces a // checkpoint that requires any read transactions to be // non-active, or killed. flmAssert( !pOldSCache->m_ui16Flags); flmAssert( !pOldSCache->m_uiUseCount); flmAssert( pOldSCache->m_ui64HighTransID == ~((FLMUINT64)0) || !pOldSCache->neededByReadTrans()); pOldSCache->unlinkCache( TRUE, NE_XFLM_OK); pOldSCache = pNextSCache; } // Allocate a cache block - either a new one or by replacing // an existing one. if( bLocalCacheAllocation) { F_BlockCacheMgr * pBlockCacheMgr = gv_XFlmSysData.pBlockCacheMgr; // Now that the mutex is locked, update stats and do other work // that couldn't be done when the block was allocated. pBlockCacheMgr->m_Usage.uiCount++; pBlockCacheMgr->m_Usage.uiByteCount += pSCache->memSize(); // Set use count to one so the block cannot be replaced. pSCache->m_bCanRelocate = TRUE; pSCache->useForThread( 0); } else { if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->allocBlock( pDb, &pSCache))) { goto Exit; } } pSCache->m_uiBlkAddress = uiBlkAddress; pSCache->setTransID( ~((FLMUINT64)0)); // Initialize the block data, dirty flag is set so that it will be // flushed as needed. pBlkHdr = pSCache->m_pBlkHdr; f_memset( pBlkHdr, 0, m_uiBlockSize); pBlkHdr->ui32BlkAddr = (FLMUINT32)uiBlkAddress; pBlkHdr->ui64TransID = pDb->m_ui64CurrTransID; pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - SIZEOF_STD_BLK_HDR); blkSetNativeFormat( pBlkHdr); #ifdef FLM_DBG_LOG flmDbgLogWrite( this, pSCache->m_uiBlkAddress, 0, pSCache->getLowTransID(), "CREATE"); #endif // Link block into the global list pSCache->m_ui16Flags |= CA_DUMMY_FLAG; pSCache->linkToGlobalListAsMRU(); // Set the dirty flag pSCache->setDirtyFlag( this); pSCache->m_ui16Flags &= ~CA_DUMMY_FLAG; // Set write inhibit bit so we will not unset the dirty bit // until the use count goes to zero. pSCache->setFlags( CA_WRITE_INHIBIT); #ifdef FLM_DBG_LOG pSCache->logFlgChange( 0, 'J'); #endif // Now that the dirty flag and write inhibit flag // have been set, link the block to the file pSCache->linkToDatabase( this); pSCache->linkToHashBucket( ppSCacheBucket); uiOldLogicalEOF = pDb->m_uiLogicalEOF; pDb->m_uiLogicalEOF = uiBlkAddress + m_uiBlockSize; // Link the block into the "new" list pSCache->linkToNewList(); // Return a pointer to the block *ppSCacheRV = pSCache; Exit: #ifdef SCACHE_LINK_CHECKING if (RC_BAD( rc)) { if (!bMutexLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bMutexLocked = TRUE; } scaVerify( 400); } #endif if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } if (RC_BAD( rc)) { *ppSCacheRV = NULL; pDb->setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: This routine releases a cache block by decrementing its use count. If the use count goes to zero, the block will be moved to the MRU position in the global cache list. ****************************************************************************/ void ScaReleaseCache( F_CachedBlock * pSCache, FLMBOOL bMutexAlreadyLocked) { if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); } // Can turn off write inhibit when the use count is about to go to zero, // because we are guaranteed at that point that nobody is going to still // update it. if (pSCache->getUseCount() == 1) { pSCache->clearFlags( CA_WRITE_INHIBIT); } pSCache->releaseForThread(); if (!bMutexAlreadyLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } } /**************************************************************************** Desc: This routine increments the use count on a cache block ****************************************************************************/ void ScaUseCache( F_CachedBlock * pSCache, FLMBOOL bMutexAlreadyLocked) { if (!bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); } pSCache->useForThread( 0); if (!bMutexAlreadyLocked) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } } /**************************************************************************** Desc: Test and set the dirty flag on a block if not already set. This is called on the case where a block didn't need to be logged because it had already been logged, but it still needs to have its dirty bit set. ****************************************************************************/ void F_Database::setBlkDirty( F_CachedBlock * pSCache) { #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags; #endif // If the dirty flag is already set, we will NOT attempt to set it. f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); #ifdef FLM_DBG_LOG ui16OldFlags = pSCache->m_ui16Flags; #endif if (!(pSCache->m_ui16Flags & CA_DIRTY)) { flmAssert( this == pSCache->m_pDatabase); pSCache->setDirtyFlag( this); } // Move the block into the dirty blocks. Even if block was // already dirty, put at the end of the list of dirty blocks. // This will make it so that when reduceCache hits a dirty // block, it is likely to also be one that will be written // out by a call to ScaFlushDirtyBlocks. pSCache->unlinkFromDatabase(); pSCache->linkToDatabase( this); // Move the block to the MRU slot in the global list if (pSCache->m_pPrevInGlobal) { pSCache->unlinkFromGlobalList(); pSCache->linkToGlobalListAsMRU(); } // Set write inhibit bit so we will not unset the dirty bit // until the use count goes to zero. pSCache->setFlags( CA_WRITE_INHIBIT); #ifdef FLM_DBG_LOG pSCache->logFlgChange( ui16OldFlags, 'O'); #endif f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: This routine logs a block before it is modified. In the shared cache system, the block is cloned. The old version of the block is marked so that it will be written to the rollback log before the new version of the block can be written to disk. NOTE: It is assumed that the caller has "used" the block that is passed in. This routine will release it once we have made a copy of the block. ****************************************************************************/ RCODE F_Database::logPhysBlk( F_Db * pDb, F_CachedBlock ** ppSCacheRV, // This is a pointer to the pointer of the // cache block that is to be logged. // If the block has not been logged before // during the transaction, a new version // of the block will be created and a // pointer to that block will be returned. // Otherwise, the pointer is unchanged. F_CachedBlock ** ppOldSCache) { RCODE rc = NE_XFLM_OK; F_CachedBlock * pSCache = *ppSCacheRV; F_BLK_HDR * pBlkHdr = pSCache->m_pBlkHdr; F_CachedBlock * pNewSCache = NULL; FLMBOOL bLockedMutex = FALSE; FLMBOOL bLocalCacheAllocation = FALSE; FLMUINT uiBlockSize = getBlockSize(); F_CachedBlock ** ppSCacheBucket; #ifdef FLM_DBG_LOG FLMUINT16 ui16OldFlags; #endif flmAssert( this == pDb->m_pDatabase); flmAssert( pSCache->m_pPrevInVersionList == NULL); if( ppOldSCache) { *ppOldSCache = NULL; } // Increment the block change count -- this is not an accurate // indication of the number of blocks that have actually changed. The // count is used by the cursor code to determine when to re-position in // the B-Tree. The value is only used by cursors operating within // an update transaction. pDb->m_uiBlkChangeCnt++; // See if the block has already been logged since the last transaction. // If so, there is no need to log it again. if( pBlkHdr->ui64TransID == pDb->m_ui64CurrTransID) { flmAssert( pDb->m_bHadUpdOper); setBlkDirty( pSCache); goto Exit; } pDb->m_bHadUpdOper = TRUE; // See if we need to free any cache or write dirty cache f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bLockedMutex = TRUE; if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( pDb))) { goto Exit; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); bLockedMutex = FALSE; // See if the transaction ID is greater than the last backup // transaction ID. If so, we need to update our block change // count. if (pBlkHdr->ui64TransID < m_uncommittedDbHdr.ui64LastBackupTransID) { m_uncommittedDbHdr.ui32BlksChangedSinceBackup++; } // pDb->m_uiTransEOF contains what the EOF address was at // the beginning of the transaction. There is no need to log the // block if it's address is beyond that point because it is a // NEW block. if (!FSAddrIsBelow( (FLMUINT)pSCache->m_pBlkHdr->ui32BlkAddr, pDb->m_uiTransEOF)) { pBlkHdr->ui64TransID = pDb->m_ui64CurrTransID; setBlkDirty( pSCache); goto Exit; } // Allocate a cache block for this new block. If we have older // versions of this block already in cache, we need to link the // new block above the older version. Try to allocate the new // block outside of the block cache mutex. If not possible, lock // the mutex and allocate. if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) { if( (pNewSCache = new( uiBlockSize) F_CachedBlock( uiBlockSize)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } bLocalCacheAllocation = TRUE; // Copy the old block's data into this one. pBlkHdr = pNewSCache->m_pBlkHdr; f_memcpy( pBlkHdr, pSCache->m_pBlkHdr, m_uiBlockSize); } f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bLockedMutex = TRUE; // Allocate a cache block - either a new one or by replacing // an existing one. if( bLocalCacheAllocation) { F_BlockCacheMgr * pBlockCacheMgr = gv_XFlmSysData.pBlockCacheMgr; // Now that the mutex is locked, update stats and do other work // that couldn't be done when the block was allocated. pBlockCacheMgr->m_Usage.uiCount++; pBlockCacheMgr->m_Usage.uiByteCount += pNewSCache->memSize(); // Set use count to one so the block cannot be replaced. pNewSCache->m_bCanRelocate = TRUE; pNewSCache->useForThread( 0); } else { if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->allocBlock( pDb, &pNewSCache))) { goto Exit; } // Copy the old block's data into this one. pBlkHdr = pNewSCache->m_pBlkHdr; f_memcpy( pBlkHdr, pSCache->m_pBlkHdr, m_uiBlockSize); } #ifdef FLM_DEBUG // Make sure the caller isn't logging one that has already been // logged. if (gv_XFlmSysData.pBlockCacheMgr->m_bDebug) { F_CachedBlock * pTmpSCache = m_pTransLogList; while (pTmpSCache) { flmAssert( pTmpSCache != pSCache); pTmpSCache = pTmpSCache->m_pNextInHashBucket; } } #endif pNewSCache->m_uiBlkAddress = pSCache->m_uiBlkAddress; #ifdef FLM_DBG_LOG flmDbgLogWrite( this, (FLMUINT)pNewSCache->m_pBlkHdr->ui32BlkAddr, 0, pDb->m_ui64CurrTransID, "NEW-VER"); #endif // Link the block to the global list pNewSCache->m_ui16Flags |= CA_DUMMY_FLAG; pNewSCache->linkToGlobalListAsMRU(); // Set flags so that appropriate flushing to log and DB will be done. pNewSCache->setDirtyFlag( this); pNewSCache->m_ui16Flags &= ~CA_DUMMY_FLAG; // Set write inhibit bit so we will not unset the dirty bit // until the use count goes to zero. pNewSCache->setFlags( CA_WRITE_INHIBIT); // Previous block address should be zero until we actually log the // prior version of the block. pBlkHdr->ui32PriorBlkImgAddr = 0; // Set the low and high trans IDs on the newly created block. pNewSCache->setTransID( ~((FLMUINT64)0)); pBlkHdr->ui64TransID = pDb->m_ui64CurrTransID; #ifdef FLM_DBG_LOG pNewSCache->logFlgChange( 0, 'L'); #endif // Determine the hash bucket the new block should be put into. flmAssert( pSCache->m_uiBlkAddress); ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( m_uiSigBitsInBlkSize, pSCache->m_uiBlkAddress); // Link new block into various lists. pSCache->unlinkFromHashBucket( ppSCacheBucket); pSCache->m_pPrevInVersionList = pNewSCache; pSCache->verifyCache( 2900); pNewSCache->m_pNextInVersionList = pSCache; pNewSCache->linkToDatabase( this); pNewSCache->linkToHashBucket( ppSCacheBucket); pNewSCache->verifyCache( 3000); // Set the high trans ID on the old block to be one less than // the current trans ID. Also set the flag indicating that // the block needs to be written to the rollback log. pSCache->setTransID( (pDb->m_ui64CurrTransID - 1)); #ifdef FLM_DBG_LOG ui16OldFlags = pSCache->m_ui16Flags; #endif if (!(pSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))) { m_uiLogCacheCount++; } pSCache->setFlags( CA_WRITE_TO_LOG); if (pSCache->getLowTransID() <= m_uncommittedDbHdr.ui64RflLastCPTransID) { pSCache->setFlags( CA_LOG_FOR_CP); } if (pSCache->m_ui16Flags & CA_DIRTY) { pSCache->setFlags( CA_WAS_DIRTY); flmAssert( this == pSCache->m_pDatabase); pSCache->unsetDirtyFlag(); // No more need to write inhibit - because the old version of the // block cannot possibly be changed. pSCache->clearFlags( CA_WRITE_INHIBIT); // Move the block out of the dirty blocks. pSCache->unlinkFromDatabase(); pSCache->linkToDatabase( this); } #ifdef FLM_DBG_LOG pSCache->logFlgChange( ui16OldFlags, 'N'); #endif // Put the old block into the list of the transaction's // log blocks pSCache->m_pPrevInHashBucket = NULL; if ((pSCache->m_pNextInHashBucket = m_pTransLogList) != NULL) { pSCache->m_pNextInHashBucket->m_pPrevInHashBucket = pSCache; } m_pTransLogList = pSCache; // Link the new block to the file log list pNewSCache->linkToLogList(); // If this is an indexing thread, the old version of the // block will probably not be needed again so put it at the LRU end // of the cache. The assumption is that a background indexing thread // has also set the FDB_DONT_POISON_CACHE flag. if (pDb->m_uiFlags & FDB_BACKGROUND_INDEXING) { pSCache->unlinkFromGlobalList(); pSCache->linkToGlobalListAsLRU(); } // Release the old block and return a pointer to the new block. if( !ppOldSCache) { ScaReleaseCache( pSCache, bLockedMutex); } else { *ppOldSCache = pSCache; } *ppSCacheRV = pNewSCache; Exit: #ifdef SCACHE_LINK_CHECKING if (RC_BAD( rc)) { if (!bLockedMutex) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); bLockedMutex = TRUE; } scaVerify( 500); } #endif if (bLockedMutex) { f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: Constructor for block cache manager. ****************************************************************************/ F_BlockCacheMgr::F_BlockCacheMgr() { m_pBlockAllocator = NULL; m_pMRUReplace = NULL; m_pLRUReplace = NULL; m_pFirstFree = NULL; m_pLastFree = NULL; m_ppHashBuckets = NULL; m_uiNumBuckets = 0; m_uiHashFailTime = 0; f_memset( &m_Usage, 0, sizeof( m_Usage)); m_uiFreeBytes = 0; m_uiFreeCount = 0; m_uiReplaceableCount = 0; m_uiReplaceableBytes = 0; m_bAutoCalcMaxDirty = FALSE; m_uiMaxDirtyCache = 0; m_uiLowDirtyCache = 0; m_uiTotalUses = 0; m_uiBlocksUsed = 0; m_uiPendingReads = 0; m_uiIoWaits = 0; m_uiHashMask = 0; m_bReduceInProgress = FALSE; #ifdef FLM_DEBUG m_bDebug = FALSE; #endif } /**************************************************************************** Desc: This routine initializes the hash table for block cache. ****************************************************************************/ RCODE F_BlockCacheMgr::initHashTbl( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiAllocSize; // Calculate the number of bits needed to represent values in the // hash table. m_uiNumBuckets = MIN_HASH_BUCKETS; m_uiHashMask = (m_uiNumBuckets - 1); uiAllocSize = (FLMUINT)sizeof( F_CachedBlock *) * m_uiNumBuckets; if (RC_BAD( rc = f_calloc( uiAllocSize, &m_ppHashBuckets))) { goto Exit; } gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); Exit: return( rc); } /**************************************************************************** Desc: This routine initializes the block cache manager. ****************************************************************************/ RCODE F_BlockCacheMgr::initCache( void) { RCODE rc = NE_XFLM_OK; #define MAX_NUM_BLOCK_SIZES 16 FLMUINT uiBlockSizes[ MAX_NUM_BLOCK_SIZES]; FLMUINT uiBlockSize; FLMUINT uiLoop; // Allocate memory for the hash table. if (RC_BAD( rc = initHashTbl())) { goto Exit; } // Initialize the cache block allocator for( uiLoop = 0, uiBlockSize = XFLM_MIN_BLOCK_SIZE; uiBlockSize <= XFLM_MAX_BLOCK_SIZE; uiLoop++, uiBlockSize *= 2) { if( uiLoop >= MAX_NUM_BLOCK_SIZES) { rc = RC_SET_AND_ASSERT( NE_XFLM_MEM); goto Exit; } uiBlockSizes[ uiLoop] = uiBlockSize + sizeof( F_CachedBlock); } uiBlockSizes[ uiLoop] = 0; if( RC_BAD( rc = FlmAllocMultiAllocator( &m_pBlockAllocator))) { goto Exit; } if (RC_BAD( rc = m_pBlockAllocator->setup( TRUE, gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, NULL, uiBlockSizes, &m_Usage.slabUsage, NULL))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine resizes the hash table for the block cache manager. NOTE: This routine assumes that the cache block mutex has been locked. ****************************************************************************/ RCODE F_BlockCacheMgr::rehash( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiNewHashTblSize; F_CachedBlock ** ppOldHashTbl; FLMUINT uiOldHashTblSize; F_CachedBlock ** ppBucket; F_CachedBlock ** ppSCacheBucket; FLMUINT uiLoop; F_CachedBlock * pTmpSCache; F_CachedBlock * pTmpNextSCache; FLMUINT uiOldMemSize; uiNewHashTblSize = caGetBestHashTblSize( m_Usage.uiCount); // At this point we better have a different hash table size // or something is mucked up! flmAssert( uiNewHashTblSize != m_uiNumBuckets); // Save the old hash table and its size. if ((ppOldHashTbl = m_ppHashBuckets) != NULL) { uiOldMemSize = f_msize( ppOldHashTbl); } else { uiOldMemSize = 0; } uiOldHashTblSize = m_uiNumBuckets; // Allocate a new hash table. if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_CachedBlock *) * (FLMUINT)uiNewHashTblSize, &m_ppHashBuckets))) { m_uiHashFailTime = FLM_GET_TIMER(); m_ppHashBuckets = ppOldHashTbl; goto Exit; } // Subtract off old size and add in new size. gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiOldMemSize); gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); m_uiNumBuckets = uiNewHashTblSize; m_uiHashMask = uiNewHashTblSize - 1; // Relink all of the cache blocks into the new // hash table. for (uiLoop = 0, ppBucket = ppOldHashTbl; uiLoop < uiOldHashTblSize; uiLoop++, ppBucket++) { pTmpSCache = *ppBucket; while (pTmpSCache) { pTmpNextSCache = pTmpSCache->m_pNextInHashBucket; // Should not be anything in a hash bucket that is not // associated with a database. flmAssert( pTmpSCache->m_pDatabase); flmAssert( pTmpSCache->m_uiBlkAddress); ppSCacheBucket = blockHash( pTmpSCache->m_pDatabase->getSigBitsInBlkSize(), pTmpSCache->m_uiBlkAddress); pTmpSCache->linkToHashBucket( ppSCacheBucket); pTmpSCache = pTmpNextSCache; } } // Throw away the old hash table. f_free( &ppOldHashTbl); Exit: return( rc); } /**************************************************************************** Desc: This routine performs various checks on an individual cache block to verify that it is linked into the proper lists, etc. This routine assumes that the cache block mutex has been locked. ****************************************************************************/ #ifdef SCACHE_LINK_CHECKING void F_CachedBlock::verifyCache( int iPlace) { F_CachedBLock * pTmpSCache; FLMUINT uiTmp = getBlkSize(); FLMUINT uiSigBitsInBlkSize; F_CachedBlock ** ppBucket; uiSigBitsInBlkSize = calcSigBits( uiTmp); ppBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( uiSigBitsInBlkSize, m_uiBlkAddress); pTmpSCache = *ppBucket; while (pTmpSCache && pTmpSCache != this) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if( pTmpSCache) { if (!m_pDatabase) { f_breakpoint( iPlace+3); } if (m_pPrevInVersionList) { f_breakpoint( iPlace+4); } // Verify that it is not in the log list. if (m_ui16Flags & CA_WRITE_TO_LOG) { f_breakpoint( iPlace+5); } pTmpSCache = m_pDatabase->getTransLogList(); while (pTmpSCache && pTmpSCache != this) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (pTmpSCache) { f_breakpoint( iPlace+6); } } else { if (m_pDatabase && !m_pPrevInVersionList) { f_breakpoint( iPlace+7); } // If the block is marked as needing to be logged, verify that // it is in the log list. if (m_ui16Flags & CA_WRITE_TO_LOG) { pTmpSCache = m_pDatabase->getTransLogList(); while (pTmpSCache && pTmpSCache != this) { pTmpSCache = pTmpSCache->m_pNextInHashBucket; } if (!pTmpSCache) { // Not in the log list f_breakpoint( iPlace+8); } // Better also have a newer version. if (!m_pPrevInVersionList) { // Not linked to a prior version. f_breakpoint( iPlace+9); } } } // Verify that the prev and next pointers do not point to itself. if (m_pPrevInVersionList == this) { f_breakpoint( iPlace+10); } if (m_pNextInVersionList == this) { f_breakpoint( iPlace+11); } } #endif /**************************************************************************** Desc: This routine performs various checks on the cache to verify that things are linked into the proper lists, etc. This routine assumes that the cache block mutex has been locked. ****************************************************************************/ #ifdef SCACHE_LINK_CHECKING FSTATIC void scaVerify( int iPlace) { FLMUINT uiLoop; F_CachedBlock ** ppBucket; F_CachedBlock * pTmpSCache; // Verify that everything in buckets has a pFile and does NOT // have a m_pPrevInVersionList for (uiLoop = 0, ppBucket = gv_XFlmSysData.pBlockCacheMgr->m_ppHashBuckets; uiLoop < gv_XFlmSysData.pBlockCacheMgr->m_uiNumBuckets; uiLoop++, ppBucket++) { pTmpSCache = *ppBucket; while (pTmpSCache) { if (!pTmpSCache->m_pDatabase) { f_breakpoint(iPlace+1); } if (pTmpSCache->m_pPrevInVersionList) { f_breakpoint(iPlace+2); } pTmpSCache = pTmpSCache->m_pNextInHashBucket; } } // Traverse the entire list - make sure that everything // with a file is hashed and linked properly // and everything without a file is NOT hashed. pTmpSCache = (F_CachedBlock *)gv_XFlmSysData.pBlockCacheMgr->m_MRUList.m_pMRUItem; while (pTmpSCache) { pTmpSCache->verifyCache( 1000 + iPlace); pTmpSCache = pTmpSCache->m_pNextInGlobal; } } #endif /**************************************************************************** Desc: This routine determines what hash table size best fits the current item count. It finds the hash bucket size whose midpoint between the minimum and maximum range is closest to the node count. ****************************************************************************/ FLMUINT caGetBestHashTblSize( FLMUINT uiCurrItemCount ) { FLMUINT uiNumHashBuckets; FLMUINT uiMaxItemsForNumHashBuckets; FLMUINT uiMinItemsForNumHashBuckets; FLMUINT uiClosestNumHashBuckets = 0; FLMUINT uiDistanceFromMidpoint; FLMUINT uiLowestDistanceFromMidpoint; FLMUINT uiHashTblItemsMidpoint; uiLowestDistanceFromMidpoint = 0xFFFFFFFF; for (uiNumHashBuckets = MIN_HASH_BUCKETS; uiNumHashBuckets <= MAX_HASH_BUCKETS; uiNumHashBuckets *= 2) { // Maximum desirable record count for a specific hash table size // we have arbitrarily chosen to be four times the number of buckets. // Minimum desirable record count we have arbitrarily chosen to be // the hash table size divided by fourn. uiMaxItemsForNumHashBuckets = maxItemCount( uiNumHashBuckets); uiMinItemsForNumHashBuckets = minItemCount( uiNumHashBuckets); // Ignore any hash bucket sizes where the current record count // is not between the desired minimum and maximum. if (uiCurrItemCount >= uiMinItemsForNumHashBuckets && uiCurrItemCount <= uiMaxItemsForNumHashBuckets) { // Calculate the midpoint between the minimum and maximum // for this particular hash table size. uiHashTblItemsMidpoint = (uiMaxItemsForNumHashBuckets - uiMinItemsForNumHashBuckets) / 2; // See how far our current record count is from this midpoint. uiDistanceFromMidpoint = (FLMUINT)((uiHashTblItemsMidpoint > uiCurrItemCount) ? (uiHashTblItemsMidpoint - uiCurrItemCount) : (uiCurrItemCount - uiHashTblItemsMidpoint)); // If the distance from the midpoint is closer than our previous // lowest distance, save it. if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) { uiClosestNumHashBuckets = uiNumHashBuckets; uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; } } } // Take the number of buckets whose middle was closest to the // current record count; if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) { // If we did not fall between any of the minimums or maximums, // we are either below the lowest minimum, or higher than the // highest maximum. uiNumHashBuckets = (FLMUINT)((uiCurrItemCount < minItemCount( MIN_HASH_BUCKETS)) ? (FLMUINT)MIN_HASH_BUCKETS : (FLMUINT)MAX_HASH_BUCKETS); } else { uiNumHashBuckets = uiClosestNumHashBuckets; } return( uiNumHashBuckets); } /**************************************************************************** Desc: This routine shuts down the shared cache manager and frees all resources allocated by it. ****************************************************************************/ F_BlockCacheMgr::~F_BlockCacheMgr() { // Free the hash table if (m_ppHashBuckets) { gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( f_msize( m_ppHashBuckets)); f_free( &m_ppHashBuckets); } if( m_pBlockAllocator) { m_pBlockAllocator->Release(); } flmAssert( !m_MRUList.m_pMRUItem && !m_MRUList.m_pLRUItem); } /**************************************************************************** Desc: This routine frees all of the cache associated with an F_Database object. ****************************************************************************/ void F_Database::freeBlockCache( void) { F_CachedBlock * pSCache; F_CachedBlock * pNextSCache; f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); // First, unlink as many as can be unlinked. pSCache = m_pSCacheList; flmAssert( !m_pPendingWriteList); while (pSCache) { f_yieldCPU(); pNextSCache = pSCache->m_pNextInDatabase; if (!pSCache->m_uiUseCount) { // Turn off all bits that would cause an assert - we don't // care at this point, because we are forcing the file to // be closed. if (pSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) { flmAssert( m_uiLogCacheCount); m_uiLogCacheCount--; } if (pSCache->m_pNextInVersionList && (pSCache->m_pNextInVersionList->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))) { flmAssert( m_uiLogCacheCount); m_uiLogCacheCount--; } #ifdef FLM_DEBUG pSCache->clearFlags( CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY); if (pSCache->m_pNextInVersionList) { pSCache->m_pNextInVersionList->clearFlags( CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY); } #endif if (pSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) { pSCache->unlinkFromLogList(); } else if( pSCache->m_ui16Flags & CA_IN_NEW_LIST) { pSCache->unlinkFromNewList(); } pSCache->unlinkCache( TRUE, NE_XFLM_OK); } else { // Another thread must have a temporary use on this block // because it is traversing cache for some reason. We // don't want to free this block until the use count // is zero, so just put it into the free list so that // when its use count goes to zero we will either // re-use or free it. pSCache->unlinkCache( FALSE, NE_XFLM_OK); pSCache->linkToFreeList( FLM_GET_TIMER()); } pSCache = pNextSCache; } // Set the F_Database cache list pointer to NULL. Even if we didn't free // all of the cache blocks right now, we at least unlinked them from // the F_Database object. m_pSCacheList = NULL; f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } /**************************************************************************** Desc: This routine computes an in-memory checksum on a cache block. This is to guard against corrupt cache blocks being written back to disk. NOTE: This routine assumes that the cache block mutex is already locked. ****************************************************************************/ #ifdef FLM_DEBUG FLMUINT F_CachedBlock::computeChecksum( void) { FLMUINT uiChecksum = 0; if( gv_XFlmSysData.pBlockCacheMgr->m_bDebug) { FLMUINT uiBlkSize = getBlkSize(); FLMBYTE * pucBlk = (FLMBYTE *)m_pBlkHdr; FLMUINT uiLoop; for( uiLoop = 0; uiLoop < uiBlkSize; uiLoop += 4, pucBlk += 4) { uiChecksum = (uiChecksum ^ (FLMUINT)(*((FLMUINT32 *)(pucBlk)))); } if (!uiChecksum) { uiChecksum = 1; } } return( uiChecksum); } #endif /**************************************************************************** Desc: This routine finishes a checkpoint. At this point we are guaranteed to have both the file lock and the write lock, and all dirty blocks have been written to the database. This is the code that writes out the log header and truncates the rollback log, roll-forward log, and database files as needed. ****************************************************************************/ RCODE F_Database::finishCheckpoint( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMBOOL bDoTruncate, FLMUINT uiCPFileNum, FLMUINT uiCPOffset, FLMUINT uiCPStartTime, FLMUINT uiTotalToWrite) { RCODE rc = NE_XFLM_OK; XFLM_DB_HDR * pCommittedDbHdr = &m_lastCommittedDbHdr; XFLM_DB_HDR saveDbHdr; FLMUINT uiNewCPFileNum; FLMUINT64 ui64CurrTransID; FLMUINT uiSaveTransOffset; FLMUINT uiSaveCPFileNum; FLMBOOL bTruncateLog = FALSE; FLMBOOL bTruncateRflFile = FALSE; FLMUINT uiTruncateRflSize = 0; FLMUINT uiLogEof; FLMUINT uiHighLogFileNumber; #ifdef FLM_DBG_LOG FLMBOOL bResetRBL = FALSE; #endif // Update the DB header to indicate that we now // have a new checkpoint. f_memcpy( &saveDbHdr, pCommittedDbHdr, sizeof( XFLM_DB_HDR)); // Save some of the values we are going to change. These values // will be needed below. ui64CurrTransID = pCommittedDbHdr->ui64CurrTransID; uiSaveTransOffset = (FLMUINT)pCommittedDbHdr->ui32RflLastTransOffset; uiSaveCPFileNum = (FLMUINT)pCommittedDbHdr->ui32RflLastCPFileNum; #ifdef FLM_DEBUG f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); // If we get to this point, there should be no dirty blocks for // the file. flmAssert( !m_uiDirtyCacheCount && !m_pPendingWriteList && (!m_pSCacheList || !(m_pSCacheList->m_ui16Flags & CA_DIRTY))); f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); #endif // Determine if we can reset the physical log. The log can be reset if // there are no blocks in the log that are needed to preserve a read // consistent view for a read transaction. By definition, this will // be the case if there are no read transactions that started before // the last transaction committed. Thus, that is what we check. // If we exceed a VERY large size, we need to wait for the read // transactions to empty out so we can force a truncation of the // log. This is also true if we are truncating the database and // changing the logical EOF. uiLogEof = (FLMUINT)pCommittedDbHdr->ui32RblEOF; uiHighLogFileNumber = FSGetFileNumber( uiLogEof); // Lock the database mutex while modifying the last committed header. lockMutex(); if (uiHighLogFileNumber > 0 || bDoTruncate || FSGetFileOffset( uiLogEof) > LOW_VERY_LARGE_LOG_THRESHOLD_SIZE) { FLMINT iWaitCnt = 0; F_Db * pFirstDb; FLMUINT ui5MinutesTime; FLMUINT ui30SecTime; char szMsgBuf[ 128]; IF_LogMessageClient * pLogMsg = NULL; FLMUINT uiFirstDbInactiveSecs; FLMUINT uiElapTime; FLMUINT uiLastMsgTime = FLM_GET_TIMER(); FLMBOOL bMustTruncate = (bDoTruncate || uiHighLogFileNumber || FSGetFileOffset( uiLogEof) >= HIGH_VERY_LARGE_LOG_THRESHOLD_SIZE) ? TRUE : FALSE; ui5MinutesTime = FLM_SECS_TO_TIMER_UNITS( 300); ui30SecTime = FLM_SECS_TO_TIMER_UNITS( 30); if (m_pCPInfo && bMustTruncate) { m_pCPInfo->uiStartWaitTruncateTime = FLM_GET_TIMER(); } pFirstDb = m_pFirstReadTrans; while ((!m_pCPInfo || !m_pCPInfo->bShuttingDown) && pFirstDb && pFirstDb->m_ui64CurrTransID < ui64CurrTransID) { FLMUINT uiTime; FLMUINT uiFirstDbInactiveTime = 0; FLMUINT64 ui64FirstDbCurrTransID = pFirstDb->m_ui64CurrTransID; FLMUINT uiFirstDbThreadId = pFirstDb->m_uiThreadId; F_Db * pTmpDb; uiTime = (FLMUINT)FLM_GET_TIMER(); if( !bMustTruncate) { pTmpDb = pFirstDb; while( pTmpDb && pTmpDb->m_ui64CurrTransID < ui64CurrTransID) { if (!pTmpDb->m_uiInactiveTime) { pTmpDb->m_uiInactiveTime = uiTime; } pTmpDb = pTmpDb->m_pNextReadTrans; } uiFirstDbInactiveTime = pFirstDb->m_uiInactiveTime; } // If the read transaction has been inactive for 5 minutes, // forcibly kill it unless it has been marked as a "don't kill" // transaction. if( !(pFirstDb->m_uiFlags & FDB_DONT_KILL_TRANS) && (bMustTruncate || (uiFirstDbInactiveTime && FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime) >= ui5MinutesTime))) { pFirstDb->m_uiKilledTime = uiTime; if ((m_pFirstReadTrans = pFirstDb->m_pNextReadTrans) != NULL) { m_pFirstReadTrans->m_pPrevReadTrans = NULL; } else { m_pLastReadTrans = NULL; } pFirstDb->m_pPrevReadTrans = NULL; if ((pFirstDb->m_pNextReadTrans = m_pFirstKilledTrans) != NULL) { pFirstDb->m_pNextReadTrans->m_pPrevReadTrans = pFirstDb; } m_pFirstKilledTrans = pFirstDb; unlockMutex(); // Log a message indicating that we have killed the transaction if ((pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) { uiElapTime = FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime); uiFirstDbInactiveSecs = FLM_TIMER_UNITS_TO_SECS( uiElapTime); f_sprintf( szMsgBuf, "Killed transaction %I64u." " Thread: %X." " Inactive time: %u seconds.", ui64FirstDbCurrTransID, (unsigned)uiFirstDbThreadId, (unsigned)uiFirstDbInactiveSecs); pLogMsg->changeColor( FLM_YELLOW, FLM_BLACK); pLogMsg->appendString( szMsgBuf); flmEndLogMessage( &pLogMsg); } lockMutex(); pFirstDb = m_pFirstReadTrans; continue; } else if (!bMustTruncate) { if (iWaitCnt >= 200) { break; } } unlockMutex(); if (!bMustTruncate) { iWaitCnt++; f_sleep( 6); } else { // Log a message indicating that we are waiting for the // transaction to complete if (FLM_ELAPSED_TIME( uiTime, uiLastMsgTime) >= ui30SecTime) { if ((pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) { uiElapTime = FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime); uiFirstDbInactiveSecs = FLM_TIMER_UNITS_TO_SECS( uiElapTime); f_sprintf( szMsgBuf, "Waiting for transaction %I64u to complete." " Thread: %X." " Inactive time: %u seconds.", ui64FirstDbCurrTransID, (unsigned)uiFirstDbThreadId, (unsigned)uiFirstDbInactiveSecs); pLogMsg->changeColor( FLM_YELLOW, FLM_BLACK); pLogMsg->appendString( szMsgBuf); flmEndLogMessage( &pLogMsg); } uiLastMsgTime = FLM_GET_TIMER(); } f_sleep( 100); } lockMutex(); pFirstDb = m_pFirstReadTrans; } if (bMustTruncate && m_pCPInfo) { m_pCPInfo->uiStartWaitTruncateTime = 0; } } if (!m_pFirstReadTrans || m_pFirstReadTrans->m_ui64CurrTransID >= ui64CurrTransID) { // We may want to truncate the log file if it has grown real big. if (uiHighLogFileNumber > 0 || FSGetFileOffset( uiLogEof) > LOG_THRESHOLD_SIZE) { bTruncateLog = TRUE; } pCommittedDbHdr->ui32RblEOF = (FLMUINT32)pCommittedDbHdr->ui16BlockSize; #ifdef FLM_DBG_LOG bResetRBL = TRUE; #endif } pCommittedDbHdr->ui32RblFirstCPBlkAddr = 0; // Set the checkpoint RFL file number and offset to be the same as // the last transaction's RFL file number and offset if nothing // is passed in. If a non-zero uiCPFileNum is passed in, it is because // we are checkpointing the last transaction that has been recovered // by the recovery process. // In this case, instead of moving the pointers all the way forward, // to the last committed transaction, we simply move them forward to // the last recovered transaction. if (uiCPFileNum) { pCommittedDbHdr->ui32RflLastCPFileNum = (FLMUINT32)uiCPFileNum; pCommittedDbHdr->ui32RflLastCPOffset = (FLMUINT32)uiCPOffset; } else { FLMBOOL bResetRflFile = FALSE; // If the RFL volume is full, and the LOG_AUTO_TURN_OFF_KEEP_RFL // flag is TRUE, change the LOG_KEEP_RFL_FILES to FALSE. if (m_pRfl->isRflVolumeFull() && pCommittedDbHdr->ui8RflKeepFiles && pCommittedDbHdr->ui8RflAutoTurnOffKeep) { pCommittedDbHdr->ui8RflKeepFiles = 0; bResetRflFile = TRUE; } pCommittedDbHdr->ui32RflLastCPFileNum = pCommittedDbHdr->ui32RflCurrFileNum; if (!pCommittedDbHdr->ui8RflKeepFiles) { pCommittedDbHdr->ui32RflLastCPOffset = 512; if (bResetRflFile) { // This will cause the RFL file to be recreated on the // next transaction - causing the keep signature to be // changed. Also need to set up to use new serial // numbers so restore can't wade into this RFL file and // attempt to start restoring from it. pCommittedDbHdr->ui32RflLastTransOffset = 0; f_createSerialNumber( pCommittedDbHdr->ucLastTransRflSerialNum); f_createSerialNumber( pCommittedDbHdr->ucNextRflSerialNum); } // If LOG_RFL_LAST_TRANS_OFFSET is zero, someone has set this up // intentionally to cause the RFL file to be created at the // beginning of the next transaction. We don't want to lose // that, so if it is zero, we don't change it. else if (pCommittedDbHdr->ui32RflLastTransOffset != 0) { pCommittedDbHdr->ui32RflLastTransOffset = 512; } uiTruncateRflSize = (FLMUINT)pCommittedDbHdr->ui32RflMinFileSize; if ((uiSaveTransOffset >= RFL_TRUNCATE_SIZE) || (uiSaveTransOffset >= uiTruncateRflSize)) { bTruncateRflFile = TRUE; if (uiTruncateRflSize > RFL_TRUNCATE_SIZE) { uiTruncateRflSize = RFL_TRUNCATE_SIZE; } else if (uiTruncateRflSize < 512) { uiTruncateRflSize = 512; } // Set to nearest 512 byte boundary uiTruncateRflSize &= 0xFFFFFE00; } } else { FLMUINT uiLastTransOffset = (FLMUINT)pCommittedDbHdr->ui32RflLastTransOffset; // If the RFL volume is not OK, and we are not currently positioned // at the beginning of an RFL file, we should set things up to roll to // the next RFL file. That way, if they need to change RFL volumes // it will be OK, and we can create the new RFL file. if (!m_pRfl->seeIfRflVolumeOk() && uiLastTransOffset > 512) { pCommittedDbHdr->ui32RflLastTransOffset = 0; pCommittedDbHdr->ui32RflCurrFileNum++; pCommittedDbHdr->ui32RflLastCPOffset = 512; pCommittedDbHdr->ui32RflLastCPFileNum = pCommittedDbHdr->ui32RflCurrFileNum; } else { // If the transaction offset is zero, we want the last CP offset // to be 512 - it should never be set to zero. It is possible // for the transaction offset to still be zero at this point if // we haven't done a non-empty transaction yet. if (!uiLastTransOffset) { uiLastTransOffset = 512; } pCommittedDbHdr->ui32RflLastCPOffset = (FLMUINT32)uiLastTransOffset; } } } // Set the checkpoint Trans ID to be the trans ID of the // last committed transaction. pCommittedDbHdr->ui64RflLastCPTransID = pCommittedDbHdr->ui64CurrTransID; unlockMutex(); // Write the log header - this will complete the checkpoint. if (RC_BAD( rc = writeDbHdr( pDbStats, pSFileHdl, pCommittedDbHdr, &m_checkpointDbHdr, TRUE))) { // Restore log header. lockMutex(); f_memcpy( pCommittedDbHdr, &saveDbHdr, sizeof( XFLM_DB_HDR)); unlockMutex(); goto Exit; } else if (bTruncateLog) { if (uiHighLogFileNumber) { (void)pSFileHdl->truncateFiles( FIRST_LOG_BLOCK_FILE_NUMBER, uiHighLogFileNumber); } pSFileHdl->truncateFile( 0, LOG_THRESHOLD_SIZE); } #ifdef FLM_DBG_LOG if (bResetRBL) { char szMsg [80]; if (bTruncateLog) { f_sprintf( szMsg, "f%u, Reset&TruncRBL, CPTID:%I64u", (unsigned)((FLMUINT)this), pCommittedDbHdr->ui64RflLastCPTransID); } else { f_sprintf( szMsg, "f%u, ResetRBL, CPTID:%I64u", (unsigned)((FLMUINT)this), pCommittedDbHdr->ui64RflLastCPTransID); } flmDbgLogMsg( szMsg); } #endif // The checkpoint is now complete. Reset the first checkpoint // block address to zero. m_uiFirstLogCPBlkAddress = 0; m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); // Save the state of the log header into the checkpointDbHdr buffer. f_memcpy( &m_checkpointDbHdr, pCommittedDbHdr, sizeof( XFLM_DB_HDR)); // See if we need to delete RFL files that are no longer in use. uiNewCPFileNum = (FLMUINT)pCommittedDbHdr->ui32RflLastCPFileNum; if (!pCommittedDbHdr->ui8RflKeepFiles && uiSaveCPFileNum != uiNewCPFileNum && uiNewCPFileNum > 1) { FLMUINT uiLastRflFileDeleted = (FLMUINT)pCommittedDbHdr->ui32RflLastFileNumDeleted; uiLastRflFileDeleted++; while (uiLastRflFileDeleted < uiNewCPFileNum) { char szLogFilePath [F_PATH_MAX_SIZE]; RCODE TempRc; FLMUINT uiFileNameSize = sizeof( szLogFilePath); FLMBOOL bNameTruncated; m_pRfl->getFullRflFileName( uiLastRflFileDeleted, szLogFilePath, &uiFileNameSize, &bNameTruncated); if (bNameTruncated) { break; } if (RC_BAD( TempRc = gv_XFlmSysData.pFileSystem->deleteFile( szLogFilePath))) { if (TempRc != NE_FLM_IO_PATH_NOT_FOUND && TempRc != NE_FLM_IO_INVALID_FILENAME) { break; } } uiLastRflFileDeleted++; } uiLastRflFileDeleted--; // If we actually deleted a file, update the log header. if (uiLastRflFileDeleted != (FLMUINT)pCommittedDbHdr->ui32RflLastFileNumDeleted) { pCommittedDbHdr->ui32RflLastFileNumDeleted = (FLMUINT32)uiLastRflFileDeleted; if (RC_BAD( rc = writeDbHdr( pDbStats, pSFileHdl, pCommittedDbHdr, &m_checkpointDbHdr, TRUE))) { goto Exit; } // Save the state of the log header into the checkpointDbHdr buffer // and update the last checkpoint time again. f_memcpy( &m_checkpointDbHdr, pCommittedDbHdr, sizeof( XFLM_DB_HDR)); m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); } } // Truncate the RFL file, if the truncate flag was set above. if (bTruncateRflFile) { (void)m_pRfl->truncate( hWaitSem, uiTruncateRflSize); } // Truncate the files, if requested to do so - this would be a request of // FlmDbReduceSize. if (bDoTruncate) { if (RC_BAD( rc = pSFileHdl->truncateFile( (FLMUINT)pCommittedDbHdr->ui32LogicalEOF))) { goto Exit; } } if( RC_BAD( rc = pSFileHdl->flush())) { goto Exit; } // Re-enable the RFL volume OK flag - in case it was turned off somewhere. m_pRfl->setRflVolumeOk(); // If we complete a checkpoint successfully, we want to set the // pFile->CheckpointRc so that new transactions can come in. // NOTE: CheckpointRc should only be set while we still have the // lock on the database - which should always be the case at this // point. This routine can only be called if we have obtained both // the write lock and the file lock. m_CheckpointRc = NE_XFLM_OK; // If we were calculating our maximum dirty cache, finish the // calculation. if (uiCPStartTime) { FLMUINT uiCPEndTime = FLM_GET_TIMER(); FLMUINT uiCPElapsedTime = FLM_ELAPSED_TIME( uiCPEndTime, uiCPStartTime); FLMUINT uiElapsedMilli; FLMUINT ui15Seconds; FLMUINT uiMaximum; FLMUINT uiLow; // Get elapsed time in milliseconds - only calculate a new maximum if // we did at least a half second worth of writing. uiElapsedMilli = FLM_TIMER_UNITS_TO_MILLI( uiCPElapsedTime); if (uiElapsedMilli >= 500) { // Calculate what could be written in 15 seconds - set maximum // to that. If calculated maximum is zero, we will not change // the current maximum. ui15Seconds = FLM_SECS_TO_TIMER_UNITS( 15); uiMaximum = (FLMUINT)(((FLMUINT64)uiTotalToWrite * (FLMUINT64)ui15Seconds) / (FLMUINT64)uiCPElapsedTime); if (uiMaximum) { // Low is maximum minus what could be written in roughly // two seconds. uiLow = uiMaximum - (uiMaximum / 7); // Only set the maximum if we are still in auto-calculate mode. if (gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty) { f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); // Test flag again after locking the mutex if (gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty) { gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache = uiMaximum; gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = uiLow; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } } } } Exit: return( rc); } /**************************************************************************** Desc: This routine performs a checkpoint. It will stay in here until it either finishes, gets interrupted, or gets an error. If we are not forcing a checkpoint, we periodically check to see if we should switch to a forced mode. We also periodically check to see if another thread needs is waiting to obtain the write lock. ****************************************************************************/ RCODE F_Database::doCheckpoint( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMBOOL bDoTruncate, FLMBOOL bForceCheckpoint, FLMINT iForceReason, FLMUINT uiCPFileNum, FLMUINT uiCPOffset) { RCODE rc = NE_XFLM_OK; FLMBOOL bWroteAll; FLMUINT uiCPStartTime = 0; FLMUINT uiTotalToWrite; FLMUINT uiMaxDirtyCache; F_CachedBlock * pSCache; FLMUINT uiTimestamp; if (m_pCPInfo) { lockMutex(); m_pCPInfo->bDoingCheckpoint = TRUE; m_pCPInfo->uiStartTime = (FLMUINT)FLM_GET_TIMER(); m_pCPInfo->bForcingCheckpoint = bForceCheckpoint; if (bForceCheckpoint) { m_pCPInfo->uiForceCheckpointStartTime = m_pCPInfo->uiStartTime; } m_pCPInfo->iForceCheckpointReason = iForceReason; m_pCPInfo->uiDataBlocksWritten = m_pCPInfo->uiLogBlocksWritten = 0; unlockMutex(); } uiTotalToWrite = (m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize; f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); if (bForceCheckpoint) { if (gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty) { uiCPStartTime = FLM_GET_TIMER(); } } // If the amount of dirty cache is over our maximum, we must at least bring // it down below the low threshhold. Otherwise, we set uiMaxDirtyCache // to the highest possible value - which will not require us to get // it below anything - because it is already within limits. if (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache && uiTotalToWrite > gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache) { uiMaxDirtyCache = gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache; } else { uiMaxDirtyCache = ~((FLMUINT)0); } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); // Write out log blocks first. bWroteAll = TRUE; if (RC_BAD( rc = flushLogBlocks( hWaitSem, pDbStats, pSFileHdl, TRUE, uiMaxDirtyCache, &bForceCheckpoint, &bWroteAll))) { goto Exit; } // If we didn't write out all log blocks, we got interrupted. if (!bWroteAll) { flmAssert( !bForceCheckpoint); goto Exit; } // Now write out dirty blocks if (RC_BAD( rc = flushDirtyBlocks( pDbStats, pSFileHdl, uiMaxDirtyCache, bForceCheckpoint, TRUE, &bWroteAll))) { goto Exit; } // If we didn't write out all dirty blocks, we got interrupted if (!bWroteAll) { flmAssert( !bForceCheckpoint); goto Exit; } // All dirty blocks and log blocks have been written, so we just // need to finish the checkpoint. if (RC_BAD( rc = finishCheckpoint( hWaitSem, pDbStats, pSFileHdl, bDoTruncate, uiCPFileNum, uiCPOffset, uiCPStartTime, uiTotalToWrite))) { goto Exit; } Exit: // If we were attempting to force a checkpoint and it failed, // we want to set m_CheckpointRc, because we want to // prevent new transactions from starting until this situation // is cleared up (see fltrbeg.cpp). Note that setting // m_CheckpointRc to something besides NE_XFLM_OK will cause // the checkpoint thread to force checkpoints whenever it is woke // up until it succeeds (see flopen.cpp). if( RC_BAD( rc) && bForceCheckpoint) { m_CheckpointRc = rc; } // Timestamp all of the items in the free list if( bForceCheckpoint) { uiTimestamp = FLM_GET_TIMER(); f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); pSCache = gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree; while (pSCache) { pSCache->m_uiBlkAddress = uiTimestamp; pSCache = pSCache->m_pNextInDatabase; } f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); } if( m_pCPInfo) { lockMutex(); m_pCPInfo->bDoingCheckpoint = FALSE; unlockMutex(); } return( rc); } /**************************************************************************** Notes: This routine assumes the cache block cache mutex is locked This is a static method, so there is no "this" pointer to the F_BlockCacheMgr object. ****************************************************************************/ FLMBOOL F_BlockRelocator::canRelocate( void * pvAlloc) { F_CachedBlock * pBlock = ((F_CachedBlock *)pvAlloc); if( !pBlock->m_bCanRelocate) { return( FALSE); } return( pBlock->m_uiUseCount ? FALSE : TRUE); } /**************************************************************************** Desc: Fixes up all pointers needed to allow an F_CachedBlock object to be moved to a different location in memory Notes: This routine assumes the cache block mutex is locked This is a static method, so there is no "this" pointer to the F_BlockCacheMgr object. ****************************************************************************/ void F_BlockRelocator::relocate( void * pvOldAlloc, void * pvNewAlloc) { F_CachedBlock * pOldSCache = (F_CachedBlock *)pvOldAlloc; F_CachedBlock * pNewSCache = (F_CachedBlock *)pvNewAlloc; F_CachedBlock ** ppBucket; F_BlockCacheMgr * pBlockCacheMgr = gv_XFlmSysData.pBlockCacheMgr; F_Database * pDatabase = pOldSCache->m_pDatabase; flmAssert( !pOldSCache->m_uiUseCount); if( pNewSCache->m_pPrevInDatabase) { pNewSCache->m_pPrevInDatabase->m_pNextInDatabase = pNewSCache; } if( pNewSCache->m_pNextInDatabase) { pNewSCache->m_pNextInDatabase->m_pPrevInDatabase = pNewSCache; } if( pNewSCache->m_pPrevInGlobal) { pNewSCache->m_pPrevInGlobal->m_pNextInGlobal = pNewSCache; } if( pNewSCache->m_pNextInGlobal) { pNewSCache->m_pNextInGlobal->m_pPrevInGlobal = pNewSCache; } if( pNewSCache->m_pPrevInReplaceList) { pNewSCache->m_pPrevInReplaceList->m_pNextInReplaceList = pNewSCache; } if( pNewSCache->m_pNextInReplaceList) { pNewSCache->m_pNextInReplaceList->m_pPrevInReplaceList = pNewSCache; } if( pNewSCache->m_pPrevInHashBucket) { pNewSCache->m_pPrevInHashBucket->m_pNextInHashBucket = pNewSCache; } if( pNewSCache->m_pNextInHashBucket) { pNewSCache->m_pNextInHashBucket->m_pPrevInHashBucket = pNewSCache; } if( pNewSCache->m_pPrevInVersionList) { pNewSCache->m_pPrevInVersionList->m_pNextInVersionList = pNewSCache; } if( pNewSCache->m_pNextInVersionList) { pNewSCache->m_pNextInVersionList->m_pPrevInVersionList = pNewSCache; } if( pDatabase) { if( pDatabase->m_pSCacheList == pOldSCache) { pDatabase->m_pSCacheList = pNewSCache; } if( pDatabase->m_pLastDirtyBlk == pOldSCache) { pDatabase->m_pLastDirtyBlk = pNewSCache; } if( pDatabase->m_pFirstInLogList == pOldSCache) { pDatabase->m_pFirstInLogList = pNewSCache; } if( pDatabase->m_pLastInLogList == pOldSCache) { pDatabase->m_pLastInLogList = pNewSCache; } if( pDatabase->m_pFirstInNewList == pOldSCache) { pDatabase->m_pFirstInNewList = pNewSCache; } if( pDatabase->m_pLastInNewList == pOldSCache) { pDatabase->m_pLastInNewList = pNewSCache; } if( pDatabase->m_pTransLogList == pOldSCache) { pDatabase->m_pTransLogList = pNewSCache; } ppBucket = pBlockCacheMgr->blockHash( pDatabase->getSigBitsInBlkSize(), pOldSCache->m_uiBlkAddress); if( *ppBucket == pOldSCache) { *ppBucket = pNewSCache; } flmAssert( pDatabase->m_pPendingWriteList != pOldSCache); } if (pBlockCacheMgr->m_MRUList.m_pMRUItem == (F_CachedItem *)pOldSCache) { pBlockCacheMgr->m_MRUList.m_pMRUItem = pNewSCache; } if (pBlockCacheMgr->m_MRUList.m_pLRUItem == (F_CachedItem *)pOldSCache) { pBlockCacheMgr->m_MRUList.m_pLRUItem = pNewSCache; } if (pBlockCacheMgr->m_MRUList.m_pLastMRUItem == (F_CachedItem *)pOldSCache) { pBlockCacheMgr->m_MRUList.m_pLastMRUItem = pNewSCache; } if (pBlockCacheMgr->m_pMRUReplace == pOldSCache) { pBlockCacheMgr->m_pMRUReplace = pNewSCache; } if (pBlockCacheMgr->m_pLRUReplace == pOldSCache) { pBlockCacheMgr->m_pLRUReplace = pNewSCache; } if (pBlockCacheMgr->m_pFirstFree == pOldSCache) { pBlockCacheMgr->m_pFirstFree = pNewSCache; } if (pBlockCacheMgr->m_pLastFree == pOldSCache) { pBlockCacheMgr->m_pLastFree = pNewSCache; } pNewSCache->m_pBlkHdr = (F_BLK_HDR *)&pNewSCache[ 1]; } /**************************************************************************** Desc: This function will encrypt the block of data passed in. This function assumes that the buffer passed in includes the block header. ****************************************************************************/ RCODE F_Database::encryptBlock( F_Dict * pDict, FLMBYTE * pucBuffer) { RCODE rc = NE_XFLM_OK; IXD * pIxd; F_COLLECTION * pCollection; FLMUINT uiLfNum; F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pucBuffer; F_ENCDEF * pEncDef = NULL; FLMUINT uiEncId; FLMUINT uiEncLen = m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr); IF_CCS * pCcs = NULL; if (!blkIsBTree( (F_BLK_HDR *)pucBuffer)) { goto Exit; } if (!isEncryptedBlk( (F_BLK_HDR *)pBlkHdr)) { goto Exit; } if (pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) { uiEncLen = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); if (!m_bTempDb) { uiEncId = ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId; // Need to get the encryption object. if (RC_BAD( rc = pDict->getEncDef( uiEncId, &pEncDef))) { goto Exit; } } } else if (isContainerBlk( pBlkHdr)) { if (!m_bTempDb) { uiLfNum = pBlkHdr->ui16LogicalFile; if (RC_BAD( rc = pDict->getCollection( uiLfNum, &pCollection, TRUE))) { if (rc == NE_XFLM_BAD_COLLECTION) { rc = NE_XFLM_OK; } goto Exit; } if( !pCollection || !pCollection->lfInfo.uiEncId) { goto Exit; } if( RC_BAD( rc = pDict->getEncDef( pCollection->lfInfo.uiEncId, &pEncDef))) { goto Exit; } } } else if( isIndexBlk( pBlkHdr)) { if( !m_bTempDb) { uiLfNum = pBlkHdr->ui16LogicalFile; if( RC_BAD( rc = pDict->getIndex( uiLfNum, NULL, &pIxd, TRUE))) { if (rc == NE_XFLM_BAD_IX) { rc = NE_XFLM_OK; } goto Exit; } if( !pIxd || !pIxd->lfInfo.uiEncId) { goto Exit; } if( RC_BAD( rc = pDict->getEncDef( pIxd->lfInfo.uiEncId, &pEncDef))) { goto Exit; } } } else { goto Exit; } if( m_bInLimitedMode) { rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); goto Exit; } if( !m_bTempDb) { flmAssert( pEncDef); pCcs = pEncDef->pCcs; } else { flmAssert( !pEncDef); pCcs = m_pWrappingKey; } flmAssert( pCcs); flmAssert( !(uiEncLen % 16)); // Encrypt the buffer in place. if( pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) { if( RC_BAD( rc = pCcs->encryptToStore( &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], uiEncLen, &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], &uiEncLen))) { goto Exit; } flmAssert( uiEncLen == (m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr))); } else { if( RC_BAD( rc = pCcs->encryptToStore( &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], uiEncLen, &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], &uiEncLen))) { goto Exit; } flmAssert( uiEncLen == (m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr))); } Exit: return( rc); } /**************************************************************************** Desc: This function will decrypt the block of data passed in. ****************************************************************************/ RCODE F_Database::decryptBlock( F_Dict * pDict, FLMBYTE * pucBuffer) { RCODE rc = NE_XFLM_OK; IXD * pIxd; F_COLLECTION * pCollection; FLMUINT uiLfNum; F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pucBuffer; FLMUINT uiEncLen; F_ENCDEF * pEncDef = NULL; IF_CCS * pCcs = NULL; if( !blkIsBTree( (F_BLK_HDR *)pucBuffer)) { goto Exit; } if( !isEncryptedBlk( (F_BLK_HDR *)pBlkHdr)) { goto Exit; } if (pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) { uiEncLen = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); if( !m_bTempDb) { if( RC_BAD( rc = pDict->getEncDef( (FLMUINT)(((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId), &pEncDef))) { goto Exit; } } } else if( isContainerBlk( pBlkHdr)) { uiEncLen = m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr); if (!m_bTempDb) { uiLfNum = pBlkHdr->ui16LogicalFile; if( RC_BAD( rc = pDict->getCollection( uiLfNum, &pCollection, TRUE))) { if( rc == NE_XFLM_BAD_COLLECTION) { rc = NE_XFLM_OK; } goto Exit; } if( !pCollection || !pCollection->lfInfo.uiEncId) { goto Exit; } // Need to get the encryption object. if( RC_BAD( rc = pDict->getEncDef( pCollection->lfInfo.uiEncId, &pEncDef))) { goto Exit; } } } else if( isIndexBlk( pBlkHdr)) { uiEncLen = m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr); if( !m_bTempDb) { uiLfNum = pBlkHdr->ui16LogicalFile; // Get the index. if( RC_BAD( rc = pDict->getIndex( uiLfNum, NULL, &pIxd, TRUE))) { if( rc == NE_XFLM_BAD_IX) { rc = NE_XFLM_OK; } goto Exit; } if( !pIxd || !pIxd->lfInfo.uiEncId) { goto Exit; } if( RC_BAD( rc = pDict->getEncDef( pIxd->lfInfo.uiEncId, &pEncDef))) { goto Exit; } } } else { goto Exit; } if( m_bInLimitedMode) { rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); goto Exit; } if( !m_bTempDb) { flmAssert( pEncDef); pCcs = pEncDef->pCcs; } else { flmAssert( !pEncDef); pCcs = m_pWrappingKey; } flmAssert( pCcs); flmAssert( !(uiEncLen % 16)); if( pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) { if (RC_BAD( rc = pCcs->decryptFromStore( &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], uiEncLen, &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], &uiEncLen))) { goto Exit; } flmAssert( uiEncLen == (m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr))); } else { if( RC_BAD( rc = pCcs->decryptFromStore( &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], uiEncLen, &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], &uiEncLen))) { goto Exit; } flmAssert( uiEncLen == (m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr))); } Exit: return( rc); } #undef new #undef delete /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI F_CachedBlock::objectAllocInit( void * pvAlloc, FLMUINT uiSize) { F_UNREFERENCED_PARM( uiSize); // Need to make sure that m_bCanRelocate is initialized to zero // prior to unlocking the mutex. This is so the allocator // doesn't see garbage values that may cause it to relocate the object // before the constructor has been called. ((F_CachedBlock *)pvAlloc)->m_bCanRelocate = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ void * F_CachedBlock::operator new( FLMSIZET uiSize, FLMUINT uiBlockSize) #ifndef FLM_NLM throw() #endif { void * pvPtr; flmAssert( uiSize == sizeof( F_CachedBlock)); if( RC_BAD( gv_XFlmSysData.pBlockCacheMgr->m_pBlockAllocator->allocBuf( &gv_XFlmSysData.pBlockCacheMgr->m_blockRelocator, uiSize + uiBlockSize, F_CachedBlock::objectAllocInit, (FLMBYTE **)&pvPtr))) { pvPtr = NULL; } flmAssert( !((F_CachedBlock *)pvPtr)->m_bCanRelocate); return( pvPtr); } /**************************************************************************** Desc: ****************************************************************************/ void * F_CachedBlock::operator new( FLMSIZET) //uiSize) #ifndef FLM_NLM throw() #endif { // This new should never be called flmAssert( 0); return( NULL); } /**************************************************************************** Desc: ****************************************************************************/ void * F_CachedBlock::operator new[]( FLMSIZET) #ifndef FLM_NLM throw() #endif { flmAssert( 0); return( NULL); } /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void * F_CachedBlock::operator new( FLMSIZET, // uiSize, const char *, // pszFileName, int) // iLineNum) #ifndef FLM_NLM throw() #endif { // This new should never be called flmAssert( 0); return( NULL); } #endif /**************************************************************************** Desc: ****************************************************************************/ #ifdef FLM_DEBUG void * F_CachedBlock::operator new[]( FLMSIZET, // uiSize, const char *, // pszFileName, int) // iLine) #ifndef FLM_NLM throw() #endif { flmAssert( 0); return( NULL); } #endif /**************************************************************************** Desc: ****************************************************************************/ void F_CachedBlock::operator delete( void * ptr) { if( !ptr) { return; } gv_XFlmSysData.pBlockCacheMgr->m_pBlockAllocator->freeBuf( (FLMBYTE **)&ptr); } /**************************************************************************** Desc: ****************************************************************************/ void F_CachedBlock::operator delete[]( void *) // ptr { flmAssert( 0); } libxflaim-5.1.969/src/rfl.h0000644000175000017500000005400010511001742016757 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains structures and definitions used for roll // forward logging. // // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: rfl.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef RFL_H #define RFL_H class IXKeyCompare; // Packet types for roll forward logging #define RFL_TRNS_BEGIN_PACKET 1 #define RFL_TRNS_COMMIT_PACKET 2 #define RFL_TRNS_ABORT_PACKET 3 #define RFL_REDUCE_PACKET 4 #define RFL_UPGRADE_PACKET 5 #define RFL_INDEX_SUSPEND_PACKET 6 #define RFL_INDEX_RESUME_PACKET 7 #define RFL_BLK_CHAIN_FREE_PACKET 8 #define RFL_ENABLE_ENCRYPTION_PACKET 9 #define RFL_WRAP_KEY_PACKET 10 #define RFL_NODE_DELETE_PACKET 11 #define RFL_NODE_CHILDREN_DELETE_PACKET 12 #define RFL_NODE_CREATE_PACKET 13 #define RFL_DOCUMENT_DONE_PACKET 14 #define RFL_INSERT_BEFORE_PACKET 15 #define RFL_NODE_FLAGS_UPDATE_PACKET 16 #define RFL_NODE_SET_PREFIX_ID_PACKET 17 #define RFL_SET_NEXT_NODE_ID_PACKET 18 #define RFL_ENC_NODE_UPDATE_PACKET 19 #define RFL_NODE_SET_NUMBER_VALUE_PACKET 20 #define RFL_NODE_SET_TEXT_VALUE_PACKET 21 #define RFL_NODE_SET_BINARY_VALUE_PACKET 22 #define RFL_DATA_PACKET 23 #define RFL_ROLL_OVER_DB_KEY_PACKET 24 #define RFL_ENC_DEF_KEY_PACKET 25 #define RFL_NODE_SET_META_VALUE_PACKET 26 #define RFL_ATTR_SET_VALUE_PACKET 27 #define RFL_ATTR_CREATE_PACKET 28 #define RFL_ATTR_DELETE_PACKET 29 #define RFL_NODE_CLEAR_VALUE_PACKET 30 #define RFL_PACKET_TYPE_MASK 0x7F // Flags for all packets #define RFL_HAVE_COUNTS_FLAG 0x01 #define RFL_HAVE_DATA_FLAG 0x02 #define RFL_FIRST_FLAG 0x04 #define RFL_LAST_FLAG 0x08 #define RFL_TRUNCATE_FLAG 0x10 #define RFL_GET_PACKET_TYPE(uiPacketType) \ ((FLMUINT)((uiPacketType) & RFL_PACKET_TYPE_MASK)) // Definitions for ROLL FORWARD LOG file header format #define RFL_NAME_POS 0 #define RFL_NAME "RFL5" #define RFL_NAME_LEN 4 #define RFL_VERSION_POS RFL_NAME_LEN #define RFL_VERSION "5.00" #define RFL_VERSION_LEN 4 #define RFL_FILE_NUMBER_POS 8 #define RFL_EOF_POS 12 #define RFL_DB_SERIAL_NUM_POS 16 #define RFL_SERIAL_NUM_POS (RFL_DB_SERIAL_NUM_POS + XFLM_SERIAL_NUM_SIZE) #define RFL_NEXT_FILE_SERIAL_NUM_POS (RFL_SERIAL_NUM_POS + XFLM_SERIAL_NUM_SIZE) #define RFL_KEEP_SIGNATURE_POS (RFL_NEXT_FILE_SERIAL_NUM_POS + XFLM_SERIAL_NUM_SIZE) #define RFL_KEEP_SIGNATURE "----KeepLog----" #define RFL_NOKEEP_SIGNATURE "--DontKeepLog--" // Buffer size needs to be a multiple of 512 for direct IO writes. #define DEFAULT_RFL_WRITE_BUFFERS 8 #define DEFAULT_RFL_BUFFER_SIZE (65536) // Definitions for packet format and sizes. #define RFL_PACKET_ADDRESS_OFFSET 0 #define RFL_PACKET_CHECKSUM_OFFSET 4 #define RFL_PACKET_TYPE_OFFSET 5 #define RFL_PACKET_BODY_LENGTH_OFFSET 6 #define RFL_PACKET_OVERHEAD 8 // Direct IO requires that we always write on 512 byte boundaries. // This means that whenever we write out a packet, we may also // have to write out up to the last 511 bytes of the prior packet // in order to be on a 512 byte boundary. Thus, the buffer must // be able to hold a full packet plus up to 512 bytes of the prior // packet. #define RFL_MAX_PACKET_SIZE (65536 - 1024) #define RFL_MAX_PACKET_BODY_SIZE (RFL_MAX_PACKET_SIZE - RFL_PACKET_OVERHEAD) typedef struct RflWaiterTag * RFL_WAITER_p; typedef struct RflWaiterTag { FLMUINT uiThreadId; FLMBOOL bIsWriter; F_SEM hESem; RCODE * pRc; RFL_WAITER_p pNext; } RFL_WAITER; typedef struct { IF_IOBufferMgr * pBufferMgr; // Write buffer manager IF_IOBuffer * pIOBuffer; FLMUINT uiCurrFileNum; // Current file number. FLMUINT uiRflBufBytes; // Number of bytes currently in the // pIOBuffer. Always points to // where the last packet ends when // writing to the log file. FLMUINT uiRflFileOffset; // Current offset in file that the // zeroeth byte in the buffer // represents. FLMBOOL bTransInProgress; // Transaction in progress using // these buffers. FLMBOOL bOkToWriteHdrs; // Is it OK to update the DB // headers with this information XFLM_DB_HDR dbHdr; // DB header to be written with // this buffer. XFLM_DB_HDR cpHdr; // Checkpoint header for this // buffer. RFL_WAITER * pFirstWaiter; RFL_WAITER * pLastWaiter; } RFL_BUFFER; /************************************************************************** Desc: This class handles all of the roll-forward logging for FLAIM. There is one of these objects allocated per F_Database. **************************************************************************/ class F_Rfl : public F_Object { public: F_Rfl(); ~F_Rfl(); RCODE setup( F_Database * pDatabase, const char * pszRflDir); RCODE finishCurrFile( F_Db * pDb, FLMBOOL bNewKeepState); RCODE logBeginTransaction( F_Db * pDb); RCODE logEndTransaction( F_Db * pDb, FLMUINT uiPacketType, FLMBOOL bThrowLogAway, FLMBOOL * pbLoggedTransEnd = NULL); RCODE logReduce( F_Db * pDb, FLMUINT uiCount); RCODE logIndexSuspendOrResume( F_Db * pDb, FLMUINT uiIndexNum, FLMUINT uiPacketType); RCODE logUpgrade( F_Db * pDb, FLMUINT uiOldVersion); RCODE logBlockChainFree( F_Db * pDb, FLMUINT64 ui64MaintDocID, FLMUINT uiStartBlkAddr, FLMUINT uiEndBlkAddr, FLMUINT uiCount); RCODE logEncryptionKey( F_Db * pDb, FLMUINT uiPacketType, FLMBYTE * pucKey, FLMUINT32 ui32KeyLen); RCODE logEncDefKey( F_Db * pDb, FLMUINT uiEncDefId, FLMBYTE * pucKeyValue, FLMUINT uiKeyValueLen, FLMUINT uiKeySize); RCODE logRollOverDbKey( F_Db * pDb); RCODE logDocumentDone( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64RootId); RCODE logNodeDelete( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId); RCODE logAttributeDelete( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64ElementId, FLMUINT uiAttrName); RCODE logNodeChildrenDelete( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiNameId); RCODE logNodeCreate( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64ParentId, eDomNodeType eNodeType, FLMUINT uiNameId, eNodeInsertLoc eLocation, FLMUINT64 ui64NodeId); RCODE logAttributeCreate( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64ElementId, FLMUINT uiNameId, FLMUINT uiNextAttrNameId); RCODE logAttributeSetValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64ElementNodeId, void * pvValue, FLMUINT uiValueLen); RCODE logNodeSetValue( F_Db * pDb, FLMUINT uiPacketType, F_CachedNode * pCachedNode); RCODE logEncryptedNodeUpdate( F_Db * pDb, F_CachedNode * pCachedNode); RCODE logNodeSetNumberValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64Value, FLMBOOL bNegative); RCODE logAttrSetValue( F_Db * pDb, F_CachedNode * pCachedNode, FLMUINT uiAttrName); RCODE logInsertBefore( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64Parent, FLMUINT64 ui64Child, FLMUINT64 ui64RefChild); RCODE logNodeFlagsUpdate( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId, FLMUINT uiFlags, FLMBOOL bSetting); RCODE logNodeSetPrefixId( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId, FLMUINT uiPrefixId); RCODE logNodeSetMetaValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64MetaValue); RCODE logSetNextNodeId( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NextNodeId); RCODE logNodeClearValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId); RCODE recover( F_Db * pDb, IF_RestoreClient * pRestore, IF_RestoreStatus * pRestoreStatus); FLMBOOL atEndOfLog( void); // Returns full log file name associated with number. // Will return the full path name. void getFullRflFileName( FLMUINT uiFileNum, char * pszFullRflFileName, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated = NULL); // Set the RFL directory. Passing in a NULL or empty // string will cause the directory to be to the same // directory where the database is located. RCODE setRflDir( const char * pszRflDir); FINLINE const char * getRflDirPtr( void) { return &m_szRflDir [0]; } FINLINE FLMBOOL isRflDirSameAsDbDir( void) { return m_bRflDirSameAsDb; } FINLINE FLMUINT getCurrFileNum( void) { return m_pCurrentBuf->uiCurrFileNum; } FINLINE FLMUINT getCurrWriteOffset( void) { return( m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes); } FINLINE FLMUINT getCurrReadOffset( void) { return( m_uiRflReadOffset + m_pCurrentBuf->uiRflFileOffset); } FINLINE FLMUINT getCurrPacketAddress( void) { return( m_uiPacketAddress); } FINLINE void getCurrSerialNum( FLMBYTE * pucSerialNum) { f_memcpy( pucSerialNum, m_ucCurrSerialNum, XFLM_SERIAL_NUM_SIZE); } FINLINE void setCurrSerialNum( FLMBYTE * pucSerialNum) { f_memcpy( m_ucCurrSerialNum, pucSerialNum, XFLM_SERIAL_NUM_SIZE); } FINLINE void getNextSerialNum( FLMBYTE * pucSerialNum) { f_memcpy( pucSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); } FINLINE void setNextSerialNum( FLMBYTE * pucSerialNum) { f_memcpy( m_ucNextSerialNum, pucSerialNum, XFLM_SERIAL_NUM_SIZE); } FINLINE FLMUINT64 getCurrTransID( void) { return( m_ui64CurrTransID); } RCODE truncate( F_SEM hWaitSem, FLMUINT uiTruncateSize); // Public functions, but only intended for internal use RCODE makeRoom( F_Db * pDb, FLMUINT uiAdditionalBytesNeeded, FLMUINT * puiCurrPacketLenRV, FLMUINT uiPacketType, FLMUINT * puiBytesAvailableRV, FLMUINT * puiPacketCountRV); FINLINE FLMBYTE * getPacketPtr( void) { return( &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[ m_pCurrentBuf->uiRflBufBytes])); } // Close the current RFL file FINLINE void closeFile( void) { if( m_pCurrentBuf->pBufferMgr) { flmAssert( !m_pCurrentBuf->pBufferMgr->isIOPending()); } if (m_pFileHdl) { m_pFileHdl->closeFile(); m_pFileHdl->Release(); m_pFileHdl = NULL; m_pCurrentBuf->uiCurrFileNum = 0; m_pCurrentBuf->uiRflBufBytes = 0; m_pCurrentBuf->uiRflFileOffset = 0; } } FINLINE FLMBOOL seeIfRflVolumeOk( void) { return m_bRflVolumeOk; } FINLINE void setRflVolumeOk( void) { m_bRflVolumeOk = TRUE; m_bRflVolumeFull = FALSE; } FINLINE FLMBOOL isRflVolumeFull( void) { return m_bRflVolumeFull; } FINLINE RCODE waitPendingWrites( void) { if (m_uiRflWriteBufs > 1) { return( m_pCurrentBuf->pBufferMgr->waitForAllPendingIO()); } else { return( NE_XFLM_OK); } } RCODE waitForWrites( F_SEM hWaitSem, RFL_BUFFER * pBuffer, FLMBOOL bIsWriter); RCODE waitForCommit( F_SEM hWaitSem); FINLINE void commitDbHdrs( XFLM_DB_HDR * pDbHdr, XFLM_DB_HDR * pCPHdr) { f_memcpy( &m_pCurrentBuf->dbHdr, pDbHdr, sizeof( XFLM_DB_HDR)); f_memcpy( &m_pCurrentBuf->cpHdr, pCPHdr, sizeof( XFLM_DB_HDR)); m_pCurrentBuf->bOkToWriteHdrs = TRUE; } FINLINE void clearDbHdrs( void) { m_pCurrentBuf->bOkToWriteHdrs = FALSE; } FLMBOOL seeIfRflWritesDone( F_SEM hWaitSem, FLMBOOL bForceWait); void wakeUpWaiter( RCODE rc, FLMBOOL bIsWriter); RCODE completeTransWrites( F_Db * pDb, FLMBOOL bCommitting, FLMBOOL bOkToUnlock); FINLINE void disableLogging( FLMUINT * puiToken) { *puiToken = ++m_uiDisableCount; } FINLINE void enableLogging( FLMUINT * puiToken) { flmAssert( m_uiDisableCount); flmAssert( *puiToken && *puiToken == m_uiDisableCount); m_uiDisableCount--; *puiToken = 0; } FINLINE FLMBOOL isLoggingEnabled( void) { return( m_uiDisableCount ? FALSE : TRUE); } private: FINLINE FLMBYTE * getPacketBodyPtr( void) { return( &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[ m_pCurrentBuf->uiRflBufBytes + RFL_PACKET_OVERHEAD])); } FINLINE FLMBOOL haveBuffSpace( FLMUINT uiSpaceNeeded ) { return( (FLMBOOL)((m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes >= uiSpaceNeeded) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE) ); } // Write the header of an RFL file. RCODE writeHeader( FLMUINT uiFileNum, FLMUINT uiEof, FLMBYTE * pucSerialNum, FLMBYTE * pucNextSerialNum, FLMBOOL bKeepSignature); // Verify the header of an RFL file. RCODE verifyHeader( FLMBYTE * pucHeader, FLMUINT uiFileNum, FLMBYTE * pucSerialNum); // Open a new RFL file. RCODE openFile( F_SEM hWaitSem, FLMUINT uiFileNum, FLMBYTE * pucSerialNum); // Create a new RFL file RCODE createFile( F_Db * pDb, FLMUINT uiFileNum, FLMBYTE * pucSerialNum, FLMBYTE * pucNextSerialNum, FLMBOOL bKeepSignature); void copyLastSector( RFL_BUFFER * pBuffer, FLMBYTE * pucOldBuffer, FLMBYTE * pucNewBuffer, FLMUINT uiCurrPacketLen, FLMBOOL bStartingNewFile); // Position to an offset in the file. RCODE positionTo( FLMUINT uiFileOffset); // Flush data to the current RFL file RCODE flush( F_Db * pDb, RFL_BUFFER * pBuffer, FLMBOOL bFinalWrite = FALSE, FLMUINT uiCurrPacketLen = 0, FLMBOOL bStartingNewFile = FALSE); void switchBuffers( void); // Flush all packets except the current one to disk. // Shift the current one down to close to or at the // beginning of the buffer. RCODE shiftPacketsDown( F_Db * pDb, FLMUINT uiCurrPacketLen, FLMBOOL bStartingNewFile); // See if we need to generate a new RFL file. RCODE seeIfNeedNewFile( F_Db * pDb, FLMUINT uiPacketLen, FLMBOOL bDoNewIfOverLowLimit); // Calculate checksum, etc. on current packet. RCODE finishPacket( F_Db * pDb, FLMUINT uiPacketType, FLMUINT uiPacketBodyLen, FLMBOOL bDoNewIfOverLowLimit); // Functions for reading log files RCODE readPacket( FLMUINT uiMinBytesNeeded); RCODE getPacket( F_Db * pDb, FLMBOOL bForceNextFile, FLMUINT * puiPacketTypeRV, const FLMBYTE ** ppucPacketBodyRV, FLMUINT * puiPacketBodyLenRV); RCODE setupTransaction( F_Db * pDb); void finalizeTransaction( void); RCODE recovTransBegin( F_Db * pDb, eRestoreAction * peAction); RCODE recovBlockChainFree( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovIndexSuspendResume( F_Db * pDb, FLMUINT uiPacketType, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovReduce( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovUpgrade( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovEncryptionKey( F_Db * pDb, FLMUINT uiPacketType, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovEncDefKey( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovRollOverDbKey( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovDocumentDone( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeDelete( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovAttributeDelete( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeChildrenDelete( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeCreate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovAttributeCreate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovInsertBefore( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovEncryptedNodeUpdate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeSetValue( F_Db * pDb, FLMUINT uiPacketType, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeSetNumberValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovAttrSetValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeFlagsUpdate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeSetPrefixId( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeSetMetaValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovSetNextNodeId( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); RCODE recovNodeClearValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction); FLMBOOL useDataOnlyBlocks( F_Db * pDb, FLMUINT uiDataLen); // Member variables F_Database * m_pDatabase; // Pointer to database RFL_BUFFER m_Buf1; RFL_BUFFER m_Buf2; F_MUTEX m_hBufMutex; RFL_BUFFER * m_pCommitBuf; // Current buffer being committedout. // NULL if no buffer is being committed. RFL_BUFFER * m_pCurrentBuf; // Current write buffer - points to // m_Buf1 or m_Buf2 FLMUINT m_uiRflWriteBufs; // Number of RFL buffers FLMUINT m_uiBufferSize; // Buffer size FLMBOOL m_bKeepRflFiles; // Keep RFL files after they are // no longer needed? FLMUINT m_uiRflMinFileSize; // Minimum RFL file size. FLMUINT m_uiRflMaxFileSize; // Maximum RFL file size. IF_FileHdl * m_pFileHdl; // File handle for writing to roll // forward log file - only need one for // the writer because we can only have // one update transaction at a time. FLMUINT m_uiLastRecoverFileNum; // Last file number to go to when // doing recovery. FLMBYTE m_ucCurrSerialNum [XFLM_SERIAL_NUM_SIZE]; // Current file's serial number. FLMUINT m_uiTransStartFile; // File the current transaction started // in. FLMUINT m_uiTransStartAddr; // Offset of start transaction packet. FLMUINT64 m_ui64CurrTransID; // Current transaction ID. FLMUINT64 m_ui64LastTransID; // Last transaction ID. FLMUINT64 m_ui64LastLoggedCommitTransID; // Last committed transaction that // was logged to the RFL FLMUINT m_uiOperCount; // Operations that have been logged for // this transaction. FLMUINT m_uiRflReadOffset; // Offset we are reading from in the // buffer - only used when in reading // mode. FLMUINT m_uiPacketAddress; // Current packet's address in the file. FLMUINT m_uiFileEOF; // End of file for current file. // Only used when reading. IF_RestoreClient * m_pRestore; // Restore object. IF_RestoreStatus * m_pRestoreStatus; // Restore status object char m_szRflDir [F_PATH_MAX_SIZE]; // RFL directory FLMBOOL m_bRflDirSameAsDb; FLMBOOL m_bCreateRflDir; FLMBYTE m_ucNextSerialNum [ XFLM_SERIAL_NUM_SIZE]; // Next file's serial number. FLMBOOL m_bRflVolumeOk; // Did we have a problem accessing the // RFL volume? FLMBOOL m_bRflVolumeFull; // Did we have a problem accessing the // RFL volume? FLMUINT m_uiLastLfNum; // Last logical file used in restore/recover eLFileType m_eLastLfType; // The Last Lfile Type IXKeyCompare * m_pIxCompareObject; IF_ResultSetCompare * m_pCompareObject; FLMUINT m_uiDisableCount; friend class F_RflOStream; }; // Prototypes for roll forward logging functions. FLMBYTE RflCalcChecksum( FLMBYTE * pucPacket, FLMUINT uiPacketBodyLen); void rflGetBaseFileName( FLMUINT uiFileNum, char * pszBaseNameOut, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated = NULL); RCODE rflGetDirAndPrefix( const char * pszDbFileName, const char * pszRflDirIn, char * pszRflDirOut); RCODE rflGetFileName( const char * szDbName, const char * szRflDir, FLMUINT uiFileNum, char * pszRflFileName); FLMBOOL rflGetFileNum( const char * pszRflFileName, FLMUINT * puiFileNum); #endif // ifdef RFL_H libxflaim-5.1.969/src/flreduce.cpp0000644000175000017500000004465410511001742020336 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Reduce the database size by move 'N' free blocks the the end of // the file and truncating the file. // // Tabs: 3 // // Copyright (c) 1992-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flreduce.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc : Reduces the size of a FLAIM database file. Notes: The size of the database file is reduced by freeing a specified number of blocks from the available (unused) block list. The blocks are moved to the end of the file and the file is truncated. If the available block list is empty, FLAIM will attemp to add blocks to the list by freeing log extent blocks. ****************************************************************************/ RCODE FLMAPI F_Db::reduceSize( FLMUINT uiCount, FLMUINT * puiCount) { RCODE rc = NE_XFLM_OK; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMUINT uiLogicalEOF; FLMUINT uiBlkAddr; FLMUINT uiNumBlksMoved; FLMUINT uiBlkSize; F_LARGEST_BLK_HDR blkHdr; FLMINT iType; FLMBOOL bFlagSet; FLMBOOL bTransActive = FALSE; FLMBOOL bLockedDb = FALSE; FLMUINT uiRflToken = 0; uiNumBlksMoved = 0; if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Make sure we are NOT in a database transaction. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // There must NOT be a shared lock on the file. if (m_uiFlags & FDB_FILE_LOCK_SHARED) { rc = RC_SET( NE_XFLM_SHARED_LOCK); goto Exit; } // Must acquire an exclusive file lock first, if it hasn't been // acquired. if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) { if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->lock( m_hWaitSem, TRUE, FLM_NO_TIMEOUT, 0, m_pDbStats ? &m_pDbStats->LockStats : NULL))) { goto Exit; } bLockedDb = TRUE; m_uiFlags |= FDB_HAS_FILE_LOCK; } // Disable RFL logging - don't want anything logged during reduce // except for the reduce packet. pRfl->disableLogging( &uiRflToken); // Keep looping to here until the count is satisfied or there // are not any more log extent blocks to turn into avail blks. // The loop does a begin transaction - move blocks - set logical // EOF and commits the transaction. During the commit if there are // not any avail blocks left then a log extent (if any) will be turned // into more avail blocks and we can do this again with more avail // blocks. // Start a database transaction if( RC_BAD(rc = beginTrans( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { goto Exit; } bTransActive = TRUE; // Make sure that commit does something. m_bHadUpdOper = TRUE; uiBlkSize = m_pDatabase->m_uiBlockSize; // Get the logical end of file and use internally. // Loop until there are not any more free blocks left or the // input count is matched. Switch on each block type found // while backing up through the file. uiLogicalEOF = m_uiLogicalEOF; while (m_uiFirstAvailBlkAddr && (!uiCount || uiNumBlksMoved < uiCount)) { // Read the last block and determine block type. if( FSGetFileOffset( uiLogicalEOF) == 0) { FLMUINT uiFileNumber = FSGetFileNumber( uiLogicalEOF) - 1; FLMUINT64 ui64FileSize; FLMUINT64 ui64Temp; if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiFileNumber, &ui64FileSize))) { goto Exit; } // Adjust to a block bounds. ui64Temp = (ui64FileSize / uiBlkSize) * uiBlkSize; if( ui64Temp < ui64FileSize) { ui64FileSize = ui64Temp + uiBlkSize; } uiLogicalEOF = FSBlkAddress( uiFileNumber, (FLMUINT)ui64FileSize); } uiBlkAddr = uiLogicalEOF - uiBlkSize; if (RC_BAD( rc = readBlkHdr( uiBlkAddr, (F_BLK_HDR *)&blkHdr, &iType))) { goto Exit; } switch (iType) { case BT_FREE: rc = m_pDatabase->freeAvailBlk( this, uiBlkAddr); break; case BT_LEAF: case BT_NON_LEAF: case BT_NON_LEAF_COUNTS: case BT_LEAF_DATA: case BT_DATA_ONLY: rc = m_pDatabase->moveBtreeBlk( this, uiBlkAddr, (FLMUINT)blkHdr.all.BTreeBlkHdr.ui16LogicalFile, getBlkLfType( &blkHdr.all.BTreeBlkHdr)); break; case BT_LFH_BLK: rc = m_pDatabase->moveLFHBlk( this, uiBlkAddr); break; default: rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); break; } if (RC_BAD(rc)) { goto Exit; } uiNumBlksMoved++; // Adjust the logical EOF to the new value. // This is complex when dealing with block files. if (FSGetFileOffset( uiLogicalEOF) == 0) { FLMUINT uiFileNumber = FSGetFileNumber( uiLogicalEOF); FLMUINT64 ui64FileOffset; if (uiFileNumber <= 1) { break; } // Leave the current file at zero bytes and move to the // previous store file. uiFileNumber--; // Compute the end of the previous block file. if (RC_BAD( rc = m_pSFileHdl->getFileSize( uiFileNumber, &ui64FileOffset))) { goto Exit; } uiLogicalEOF = FSBlkAddress( uiFileNumber, (FLMUINT)ui64FileOffset); } uiLogicalEOF -= uiBlkSize; } // Log the reduce packet to the RFL if we are not in the middle of a // restore or recovery. Will need to re-enable logging temporarily // and then turn it back off after logging the packet. pRfl->enableLogging( &uiRflToken); // Log the reduce. if( RC_BAD( rc = pRfl->logReduce( this, uiCount))) { goto Exit; } // Turn logging back off. pRfl->disableLogging( &uiRflToken); if (RC_BAD( rc)) { goto Exit; } // Commit the transaction. if (m_uiFlags & FDB_DO_TRUNCATE) { bFlagSet = TRUE; } else { bFlagSet = FALSE; m_uiFlags |= FDB_DO_TRUNCATE; } bTransActive = FALSE; rc = commitTrans( uiLogicalEOF, TRUE); if (!bFlagSet) { m_uiFlags &= (~(FDB_DO_TRUNCATE)); } if (RC_BAD( rc)) { goto Exit; } Exit: if( RC_BAD( rc) && bTransActive) { (void)abortTrans(); uiNumBlksMoved = 0; } if (puiCount) { // May be more than the count requested. *puiCount = uiNumBlksMoved; } if (uiRflToken) { pRfl->enableLogging( &uiRflToken); } if (bLockedDb) { (void)m_pDatabase->m_pDatabaseLockObj->unlock(); m_uiFlags &= ~FDB_HAS_FILE_LOCK; } return( rc); } /**************************************************************************** Desc: Read the block header and return the type of block it is ****************************************************************************/ RCODE F_Db::readBlkHdr( FLMUINT uiBlkAddress, F_BLK_HDR * pBlkHdr, FLMINT * piType ) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesRead; FLMUINT uiNumLooks; F_CachedBlock * pBlkSCache; XFLM_LFILE_STATS * pLFileStats; F_TMSTAMP StartTime; FLMUINT64 ui64ElapMilli = 0; // First see if the block is in cache. // Previous writes may not have been forced out to cache. if (RC_BAD( rc = m_pDatabase->getBlock( this, NULL, uiBlkAddress, &uiNumLooks, &pBlkSCache))) { goto Exit; } if (pBlkSCache) // If found in cache ... { f_memcpy( pBlkHdr, pBlkSCache->getBlockPtr(), SIZEOF_LARGEST_BLK_HDR); ScaReleaseCache( pBlkSCache, FALSE); } else { if (m_pDbStats) { ui64ElapMilli = 0; f_timeGetTimeStamp( &StartTime); } rc = m_pSFileHdl->readBlock( uiBlkAddress, SIZEOF_LARGEST_BLK_HDR, pBlkHdr, &uiBytesRead); if (m_pDbStats) { flmAddElapTime( &StartTime, &ui64ElapMilli); if (RC_BAD( rc)) { m_pDbStats->bHaveStats = TRUE; m_pDbStats->uiReadErrors++; } } // Convert the block header if necessary. if (blkIsNonNativeFormat( pBlkHdr)) { convertBlkHdr( pBlkHdr); } if (m_pDbStats && RC_OK( rc)) { FLMUINT uiLFileNum; XFLM_BLOCKIO_STATS * pBlockIOStats; uiLFileNum = (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile; if (!uiLFileNum) { pLFileStats = NULL; } else { if( RC_BAD( flmStatGetLFile( m_pDbStats, uiLFileNum, getBlkLfType((F_BTREE_BLK_HDR *)pBlkHdr), 0, &pLFileStats, NULL, NULL))) { pLFileStats = NULL; } } if ((pBlockIOStats = flmGetBlockIOStatPtr( m_pDbStats, pLFileStats, (FLMBYTE *)pBlkHdr)) != NULL) { m_pDbStats->bHaveStats = TRUE; if (pLFileStats) { pLFileStats->bHaveStats = TRUE; } pBlockIOStats->BlockReads.ui64ElapMilli += ui64ElapMilli; pBlockIOStats->BlockReads.ui64Count++; pBlockIOStats->BlockReads.ui64TotalBytes += SIZEOF_LARGEST_BLK_HDR; } } if (RC_BAD( rc)) { if (rc != NE_FLM_IO_END_OF_FILE && rc != NE_XFLM_MEM) { m_pSFileHdl->releaseFiles(); } goto Exit; } } if (piType) { *piType = (FLMINT)pBlkHdr->ui8BlkType; } Exit: return( rc ); } /**************************************************************************** Desc: Find where in the b-tree a matching block is located. Move to a free block and change all pointers to the block. Notes: Some of this code could be called in movePcodeLFHBlk but we have to worry about if the block is a root or right most leaf block. ****************************************************************************/ RCODE F_Database::moveBtreeBlk( F_Db * pDb, FLMUINT uiBlkAddr, FLMUINT uiLfNumber, eLFileType eLfType) { RCODE rc; IXD * pIxd; LFILE * pLFile; F_COLLECTION * pCollection = NULL; F_CachedBlock * pFreeSCache = NULL; FLMBOOL bReleaseCache = FALSE; F_Btree * pbtree = NULL; FLMBOOL bHaveCounts; FLMBOOL bHaveData; IXKeyCompare compareObject; IF_ResultSetCompare * pCompareObject = NULL; // Get an F_Btree object if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } if (eLfType == XFLM_LF_COLLECTION) { if (RC_BAD( rc = pDb->m_pDict->getCollection( uiLfNumber, &pCollection))) { goto Exit; } pLFile = &pCollection->lfInfo; bHaveCounts = FALSE; bHaveData = TRUE; } else { if (RC_BAD( rc = pDb->m_pDict->getIndex( uiLfNumber, &pLFile, &pIxd, TRUE))) { goto Exit; } bHaveCounts = (pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE; bHaveData = (pIxd->pFirstData) ? TRUE : FALSE; pCompareObject = &compareObject; compareObject.setIxInfo( pDb, pIxd); } // Need to make sure that LFILE is up to date. // Force reading it in. if (RC_BAD( rc = lFileRead( pDb, pLFile, pCollection))) { goto Exit; } // If we are moving the root block, create a new dictionary so // that the LFILE can be modified safely to point to the new // root block. if (pLFile->uiRootBlk == uiBlkAddr) { // Create a new dictionary if (!(pDb->m_uiFlags & FDB_UPDATED_DICTIONARY)) { if (RC_BAD( rc = pDb->dictClone())) { goto Exit; } // Re-get the LFile if (eLfType == XFLM_LF_COLLECTION) { if (RC_BAD( rc = pDb->m_pDict->getCollection( uiLfNumber, &pCollection))) { goto Exit; } pLFile = &pCollection->lfInfo; } else { if (RC_BAD( rc = pDb->m_pDict->getIndex( uiLfNumber, &pLFile, &pIxd, TRUE))) { goto Exit; } pCompareObject = &compareObject; compareObject.setIxInfo( pDb, pIxd); } } } if (RC_BAD( rc = pbtree->btOpen( pDb, pLFile, bHaveCounts, bHaveData, pCompareObject))) { goto Exit; } // Get the next free block. if (RC_BAD( rc = blockUseNextAvail( pDb, &pFreeSCache))) { goto Exit; } bReleaseCache = TRUE; // Move B-tree block to free block. if (RC_BAD( rc = pbtree->btMoveBlock( (FLMUINT32)uiBlkAddr, (FLMUINT32)pFreeSCache->m_uiBlkAddress))) { goto Exit; } Exit: if (bReleaseCache) { ScaReleaseCache( pFreeSCache, FALSE); } if (pbtree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); } if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: Find where anb LFH input block is located. Move to a free block and change all pointers to the block. ****************************************************************************/ RCODE F_Database::moveLFHBlk( F_Db * pDb, FLMUINT uiBlkAddr) { RCODE rc; F_CachedBlock * pSCache; FLMBOOL bReleaseCache = FALSE; F_CachedBlock * pFreeSCache = NULL; FLMBOOL bReleaseCache2 = FALSE; F_BLK_HDR * pBlkHdr; F_LF_HDR * pLfHdr; F_BLK_HDR * pFreeBlkHdr; FLMUINT uiLeftBlkAddr; FLMUINT uiRightBlkAddr; FLMUINT uiFreeBlkAddr; FLMUINT uiSavePriorBlkImgAddr; FLMUINT uiPos; FLMUINT uiEndPos; F_COLLECTION * pTmpCollection; LFILE * pTmpLFile; if (RC_BAD( rc = getBlock( pDb, NULL, uiBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } pBlkHdr = pSCache->m_pBlkHdr; // Get left and rigth block addresses. // Get next avail block and move data over uiLeftBlkAddr = (FLMUINT)pBlkHdr->ui32PrevBlkInChain; uiRightBlkAddr = (FLMUINT)pBlkHdr->ui32NextBlkInChain; if (RC_BAD( rc = blockUseNextAvail( pDb, &pFreeSCache))) { goto Exit; } bReleaseCache2 = TRUE; pFreeBlkHdr = pFreeSCache->m_pBlkHdr; uiFreeBlkAddr = (FLMUINT)pFreeBlkHdr->ui32BlkAddr; // The free block has been logged and set to dirty in // blockUseNextAvail(). // BUT, need to preserve prior image block address - it should // NOT be copied over from the block we are switching with. uiSavePriorBlkImgAddr = (FLMUINT)pFreeBlkHdr->ui32PriorBlkImgAddr; f_memcpy( pFreeBlkHdr, pBlkHdr, m_uiBlockSize); pFreeBlkHdr->ui32BlkAddr = (FLMUINT32)uiFreeBlkAddr; // Restore the saved previous transaction ID and block address. pFreeBlkHdr->ui32PriorBlkImgAddr = (FLMUINT32)uiSavePriorBlkImgAddr; // Fix up any LFile entries that were pointing to the // original LFH block uiPos = SIZEOF_STD_BLK_HDR; uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pFreeBlkHdr); // Create a new dictionary if (!(pDb->m_uiFlags & FDB_UPDATED_DICTIONARY)) { if (RC_BAD( rc = pDb->dictClone())) { goto Exit; } } // Iterate over the set of LFiles in the block and // update their LFH block addresses pLfHdr = (F_LF_HDR *)((FLMBYTE *)pFreeBlkHdr + SIZEOF_STD_BLK_HDR); while (uiPos < uiEndPos) { if (pLfHdr->ui32LfType != (FLMUINT32)XFLM_LF_INVALID) { if (pLfHdr->ui32LfType == (FLMUINT32)XFLM_LF_COLLECTION) { if (RC_BAD( rc = pDb->m_pDict->getCollection( (FLMUINT)pLfHdr->ui32LfNumber, &pTmpCollection))) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } pTmpLFile = &pTmpCollection->lfInfo; } else { flmAssert( pLfHdr->ui32LfType == (FLMUINT32)XFLM_LF_INDEX); if (RC_BAD( rc = pDb->m_pDict->getIndex( (FLMUINT)pLfHdr->ui32LfNumber, &pTmpLFile, NULL, TRUE))) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } pTmpLFile->uiBlkAddress = uiFreeBlkAddr; } uiPos += sizeof( F_LF_HDR); pLfHdr++; } // Done with both blocks. ScaReleaseCache( pFreeSCache, FALSE); ScaReleaseCache( pSCache, FALSE); bReleaseCache2 = bReleaseCache = FALSE; // Read left and right blocks and adjust their // pointers to point to the new block. // This doesn't matter what level of the b-tree // you are on. if (uiLeftBlkAddr) { if (RC_BAD( rc = getBlock( pDb, NULL, uiLeftBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiFreeBlkAddr; ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } if (uiRightBlkAddr) { if (RC_BAD( rc = getBlock( pDb, NULL, uiRightBlkAddr, NULL, &pSCache))) { goto Exit; } bReleaseCache = TRUE; if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) { goto Exit; } pSCache->m_pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiFreeBlkAddr; ScaReleaseCache( pSCache, FALSE); bReleaseCache = FALSE; } Exit: if (bReleaseCache) { ScaReleaseCache( pSCache, FALSE); } if (bReleaseCache2) { ScaReleaseCache( pFreeSCache, FALSE); } if( RC_BAD( rc)) { pDb->setMustAbortTrans( rc); } return( rc); } /**************************************************************************** Desc: Free the input avail block. Link the block out of the free list ****************************************************************************/ RCODE F_Database::freeAvailBlk( F_Db * pDb, FLMUINT uiBlkAddr) { RCODE rc = NE_XFLM_OK; F_LARGEST_BLK_HDR blkHdr; FLMUINT uiPrevBlkAddr; FLMUINT uiNextBlkAddr; F_CachedBlock * pSCache; // Check for first avail block condition. if (uiBlkAddr == pDb->m_uiFirstAvailBlkAddr) { if (RC_OK( rc = blockUseNextAvail( pDb, &pSCache))) { ScaReleaseCache( pSCache, FALSE); } goto Exit; } // Not first block, unlink from list. // Read the block header and get pointers if (RC_BAD( rc = pDb->readBlkHdr( uiBlkAddr, (F_BLK_HDR *)&blkHdr, (FLMINT *)0))) { goto Exit; } uiPrevBlkAddr = (FLMUINT)blkHdr.all.stdBlkHdr.ui32PrevBlkInChain; uiNextBlkAddr = (FLMUINT)blkHdr.all.stdBlkHdr.ui32NextBlkInChain; // Read the previous block, if any if (uiPrevBlkAddr) { if (RC_BAD( rc = getBlock( pDb, NULL, uiPrevBlkAddr, NULL, &pSCache))) { goto Exit; } // Log the block before modifying it. if (RC_OK( rc = logPhysBlk( pDb, &pSCache))) { pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiNextBlkAddr; } ScaReleaseCache( pSCache, FALSE); if (RC_BAD( rc)) { goto Exit; } } // Read the next block, if any. if (uiNextBlkAddr) { if (RC_BAD( rc = getBlock( pDb, NULL, uiNextBlkAddr, NULL, &pSCache))) { goto Exit; } // Log the block before modifying it. if (RC_OK( rc = logPhysBlk( pDb, &pSCache))) { pSCache->m_pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiPrevBlkAddr; } ScaReleaseCache( pSCache, FALSE); if (RC_BAD( rc)) { goto Exit; } } Exit: return( rc); } libxflaim-5.1.969/src/fcollate.cpp0000644000175000017500000001443210511001742020325 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines for building collation keys // // Tabs: 3 // // Copyright (c) 1993-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fcollate.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /************************************************************************** Desc: Get the collation string and convert back to a text string Ret: Length of new wpStr Notes: Allocates the area for the word string buffer if will be over 256. ***************************************************************************/ RCODE flmColText2StorageText( const FLMBYTE * pucColStr, // Points to the collated string FLMUINT uiColStrLen, // Length of the collated string FLMBYTE * pucStorageBuf, // Output string to build - TEXT string FLMUINT * puiStorageLen, // In: Size of buffer, Out: Bytes used FLMUINT uiLang, FLMBOOL * pbDataTruncated, // Sets to TRUE if data had been truncated FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring { #define LOCAL_CHARS 150 FLMBYTE ucWPStr[ LOCAL_CHARS * 2 + LOCAL_CHARS / 5 ]; // Sample + 20% FLMBYTE * pucWPPtr = NULL; FLMBYTE * pucAllocatedWSPtr = NULL; FLMUINT uiWPStrLen; FLMBYTE * pucStoragePtr; FLMUINT uiUnconvChars; FLMUINT uiTmp; FLMUINT uiMaxStorageBytes = *puiStorageLen; FLMUINT uiMaxWPBytes; FLMUINT uiStorageOffset; FLMBYTE ucTmpSen[ 5]; FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; RCODE rc = NE_FLM_OK; if( uiColStrLen > LOCAL_CHARS) { // If it won't fit, allocate a new buffer if( RC_BAD( rc = f_alloc( XFLM_MAX_KEY_SIZE * 2, &pucWPPtr))) { goto Exit; } pucAllocatedWSPtr = pucWPPtr; uiMaxWPBytes = uiWPStrLen = XFLM_MAX_KEY_SIZE * 2; } else { pucWPPtr = &ucWPStr[ 0]; uiMaxWPBytes = uiWPStrLen = sizeof( ucWPStr); } if( (uiLang >= FLM_FIRST_DBCS_LANG) && (uiLang <= FLM_LAST_DBCS_LANG)) { if( RC_BAD( rc = f_asiaColStr2WPStr( pucColStr, uiColStrLen, pucWPPtr, &uiWPStrLen, &uiUnconvChars, pbDataTruncated, pbFirstSubstring))) { goto Exit; } } else { if( RC_BAD( rc = f_colStr2WPStr( pucColStr, uiColStrLen, pucWPPtr, &uiWPStrLen, uiLang, &uiUnconvChars, pbDataTruncated, pbFirstSubstring))) { goto Exit; } } // Copy word string to the storage string area uiWPStrLen >>= 1; // Convert # of bytes to # of words pucStoragePtr = pucStorageBuf; uiStorageOffset = 0; // Encode the number of characters as a SEN. If pucEncPtr is // NULL, the caller is only interested in the length of the encoded // string, so a temporary buffer is used to call f_encodeSEN. uiTmp = f_encodeSEN( uiWPStrLen - uiUnconvChars, &pucTmpSen); if( (uiStorageOffset + uiTmp) >= uiMaxStorageBytes) { rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucStoragePtr, &ucTmpSen[ 0], uiTmp); uiStorageOffset += uiTmp; // Encode each of the WP characters into UTF-8 while( uiWPStrLen--) { FLMBYTE ucChar; FLMBYTE ucCharSet; FLMUNICODE uChar; // Put the character in a local variable for speed ucChar = *pucWPPtr++; ucCharSet = *pucWPPtr++; if( ucCharSet == 0xFF && ucChar == 0xFF) { uChar = (((FLMUNICODE)*(pucWPPtr + 1)) << 8) | *pucWPPtr; pucWPPtr += 2; uiWPStrLen--; // Skip past 4 bytes for UNICODE } else { if( RC_BAD( rc = f_wpToUnicode( (((FLMUINT16)ucCharSet) << 8) + ucChar, &uChar))) { goto Exit; } } uiTmp = uiMaxStorageBytes - uiStorageOffset; if( RC_BAD( rc = f_uni2UTF8( uChar, &pucStorageBuf[ uiStorageOffset], &uiTmp))) { goto Exit; } uiStorageOffset += uiTmp; } if( uiStorageOffset >= uiMaxStorageBytes) { rc = RC_SET( NE_FLM_CONV_DEST_OVERFLOW); goto Exit; } // Tack on a trailing NULL byte pucStorageBuf[ uiStorageOffset++] = 0; // Return the length of the storage buffer *puiStorageLen = uiStorageOffset; Exit: if( pucAllocatedWSPtr) { f_free( &pucAllocatedWSPtr); } return( rc); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_DbSystem::compareUTF8Strings( const FLMBYTE * pucLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMBYTE * pucRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult) { return( f_compareUTF8Strings( pucLString, uiLStrBytes, bLeftWild, pucRString, uiRStrBytes, bRightWild, uiCompareRules, uiLanguage, piResult)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE F_DbSystem::compareUnicodeStrings( const FLMUNICODE * puzLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMUNICODE * puzRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult) { return( f_compareUnicodeStrings( puzLString, uiLStrBytes, bLeftWild, puzRString, uiRStrBytes, bRightWild, uiCompareRules, uiLanguage, piResult)); } /*************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_DbSystem::utf8IsSubStr( const FLMBYTE * pszString, const FLMBYTE * pszSubString, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMBOOL * pbExists) { return( f_utf8IsSubStr( pszString, pszSubString, uiCompareRules, uiLanguage, pbExists)); } libxflaim-5.1.969/src/fsdatacu.cpp0000644000175000017500000004377210511001742020337 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Cursor routines to get the complexity of the file system out // of the search code. // // Tabs: 3 // // Copyright (c) 2000-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fsdatacu.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "fscursor.h" /**************************************************************************** Desc: ****************************************************************************/ FSCollectionCursor::FSCollectionCursor() { m_pbTree = NULL; m_bTreeOpen = FALSE; m_pCollection = NULL; m_bDocumentIds = FALSE; m_pLFile = NULL; m_pDb = NULL; m_eTransType = XFLM_NO_TRANS; resetCursor(); } /**************************************************************************** Desc: ****************************************************************************/ FSCollectionCursor::~FSCollectionCursor() { closeBTree(); if (m_pbTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pbTree); } } /**************************************************************************** Desc: Resets any allocations, keys, state, etc. ****************************************************************************/ void FSCollectionCursor::resetCursor( void) { closeBTree(); m_uiCollection = 0; m_bDocumentIds = FALSE; m_uiBlkChangeCnt = 0; m_ui64CurrTransId = 0; m_ui64CurNodeId = 0; m_bAtBOF = TRUE; m_bAtEOF = FALSE; m_bSetup = FALSE; } /**************************************************************************** Desc: Resets to a new transaction that may change the read consistency of the query. ****************************************************************************/ RCODE FSCollectionCursor::resetTransaction( F_Db * pDb) { RCODE rc = NE_XFLM_OK; F_COLLECTION * pCollection; if (RC_BAD( rc = pDb->m_pDict->getCollection( m_uiCollection, &pCollection))) { goto Exit; } if (pCollection != m_pCollection) { m_pCollection = pCollection; m_pLFile = &pCollection->lfInfo; if (m_bTreeOpen) { closeBTree(); } m_pDb = pDb; m_eTransType = pDb->m_eTransType; } m_ui64CurrTransId = pDb->m_ui64CurrTransID; m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt; Exit: return( rc); } /**************************************************************************** Desc: Open the F_Btree object if not already open. ****************************************************************************/ RCODE FSCollectionCursor::openBTree( F_Db * pDb ) { RCODE rc = NE_XFLM_OK; if (!m_bTreeOpen) { if (!m_pbTree) { if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &m_pbTree))) { goto Exit; } } Open_Btree: if (RC_BAD( rc = m_pbTree->btOpen( pDb, m_pLFile, FALSE, FALSE))) { goto Exit; } m_bTreeOpen = TRUE; m_pDb = pDb; m_eTransType = pDb->m_eTransType; } else { if (pDb != m_pDb || pDb->m_eTransType != m_eTransType) { closeBTree(); goto Open_Btree; } } Exit: return( rc); } /**************************************************************************** Desc: Set the node position. ****************************************************************************/ RCODE FSCollectionCursor::setNodePosition( F_Db * pDb, FLMBOOL bGoingForward, FLMUINT64 ui64NodeId, FLMUINT64 * pui64FoundNodeId, F_Btree * pBTree // BTree to use. NULL means use our // internal one. ) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMBOOL bNeg; FLMUINT uiBytesProcessed; FLMUINT64 ui64TmpNodeId; IF_DOMNode * pNode = NULL; // if pBTree is NULL, we are to use m_pbTree. Otherwise, we // need to open the pBTree and use it. if (!pBTree) { if (RC_BAD( rc = openBTree( pDb))) { goto Exit; } pBTree = m_pbTree; } uiKeyLen = sizeof( ucKey); if (RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if (RC_BAD( rc = pBTree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) { if (rc != NE_XFLM_EOF_HIT) { goto Exit; } } if (bGoingForward) { if (rc == NE_XFLM_EOF_HIT) { goto Exit; } } else { // Going backwards or to last. See if we positioned too far. if (rc == NE_XFLM_BOF_HIT || rc == NE_XFLM_EOF_HIT) { // Position to last key in tree. if (RC_BAD( rc = pBTree->btLastEntry( ucKey, sizeof( ucKey), &uiKeyLen, NULL, NULL, NULL))) { goto Exit; } } else { if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64TmpNodeId, &bNeg, &uiBytesProcessed))) { goto Exit; } if (ui64TmpNodeId > ui64NodeId) { // Position to the previous key. if (RC_BAD( rc = pBTree->btPrevEntry( ucKey, sizeof( ucKey), &uiKeyLen, NULL, NULL, NULL))) { goto Exit; } } } } if (!m_bDocumentIds) { if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, pui64FoundNodeId, &bNeg, &uiBytesProcessed))) { goto Exit; } } else { // Need to position to a document ID if we are looking only for document // root nodes. for (;;) { if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64TmpNodeId, &bNeg, &uiBytesProcessed))) { goto Exit; } // The following code tests to see if we have gone past the // from or until node id. It is an optimization that isn't // actually necessary for the code to work properly, because // the outside code also makes this check. This just allows // us to quit earlier than we otherwise might while looking // for a document root node. if (bGoingForward) { // If we have gone past the until node id, we are done. if (ui64TmpNodeId > m_ui64UntilNodeId) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { // If we have gone past the from node id, we are done. if (ui64TmpNodeId < m_ui64FromNodeId) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } } if (RC_BAD( rc = pDb->getNode( m_uiCollection, ui64TmpNodeId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Better be able to find the node at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } } // If the node is a root node, we have a document we can // process. if (((F_DOMNode *)pNode)->isRootNode()) { *pui64FoundNodeId = ui64TmpNodeId; break; } // Need to go to the next or previous node. if (bGoingForward) { if (RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) { goto Exit; } } else { if (RC_BAD( rc = pBTree->btPrevEntry( ucKey, uiKeyLen, &uiKeyLen))) { goto Exit; } } } } Exit: if (RC_BAD( rc)) { if (pBTree == m_pbTree) { closeBTree(); } } if (pNode) { pNode->Release(); } return( rc); } /**************************************************************************** Desc: Setup the from and until keys in the cursor. Return counts after positioning to the from and until key in the index. This code does not work with multiple key sets of FROM/UNTIL keys. ****************************************************************************/ RCODE FSCollectionCursor::setupRange( F_Db * pDb, FLMUINT uiCollection, FLMBOOL bDocumentIds, FLMUINT64 ui64LowNodeId, FLMUINT64 ui64HighNodeId, FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks FLMUINT * puiTotalNodes, // [out] FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. { RCODE rc = NE_XFLM_OK; F_Btree * pUntilBTree = NULL; m_bAtBOF = TRUE; m_bAtEOF = FALSE; m_uiCollection = uiCollection; m_bDocumentIds = bDocumentIds; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } m_ui64FromNodeId = ui64LowNodeId; m_ui64UntilNodeId = ui64HighNodeId; m_bSetup = TRUE; m_ui64CurNodeId = 0; // Want any of the counts back? if (puiLeafBlocksBetween || puiTotalNodes) { if (puiLeafBlocksBetween) { *puiLeafBlocksBetween = 0; } if (puiTotalNodes) { *puiTotalNodes = 0; } if (pbTotalsEstimated) { *pbTotalsEstimated = FALSE; } // Position to the FROM and UNTIL key so we can get the stats. if (RC_BAD( rc = setNodePosition( pDb, TRUE, m_ui64FromNodeId, &m_ui64CurNodeId, NULL))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } goto Exit; } // All nodes between FROM and UNTIL may be gone. if (m_ui64CurNodeId < m_ui64UntilNodeId) { FLMUINT64 ui64TmpNodeId; // Get a btree object if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree))) { goto Exit; } if (RC_BAD( rc = pUntilBTree->btOpen( pDb, m_pLFile, FALSE, FALSE))) { goto Exit; } // We better be able to at least find m_ui64CurNodeId going // backward from m_ui64UntilNodeId. if (RC_BAD( rc = setNodePosition( pDb, FALSE, m_ui64UntilNodeId, &ui64TmpNodeId, pUntilBTree))) { goto Exit; } if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, puiLeafBlocksBetween, puiTotalNodes, pbTotalsEstimated, (pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) { goto Exit; } } } Exit: if (pUntilBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree); } return( rc); } /**************************************************************************** Desc: Return the current node. ****************************************************************************/ RCODE FSCollectionCursor::currentNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId ) { RCODE rc = NE_XFLM_OK; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } if (m_bAtBOF) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if (m_bAtEOF) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } flmAssert( m_ui64CurNodeId); if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Make sure the current node is positioned in the range for the cursor. ****************************************************************************/ RCODE FSCollectionCursor::checkIfNodeInRange( FLMBOOL bPositionForward) { RCODE rc = NE_XFLM_OK; if (bPositionForward) { if (m_ui64CurNodeId > m_ui64UntilNodeId) { m_bAtEOF = TRUE; rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } } else { if (m_ui64CurNodeId < m_ui64FromNodeId) { m_bAtBOF = TRUE; rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Position to and return the first node. ****************************************************************************/ RCODE FSCollectionCursor::firstNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId ) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); // If at BOF and we have a node, then we are positioned on the first // node already - this would have happened if we had positioned to // calculate a cost. Rather than do the positioning again, we simply // set m_bAtBOF to FALSE. if (m_bAtBOF && m_ui64CurNodeId) { m_bAtBOF = FALSE; } else { m_bAtBOF = m_bAtEOF = FALSE; if (RC_BAD( rc = setNodePosition( pDb, TRUE, m_ui64FromNodeId, &m_ui64CurNodeId, NULL))) { if (rc == NE_XFLM_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } } // Make sure the current node ID is within the FROM/UNTIL range. if (RC_BAD( rc = checkIfNodeInRange( TRUE))) { goto Exit; } if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) { goto Exit; } Exit: if (RC_BAD( rc)) { m_ui64CurNodeId = 0; } return( rc); } /**************************************************************************** Desc: Position to the next node. ****************************************************************************/ RCODE FSCollectionCursor::nextNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId ) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); if (m_bAtEOF) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } if (m_bAtBOF || !m_ui64CurNodeId) { rc = firstNode( pDb, ppNode, pui64NodeId); goto Exit; } if (m_bDocumentIds) { if (RC_BAD( rc = pDb->getNode( m_uiCollection, m_ui64CurNodeId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; // Allow to just fall through to below and call setNodePosition. } else { goto Exit; } } else if (((F_DOMNode *)pNode)->isRootNode()) { if (RC_BAD( rc = pNode->getNextDocument( pDb, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_EOF_HIT; m_bAtEOF = TRUE; } } else { if( RC_BAD( rc = pNode->getNodeId( m_pDb, &m_ui64CurNodeId))) { goto Exit; } if (RC_OK( rc = checkIfNodeInRange( TRUE))) { if (pui64NodeId) { *pui64NodeId = m_ui64CurNodeId; } if (ppNode) { if (*ppNode) { (*ppNode)->Release(); } *ppNode = pNode; pNode = NULL; } } } goto Exit; } // Fall through to below to call setNodePosition. } // Get the next node, if any if (m_ui64CurNodeId == ~((FLMUINT64)0)) { rc = RC_SET( NE_XFLM_EOF_HIT); m_bAtEOF = TRUE; goto Exit; } // See if we need to reset the b-tree object we are using if (m_bTreeOpen && (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) { closeBTree(); } if (RC_BAD( rc = setNodePosition( pDb, TRUE, m_ui64CurNodeId + 1, &m_ui64CurNodeId, NULL))) { if (rc == NE_XFLM_EOF_HIT) { m_bAtEOF = TRUE; } goto Exit; } // Make sure the current node ID is within the FROM/UNTIL range. if (RC_BAD( rc = checkIfNodeInRange( TRUE))) { goto Exit; } if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) { goto Exit; } Exit: if (pNode) { pNode->Release(); } if (RC_BAD( rc)) { m_ui64CurNodeId = 0; } return( rc); } /**************************************************************************** Desc: Position to and return the last node. ****************************************************************************/ RCODE FSCollectionCursor::lastNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId ) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); // Position to the until node m_bAtBOF = m_bAtEOF = FALSE; if (RC_BAD( rc = setNodePosition( pDb, FALSE, m_ui64UntilNodeId, &m_ui64CurNodeId, NULL))) { if (rc == NE_XFLM_BOF_HIT) { m_bAtBOF = TRUE; } goto Exit; } // Make sure the current node ID is within the FROM/UNTIL range. if (RC_BAD( rc = checkIfNodeInRange( FALSE))) { goto Exit; } if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) { goto Exit; } Exit: if (RC_BAD( rc)) { m_ui64CurNodeId = 0; } return( rc); } /**************************************************************************** Desc: Position to the previous node. ****************************************************************************/ RCODE FSCollectionCursor::prevNode( F_Db * pDb, IF_DOMNode ** ppNode, FLMUINT64 * pui64NodeId ) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; if (RC_BAD( rc = checkTransaction( pDb))) { goto Exit; } flmAssert( m_bSetup); if (m_bAtBOF) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if (m_bAtEOF || !m_ui64CurNodeId) { rc = lastNode( pDb, ppNode, pui64NodeId); goto Exit; } if (m_bDocumentIds) { if (RC_BAD( rc = pDb->getNode( m_uiCollection, m_ui64CurNodeId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; // Allow to just fall through to below and call setNodePosition. } else { goto Exit; } } else if (((F_DOMNode *)pNode)->isRootNode()) { if (RC_BAD( rc = pNode->getPreviousDocument( pDb, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_BOF_HIT; m_bAtBOF = TRUE; } } else { if( RC_BAD( rc = pNode->getNodeId( m_pDb, &m_ui64CurNodeId))) { goto Exit; } if (RC_OK( rc = checkIfNodeInRange( FALSE))) { if (pui64NodeId) { *pui64NodeId = m_ui64CurNodeId; } if (ppNode) { if (*ppNode) { (*ppNode)->Release(); } *ppNode = pNode; pNode = NULL; } } } goto Exit; } // Fall through to below to call setNodePosition. } // Get the previous node, if any if (m_ui64CurNodeId == 1) { rc = RC_SET( NE_XFLM_BOF_HIT); m_bAtBOF = TRUE; goto Exit; } // See if we need to reset the b-tree object we are using if (m_bTreeOpen && (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) { closeBTree(); } if (RC_BAD( rc = setNodePosition( pDb, FALSE, m_ui64CurNodeId - 1, &m_ui64CurNodeId, NULL))) { if (rc == NE_XFLM_BOF_HIT) { m_bAtBOF = TRUE; } goto Exit; } // Make sure the current node ID is within the FROM/UNTIL range. if (RC_BAD( rc = checkIfNodeInRange( FALSE))) { goto Exit; } if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) { goto Exit; } Exit: if (pNode) { pNode->Release(); } if (RC_BAD( rc)) { m_ui64CurNodeId = 0; } return( rc); } libxflaim-5.1.969/src/funicode.cpp0000644000175000017500000005106610511001742020334 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the Unicode conversion routines // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: funicode.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ //#define DEF_FLM_UNI_GLOBALS #include "flaimsys.h" /**************************************************************************** Desc: Encode a string into FLAIM's internal text format (SEN-prefixed, null-terminated UTF8). The string is prefixed with a SEN that indicates the number of characters, not including the terminating NULL. This routine can be called with a NULL input buffer. If it is called in this way, the length of the encoded string will be returned in *puiBufLength, but no encoding will actually be performed. ****************************************************************************/ RCODE flmUnicode2Storage( const FLMUNICODE * puzStr, // UNICODE string to encode FLMUINT uiStrLen, // 0 = Unknown FLMBYTE * pucBuf, // Destination buffer FLMUINT * puiBufLength, // [IN ] Size of pucBuf, // [OUT] Amount of pucBuf used FLMUINT * puiCharCount) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEncPtr; const FLMUNICODE * puzPtr = NULL; FLMUINT uiMaxLen; FLMUINT uiCharsEncoded = 0; FLMUINT uiEncodedLen = 0; FLMUINT uiTmp; FLMUNICODE uChar; FLMBYTE ucTmpSen[ 5]; FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; if( !pucBuf) { uiMaxLen = (~(FLMUINT)0); } else { uiMaxLen = *puiBufLength; } // If uiStrLen is 0, determine the number of characters. if( !uiStrLen) { uiStrLen = f_unilen( puzStr); } else if( puzStr[ uiStrLen] != 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } if( puiCharCount) { *puiCharCount = uiStrLen; } if( !uiStrLen) { // Nothing to encode *puiBufLength = 0; goto Exit; } pucEncPtr = pucBuf; // Encode the number of characters as a SEN. If pucEncPtr is // NULL, the caller is only interested in the length of the encoded // string, so a temporary buffer is used to call f_encodeSEN. uiTmp = f_encodeSEN( uiStrLen, &pucTmpSen); if( pucEncPtr) { if( (uiEncodedLen + uiTmp) >= uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if( uiTmp == 1) { *pucEncPtr++ = ucTmpSen[ 0]; } else { f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp); pucEncPtr += uiTmp; } } uiEncodedLen += uiTmp; // Encode the string using UTF-8 puzPtr = puzStr; if( uiStrLen) { while( (uChar = *puzPtr) != 0) { if( (uiTmp = uiMaxLen - uiEncodedLen) == 0) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if( uChar <= 127) { if( pucEncPtr) { *pucEncPtr++ = (FLMBYTE)uChar; } uiEncodedLen++; } else { if( RC_BAD( rc = f_uni2UTF8( uChar, pucEncPtr, &uiTmp))) { goto Exit; } if( pucEncPtr) { pucEncPtr += uiTmp; } uiEncodedLen += uiTmp; } puzPtr++; uiCharsEncoded++; } // Make sure the string length (which may have been provided by // the caller) was correct. if( uiCharsEncoded != uiStrLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } } // Terminate the string with a 0 byte if( (uiMaxLen - uiEncodedLen) < 1) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if( pucEncPtr) { *pucEncPtr++ = 0; } uiEncodedLen++; // Return the length of the encoded string *puiBufLength = uiEncodedLen; Exit: return( rc); } /**************************************************************************** Desc: Encode a string into FLAIM's internal text format (SEN-prefixed, null-terminated UTF8). The string is prefixed with a SEN that indicates the number of characters, not including the terminating NULL. This routine can be called with a NULL input buffer. If it is called in this way, the length of the encoded string will be returned in *puiBufLength, but no encoding will actually be performed. ****************************************************************************/ RCODE flmNative2Storage( char * pszStr, // Native string to encode FLMUINT uiStrLen, // 0 = Unknown FLMBYTE * pucBuf, // Destination buffer FLMUINT * puiBufLength, // [IN ] Size of pucBuf, // [OUT] Amount of pucBuf used FLMUINT * puiCharCount) { FLMBYTE * pucEncPtr; char * pszPtr = NULL; FLMUINT uiMaxLen; FLMUINT uiCharsEncoded = 0; FLMUINT uiEncodedLen = 0; FLMUINT uiTmp; FLMBYTE ucTmpSen[ 5]; FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; FLMBOOL bDirectCopy = FALSE; RCODE rc = NE_XFLM_OK; if( !pucBuf) { uiMaxLen = (~(FLMUINT)0); } else { uiMaxLen = *puiBufLength; } // If uiStrLen is 0, determine the number of characters. if( !uiStrLen) { #ifdef FLM_ASCII_PLATFORM bDirectCopy = TRUE; for( pszPtr = pszStr; *pszPtr; pszPtr++, uiStrLen++) { if( (FLMBYTE)*pszPtr > 0x7F) { bDirectCopy = FALSE; } } #else uiStrLen = f_strlen( pszStr); #endif } else if( pszStr[ uiStrLen] != 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } if( puiCharCount) { *puiCharCount = uiStrLen; } if( !uiStrLen) { // Nothing to encode *puiBufLength = 0; goto Exit; } pucEncPtr = pucBuf; // Encode the number of characters as a SEN. If pucEncPtr is // NULL, the caller is only interested in the length of the encoded // string, so a temporary buffer is used to call f_encodeSEN. uiTmp = f_encodeSEN( uiStrLen, &pucTmpSen); if( pucEncPtr) { if( (uiEncodedLen + uiTmp) >= uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp); pucEncPtr += uiTmp; } uiEncodedLen += uiTmp; // Encode the string using UTF-8 if( uiStrLen) { if( bDirectCopy) { // Since all of the characters are ASCII and have // values <= 0x7F, the string can be copied directly // into the destination buffer buffer. if( uiEncodedLen + uiStrLen >= uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if( pucEncPtr) { f_memcpy( pucEncPtr, pszStr, uiStrLen); pucEncPtr += uiStrLen; } uiEncodedLen += uiStrLen; } else { pszPtr = pszStr; while( *pszPtr) { if( (uiTmp = uiMaxLen - uiEncodedLen) == 0) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } // Convert the native character to ASCII before // mapping it to Unicode. if( RC_BAD( rc = f_uni2UTF8( (FLMUNICODE)f_toascii( *pszPtr), pucEncPtr, &uiTmp))) { goto Exit; } uiEncodedLen += uiTmp; if( pucEncPtr) { pucEncPtr += uiTmp; } pszPtr++; uiCharsEncoded++; } // Make sure the string length (which may have been provided by // the caller) was correct. if( uiCharsEncoded != uiStrLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } } } // Terminate the string with a 0 byte if( (uiMaxLen - uiEncodedLen) == 0) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if( pucEncPtr) { *pucEncPtr++ = 0; } uiEncodedLen++; // Return the length of the encoded string *puiBufLength = uiEncodedLen; Exit: return( rc); } /**************************************************************************** Desc: Encode a string into FLAIM's internal text format (SEN-prefixed, null-terminated UTF8). The string is prefixed with a SEN that indicates the number of characters, not including the terminating NULL. This routine can be called with a NULL input buffer. If it is called in this way, the length of the encoded string will be returned in *puiBufLength, but no encoding will actually be performed. ****************************************************************************/ RCODE flmUTF8ToStorage( const FLMBYTE * pucUTF8, // UTF8 string to encode FLMUINT uiBytesInBuffer, // Maximum bytes to process from source FLMBYTE * pucBuf, // Destination buffer FLMUINT * puiBufLength) // [IN ] Size of pucBuf, // [OUT] Amount of pucBuf used { RCODE rc = NE_XFLM_OK; FLMBYTE * pucEncPtr; const FLMBYTE * pucPtr = NULL; FLMUINT uiMaxLen; FLMUINT uiEncodedLen = 0; FLMUINT uiCharCount; FLMUINT uiByteCount; FLMUINT uiTmp; FLMUNICODE uChar; FLMBYTE ucTmpSen[ 5]; FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; const FLMBYTE * pucEnd = NULL; if( !pucBuf) { uiMaxLen = (~(FLMUINT)0); } else { uiMaxLen = *puiBufLength; } if( uiBytesInBuffer) { pucEnd = &pucUTF8[ uiBytesInBuffer]; } // Determine the number of bytes and characters in the // string uiCharCount = 0; pucPtr = pucUTF8; for( ;;) { if( RC_BAD( rc = f_getCharFromUTF8Buf( &pucPtr, pucEnd, &uChar))) { goto Exit; } if( !uChar) { break; } uiCharCount++; } if( !uiCharCount) { *puiBufLength = 0; goto Exit; } pucEncPtr = pucBuf; // Encode the number of characters as a SEN. If pucEncPtr is // NULL, the caller is only interested in the length of the encoded // string, so a temporary buffer is used to call f_encodeSEN. uiTmp = f_encodeSEN( uiCharCount, &pucTmpSen); if( pucEncPtr) { if( (uiEncodedLen + uiTmp) >= uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp); pucEncPtr += uiTmp; } uiEncodedLen += uiTmp; // Copy the UTF8 characters into the destination buffer uiByteCount = (FLMUINT)(pucPtr - pucUTF8); if( pucEncPtr) { if( (uiMaxLen - uiEncodedLen) < uiByteCount) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } f_memcpy( pucEncPtr, pucUTF8, uiByteCount); pucEncPtr += uiByteCount; } uiEncodedLen += uiByteCount; // Terminate the string with a 0 byte if( uiEncodedLen == uiMaxLen) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } if( pucEncPtr) { *pucEncPtr++ = 0; } uiEncodedLen++; // Return the length of the encoded string *puiBufLength = uiEncodedLen; Exit: return( rc); } /**************************************************************************** Desc: Convert text storage string to Unicode. Notes: If puzOutBuf is NULL, only a count is returned in puiOutBufLen to indicate the number of bytes needed to contain the data. Two (unicode) bytes must be added to this value to account for null termination. ****************************************************************************/ RCODE flmStorage2Unicode( FLMUINT uiType, FLMUINT uiBufLength, const FLMBYTE * pucBuffer, FLMUINT * puiOutBufLen, // [IN] Number of bytes available in buffer // [OUT] Returns the number of bytes that are needed to // represent the data. The null termination byte(s) are // not included in this value. void * pOutBuf) // [IN/OUT] Buffer to hold the data. { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMUINT uiOffset = 0; FLMUINT uiSenLen; FLMUINT uiNumChars; FLMUINT uiMaxOutChars; FLMBYTE ucTempBuf[ 64]; FLMUNICODE * puzOutBuf = NULL; FLMBYTE * pszOutBuf = NULL; const FLMBYTE * pucEnd; FLMUINT uiDecodeCount; if( !pucBuffer || !uiBufLength) { ucTempBuf[ 0] = 0; // SEN encoding of 0 ucTempBuf[ 1] = 0; // String terminator pucBuffer = &ucTempBuf[ 0]; uiBufLength = 2; } else if( uiType != XFLM_TEXT_TYPE) { // If the value is a number, convert to text if( uiType == XFLM_NUMBER_TYPE) { FLMUINT uiTmp; uiTmp = sizeof( ucTempBuf); if( RC_BAD( rc = flmStorageNum2StorageText( pucBuffer, uiBufLength, ucTempBuf, &uiTmp))) { goto Exit; } pucBuffer = &ucTempBuf[ 0]; uiBufLength = uiTmp; } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } } if( !uiBufLength) { if( puiOutBufLen) { if( *puiOutBufLen >= 2) { *((FLMUNICODE *)pOutBuf) = 0; } *puiOutBufLen = 0; } goto Exit; } pucEnd = &pucBuffer[ uiBufLength]; uiSenLen = f_getSENLength( *pucBuffer); if( pucBuffer + uiSenLen >= pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucBuffer, pucEnd, &uiNumChars))) { goto Exit; } // If only a length is needed (number of bytes), we can // return that without parsing the string if( !pOutBuf) { uiOffset = uiNumChars; goto Exit; } flmAssert( puiOutBufLen); uiMaxOutChars = (*puiOutBufLen) / sizeof( FLMUNICODE); puzOutBuf = (FLMUNICODE *)pOutBuf; // If we have a zero-length string, jump to exit. if( !uiNumChars) { if( (pucBuffer + 1) != pucEnd || *pucBuffer != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( *pucBuffer != 0) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } else if (!uiMaxOutChars) { goto Overflow_Error; } if( puzOutBuf) { *puzOutBuf = 0; } else { *pszOutBuf = 0; } goto Exit; } // Parse through the string, outputting data to the buffer as we go. uChar = 0; uiDecodeCount = 0; for( ;;) { // Decode the bytes. if( RC_BAD( rc = f_getCharFromUTF8Buf( &pucBuffer, pucEnd, &uChar))) { goto Exit; } if( !uChar) { break; } if( uiOffset == uiMaxOutChars) { goto Overflow_Error; } if( puzOutBuf) { puzOutBuf[ uiOffset++] = uChar; } else { if ( uChar <= 0xFF) { uChar = (FLMUNICODE)f_tonative( (FLMBYTE)uChar); pszOutBuf[ uiOffset++] = (FLMBYTE)uChar; } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } } uiDecodeCount++; } if( uChar || uiDecodeCount != uiNumChars) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } // There is room for the 0 terminating character, but we // will not increment return length. if( uiOffset < uiMaxOutChars) { if( puzOutBuf) { puzOutBuf[ uiOffset] = 0; } else { pszOutBuf[ uiOffset] = 0; } } else { Overflow_Error: flmAssert( uiOffset == uiMaxOutChars); // If uiOffset is zero, so is uiMaxOutChars, which means // that we can't even put out the zero terminator. if (uiOffset) { uiOffset--; if( puzOutBuf) { puzOutBuf[ uiOffset] = 0; } else { pszOutBuf[ uiOffset] = 0; } } rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } Exit: if( puiOutBufLen) { *puiOutBufLen = uiOffset + uiOffset; } return( rc); } /**************************************************************************** Desc: Converts storage formats to UNICODE ****************************************************************************/ RCODE flmStorage2Unicode( FLMUINT uiType, FLMUINT uiStorageLength, const FLMBYTE * pucStorageBuffer, F_DynaBuf * pBuffer) { RCODE rc = NE_XFLM_OK; FLMBYTE ucTempBuf[ 80]; const FLMBYTE * pucEnd; FLMUINT uiSenLen; FLMUINT uiNumChars; FLMUNICODE * puzDestBuffer; pBuffer->truncateData( 0); if( uiType != XFLM_TEXT_TYPE) { // If the value is a number, convert to text if( uiType == XFLM_NUMBER_TYPE) { FLMUINT uiTmp; uiStorageLength = sizeof( ucTempBuf); if( RC_BAD( rc = flmStorageNum2StorageText( pucStorageBuffer, uiStorageLength, ucTempBuf, &uiTmp))) { goto Exit; } pucStorageBuffer = &ucTempBuf[ 0]; uiStorageLength = uiTmp; } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } } pucEnd = &pucStorageBuffer[ uiStorageLength]; uiSenLen = f_getSENLength( *pucStorageBuffer); if( pucStorageBuffer + uiSenLen >= pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucStorageBuffer, pucEnd, &uiNumChars))) { goto Exit; } if( RC_BAD( rc = pBuffer->allocSpace( (uiNumChars + 1) * sizeof( FLMUNICODE), (void **)&puzDestBuffer))) { goto Exit; } // Parse through the string outputting data to the buffer as we go for( ;;) { if( RC_BAD( rc = f_getCharFromUTF8Buf( &pucStorageBuffer, pucEnd, puzDestBuffer))) { goto Exit; } if( !(*puzDestBuffer)) { break; } puzDestBuffer++; } Exit: return( rc); } /**************************************************************************** Desc: Converts a storage buffer to UTF-8 (null-terminated) text ****************************************************************************/ RCODE flmStorage2UTF8( FLMUINT uiType, FLMUINT uiBufLength, const FLMBYTE * pucBuffer, FLMUINT * puiOutBufLen, // [IN] Number of bytes available in buffer // [OUT] Returns the number of bytes that are needed to // represent the data. The null termination byte is not // included in this value. FLMBYTE * pucOutBuf) // [OUT] Buffer to hold the data. { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucEnd; FLMBYTE ucTempBuf[ 64]; FLMUINT uiSenLen; if( !pucBuffer) { ucTempBuf[ 0] = 0; // SEN encoding of 0 ucTempBuf[ 1] = 0; // String terminator pucBuffer = &ucTempBuf[ 0]; uiBufLength = 2; } else if( uiType != XFLM_TEXT_TYPE) { // If the value is a number, convert to text if( uiType == XFLM_NUMBER_TYPE) { FLMUINT uiTmp; uiTmp = sizeof( ucTempBuf); if( RC_BAD( rc = flmStorageNum2StorageText( pucBuffer, uiBufLength, ucTempBuf, &uiTmp))) { goto Exit; } pucBuffer = &ucTempBuf[ 0]; uiBufLength = uiTmp; } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } } if( !uiBufLength) { if( *puiOutBufLen && pucOutBuf) { *pucOutBuf = 0; } *puiOutBufLen = 0; goto Exit; } pucEnd = &pucBuffer[ uiBufLength]; uiSenLen = f_getSENLength( *pucBuffer); if( pucBuffer + uiSenLen >= pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucBuffer, pucEnd, NULL))) { goto Exit; } if( pucOutBuf) { if( *puiOutBufLen >= uiBufLength - uiSenLen) { f_memcpy( pucOutBuf, pucBuffer, uiBufLength - uiSenLen); } } *puiOutBufLen = (uiBufLength - uiSenLen) - 1; Exit: return( rc); } /**************************************************************************** Desc: Reads and returns the value of the SEN indicating the number of characters encoded into the storage (UTF-8) string ****************************************************************************/ RCODE flmGetCharCountFromStorageBuf( const FLMBYTE ** ppucBuf, FLMUINT uiBufSize, FLMUINT * puiNumChars, FLMUINT * puiSenLen) { RCODE rc = NE_XFLM_OK; FLMUINT uiSenLen; FLMUINT uiNumChars; if( !uiBufSize) { if( puiNumChars) { *puiNumChars = 0; } if( puiSenLen) { *puiSenLen = 0; } goto Exit; } if( (uiSenLen = f_getSENLength( (*ppucBuf)[ 0])) >= uiBufSize) { rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } if( RC_BAD( rc = f_decodeSEN( ppucBuf, *ppucBuf + uiSenLen, &uiNumChars))) { goto Exit; } if( puiNumChars) { *puiNumChars = uiNumChars; } if( puiSenLen) { *puiSenLen = uiSenLen; } Exit: return( rc); } /**************************************************************************** Desc: This routine converts an internal number to internal (ASCII) text. Notes: If the buffer pointer is NULL, the routine just determines how much buffer space is needed to store the number in a text string. ****************************************************************************/ RCODE flmStorageNum2StorageText( const FLMBYTE * pucNum, FLMUINT uiNumLen, FLMBYTE * pucBuffer, FLMUINT * puiBufLen) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num; FLMINT64 i64Num; FLMUINT uiOffset = 0; FLMBOOL bNeg = FALSE; char szTmpBuf[ 64]; if( RC_BAD( rc = flmStorage2Number64( XFLM_NUMBER_TYPE, uiNumLen, pucNum, &ui64Num, NULL))) { if( rc == NE_XFLM_CONV_NUM_UNDERFLOW) { if( RC_BAD( rc = flmStorage2Number64( XFLM_NUMBER_TYPE, uiNumLen, pucNum, NULL, &i64Num))) { goto Exit; } ui64Num = (FLMUINT64)-i64Num; bNeg = TRUE; } else { goto Exit; } } if( bNeg) { szTmpBuf[ uiOffset++] = '-'; } uiOffset += f_sprintf( &szTmpBuf[ uiOffset], "%I64u", ui64Num); if( RC_BAD( rc = flmNative2Storage( szTmpBuf, uiOffset, pucBuffer, puiBufLen, NULL))) { goto Exit; } Exit: return( rc); } libxflaim-5.1.969/src/f_ccs.h0000644000175000017500000000455510511001742017263 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Controlled Cryptographic Services (CCS) interface // // Tabs: 3 // // Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: f_nici.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef F_CCS_H #define F_CCS_H RCODE flmAllocCCS( IF_CCS ** ppCCS); /**************************************************************************** Desc: Controlled Cryptographic Services (CCS) interface ****************************************************************************/ class IF_CCS : public F_Object { public: virtual ~IF_CCS() { } virtual RCODE init( FLMBOOL bKeyIsWrappingKey, FLMUINT uiAlgType) = 0; virtual RCODE generateEncryptionKey( FLMUINT uiEncKeySize) = 0; virtual RCODE generateWrappingKey( FLMUINT uiEncKeySize) = 0; virtual RCODE encryptToStore( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV = NULL) = 0; virtual RCODE decryptFromStore( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV = NULL) = 0; virtual RCODE getKeyToStore( FLMBYTE ** ppucKeyInfo, FLMUINT32 * pui32BufLen, FLMBYTE * pszEncKeyPasswd = NULL, IF_CCS * pWrappingCcs = NULL) = 0; virtual RCODE setKeyFromStore( FLMBYTE * pucKeyInfo, FLMBYTE * pszEncKeyPasswd, IF_CCS * pWrappingCcs) = 0; virtual FLMUINT getIVLen( void) = 0; virtual RCODE generateIV( FLMUINT uiIVLen, FLMBYTE * pucIV) = 0; }; #endif // F_CCS_H libxflaim-5.1.969/src/flaimsys.h0000644000175000017500000056464110511001742020044 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This is the master header file for FLAIM. It is included by nearly // all of the source files. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flaimsys.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FLAIMSYS_H #define FLAIMSYS_H #include "xflaim.h" #undef FLM_HAS_ENCRYPTION #ifdef FLM_USE_NICI #define FLM_HAS_ENCRYPTION #endif #ifdef HAVE_CONFIG_H #include "../config.h" #endif #if defined( FLM_WIN) // Conversion from XXX to YYY, possible loss of data #pragma warning( disable : 4244) // Local variable XXX may be used without having been initialized #pragma warning( disable : 4701) // Function XXX not inlined #pragma warning( disable : 4710) #endif #if defined( FLM_WATCOM_NLM) // Disable "Warning! W549: col(XX) 'sizeof' operand contains // compiler generated information" #pragma warning 549 9 #endif // Put all forward references here class F_Database; class F_Dict; class F_Db; class F_NameTable; class F_IOBuffer; class F_Rfl; class F_Btree; class F_DOMNode; class F_NodeList; class F_Query; class F_DbRebuild; class F_DbCheck; class F_DbInfo; class F_KeyCollector; class FSIndexCursor; class FSCollectionCursor; class F_CachedBlock; class F_CachedNode; class F_GlobalCacheMgr; class F_BlockCacheMgr; class F_NodeCacheMgr; class F_NodeBufferIStream; class F_BTreeIStream; class F_QueryResultSet; class F_BTreeInfo; class F_AttrItem; class F_NodeVerifier; class F_RebuildNodeIStream; // Internal includes #include "fcollate.h" #include "fdict.h" #include "fxml.h" #include "fstructs.h" #include "fcache.h" #include "flmstat.h" #include "fxpath.h" #include "fbtrset.h" #include "fquery.h" #include "fcollate.h" #include "f_btree.h" #include "f_btpool.h" #include "rfl.h" #include "filesys.h" #include "flog.h" #include "f_ccs.h" RCODE MapErrnoToFlaimErr( int err, RCODE defaultRc); // Misc. global constants #ifdef DEFINE_NUMBER_MAXIMUMS #define GV_EXTERN #else #define GV_EXTERN extern #endif GV_EXTERN FLMBOOL gv_b32BitPlatform #ifdef DEFINE_NUMBER_MAXIMUMS = (FLMBOOL)(sizeof( FLMUINT) == 4 ? TRUE : FALSE) #endif ; GV_EXTERN FLMUINT gv_uiMaxUInt32Val #ifdef DEFINE_NUMBER_MAXIMUMS = (FLMUINT)0xFFFFFFFF #endif ; GV_EXTERN FLMUINT gv_uiMaxUIntVal #ifdef DEFINE_NUMBER_MAXIMUMS = (FLMUINT)(~(FLMUINT)0) #endif ; GV_EXTERN FLMUINT gv_uiMaxSignedIntVal #ifdef DEFINE_NUMBER_MAXIMUMS = (FLMUINT)((((~(FLMUINT)0) << 1) >> 1)) #endif ; GV_EXTERN FLMUINT64 gv_ui64MaxSignedIntVal #ifdef DEFINE_NUMBER_MAXIMUMS = (FLMUINT64)((((~(FLMUINT64)0) << 1) >> 1)) #endif ; GV_EXTERN F_DbSystem * gv_pXFlmDbSystem #ifdef DEFINE_NUMBER_MAXIMUMS = NULL #endif ; // A global module lock allows us to properly implement DllCanUnloadNow // This is only used in a COM environment. The functions are actually // defined in fdllmain.cpp extern void LockModule(void); extern void UnlockModule(void); #define MAX_DIRTY_NODES_THRESHOLD 1024 #define MAX_DOM_HEADER_SIZE 118 #define FIXED_DOM_HEADER_SIZE 94 #define XFLM_FIXED_SIZE_HEADER_TOKEN 0xFF // NOTE: ENCRYPT_MIN_CHUNK_SIZE should always be a power of 2 // getEncLen supposes that it is. #define ENCRYPT_MIN_CHUNK_SIZE 16 #define ENCRYPT_BOUNDARY_MASK (~(ENCRYPT_MIN_CHUNK_SIZE - 1)) /***************************************************************************** Desc: ******************************************************************************/ FINLINE RCODE convertToUINT( FLMUINT64 ui64Num, FLMBOOL bNeg, FLMUINT * puiNum) { if( bNeg) { return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); } if( gv_b32BitPlatform && ui64Num > 0xFFFFFFFF) { return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); } *puiNum = (FLMUINT)ui64Num; return( NE_XFLM_OK); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE RCODE convertToUINT32( FLMUINT64 ui64Num, FLMBOOL bNeg, FLMUINT32 * pui32Num) { if( bNeg) { return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); } if( ui64Num > 0xFFFFFFFF) { return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); } *pui32Num = (FLMUINT32)ui64Num; return( NE_XFLM_OK); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE RCODE convertToUINT64( FLMUINT64 ui64Num, FLMBOOL bNeg, FLMUINT64 * pui64Num) { if( bNeg) { return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); } *pui64Num = ui64Num; return( NE_XFLM_OK); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE RCODE convertToINT( FLMUINT64 ui64Num, FLMBOOL bNeg, FLMINT * piNum) { if( bNeg) { if (ui64Num == (FLMUINT64)(FLM_MAX_INT) + 1) { *piNum = FLM_MIN_INT; } else if( ui64Num > (FLMUINT64)(FLM_MAX_INT) + 1) { return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); } else { *piNum = -((FLMINT)ui64Num); } } else { if( ui64Num > (FLMUINT64)FLM_MAX_INT) { return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); } *piNum = (FLMINT)ui64Num; } return( NE_XFLM_OK); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE RCODE convertToINT32( FLMUINT64 ui64Num, FLMBOOL bNeg, FLMINT32 * pi32Num) { if( bNeg) { if (ui64Num == (FLMUINT64)(FLM_MAX_INT32) + 1) { *pi32Num = FLM_MIN_INT32; } else if( ui64Num > (FLMUINT64)(FLM_MAX_INT32) + 1) { return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); } else { *pi32Num = -((FLMINT32)ui64Num); } } else { if( ui64Num > (FLMUINT64)FLM_MAX_INT32) { return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); } *pi32Num = (FLMINT32)ui64Num; } return( NE_XFLM_OK); } /***************************************************************************** Desc: ******************************************************************************/ FINLINE RCODE convertToINT64( FLMUINT64 ui64Num, FLMBOOL bNeg, FLMINT64 * pi64Num) { if( bNeg) { if( ui64Num > gv_ui64MaxSignedIntVal + 1) { return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); } *pi64Num = -(FLMINT64)ui64Num; } else { if( ui64Num > gv_ui64MaxSignedIntVal) { return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); } *pi64Num = (FLMINT64)ui64Num; } return( NE_XFLM_OK); } /***************************************************************************** Desc: Calculate the number of bytes extra there are beyond the closest encryption boundary. ******************************************************************************/ FINLINE FLMUINT extraEncBytes( FLMUINT uiDataLen) { // This works if ENCRYPT_MIN_CHUNK_SIZE is a power of 2 // Otherwise, it needs to be changed to uiDataLen % ENCRYPT_MIN_CHUNK_SIZE return( uiDataLen & (ENCRYPT_MIN_CHUNK_SIZE - 1)); } /***************************************************************************** Desc: Calculate the encryption length for a piece of data. ******************************************************************************/ FINLINE FLMUINT getEncLen( FLMUINT uiDataLen) { return( extraEncBytes( uiDataLen) ? ((uiDataLen + ENCRYPT_MIN_CHUNK_SIZE) & ENCRYPT_BOUNDARY_MASK) : uiDataLen); } /*============================================================================ Some simple inline functions for dealing with tag numbers ============================================================================*/ FINLINE FLMBOOL elementIsUserDefined( FLMUINT uiNum) { return( uiNum <= XFLM_MAX_ELEMENT_NUM ? TRUE : FALSE); } FINLINE FLMBOOL attributeIsUserDefined( FLMUINT uiNum) { return( uiNum <= XFLM_MAX_ATTRIBUTE_NUM ? TRUE : FALSE); } FINLINE FLMBOOL elementIsReservedTag( FLMUINT uiNum) { return( uiNum >= XFLM_FIRST_RESERVED_ELEMENT_TAG && uiNum <= XFLM_LAST_RESERVED_ELEMENT_TAG ? TRUE : FALSE); } FINLINE FLMBOOL attributeIsReservedTag( FLMUINT uiNum) { return( uiNum >= XFLM_FIRST_RESERVED_ATTRIBUTE_TAG && uiNum <= XFLM_LAST_RESERVED_ATTRIBUTE_TAG ? TRUE : FALSE); } /**************************************************************************** Stuff for F_NameTable class ****************************************************************************/ typedef struct FlmTagInfoTag { FLMUINT uiType; FLMUNICODE * puzTagName; FLMUINT uiTagNum; FLMUINT uiDataType; FLMUNICODE * puzNamespace; } FLM_TAG_INFO; /**************************************************************************** Desc: Class for name/number lookup. ****************************************************************************/ class F_NameTable : public F_Object { public: F_NameTable(); ~F_NameTable(); void clearTable( FLMUINT uiPoolBlkSize); RCODE addReservedDictTags( void); RCODE getNextTagTypeAndNumOrder( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName = NULL, char * pszTagName = NULL, FLMUINT uiNameBufSize = 0, FLMUINT * puiTagNum = NULL, FLMUINT * puiDataType = NULL, FLMUNICODE * puzNamespace = NULL, FLMUINT uiNamespaceBufSize = 0, FLMBOOL bTruncatedNamesOk = TRUE); RCODE getNextTagTypeAndNameOrder( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName = NULL, char * pszTagName = NULL, FLMUINT uiNameBufSize = 0, FLMUINT * puiTagNum = NULL, FLMUINT * puiDataType = NULL, FLMUNICODE * puzNamespace = NULL, FLMUINT uiNamespaceBufSize = 0, FLMBOOL bTruncatedNamesOk = TRUE); RCODE getFromTagTypeAndName( F_Db * pDb, FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, FLMBOOL bMatchNamespace, const FLMUNICODE * puzNamespace = NULL, FLMUINT * puiTagNum = NULL, FLMUINT * puiDataType = NULL); RCODE getFromTagTypeAndNum( F_Db * pDb, FLMUINT uiType, FLMUINT uiTagNum, FLMUNICODE * puzTagName = NULL, char * pszTagName = NULL, FLMUINT * puiNameBufSize = NULL, FLMUINT * puiDataType = NULL, FLMUNICODE * puzNamespace = NULL, char * pszNamespace = NULL, FLMUINT * puiNamespaceBufSize = NULL, FLMBOOL bTruncatedNamesOk = TRUE); RCODE addTag( FLMUINT uiType, FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiDataType = 0, FLMUNICODE * puzNamespace = NULL, FLMBOOL bCheckDuplicates = TRUE, FLMBOOL bLimitNumToLoad = TRUE); void sortTags( void); void removeTag( FLMUINT uiType, FLMUINT uiTagNum); RCODE cloneNameTable( F_NameTable * pSrcNameTable); RCODE importFromNameTable( F_NameTable * pSrcNameTable); FINLINE FLMBOOL haveAllElements( void) { return m_bLoadedAllElements; } FINLINE FLMBOOL haveAllAttributes( void) { return m_bLoadedAllAttributes; } virtual FLMINT FLMAPI AddRef( void); virtual FLMINT FLMAPI Release( void); private: RCODE allocTag( FLMUINT uiType, FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiDataType, FLMUNICODE * puzNamespace, FLM_TAG_INFO ** ppTagInfo); RCODE reallocSortTables( FLMUINT uiNewTblSize); RCODE copyTagName( FLMUNICODE * puzDestTagName, char * pszDestTagName, FLMUINT * puiDestBufSize, FLMUNICODE * puzSrcTagName, FLMBOOL bTruncatedNamesOk); FLM_TAG_INFO * findTagByTypeAndNum( FLMUINT uiType, FLMUINT uiTagNum, FLMUINT * puiInsertPos = NULL); FLM_TAG_INFO * findTagByTypeAndName( FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, FLMBOOL bMatchNamespace, const FLMUNICODE * puzNamespace, FLMBOOL * pbAmbiguous, FLMUINT * puiInsertPos = NULL); RCODE insertTagInTables( FLM_TAG_INFO * pTagInfo, FLMUINT uiTagTypeAndNameTblInsertPos, FLMUINT uiTagTypeAndNumTblInsertPos); FLMUNICODE * findNamespace( FLMUNICODE * puzNamespace, FLMUINT * puiInsertPos); RCODE insertNamespace( FLMUNICODE * puzNamespace, FLMUINT uiInsertPos); F_Pool m_pool; FLMUINT m_uiMemoryAllocated; FLM_TAG_INFO ** m_ppSortedByTagTypeAndName; FLM_TAG_INFO ** m_ppSortedByTagTypeAndNum; FLMUINT m_uiTblSize; FLMUINT m_uiNumTags; FLMBOOL m_bTablesSorted; FLMBOOL m_bLoadedAllElements; FLMBOOL m_bLoadedAllAttributes; FLMUINT m_uiNumElementsLoaded; FLMUINT m_uiNumAttributesLoaded; FLMUNICODE ** m_ppuzNamespaces; FLMUINT m_uiNamespaceTblSize; FLMUINT m_uiNumNamespaces; friend class F_Db; }; /**************************************************************************** Storage conversion functions. ****************************************************************************/ #define FLM_MAX_NUM_BUF_SIZE 9 RCODE flmStorage2Number( FLMUINT uiType, FLMUINT uiBufLen, const FLMBYTE * pucBuf, FLMUINT * puiNum, FLMINT * piNum); RCODE flmStorage2Number64( FLMUINT uiType, FLMUINT uiBufLen, const FLMBYTE * pucBuf, FLMUINT64 * pui64Num, FLMINT64 * pi64Num); RCODE flmNumber64ToStorage( FLMUINT64 ui64Num, FLMUINT * puiBufLen, FLMBYTE * pucBuf, FLMBOOL bNegative, FLMBOOL bCollation); RCODE FlmUINT2Storage( FLMUINT uiNum, FLMUINT * puiBufLen, FLMBYTE * pucBuf); RCODE FlmINT2Storage( FLMINT iNum, FLMUINT * puiBufLen, FLMBYTE * pucBuf); RCODE flmUTF8ToStorage( const FLMBYTE * pucUTF8, FLMUINT uiBytesInBuffer, FLMBYTE * pucBuf, FLMUINT * puiBufLength); RCODE flmGetCharCountFromStorageBuf( const FLMBYTE ** ppucBuf, FLMUINT uiBufSize, FLMUINT * puiNumChars, FLMUINT * puiSenLen = NULL); RCODE flmStorage2UTF8( FLMUINT uiType, FLMUINT uiBufLength, const FLMBYTE * pucBuffer, FLMUINT * puiOutBufLen, FLMBYTE * pucOutBuf); RCODE flmStorage2Unicode( FLMUINT uiType, FLMUINT uiBufLength, const FLMBYTE * pucBuffer, FLMUINT * puiOutBufLen, void * pOutBuf); RCODE flmStorage2Unicode( FLMUINT uiType, FLMUINT uiStorageLength, const FLMBYTE * pucStorageBuffer, F_DynaBuf * pBuffer); RCODE flmUnicode2Storage( const FLMUNICODE * puzStr, FLMUINT uiStrLen, FLMBYTE * pucBuf, FLMUINT * puiBufLength, FLMUINT * puiCharCount); RCODE flmStorageNum2StorageText( const FLMBYTE * pucNum, FLMUINT uiNumLen, FLMBYTE * pucBuffer, FLMUINT * puiBufLen); /**************************************************************************** Desc: Returns the size of buffer needed to hold the unicode string in FLAIM's storage format. ****************************************************************************/ FINLINE RCODE FlmGetUnicodeStorageLength( FLMUNICODE * puzStr, FLMUINT * puiByteCount) { FLMUINT uiByteCount; RCODE rc; if( RC_BAD( rc = flmUnicode2Storage( puzStr, 0, NULL, &uiByteCount, NULL))) { return( rc); } *puiByteCount = uiByteCount + sizeof( FLMUNICODE); return( NE_XFLM_OK); } /**************************************************************************** Desc: Copies and formats a Unicode string into FLAIM's storage format. The Unicode string must be in little-endian byte order. Unicode values that are not represented as WordPerfect 6.x characters are preserved as non-WP characters. ****************************************************************************/ FINLINE RCODE FlmUnicode2Storage( FLMUNICODE * puzStr, FLMUINT * puiBufLength, // [IN] size of pBuf, // [OUT] amount of pBuf used to hold puzStr FLMBYTE * pBuf) { return( flmUnicode2Storage( puzStr, 0, pBuf, puiBufLength, NULL)); } /**************************************************************************** Desc: Converts from FLAIM's internal storage format to a Unicode string ****************************************************************************/ FINLINE RCODE FlmStorage2Unicode( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuffer, FLMUINT * puiOutBufLen, FLMUNICODE * puzOutBuf) { return( flmStorage2Unicode( uiType, uiBufLength, pBuffer, puiOutBufLen, puzOutBuf)); } /**************************************************************************** Desc: Convert storage text into a null-terminated UTF-8 string ****************************************************************************/ FINLINE RCODE FlmStorage2UTF8( FLMUINT uiType, FLMUINT uiBufLength, FLMBYTE * pBuffer, FLMUINT * puiOutBufLenRV, // [IN] Specified the number of bytes available in buffer. // [OUT] Returns the number of bytes of UTF-8 text. This value // does not include a terminating NULL byte in the buffer. FLMBYTE * pOutBuffer) // [OUT] The buffer that will hold the output UTF-8 text. // If this value is NULL then only bufLenRV will computed so that // the caller may know how many bytes to allocate for a buffer. { return( flmStorage2UTF8( uiType, uiBufLength, pBuffer, puiOutBufLenRV, pOutBuffer)); } /**************************************************************************** Desc: ****************************************************************************/ typedef struct FlmVectorElementTag { FLMUINT64 ui64ID; FLMUINT uiNameId; FLMUINT uiFlags; #define VECT_SLOT_HAS_DATA 0x01 #define VECT_SLOT_HAS_ID 0x02 #define VECT_SLOT_RIGHT_TRUNCATED 0x04 #define VECT_SLOT_LEFT_TRUNCATED 0x08 #define VECT_SLOT_HAS_NAME_ID 0x10 #define VECT_SLOT_IS_ATTR 0x20 #define VECT_SLOT_IS_DATA 0x40 FLMUINT uiDataType; FLMUINT uiDataLength; FLMUINT uiDataOffset; } F_VECTOR_ELEMENT; /***************************************************************************** Desc: Used to build keys and data components *****************************************************************************/ class F_DataVector : public IF_DataVector { public: // Constructor/Destructor F_DataVector(); virtual ~F_DataVector(); // Setter methods FINLINE void FLMAPI setDocumentID( FLMUINT64 ui64DocumentID) { m_ui64DocumentID = ui64DocumentID; } RCODE FLMAPI setID( FLMUINT uiElementNumber, FLMUINT64 ui64ID); RCODE FLMAPI setNameId( FLMUINT uiElementNumber, FLMUINT uiNameId, FLMBOOL bIsAttr, FLMBOOL bIsData); RCODE FLMAPI setINT( FLMUINT uiElementNumber, FLMINT iNum); RCODE FLMAPI setINT64( FLMUINT uiElementNumber, FLMINT64 i64Num); RCODE FLMAPI setUINT( FLMUINT uiElementNumber, FLMUINT uiNum); RCODE FLMAPI setUINT64( FLMUINT uiElementNumber, FLMUINT64 ui64Num); RCODE FLMAPI setUnicode( FLMUINT uiElementNumber, const FLMUNICODE * puzUnicode); RCODE FLMAPI setUTF8( FLMUINT uiElementNumber, const FLMBYTE * pszUtf8, FLMUINT uiBytesInBuffer = 0); FINLINE RCODE FLMAPI setBinary( FLMUINT uiElementNumber, const void * pvBinary, FLMUINT uiBinaryLen) { return( storeValue( uiElementNumber, XFLM_BINARY_TYPE, (FLMBYTE *)pvBinary, uiBinaryLen)); } FINLINE void FLMAPI setRightTruncated( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { setRightTruncated( pVector); } else { // Need to set some data value before setting // truncated. flmAssert( 0); } } FINLINE void FLMAPI setLeftTruncated( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { setLeftTruncated( pVector); } else { // Need to set some data value before setting // truncated. flmAssert( 0); } } FINLINE void FLMAPI clearRightTruncated( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { clearRightTruncated( pVector); } else { // Need to set some data value before clearing // truncated. flmAssert( 0); } } FINLINE void FLMAPI clearLeftTruncated( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { clearLeftTruncated( pVector); } else { // Need to set some data value before clearing // truncated. flmAssert( 0); } } FINLINE FLMBOOL FLMAPI isRightTruncated( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { return( isRightTruncated( pVector)); } return( FALSE); } FINLINE FLMBOOL FLMAPI isLeftTruncated( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { return( isLeftTruncated( pVector)); } return( FALSE); } // Getter methods FINLINE FLMUINT64 FLMAPI getDocumentID( void) { return( m_ui64DocumentID); } FINLINE FLMUINT64 FLMAPI getID( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_ID)) == NULL) { return( 0); } else { return( pVector->ui64ID); } } FINLINE FLMUINT FLMAPI getNameId( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) { return( 0); } else { return( pVector->uiNameId); } } FINLINE FLMBOOL FLMAPI isAttr( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) { return( FALSE); } else { return( (pVector->uiFlags & VECT_SLOT_IS_ATTR) ? TRUE : FALSE); } } FINLINE FLMBOOL FLMAPI isDataComponent( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) { return( FALSE); } else { return( (pVector->uiFlags & VECT_SLOT_IS_DATA) ? TRUE : FALSE); } } FINLINE FLMBOOL FLMAPI isKeyComponent( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) { return( FALSE); } else { return( (pVector->uiFlags & VECT_SLOT_IS_DATA) ? FALSE : TRUE); } } FLMUINT FLMAPI getDataLength( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) == NULL) { return( 0); } else { return( pVector->uiDataLength); } } FLMUINT FLMAPI getDataType( FLMUINT uiElementNumber) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) == NULL) { return( XFLM_UNKNOWN_TYPE); } else { return( pVector->uiDataType); } } RCODE FLMAPI getUTF8Ptr( FLMUINT uiElementNumber, const FLMBYTE ** ppszUTF8, FLMUINT * puiBufLen); FINLINE RCODE FLMAPI getINT( FLMUINT uiElementNumber, FLMINT * piNum) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2Number( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), NULL, piNum) : RC_SET( NE_XFLM_NOT_FOUND))); } FINLINE RCODE FLMAPI getINT64( FLMUINT uiElementNumber, FLMINT64 * pi64Num) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2Number64( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), NULL, pi64Num) : RC_SET( NE_XFLM_NOT_FOUND))); } FINLINE RCODE FLMAPI getUINT( FLMUINT uiElementNumber, FLMUINT * puiNum) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2Number( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), puiNum, NULL) : RC_SET( NE_XFLM_NOT_FOUND))); } FINLINE RCODE FLMAPI getUINT64( FLMUINT uiElementNumber, FLMUINT64 * pui64Num) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2Number64( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), pui64Num, NULL) : RC_SET( NE_XFLM_NOT_FOUND))); } RCODE FLMAPI getUnicode( FLMUINT uiElementNumber, FLMUNICODE ** ppuzUnicode); FINLINE RCODE FLMAPI getUnicode( FLMUINT uiElementNumber, F_DynaBuf * pBuffer) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2Unicode( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), pBuffer) : RC_SET( NE_XFLM_NOT_FOUND))); } FINLINE RCODE FLMAPI getUnicode( FLMUINT uiElementNumber, FLMUNICODE * puzUnicode, FLMUINT * puiBufLen) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2Unicode( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), puiBufLen, puzUnicode) : RC_SET( NE_XFLM_NOT_FOUND))); } FINLINE RCODE FLMAPI getUTF8( FLMUINT uiElementNumber, FLMBYTE * pszUTF8, FLMUINT * puiBufLen) { F_VECTOR_ELEMENT * pVector; return( (RCODE)((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL ? flmStorage2UTF8( pVector->uiDataType, pVector->uiDataLength, (FLMBYTE *)getDataPtr( pVector), puiBufLen, pszUTF8) : RC_SET( NE_XFLM_NOT_FOUND))); } FINLINE RCODE FLMAPI getBinary( FLMUINT uiElementNumber, void * pvBuffer, FLMUINT * puiLength) { F_VECTOR_ELEMENT * pVector; if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { *puiLength = f_min( (*puiLength), pVector->uiDataLength); if (pvBuffer && *puiLength) { f_memcpy( pvBuffer, getDataPtr( pVector), *puiLength); } return( NE_XFLM_OK); } else { *puiLength = 0; } return( RC_SET( NE_XFLM_NOT_FOUND)); } FINLINE RCODE FLMAPI getBinary( FLMUINT uiElementNumber, F_DynaBuf * pBuffer) { F_VECTOR_ELEMENT * pVector; pBuffer->truncateData( 0); if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) { return( pBuffer->appendData( getDataPtr( pVector), pVector->uiDataLength)); } return( RC_SET( NE_XFLM_NOT_FOUND)); } RCODE FLMAPI outputKey( IF_Db * pDb, FLMUINT uiIndexNum, FLMUINT uiMatchFlags, FLMBYTE * pucKeyBuf, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen); RCODE FLMAPI outputData( IF_Db * pDb, FLMUINT uiIndexNum, FLMBYTE * pucDataBuf, FLMUINT uiDataBufSize, FLMUINT * puiDataLen); RCODE FLMAPI inputKey( IF_Db * pDb, FLMUINT uiIndexNum, const FLMBYTE * pucKey, FLMUINT uiKeyLen); RCODE FLMAPI inputData( IF_Db * pDb, FLMUINT uiIndexNum, const FLMBYTE * pucData, FLMUINT uiDataLen); // Miscellaneous methods void FLMAPI reset( void); FINLINE const void * FLMAPI getDataPtr( FLMUINT uiElementNumber) { return( getDataPtr( getVector( uiElementNumber, VECT_SLOT_HAS_DATA))); } private: RCODE allocVectorArray( FLMUINT uiElementNumber); RCODE storeValue( FLMINT uiElementNumber, FLMUINT uiDataType, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBYTE ** ppucDataPtr = NULL); FINLINE F_VECTOR_ELEMENT * getVector( FLMUINT uiElementNumber, FLMUINT uiTestFlags) { F_VECTOR_ELEMENT * pVector; if (uiElementNumber >= m_uiNumElements) { return( NULL); } pVector = &m_pVectorElements [uiElementNumber]; if (!(pVector->uiFlags & uiTestFlags)) { return( NULL); } else { return( pVector); } } FINLINE FLMBOOL isRightTruncated( F_VECTOR_ELEMENT * pVector) { return( (pVector->uiFlags & VECT_SLOT_RIGHT_TRUNCATED) ? TRUE : FALSE); } FINLINE void setRightTruncated( F_VECTOR_ELEMENT * pVector) { pVector->uiFlags |= VECT_SLOT_RIGHT_TRUNCATED; } FINLINE void clearRightTruncated( F_VECTOR_ELEMENT * pVector) { pVector->uiFlags &= (~(VECT_SLOT_RIGHT_TRUNCATED)); } FINLINE FLMBOOL isLeftTruncated( F_VECTOR_ELEMENT * pVector) { return( (pVector->uiFlags & VECT_SLOT_LEFT_TRUNCATED) ? TRUE : FALSE); } FINLINE void setLeftTruncated( F_VECTOR_ELEMENT * pVector) { pVector->uiFlags |= VECT_SLOT_LEFT_TRUNCATED; } FINLINE void clearLeftTruncated( F_VECTOR_ELEMENT * pVector) { pVector->uiFlags &= (~(VECT_SLOT_LEFT_TRUNCATED)); } FINLINE void * getDataPtr( F_VECTOR_ELEMENT * pVector) { if (!pVector || !pVector->uiDataLength) { return( NULL); } else if (pVector->uiDataLength <= sizeof( FLMUINT)) { return( (void *)&pVector->uiDataOffset); } else { return( (void *)(m_pucDataBuf + pVector->uiDataOffset)); } } RCODE outputKey( IXD * pIxd, FLMUINT uiMatchFlags, FLMBYTE * pucKeyBuf, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT uiSearchKeyFlag); RCODE outputData( IXD * pIxd, FLMBYTE * pucDataBuf, FLMUINT uiDataBufSize, FLMUINT * puiDataLen); RCODE inputKey( IXD * pIxd, const FLMBYTE * pucKey, FLMUINT uiKeyLen); RCODE inputData( IXD * pIxd, const FLMBYTE * pucData, FLMUINT uiDataLen); #define MIN_VECTOR_ELEMENTS 6 F_VECTOR_ELEMENT m_VectorArray [MIN_VECTOR_ELEMENTS]; F_VECTOR_ELEMENT * m_pVectorElements; // Pointer to vector elements FLMUINT m_uiVectorArraySize; // Size of vector array FLMUINT m_uiNumElements; // Number of elements actually // populated in the array. FLMBYTE m_ucIntDataBuf[ 32]; // Internal data buffer FLMBYTE * m_pucDataBuf; // Values stored here FLMUINT m_uiDataBufLength; // Bytes of data allocated FLMUINT m_uiDataBufOffset; // Current offset into allocated // data buffer. FLMUINT64 m_ui64DocumentID; // Document ID; friend class F_Db; friend class FSIndexCursor; friend class FSCollectionCursor; friend class F_QueryResultSet; }; // Flags for the m_uiFlags member of the F_Db object #define FDB_UPDATED_DICTIONARY 0x0001 // Flag indicating whether the file's // local dictionary was updated // during the transaction. #define FDB_DO_TRUNCATE 0x0002 // Truncate log extents at the end // of a transaction. #define FDB_HAS_FILE_LOCK 0x0004 // FDB has a file lock. #define FDB_FILE_LOCK_SHARED 0x0008 // File lock is shared. Update // transactions are not allowed when // the lock is shared. #define FDB_FILE_LOCK_IMPLICIT 0x0010 // File lock is implicit - means file // lock was obtained when the update // transaction began and cannot be // released by a call to FlmDbUnlock. #define FDB_DONT_KILL_TRANS 0x0020 // Do not attempt to kill an active // read transaction on this database // handle. This is used by FlmDbBackup. #define FDB_INTERNAL_OPEN 0x0040 // FDB is an internal one used by a // background thread. #define FDB_DONT_POISON_CACHE 0x0080 // If blocks are read from disk during // a transaction, release them at the LRU // end of the cache chain. #define FDB_UPGRADING 0x0100 // Database is being upgraded. #define FDB_REPLAYING_RFL 0x0200 // Database is being recovered #define FDB_REPLAYING_COMMIT 0x0400 // During replay of the RFL, this // is an actual call to FlmDbTransCommit. #define FDB_BACKGROUND_INDEXING 0x0800 // FDB is being used by a background indexing // thread #define FDB_HAS_WRITE_LOCK 0x1000 // FDB has the write lock #define FDB_REBUILDING_DATABASE 0x2000 // Database is being rebuilt #define FDB_SWEEP_SCHEDULED 0x4000 // Sweep operation scheduled due to a // dictionary change during the transaction /***************************************************************************** Desc: Class for performing database backup. *****************************************************************************/ class F_Backup : public IF_Backup { public: F_Backup(); virtual ~F_Backup(); FINLINE FLMUINT64 FLMAPI getBackupTransId( void) { return( m_ui64TransId); } FINLINE FLMUINT64 FLMAPI getLastBackupTransId( void) { return( m_ui64LastBackupTransId); } RCODE FLMAPI backup( const char * pszBackupPath, const char * pszPassword, IF_BackupClient * pClient, IF_BackupStatus * pStatus, FLMUINT * puiIncSeqNum); RCODE FLMAPI endBackup( void); private: void reset( void); F_Db * m_pDb; eDbTransType m_eTransType; FLMUINT64 m_ui64TransId; FLMUINT64 m_ui64LastBackupTransId; FLMUINT m_uiDbVersion; FLMUINT m_uiBlkChgSinceLastBackup; FLMBOOL m_bTransStarted; FLMUINT m_uiBlockSize; FLMUINT m_uiLogicalEOF; FLMUINT m_uiFirstReqRfl; FLMUINT m_uiIncSeqNum; FLMBOOL m_bCompletedBackup; eDbBackupType m_eBackupType; RCODE m_backupRc; FLMBYTE m_ucNextIncSerialNum[ XFLM_SERIAL_NUM_SIZE]; char m_szDbPath[ F_PATH_MAX_SIZE]; XFLM_DB_HDR m_dbHdr; friend class F_Db; }; /***************************************************************************** Desc: An implementation of IF_Backup_Client that backs up to the local hard disk. *****************************************************************************/ class F_DefaultBackupClient : public IF_BackupClient { public: F_DefaultBackupClient( const char * pszBackupPath); virtual ~F_DefaultBackupClient(); RCODE FLMAPI WriteData( const void * pvBuffer, FLMUINT uiBytesToWrite); virtual FLMINT FLMAPI getRefCount( void) { return( IF_BackupClient::getRefCount()); } virtual FLMINT FLMAPI AddRef( void) { return( IF_BackupClient::AddRef()); } virtual FLMINT FLMAPI Release( void) { return( IF_BackupClient::Release()); } private: char m_szPath[ F_PATH_MAX_SIZE]; IF_MultiFileHdl * m_pMultiFileHdl; FLMUINT64 m_ui64Offset; RCODE m_rc; }; /***************************************************************************** Desc: The F_FSRestore class is used to read backup and RFL files from a disk file system. *****************************************************************************/ class F_FSRestore : public IF_RestoreClient { public: virtual ~F_FSRestore(); F_FSRestore(); RCODE setup( const char * pszDbPath, const char * pszBackupSetPath, const char * pszRflDir); RCODE FLMAPI openBackupSet( void); RCODE FLMAPI openIncFile( FLMUINT uiFileNum); RCODE FLMAPI openRflFile( FLMUINT uiFileNum); RCODE FLMAPI read( FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesRead); RCODE FLMAPI close( void); RCODE FLMAPI abortFile( void); virtual FLMINT FLMAPI getRefCount( void) { return( IF_RestoreClient::getRefCount()); } virtual FLMINT FLMAPI AddRef( void) { return( IF_RestoreClient::AddRef()); } virtual FLMINT FLMAPI Release( void) { return( IF_RestoreClient::Release()); } protected: IF_FileHdl * m_pFileHdl; IF_MultiFileHdl * m_pMultiFileHdl; FLMUINT64 m_ui64Offset; FLMUINT m_uiDbVersion; char m_szDbPath[ F_PATH_MAX_SIZE]; char m_szBackupSetPath[ F_PATH_MAX_SIZE]; char m_szRflDir[ F_PATH_MAX_SIZE]; FLMBOOL m_bSetupCalled; FLMBOOL m_bOpen; }; /***************************************************************************** Desc: Default implementation of a restore status object than can be inherited by a user implementation. *****************************************************************************/ class F_DefaultRestoreStatus : public IF_RestoreStatus { public: F_DefaultRestoreStatus() { } RCODE FLMAPI reportProgress( eRestoreAction * peAction, FLMUINT64, // ui64BytesToDo, FLMUINT64) // ui64BytesDone { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportError( eRestoreAction * peAction, RCODE) // rcErr { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportBeginTrans( eRestoreAction * peAction, FLMUINT64) // ui64TransId { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportCommitTrans( eRestoreAction * peAction, FLMUINT64) // ui64TransId { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportAbortTrans( eRestoreAction * peAction, FLMUINT64) // ui64TransId { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportRemoveData( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiLfNum, FLMUINT, // uiKeyLen, FLMBYTE *) // pucKey { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportInsertData( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiLfNum, FLMUINT, // uiKeyLen, FLMBYTE *) // pucKey { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportReplaceData( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiLfNum, FLMUINT, // uiKeyLen, FLMBYTE *) // pucKey { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportLFileCreate( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT) // uiLfNum { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportLFileUpdate( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiLfNum, FLMUINT, // uiRootBlk, FLMUINT64, // ui64NextNodeId, FLMUINT64, // ui64FirstDocId, FLMUINT64 // ui64LastDocId ) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportUpdateDict( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiDictType, FLMUINT, // uiDictNum, FLMBOOL) // bDeleting { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportIndexSuspend( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT) // uiIndexNum { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportIndexResume( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT) // uiIndexNum { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportReduce( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT) // uiCount { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportUpgrade( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiOldDbVersion, FLMUINT) // uiNewDbVersion { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportEnableEncryption( eRestoreAction * peAction, FLMUINT64 // ui64TransId ) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportWrapKey( eRestoreAction * peAction, FLMUINT64) // ui64TransId { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportOpenRflFile( eRestoreAction * peAction, FLMUINT) // uiFileNum { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportRflRead( eRestoreAction * peAction, FLMUINT, // uiFileNum, FLMUINT) // uiBytesRead { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } virtual FLMINT FLMAPI getRefCount( void) { return( IF_RestoreStatus::getRefCount()); } virtual FLMINT FLMAPI AddRef( void) { return( IF_RestoreStatus::AddRef()); } virtual FLMINT FLMAPI Release( void) { return( IF_RestoreStatus::Release()); } }; // Indexing actions typedef enum IxActionTag { IX_UNLINK_NODE = 0, IX_LINK_NODE, IX_DEL_NODE_VALUE, IX_ADD_NODE_VALUE, IX_LINK_AND_ADD_NODE } IxAction; typedef struct ElmAttrStateInfo { FLMUINT uiDictType; FLMUINT uiDictNum; FLMUINT uiState; FLMUINT64 ui64StateChangeCount; } ELM_ATTR_STATE_INFO; typedef struct KEY_GEN_INFO { FLMUINT64 ui64DocumentID; IXD * pIxd; FLMBOOL bIsAsia; FLMBOOL bIsCompound; CDL_HDR * pCdlTbl; FLMBOOL bUseSubtreeNodes; FLMBOOL bAddKeys; FLMBYTE * pucKeyBuf; FLMBYTE * pucData; FLMUINT uiDataBufSize; FLMBOOL bDataBufAllocated; } KEY_GEN_INFO; typedef struct OLD_NODE_DATA { eDomNodeType eNodeType; FLMUINT uiCollection; FLMUINT64 ui64NodeId; FLMUINT uiNameId; FLMBYTE * pucData; FLMUINT uiDataLen; } OLD_NODE_DATA; /***************************************************************************** Desc: *****************************************************************************/ class F_OldNodeList : public F_Object { public: F_OldNodeList() { m_pNodeList = NULL; m_uiListSize = 0; m_uiNodeCount = 0; m_pool.poolInit( 512); } ~F_OldNodeList(); RCODE setup( void); FLMBOOL findNodeInList( eDomNodeType eNodeType, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiNameId, FLMBYTE ** ppucData, FLMUINT * puiDataLen, FLMUINT * puiInsertPos); RCODE addNodeToList( F_Db * pDb, F_DOMNode * pNode); void resetList( void); FINLINE FLMUINT getNodeCount( void) { return( m_uiNodeCount); } private: OLD_NODE_DATA * m_pNodeList; F_Pool m_pool; FLMUINT m_uiListSize; FLMUINT m_uiNodeCount; }; /***************************************************************************** Desc: *****************************************************************************/ class F_Db : public IF_Db { public: F_Db( FLMBOOL bInternalOpen); virtual ~F_Db(); RCODE FLMAPI transBegin( eDbTransType eTransType, FLMUINT uiMaxLockWait = FLM_NO_TIMEOUT, FLMUINT uiFlags = 0, XFLM_DB_HDR * pDbHeader = NULL); RCODE FLMAPI transBegin( IF_Db * pDb); RCODE FLMAPI transCommit( FLMBOOL * pbEmpty = NULL); RCODE FLMAPI transAbort( void); FINLINE eDbTransType FLMAPI getTransType( void) { return( m_eTransType); } RCODE FLMAPI doCheckpoint( FLMUINT uiTimeout); RCODE FLMAPI dbLock( eLockType lockType, FLMINT iPriority, FLMUINT uiTimeout); RCODE FLMAPI dbUnlock( void); RCODE FLMAPI getLockType( eLockType * pLockType, FLMBOOL * pbImplicit); RCODE FLMAPI getLockInfo( FLMINT iPriority, eLockType * pCurrLockType, FLMUINT * puiThreadId, FLMUINT * puiNumExclQueued, FLMUINT * puiNumSharedQueued, FLMUINT * puiPriorityCount); RCODE dupTrans( FLMUINT64 ui64TransId); RCODE demoteTrans( void); RCODE cancelTrans( FLMUINT64 ui64TransId); RCODE getCommitCnt( FLMUINT64 * pui64CommitCount); // Index methods RCODE FLMAPI indexStatus( FLMUINT uiIndexNum, XFLM_INDEX_STATUS * pIndexStatus); RCODE FLMAPI indexGetNext( FLMUINT * puiIndexNum); RCODE FLMAPI indexSuspend( FLMUINT uiIndexNum); RCODE FLMAPI indexResume( FLMUINT uiIndexNum); // Retrieval Functions RCODE FLMAPI keyRetrieve( FLMUINT uiIndex, IF_DataVector * ifpSearchKey, FLMUINT uiFlags, IF_DataVector * ifpFoundKey); RCODE FLMAPI enableEncryption( void); RCODE FLMAPI wrapKey( const char * pszPassword = NULL); RCODE FLMAPI rollOverDbKey( void); RCODE FLMAPI changeItemState( FLMUINT uiDictType, FLMUINT uiDictNum, const char * pszState); RCODE FLMAPI reduceSize( FLMUINT uiCount, FLMUINT * puiCountRV); RCODE FLMAPI upgrade( IF_UpgradeClient * pUpgradeClient); RCODE FLMAPI createRootElement( FLMUINT uiCollection, FLMUINT uiNameId, IF_DOMNode ** ppElementNode, FLMUINT64 * pui64NodeId = NULL); RCODE FLMAPI createDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode, FLMUINT64 * pui64NodeId = NULL); RCODE FLMAPI getFirstDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode); RCODE FLMAPI getLastDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode); RCODE FLMAPI getDocument( FLMUINT uiCollection, FLMUINT uiFlags, FLMUINT64 ui64DocumentId, IF_DOMNode ** ppDocumentNode); RCODE FLMAPI documentDone( FLMUINT uiCollection, FLMUINT64 ui64RootId); RCODE FLMAPI documentDone( IF_DOMNode * pDocNode); FINLINE RCODE FLMAPI createElementDef( const char * pszNamespaceURI, const char * pszElementName, FLMUINT uiDataType, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) { return( createElemOrAttrDef( TRUE, FALSE, pszNamespaceURI, pszElementName, uiDataType, FALSE, puiElementNameId, (F_DOMNode **)ppDocumentNode)); } FINLINE RCODE FLMAPI createElementDef( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT uiDataType, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) { return( createElemOrAttrDef( TRUE, TRUE, puzNamespaceURI, puzElementName, uiDataType, FALSE, puiElementNameId, (F_DOMNode **)ppDocumentNode)); } FINLINE RCODE FLMAPI createUniqueElmDef( const char * pszNamespaceURI, const char * pszElementName, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) { return( createElemOrAttrDef( TRUE, FALSE, pszNamespaceURI, pszElementName, XFLM_NODATA_TYPE, TRUE, puiElementNameId, (F_DOMNode **)ppDocumentNode)); } FINLINE RCODE FLMAPI createUniqueElmDef( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) { return( createElemOrAttrDef( TRUE, TRUE, puzNamespaceURI, puzElementName, XFLM_NODATA_TYPE, TRUE, puiElementNameId, (F_DOMNode **)ppDocumentNode)); } RCODE FLMAPI getElementNameId( const char * pszNamespaceURI, const char * pszElementName, FLMUINT * puiElementNameId); RCODE FLMAPI getElementNameId( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT * puiElementNameId); FINLINE RCODE FLMAPI createAttributeDef( const char * pszNamespaceURI, const char * pszAttributeName, FLMUINT uiDataType, FLMUINT * puiAttributeNameId, IF_DOMNode ** ppDocumentNode = NULL) { return( createElemOrAttrDef( FALSE, FALSE, pszNamespaceURI, pszAttributeName, uiDataType, FALSE, puiAttributeNameId, (F_DOMNode **)ppDocumentNode)); } FINLINE RCODE FLMAPI createAttributeDef( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzAttributeName, FLMUINT uiDataType, FLMUINT * puiAttributeNameId, IF_DOMNode ** ppDocumentNode = NULL) { return( createElemOrAttrDef( FALSE, TRUE, puzNamespaceURI, puzAttributeName, uiDataType, FALSE, puiAttributeNameId, (F_DOMNode **)ppDocumentNode)); } RCODE FLMAPI getAttributeNameId( const char * pszNamespaceURI, const char * pszAttributeName, FLMUINT * puiAttributeNameId); RCODE FLMAPI getAttributeNameId( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzAttributeName, FLMUINT * puiAttributeNameId); FINLINE RCODE FLMAPI createPrefixDef( const char * pszPrefixName, FLMUINT * puiPrefixNumber) { return( createPrefixDef( FALSE, pszPrefixName, puiPrefixNumber)); } FINLINE RCODE FLMAPI createPrefixDef( const FLMUNICODE * puzPrefixName, FLMUINT * puiPrefixNumber) { return( createPrefixDef( TRUE, puzPrefixName, puiPrefixNumber)); } RCODE FLMAPI getPrefixId( const char * pszPrefixName, FLMUINT * puiPrefixNumber); RCODE FLMAPI getPrefixId( const FLMUNICODE * puzPrefixName, FLMUINT * puiPrefixNumber); FINLINE RCODE FLMAPI createEncDef( const char * pszEncType, const char * pszEncName, FLMUINT uiKeySize = 0, FLMUINT * puiEncDefNumber = NULL) { return( createEncDef( FALSE, pszEncType, pszEncName, uiKeySize, puiEncDefNumber)); } FINLINE RCODE FLMAPI createEncDef( const FLMUNICODE * puzEncType, const FLMUNICODE * puzEncName, FLMUINT uiKeySize = 0, FLMUINT * puiEncDefNumber = NULL) { return( createEncDef( TRUE, puzEncType, puzEncName, uiKeySize, puiEncDefNumber)); } RCODE FLMAPI getEncDefId( const char * pszEncDefName, FLMUINT * puiEncDefNumber); RCODE FLMAPI getEncDefId( const FLMUNICODE * puzEncDefName, FLMUINT * puiEncDefNumber); FINLINE RCODE FLMAPI createCollectionDef( const char * pszCollectionName, FLMUINT * puiCollectionNumber, FLMUINT uiEncNumber = 0) { return( createCollectionDef( FALSE, pszCollectionName, puiCollectionNumber, uiEncNumber)); } FINLINE RCODE FLMAPI createCollectionDef( const FLMUNICODE * puzCollectionName, FLMUINT * puiCollectionNumber, FLMUINT uiEncNumber = 0) { return( createCollectionDef( TRUE, puzCollectionName, puiCollectionNumber, uiEncNumber)); } RCODE FLMAPI getCollectionNumber( const char * pszCollectionName, FLMUINT * puiCollectionNumber); RCODE FLMAPI getCollectionNumber( const FLMUNICODE * puzCollectionName, FLMUINT * puiCollectionNumber); RCODE FLMAPI getIndexNumber( const char * pszIndexName, FLMUINT * puiIndexNumber); RCODE FLMAPI getIndexNumber( const FLMUNICODE * puzIndexName, FLMUINT * puiIndexNumber); RCODE FLMAPI getDictionaryDef( FLMUINT uiDictType, FLMUINT uiDictNumber, IF_DOMNode ** ppDocumentNode); RCODE FLMAPI getDictionaryName( FLMUINT uiDictType, FLMUINT uiDictNumber, char * pszName, FLMUINT * puiNameBufSize, char * pszNamespace = NULL, FLMUINT * puiNamespaceBufSize = NULL); RCODE FLMAPI getDictionaryName( FLMUINT uiDictType, FLMUINT uiDictNumber, FLMUNICODE * puzName, FLMUINT * puiNameBufSize, FLMUNICODE * puzNamespace = NULL, FLMUINT * puiNamespaceBufSize = NULL); RCODE FLMAPI getNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId, IF_DOMNode ** ifppNode) { return( getNode( uiCollection, ui64NodeId, XFLM_EXACT, (F_DOMNode **)ifppNode)); } FINLINE RCODE getNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_DOMNode ** ppNode) { return( getNode( uiCollection, ui64NodeId, XFLM_EXACT, ppNode)); } FINLINE RCODE getNextNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId, F_DOMNode ** ppNode) { return( getNode( uiCollection, ui64NodeId, XFLM_EXCL, ppNode)); } RCODE FLMAPI getAttribute( FLMUINT uiCollection, FLMUINT64 ui64ElementId, FLMUINT uiAttrName, IF_DOMNode ** ppNode); RCODE FLMAPI getDataType( FLMUINT uiDictType, FLMUINT uiNameId, FLMUINT * puiDataType); RCODE FLMAPI backupBegin( eDbBackupType eBackupType, eDbTransType eTransType, FLMUINT uiMaxLockWait, IF_Backup ** ppBackup); void FLMAPI getRflFileName( FLMUINT uiFileNum, FLMBOOL bBaseOnly, char * pszFileName, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated = NULL); RCODE FLMAPI import( IF_IStream * pIStream, FLMUINT uiCollection, IF_DOMNode * pNodeToLinkTo = NULL, eNodeInsertLoc eInsertLoc = XFLM_LAST_CHILD, XFLM_IMPORT_STATS * pImportStats = NULL); RCODE FLMAPI importDocument( IF_IStream * ifpStream, FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode = NULL, XFLM_IMPORT_STATS * pImportStats = NULL); RCODE FLMAPI exportXML( IF_DOMNode * pStartNode, IF_OStream * pOStream, eExportFormatType eFormat = XFLM_EXPORT_INDENT); RCODE FLMAPI setNextNodeId( FLMUINT uiCollection, FLMUINT64 ui64NextNodeId); RCODE FLMAPI setNextDictNum( FLMUINT uiDictType, FLMUINT uiDictNumber); // Configuration methods RCODE FLMAPI setRflKeepFilesFlag( FLMBOOL bKeep); RCODE FLMAPI getRflKeepFlag( FLMBOOL * pbKeep); RCODE FLMAPI setRflDir( const char * pszNewRflDir); void FLMAPI getRflDir( char * pszRflDir); RCODE FLMAPI getRflFileNum( FLMUINT * puiRflFileNum); RCODE FLMAPI getHighestNotUsedRflFileNum( FLMUINT * puiHighestNotUsedRflFileNum); RCODE FLMAPI setRflFileSizeLimits( FLMUINT uiMinRflSize, FLMUINT uiMaxRflSize); RCODE FLMAPI getRflFileSizeLimits( FLMUINT * puiRflMinFileSize, FLMUINT * puiRflMaxFileSize); RCODE FLMAPI rflRollToNextFile( void); RCODE FLMAPI setKeepAbortedTransInRflFlag( FLMBOOL bKeep); RCODE FLMAPI getKeepAbortedTransInRflFlag( FLMBOOL * pbKeep); RCODE FLMAPI setAutoTurnOffKeepRflFlag( FLMBOOL bAutoTurnOff); RCODE FLMAPI getAutoTurnOffKeepRflFlag( FLMBOOL * pbAutoTurnOff); FINLINE void FLMAPI setFileExtendSize( FLMUINT uiFileExtendSize) { m_pDatabase->m_uiFileExtendSize = uiFileExtendSize; } FINLINE FLMUINT FLMAPI getFileExtendSize( void) { return( m_pDatabase->m_uiFileExtendSize); } FINLINE void FLMAPI setAppData( void * pvAppData) { m_pvAppData = pvAppData; } FINLINE void * FLMAPI getAppData( void) { return( m_pvAppData); } FINLINE void FLMAPI setDeleteStatusObject( IF_DeleteStatus * pDeleteStatus) { if (m_pDeleteStatus) { m_pDeleteStatus->Release(); } if ((m_pDeleteStatus = pDeleteStatus) != NULL) { m_pDeleteStatus->AddRef(); } } FINLINE void FLMAPI setCommitClientObject( IF_CommitClient * pCommitClient) { if (m_pCommitClient) { m_pCommitClient->Release(); } m_pCommitClient = pCommitClient; if (m_pCommitClient) { m_pCommitClient->AddRef(); } } FINLINE void FLMAPI setIndexingClientObject( IF_IxClient * pIxClient) { if (m_pIxClient) { m_pIxClient->Release(); } m_pIxClient = pIxClient; if (m_pIxClient) { m_pIxClient->AddRef(); } } FINLINE void FLMAPI setIndexingStatusObject( IF_IxStatus * ifpIxStatus) { if (m_pIxStatus) { m_pIxStatus->Release(); } m_pIxStatus = ifpIxStatus; if (m_pIxStatus) { m_pIxStatus->AddRef(); } } // Configuration information getting methods FINLINE FLMUINT FLMAPI getDbVersion( void) { return( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion); } FINLINE FLMUINT FLMAPI getBlockSize( void) { return( m_pDatabase->m_uiBlockSize); } FINLINE FLMUINT FLMAPI getDefaultLanguage( void) { return( m_pDatabase->m_uiDefaultLanguage); } FINLINE FLMUINT64 FLMAPI getTransID( void) { if (m_eTransType != XFLM_NO_TRANS) { return( m_ui64CurrTransID); } else if (m_uiFlags & FDB_HAS_FILE_LOCK) { return( m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID); } return( 0); } void FLMAPI getCheckpointInfo( XFLM_CHECKPOINT_INFO * pCheckpointInfo); RCODE FLMAPI getDbControlFileName( char * pszControlFileName, FLMUINT uiControlFileBufSize) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen = f_strlen( m_pDatabase->m_pszDbPath); if (uiLen + 1 > uiControlFileBufSize) { uiLen = uiControlFileBufSize - 1; rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); } f_memcpy( pszControlFileName, m_pDatabase->m_pszDbPath, uiLen); pszControlFileName [uiLen] = 0; return( rc); } FINLINE FLMBOOL threadWaitingLock( void) { return( m_pDatabase->m_pDatabaseLockObj->getWaiterCount() ? TRUE : FALSE); } RCODE FLMAPI getLockWaiters( IF_LockInfoClient * pLockInfo); RCODE FLMAPI getLastBackupTransID( FLMUINT64 * pui64LastBackupTransID); RCODE FLMAPI getBlocksChangedSinceBackup( FLMUINT * puiBlocksChangedSinceBackup); RCODE FLMAPI getNextIncBackupSequenceNum( FLMUINT * puiNextIncBackupSequenceNum); void FLMAPI getSerialNumber( char * pucSerialNumber); RCODE FLMAPI getDiskSpaceUsage( FLMUINT64 * pui64DataSize, FLMUINT64 * pui64RollbackSize, FLMUINT64 * pui64RflSize); FINLINE RCODE FLMAPI getMustCloseRC( void) { return( m_pDatabase->m_rcMustClose); } FINLINE RCODE FLMAPI getAbortRC( void) { return( m_AbortRc); } FINLINE RCODE startTransaction( eDbTransType eReqTransType, FLMBOOL * pbStartedTrans) { RCODE rc; if( m_eTransType != XFLM_NO_TRANS) { return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP)); } if( !pbStartedTrans) { return( RC_SET( NE_XFLM_NO_TRANS_ACTIVE)); } if( RC_BAD( rc = transBegin( eReqTransType))) { return( rc); } *pbStartedTrans = TRUE; return( NE_XFLM_OK); } FINLINE RCODE checkTransaction( eDbTransType eReqTransType, FLMBOOL * pbStartedTrans) { if( m_AbortRc) { return( m_AbortRc); } else if( m_eTransType >= eReqTransType) { return( NE_XFLM_OK); } return( startTransaction( eReqTransType, pbStartedTrans)); } FINLINE void FLMAPI setMustAbortTrans( RCODE rc) { if( RC_BAD( rc) && RC_OK( m_AbortRc)) { m_AbortRc = rc; } } RCODE getDictionary( F_Dict ** ppDict); FINLINE RCODE checkState( const char * pszFileName, FLMINT iLineNumber) { RCODE rc = NE_XFLM_OK; if (m_bMustClose) { m_pDatabase->logMustCloseReason( pszFileName, iLineNumber); rc = RC_SET( NE_XFLM_MUST_CLOSE_DATABASE); } return( rc); } RCODE getElmAttrInfo( FLMUINT uiType, FLMUINT64 ui64DocumentID, F_AttrElmInfo * pDefInfo, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE getCollectionDef( FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzCollectionName, FLMUINT * puiCollectionNumber, FLMUINT * puiEncId); RCODE getPrefixDef( F_Dict * pDict, FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzPrefixName, FLMUINT * puiPrefixNumber); RCODE getEncDefDef( F_Dict * pDict, FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzEncDefName, FLMUINT * puiEncDefNumber, FLMUINT * puiEncDefKeySize, IF_CCS ** ppCcs); RCODE getIndexDef( FLMUINT64 ui64DocumentID, FLMUNICODE ** ppuzIndexName, FLMUINT * puiIndexNumber, FLMUINT * puiCollectionNumber, FLMUINT * puiLanguage, FLMUINT * puiFlags, FLMUINT64 * pui64LastDocIndexed, FLMUINT * puiEncId, F_DOMNode ** ppNode, FLMBOOL bOpeningDict, FLMBOOL bDeleting); RCODE getIndexComponentDef( F_Dict * pDict, F_DOMNode * pElementNode, FLMUINT uiElementId, IXD * pIxd, ICD * pIcd); RCODE getNameTable( F_NameTable ** ppNameTable); FINLINE F_Database * getDatabase( void) { return m_pDatabase; } RCODE backgroundIndexBuild( IF_Thread * pThread, FLMBOOL * pbShutdown, FLMINT * piErrorLine); FINLINE FLMUINT getLogicalEOF( void) { return( m_uiLogicalEOF); } // Key Collector object, used when checking indexes FINLINE void setKeyCollector( F_KeyCollector * pKeyColl) { m_pKeyColl = pKeyColl; } FINLINE F_KeyCollector * getKeyCollector( void) { return m_pKeyColl; } RCODE waitForMaintenanceToComplete( void); FINLINE F_Dict * getDict( void) { return( m_pDict); } FINLINE F_OldNodeList * getOldNodeList( void) { return( m_pOldNodeList); } void removeCollectionNodes( FLMUINT uiCollection, FLMUINT64 ui64TransId); private: RCODE createElemOrAttrDef( FLMBOOL bElement, FLMBOOL bUnicode, const void * pvNamespaceURI, const void * pvLocalName, FLMUINT uiDataType, FLMBOOL bUniqueChildElms, FLMUINT * puiNameId, F_DOMNode ** ppRootNode); RCODE createPrefixDef( FLMBOOL bUnicode, const void * pvName, FLMUINT * puiPrefixNumber); RCODE createCollectionDef( FLMBOOL bUnicode, const void * pvName, FLMUINT * puiCollectionNumber, FLMUINT uiEncDefId); RCODE createEncDef( FLMBOOL bUnicode, const void * pvEncType, const void * pvEncName, FLMUINT uiKeySize, FLMUINT * puiEncDefId); // This routine assumes that the database mutex is locked FINLINE void linkToDict( F_Dict * pDict) { if (pDict != m_pDict) { if (m_pDict) { unlinkFromDict(); } if ((m_pDict = pDict) != NULL) { pDict->incrUseCount(); } } } // This routine assumes the database mutex is locked. FINLINE void unlinkFromDict( void) { if (m_pDict) { // If the use count goes to zero and the F_Dict is not the first one // in the file's list or it is not linked to a file, unlink the F_Dict // object from its database and delete it. if (!m_pDict->decrUseCount() && (m_pDict->getPrev() || !m_pDict->getDatabase())) { m_pDict->unlinkFromDatabase(); } m_pDict = NULL; } } RCODE linkToDatabase( F_Database * pDatabase); void unlinkFromDatabase(); RCODE initDbFiles( const char * pszRflDir, const char * pszDictFileName, const char * pszDictBuf, XFLM_CREATE_OPTS * pCreateOpts); RCODE beginBackgroundTrans( IF_Thread * pThread); RCODE beginTrans( eDbTransType eTransType, FLMUINT uiMaxLockWait = FLM_NO_TIMEOUT, FLMUINT uiFlags = 0, XFLM_DB_HDR * pDbHdr = NULL); RCODE beginTrans( F_Db * pDb); RCODE commitTrans( FLMUINT uiNewLogicalEOF, FLMBOOL bForceCheckpoint, FLMBOOL * pbEmpty = NULL); RCODE abortTrans( FLMBOOL bOkToLogAbort = TRUE); RCODE readRollbackLog( FLMUINT uiLogEOF, FLMUINT * puiCurrAddr, F_BLK_HDR * pBlkHdr, FLMBOOL * pbIsBeforeImageBlk); RCODE processBeforeImage( FLMUINT uiLogEOF, FLMUINT * puiCurrAddrRV, F_BLK_HDR * pBlkHdr, FLMBOOL bDoingRecovery, FLMUINT64 ui64MaxTransID); RCODE physRollback( FLMUINT uiLogEOF, FLMUINT uiFirstLogBlkAddr, FLMBOOL bDoingRecovery, FLMUINT64 ui64MaxTransID); void completeOpenOrCreate( RCODE rc, FLMBOOL bNewDatabase); RCODE startBackgroundIndexing( void); void unlinkFromTransList( FLMBOOL bCommitting); RCODE lockExclusive( FLMUINT uiMaxLockWait); void unlockExclusive( void); RCODE readDictionary( void); RCODE dictCreate( const char * pszDictPath, const char * pszDictBuf); RCODE dictOpen( void); RCODE dictReadLFH( void); RCODE dictReadDefs( FLMUINT uiDictType); RCODE dictClone( void); RCODE createNewDict( void); FINLINE void getDbHdrInfo( XFLM_DB_HDR * pDbHdr) { // IMPORTANT NOTE: Any changes to this method should also be // mirrored with changes to the other getDbHdrInfo call - see below. m_ui64CurrTransID = pDbHdr->ui64CurrTransID; m_uiLogicalEOF = (FLMUINT)pDbHdr->ui32LogicalEOF; // If we are doing a read transaction, this is only needed // if we are checking the database. m_uiFirstAvailBlkAddr = (FLMUINT)pDbHdr->ui32FirstAvailBlkAddr; } FINLINE void getDbHdrInfo( F_Db * pDb) { m_ui64CurrTransID = pDb->m_ui64CurrTransID; m_uiLogicalEOF = pDb->m_uiLogicalEOF; // If we are doing a read transaction, this is only needed // if we are checking the database. m_uiFirstAvailBlkAddr = pDb->m_uiFirstAvailBlkAddr; } FINLINE FLMBOOL okToCommitTrans( void) { return( m_eTransType == XFLM_READ_TRANS || m_AbortRc == NE_XFLM_OK ? TRUE : FALSE); } RCODE processDupKeys( IXD * pIxd); RCODE keysCommit( FLMBOOL bCommittingTrans, FLMBOOL bSortKeys = TRUE); RCODE refUpdate( LFILE * pLFile, IXD * pIxd, KREF_ENTRY * pKrefEntry, FLMBOOL bNormalUpdate); FINLINE RCODE flushKeys( void) { RCODE rc = NE_XFLM_OK; if( m_bKrefSetup) { if( m_uiKrefCount) { if (RC_BAD( rc = keysCommit( FALSE))) { goto Exit; } } m_pKrefReset = m_pKrefPool->poolMark(); } Exit: return( rc); } RCODE krefCntrlCheck( void); void krefCntrlFree( void); FINLINE FLMBOOL isKrefOverThreshold( void) { if( (((m_pKrefPool->getBlockSize() * 3) - 250) <= m_uiTotalKrefBytes) || m_uiKrefCount > (m_uiKrefTblSize - 128)) { return( TRUE); } return( FALSE); } RCODE addToKrefTbl( FLMUINT uiKeyLen, FLMUINT uiDataLen); RCODE verifyKeyContext( FLMBOOL * pbVerified); RCODE buildContext( ICD * pIcd, FLMUINT uiKeyLen, FLMUINT uiDataLen); RCODE buildData( ICD * pIcd, FLMUINT uiKeyLen, FLMUINT uiDataLen); RCODE finishKeyComponent( ICD * pIcd, FLMUINT uiKeyLen); RCODE genTextKeyComponents( F_DOMNode * pNode, ICD * pIcd, FLMUINT uiKeyLen, FLMBYTE ** ppucTmpBuf, FLMUINT * puiTmpBufSize, void ** ppvMark); RCODE genOtherKeyComponent( F_DOMNode * pNode, ICD * pIcd, FLMUINT uiKeyLen); RCODE buildKeys( ICD * pIcd, FLMUINT uiKeyLen); RCODE buildKeys( FLMUINT64 ui64DocumentID, IXD * pIxd, CDL_HDR * pCdlTbl, FLMBOOL bUseSubtreeNodes, FLMBOOL bAddKeys); RCODE genIndexKeys( FLMUINT64 ui64DocumentID, F_DOMNode * pNode, IXD * pIxd, ICD * pIcd, IxAction eAction); RCODE updateIndexKeys( FLMUINT uiCollectionNum, F_DOMNode * pNode, IxAction eAction, FLMBOOL bStartOfUpdate, FLMBOOL * pbIsIndexed = NULL); RCODE attrIsInIndexDef( FLMUINT uiAttrNameId, FLMBOOL * pbIsInIndexDef); void indexingAfterCommit( void); void indexingAfterAbort( void); RCODE addToStopList( FLMUINT uiIndexNum); RCODE addToStartList( FLMUINT uiIndexNum); void stopBackgroundIndexThread( FLMUINT uiIndexNum, FLMBOOL bWait, FLMBOOL * pbStopped); RCODE startIndexBuild( FLMUINT uiIndexNum); RCODE checkDictDefInfo( FLMUINT64 ui64DocumentID, FLMBOOL bDeleting, FLMUINT * puiDictType, FLMUINT * puiDictNumber); RCODE dictDocumentDone( FLMUINT64 ui64DocumentID, FLMBOOL bDeleting, FLMUINT * puiDictDefType); RCODE outputContextKeys( FLMUINT64 ui64DocumentId, IXD * pIxd, IX_CONTEXT * pIxContext, IX_CONTEXT ** ppIxContextList); RCODE removeCdls( FLMUINT64 ui64DocumentId, IXD * pIxd, IX_CONTEXT * pIxContext, ICD * pRefIcd); RCODE indexDocument( IXD * pIxd, F_DOMNode * pDocNode); RCODE indexSetOfDocuments( FLMUINT uiIndexNum, FLMUINT64 ui64StartDocumentId, FLMUINT64 ui64EndDocumentId, IF_IxStatus * ifpIxStatus, IF_IxClient * ifpIxClient, XFLM_INDEX_STATUS * pIndexStatus, FLMBOOL * pbHitEnd, IF_Thread * pThread = NULL); RCODE setIxStateInfo( FLMUINT uiIndexNum, FLMUINT64 ui64LastDocumentIndexed, FLMUINT uiState); RCODE buildIndex( FLMUINT uiIndexNum, FLMUINT uiState); RCODE readBlkHdr( FLMUINT uiBlkAddress, F_BLK_HDR * pBlkHdr, FLMINT * piType); XFLM_LFILE_STATS * getLFileStatPtr( LFILE * pLFile); RCODE checkAndUpdateState( eDomNodeType eNodeType, FLMUINT uiNameId); #define FLM_UPD_ADD 0x00001 #define FLM_UPD_INTERNAL_CHANGE 0x00004 RCODE findNode( FLMUINT uiCollection, FLMUINT64 * pui64NodeId, FLMUINT uiFlags); RCODE getNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiFlags, F_DOMNode ** ppNode); RCODE _updateNode( F_CachedNode * pNode, FLMUINT uiFlags); FINLINE RCODE updateNode( F_CachedNode * pCachedNode, FLMUINT uiFlags) { flmAssert( !m_pDatabase->m_pRfl->isLoggingEnabled()); if( uiFlags || pCachedNode->getCollection() == XFLM_DICT_COLLECTION) { return( _updateNode( pCachedNode, uiFlags)); } if( !pCachedNode->nodeIsDirty()) { pCachedNode->setNodeDirty( this, FALSE); } return( NE_XFLM_OK); } RCODE getCachedBTree( FLMUINT uiCollection, F_Btree ** ppBTree); RCODE flushNode( F_Btree * pBTree, F_CachedNode * pNode); RCODE purgeNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId); RCODE allocNode( eDomNodeType eNodeType, F_DOMNode ** ppNode); RCODE createRootNode( FLMUINT uiCollection, FLMUINT uiElementNameId, eDomNodeType eNodeType, F_DOMNode ** ppNewNode, FLMUINT64 * pui64NodeId = NULL); RCODE sweep( IF_Thread * pThread); RCODE sweepGatherList( ELM_ATTR_STATE_INFO ** ppStateTbl, FLMUINT * puiNumItems); RCODE sweepCheckElementState( F_DOMNode * pElementNode, ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT * puiNumItems, FLMBOOL * pbStartedTrans); RCODE sweepCheckAttributeStates( F_DOMNode * pElementNode, ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT * puiNumItems, FLMBOOL * pbStartedTrans); RCODE sweepFinalizeStates( ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT uiNumItems, FLMBOOL * pbStartedTrans); RCODE flushDirtyNodes( void); RCODE flushDirtyNode( F_CachedNode * pNode); RCODE maintBlockChainFree( FLMUINT64 ui64MaintDocID, FLMUINT uiBlocksToDelete, FLMUINT uiExpectedEndAddr, FLMUINT * puiBlocksFreed); RCODE encryptData( FLMUINT uiEncDefId, FLMBYTE * pucIV, FLMBYTE * pucBuffer, FLMUINT uiBufferSize, FLMUINT uiDataLen, FLMUINT * puiEncryptedLength); RCODE decryptData( FLMUINT uiEncDefId, FLMBYTE * pucIV, void * pvInBuf, FLMUINT uiInLen, void * pvOutBuf, FLMUINT uiOutBufLen); RCODE createDbKey(); // Private data members. F_Database * m_pDatabase; // Pointer to F_Database object F_Dict * m_pDict; // Pointer to dictionary object F_Db * m_pNextForDatabase; // Next F_Db associated with F_Database // NOTE: gv_XFlmSysData.hShareMutex // must be locked to set this F_Db * m_pPrevForDatabase; // Prev F_Db associated with F_Database // NOTE: gv_XFlmSysData.hShareMutex // must be locked to set this void * m_pvAppData; // Application data that is used // to associate this F_Db with // an object in the application // space. FLMUINT m_uiThreadId; // Thread that started the current // transaction, if any. NOTE: // Only set on transaction begin. // Hence, if operations are performed // by multiple threads, within the // transaction, it will not necessarily // reflect the thread that is currently // using the F_Db. FLMBOOL m_bMustClose; // An error has occurred that requires // the application to stop using (close) // this FDB F_SuperFileHdl * m_pSFileHdl; // Pointer to the super file handle FLMUINT m_uiFlags; // Flags for this F_Db. // TRANSACTION STATE STUFF FLMUINT m_uiTransCount; // Transaction counter for the F_Db. // Incremented whenever a transaction // is started on this F_Db. Used so // that FLAIM can tell if an implicit // transaction it started is still in // effect. This should NOT be // confused with update transaction // IDs. eDbTransType m_eTransType; // Type of transaction RCODE m_AbortRc; // If not NE_XFLM_OK, transaction must be // aborted. FLMUINT64 m_ui64CurrTransID;// Current transaction ID. FLMUINT m_uiFirstAvailBlkAddr; // Address of first block in avail list FLMUINT m_uiLogicalEOF; // Current logical end of file. New // blocks are allocated at this address. FLMUINT m_uiUpgradeCPFileNum; FLMUINT m_uiUpgradeCPOffset; // RFL file number and offset to set // RFL to during an upgrade operation // that happens during a restore or // recovery. FLMUINT m_uiTransEOF; // Address of logical end of file // when the last transaction // committed. A block beyond this // point in the file is going to be // a new block and will not need to // be logged. KEY_GEN_INFO m_keyGenInfo; // Information for generating index // keys. F_TMSTAMP m_TransStartTime; // Transaction start time, for stats // KREF STUFF KREF_ENTRY ** m_pKrefTbl; // Pointer to KREF table, which is an array // of KREF_ENTRY * pointers. FLMUINT m_uiKrefTblSize; // KREF table size. FLMUINT m_uiKrefCount; // Number of entries in KREF table that // are currently used. FLMUINT m_uiTotalKrefBytes; // Total number of entries allocated // in the pool. FLMBYTE * m_pucKrefKeyBuf; // Pointer to temporary key buffer. FLMBOOL m_bKrefSetup; // True if the KRef table has been initialized. F_Pool * m_pKrefPool; // Memory pool to use FLMBOOL m_bReuseKrefPool; // Reuse pool instead of free it? FLMBOOL m_bKrefCompoundKey; // True if a compound key has been processed. void * m_pKrefReset; // Used to reset the Kref pool on // indexing failures F_Pool m_tmpKrefPool; // KREF pool to be used during // read transactions - only used when // checking indexes. // UPDATE TRANSACTION STUFF FLMBOOL m_bHadUpdOper; // Did this transaction have any // updates? FLMUINT m_uiBlkChangeCnt; // Number of times ScaLogPhysBlk has // been called during this transaction. // This is used by the cursor code to // know when it is necessary to // re-position in the B-Tree.0 IXD_FIXUP * m_pIxdFixups; // List of indexes whose IXD needs // to be restored to its prior // state if the transaction aborts // READ TRANSACTION STUFF F_Db * m_pNextReadTrans; // Next active read transaction for // this database. // NOTE: If uiKilledTime (see below) // is non-zero, then transaction is // in killed list. F_Db * m_pPrevReadTrans; // Previous active read transaction // for this database. // NOTE: If m_uiKilledTime (see below) // is non-zero, then transaction is // in killed list. FLMUINT m_uiInactiveTime; // If non-zero, this is the last time // the checkpoint thread marked this // transaction as inactive. If zero, // it means that the transaction is // active, or it has not been marked // by the checkpoint thread as // inactive. If it stays non-zero for // five or more minutes, it will be // killed. FLMUINT m_uiKilledTime; // Time transaction was killed, if // non-zero. // Misc. DB Info. FLMBOOL m_bItemStateUpdOk;// This variable is used to ensure // that FlmDbSweep / recovery are the // only ways that: // 1) an element or attribute's state // can be changed to 'unused' // 2) a 'purge' element or attribute // can be deleted F_Pool m_tempPool; // Temporary memory pool. It // is only used for the duration of // a FLAIM operation and then reset. // The first block in the pool is // retained between operations to // help performance. // Callback functions. IF_DeleteStatus * m_pDeleteStatus; // Handles status info coming back // from deleting a BTree IF_IxClient * m_pIxClient; // Indexing callback IF_IxStatus * m_pIxStatus; // Indexing status callback IF_CommitClient * m_pCommitClient; // Commit callback XFLM_STATS * m_pStats; XFLM_DB_STATS * m_pDbStats; // DB statistics pointer. XFLM_LFILE_STATS * m_pLFileStats; // LFILE statistics pointer. FLMUINT m_uiLFileAllocSeq;// Allocation sequence number for // LFILE statistics array so we // can tell if the array has been // reallocated and we need to reset // our pLFileStats pointer. XFLM_STATS m_Stats; // Statistics kept here until end // of transaction. FLMBOOL m_bStatsInitialized;// Has statistics structure been // initialized? F_BKGND_IX * m_pIxStartList; // Indexing threads to start at // the conclusion of the transaction. F_BKGND_IX * m_pIxStopList; // Indexing threads to stop at // the conclusion of the transaction. F_Btree * m_pCachedBTree; // BTree object used for node operations F_KeyCollector * m_pKeyColl; // Special purpose object used when checking // indexes in the F_DbCheck class. F_OldNodeList * m_pOldNodeList; // List of old truncated nodes to use // updating indexes. FLMUINT m_uiDirtyNodeCount; F_SEM m_hWaitSem; // Semaphore that is used when // waiting for reads to complete friend class F_Database; friend class F_Dict; friend class F_DbSystem; friend class F_Rfl; friend class F_Btree; friend class F_Backup; friend class F_DOMNode; friend class F_BTreeIStream; friend class F_DataVector; friend class F_NodeList; friend class F_XMLImport; friend class F_DbRebuild; friend class F_DbCheck; friend class F_Query; friend class FSIndexCursor; friend class FSCollectionCursor; friend class F_BtRSFactory; friend class F_BtResultSet; friend class F_CachedBlock; friend class F_CachedNode; friend class F_BlockCacheMgr; friend class F_NodeCacheMgr; friend class F_GlobalCacheMgr; friend class F_QueryResultSet; friend class F_BTreeInfo; friend class F_AttrItem; }; /**************************************************************************** Stuff for F_Query class ****************************************************************************/ #define FLM_FALSE 1 #define FLM_TRUE 2 #define FLM_UNK 4 FINLINE FLMBOOL isLegalOperator( eQueryOperators eOperator) { return( (eOperator >= XFLM_AND_OP && eOperator <= XFLM_RBRACKET_OP) ? TRUE : FALSE); } FINLINE FLMBOOL isLogicalOp( eQueryOperators eOperator) { return( (eOperator >= XFLM_AND_OP && eOperator <= XFLM_NOT_OP) ? TRUE : FALSE); } FINLINE FLMBOOL isCompareOp( eQueryOperators eOperator) { return( (eOperator >= XFLM_EQ_OP && eOperator <= XFLM_GE_OP) ? TRUE : FALSE); } FINLINE FLMBOOL isArithOp( eQueryOperators eOperator) { return( (eOperator >= XFLM_FIRST_ARITH_OP && eOperator <= XFLM_LAST_ARITH_OP) ? TRUE : FALSE); } FINLINE FLMBOOL isUnsigned( eValTypes eValType) { return( eValType == XFLM_UINT_VAL || eValType == XFLM_UINT64_VAL ? TRUE : FALSE); } FINLINE FLMBOOL isSigned( eValTypes eValType) { return( eValType == XFLM_INT_VAL || eValType == XFLM_INT64_VAL ? TRUE : FALSE); } FINLINE FLMBOOL is64BitVal( eValTypes eValType) { return( eValType == XFLM_UINT64_VAL || eValType == XFLM_INT64_VAL ? TRUE : FALSE); } FINLINE FLMBOOL isNativeNum( eValTypes eValType) { return( eValType == XFLM_UINT_VAL || eValType == XFLM_INT_VAL ? TRUE : FALSE); } FINLINE FLMBOOL isUniversal( FQNODE * pQNode) { return( pQNode->bNotted); } FINLINE FLMBOOL isExistential( FQNODE * pQNode) { return( !pQNode->bNotted); } FINLINE FLMBOOL isBoolNode( FQNODE * pQNode ) { return( (pQNode->eNodeType == FLM_VALUE_NODE && pQNode->currVal.eValType == XFLM_BOOL_VAL) ? TRUE : FALSE); } FINLINE FLMBOOL isSigned( FQVALUE * pValue) { if( pValue->eValType == XFLM_INT_VAL || pValue->eValType == XFLM_INT64_VAL) { return( TRUE); } return( FALSE); } FINLINE FLMBOOL isUnsigned( FQVALUE * pValue) { if( pValue->eValType == XFLM_UINT_VAL || pValue->eValType == XFLM_UINT64_VAL) { return( TRUE); } return( FALSE); } typedef struct ExprState * EXPR_STATE_p; typedef struct ExprState { FQNODE * pExpr; FQNODE * pCurOperatorNode; FQNODE * pLastNode; FLMUINT uiNestLevel; FLMBOOL bExpectingOperator; FLMBOOL bExpectingLParen; FQFUNCTION * pQFunction; XPATH_COMPONENT * pXPathComponent; FLMUINT uiNumExprNeeded; FLMUINT uiNumExpressions; EXPR_STATE_p pPrev; EXPR_STATE_p pNext; } EXPR_STATE; /***************************************************************************** Desc: Object for gathering node information. *****************************************************************************/ class F_NodeInfo : public IF_NodeInfo { public: F_NodeInfo() { clearNodeInfo(); } virtual ~F_NodeInfo() { } FINLINE void FLMAPI clearNodeInfo( void) { f_memset( &m_nodeInfo, 0, sizeof( m_nodeInfo)); m_ui64TotalNodes = 0; } RCODE FLMAPI addNodeInfo( IF_Db * pDb, IF_DOMNode * pNode, FLMBOOL bDoSubTree, FLMBOOL bDoSelf = TRUE); FINLINE FLMUINT64 FLMAPI getTotalNodeCount( void) { return( m_ui64TotalNodes); } FINLINE void FLMAPI getNodeInfo( XFLM_NODE_INFO * pNodeInfo) { f_memcpy( pNodeInfo, &m_nodeInfo, sizeof( m_nodeInfo)); } private: XFLM_NODE_INFO m_nodeInfo; FLMUINT64 m_ui64TotalNodes; }; typedef struct BTREE_INFO { FLMUINT uiLfNum; char * pszLfName; FLMUINT uiNumLevels; XFLM_BTREE_LEVEL_INFO levelInfo [MAX_LEVELS]; } BTREE_INFO; /***************************************************************************** Desc: Object for gathering B-Tree information. *****************************************************************************/ class F_BTreeInfo : public IF_BTreeInfo { public: F_BTreeInfo() { m_pIndexArray = NULL; m_uiIndexArraySize = 0; m_uiNumIndexes = 0; m_pCollectionArray = NULL; m_uiCollectionArraySize = 0; m_uiNumCollections = 0; m_pool.poolInit( 512); } virtual ~F_BTreeInfo() { if (m_pIndexArray) { f_free( &m_pIndexArray); } if (m_pCollectionArray) { f_free( &m_pCollectionArray); } m_pool.poolFree(); } FINLINE void FLMAPI clearBTreeInfo( void) { m_uiNumIndexes = 0; m_uiNumCollections = 0; } RCODE FLMAPI collectIndexInfo( IF_Db * pDb, FLMUINT uiIndexNum, IF_BTreeInfoStatus * pInfoStatus); RCODE FLMAPI collectCollectionInfo( IF_Db * pDb, FLMUINT uiCollectionNum, IF_BTreeInfoStatus * pInfoStatus); FINLINE FLMUINT FLMAPI getNumIndexes( void) { return( m_uiNumIndexes); } FINLINE FLMUINT FLMAPI getNumCollections( void) { return( m_uiNumCollections); } FINLINE FLMBOOL FLMAPI getIndexInfo( FLMUINT uiNthIndex, FLMUINT * puiIndexNum, char ** ppszIndexName, FLMUINT * puiNumLevels) { if (uiNthIndex < m_uiNumIndexes) { *puiIndexNum = m_pIndexArray [uiNthIndex].uiLfNum; *puiNumLevels = m_pIndexArray [uiNthIndex].uiNumLevels; *ppszIndexName = m_pIndexArray [uiNthIndex].pszLfName; return( TRUE); } else { *puiIndexNum = 0; *ppszIndexName = NULL; *puiNumLevels = 0; return( FALSE); } } FINLINE FLMBOOL FLMAPI getCollectionInfo( FLMUINT uiNthCollection, FLMUINT * puiCollectionNum, char ** ppszCollectionName, FLMUINT * puiNumLevels) { if (uiNthCollection < m_uiNumCollections) { *puiCollectionNum = m_pCollectionArray [uiNthCollection].uiLfNum; *puiNumLevels = m_pCollectionArray [uiNthCollection].uiNumLevels; *ppszCollectionName = m_pCollectionArray [uiNthCollection].pszLfName; return( TRUE); } else { *puiCollectionNum = 0; *puiNumLevels = 0; *ppszCollectionName = NULL; return( FALSE); } } FINLINE FLMBOOL FLMAPI getIndexLevelInfo( FLMUINT uiNthIndex, FLMUINT uiBTreeLevel, XFLM_BTREE_LEVEL_INFO * pLevelInfo) { if (uiNthIndex < m_uiNumIndexes && uiBTreeLevel < m_pIndexArray [uiNthIndex].uiNumLevels) { f_memcpy( pLevelInfo, &(m_pIndexArray [uiNthIndex].levelInfo [uiBTreeLevel]), sizeof( XFLM_BTREE_LEVEL_INFO)); return( TRUE); } else { return( FALSE); } } FINLINE FLMBOOL FLMAPI getCollectionLevelInfo( FLMUINT uiNthCollection, FLMUINT uiBTreeLevel, XFLM_BTREE_LEVEL_INFO * pLevelInfo) { if (uiNthCollection < m_uiNumCollections && uiBTreeLevel < m_pCollectionArray [uiNthCollection].uiNumLevels) { f_memcpy( pLevelInfo, &(m_pCollectionArray [uiNthCollection].levelInfo [uiBTreeLevel]), sizeof( XFLM_BTREE_LEVEL_INFO)); return( TRUE); } else { return( FALSE); } } private: RCODE collectBlockInfo( F_Db * pDb, LFILE * pLFile, BTREE_INFO * pBTreeInfo, F_BTREE_BLK_HDR * pBlkHdr, IXD * pIxd); RCODE collectBTreeInfo( F_Db * pDb, LFILE * pLFile, BTREE_INFO * pBTreeInfo, IXD * pIxd); FINLINE RCODE doCallback( void) { if (m_pInfoStatus) { return( m_pInfoStatus->infoStatus( m_uiCurrLfNum, m_bIsCollection, m_pszCurrLfName, m_uiCurrLevel, m_ui64CurrLfBlockCount, m_ui64CurrLevelBlockCount, m_ui64TotalBlockCount)); } else { return( NE_XFLM_OK); } } BTREE_INFO * m_pIndexArray; FLMUINT m_uiIndexArraySize; FLMUINT m_uiNumIndexes; BTREE_INFO * m_pCollectionArray; FLMUINT m_uiCollectionArraySize; FLMUINT m_uiNumCollections; F_Pool m_pool; // Items for the callback function. IF_BTreeInfoStatus * m_pInfoStatus; FLMUINT m_uiBlockSize; FLMUINT m_uiCurrLfNum; FLMBOOL m_bIsCollection; char * m_pszCurrLfName; FLMUINT m_uiCurrLevel; FLMUINT64 m_ui64CurrLfBlockCount; FLMUINT64 m_ui64CurrLevelBlockCount; FLMUINT64 m_ui64TotalBlockCount; }; RCODE ixKeyCompare( F_Db * pDb, IXD * pIxd, F_DataVector * pSearchKey, F_OldNodeList * pOldNodeList1, F_OldNodeList * pOldNodeList2, FLMBOOL bCompareDocId, FLMBOOL bCompareNodeIds, const void * pvKey1, FLMUINT uiKeyLen1, const void * pvKey2, FLMUINT uiKeyLen2, FLMINT * piCompare); /******************************************************************** Desc: Class for comparing two keys in an index. ********************************************************************/ class IXKeyCompare : public IF_ResultSetCompare { public: IXKeyCompare() { // m_pDb is used to sort truncated keys if necessary. // m_pIxd is used for comparison m_pDb = NULL; m_pIxd = NULL; m_pSearchKey = NULL; m_pOldNodeList = NULL; m_bCompareDocId = TRUE; m_bCompareNodeIds = TRUE; } virtual ~IXKeyCompare() { if (m_pOldNodeList) { m_pOldNodeList->Release(); } } FINLINE RCODE FLMAPI compare( const void * pvKey1, FLMUINT uiKeyLen1, const void * pvKey2, FLMUINT uiKeyLen2, FLMINT * piCompare) { return( ixKeyCompare( m_pDb, m_pIxd, m_pSearchKey, m_pOldNodeList, m_pOldNodeList, m_bCompareDocId, m_bCompareNodeIds, pvKey1, uiKeyLen1, pvKey2, uiKeyLen2, piCompare)); } FINLINE void setOldNodeList( F_OldNodeList * pOldNodeList) { flmAssert( !m_pOldNodeList); if ((m_pOldNodeList = pOldNodeList) != NULL) { m_pOldNodeList->AddRef(); } } FINLINE void setIxInfo( F_Db * pDb, IXD * pIxd) { m_pDb = pDb; m_pIxd = pIxd; } FINLINE void setSearchKey( F_DataVector * pSearchKey) { m_pSearchKey = pSearchKey; } FINLINE void setCompareNodeIds( FLMBOOL bCompareNodeIds) { m_bCompareNodeIds = bCompareNodeIds; } FINLINE void setCompareDocId( FLMBOOL bCompareDocId) { m_bCompareDocId = bCompareDocId; } virtual FLMINT FLMAPI getRefCount( void) { return( IF_ResultSetCompare::getRefCount()); } virtual FLMINT FLMAPI AddRef( void) { return( IF_ResultSetCompare::AddRef()); } virtual FLMINT FLMAPI Release( void) { return( IF_ResultSetCompare::Release()); } private: F_Db * m_pDb; IXD * m_pIxd; F_DataVector * m_pSearchKey; F_OldNodeList * m_pOldNodeList; FLMBOOL m_bCompareDocId; FLMBOOL m_bCompareNodeIds; }; /*============================================================================= Desc: Result set class for queries that do sorting. =============================================================================*/ class F_QueryResultSet : public F_Object { public: F_QueryResultSet() { m_pBTree = NULL; m_pResultSetDb = NULL; m_pSrcDb = NULL; m_pIxd = NULL; m_uiCurrPos = FLM_MAX_UINT; m_uiCount = 0; m_bPositioned = FALSE; m_hMutex = F_MUTEX_NULL; } ~F_QueryResultSet(); // Initialize the result set RCODE initResultSet( FLMBOOL bUseIxCompareObj, FLMBOOL bEnableEncryption); FINLINE void setIxInfo( F_Db * pSrcDb, IXD * pIxd) { m_pSrcDb = pSrcDb; m_pIxd = pIxd; m_compareObj.setIxInfo( pSrcDb, pIxd); } // Entry Add and Sort Methods RCODE addEntry( // Variable or fixed length entry coming in FLMBYTE * pucKey, // key for sorting. FLMUINT uiKeyLength, FLMBOOL bLockMutex); // Methods to read entries. RCODE getFirst( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex); RCODE getLast( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex); RCODE getNext( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex); RCODE getPrev( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex); RCODE getCurrent( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex); RCODE positionToEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, F_DataVector * pSearchKey, FLMUINT uiFlags, FLMBOOL bLockMutex); RCODE positionToEntry( FLMUINT uiPosition, FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex); FINLINE FLMUINT getCount( void) { return( m_uiCount); } FINLINE FLMUINT getCurrPos( void) { return( m_uiCurrPos); } FINLINE void lockMutex( void) { f_mutexLock( m_hMutex); } FINLINE void unlockMutex( void) { f_mutexUnlock( m_hMutex); } private: char m_szResultSetDibName [F_PATH_MAX_SIZE]; F_Db * m_pResultSetDb; F_Btree * m_pBTree; LFILE m_LFile; F_Db * m_pSrcDb; IXD * m_pIxd; IXKeyCompare m_compareObj; FLMUINT m_uiCurrPos; FLMUINT m_uiCount; FLMBOOL m_bPositioned; F_MUTEX m_hMutex; }; typedef struct RS_WAITER { FLMUINT uiThreadId; // Thread of waiter F_SEM hESem; // Semaphore to signal to wake up thread. RCODE * pRc; // Pointer to return code that is to // be set. FLMUINT uiWaitStartTime; // Time we started waiting. FLMUINT uiTimeLimit; // Maximum time (milliseconds) to wait // before timing out. FLMUINT uiNumToWaitFor;// Wait until we get at least this many // in the result set - or until the // result set is complete. RS_WAITER * pNext; // Next lock waiter in list. RS_WAITER * pPrev; // Previous lock waiter in list. } RS_WAITER; /**************************************************************************** Desc: Class for setting up query criteria ****************************************************************************/ class F_Query : public IF_Query { public: F_Query(); virtual ~F_Query(); // Methods for constructing a query FINLINE RCODE FLMAPI setLanguage( FLMUINT uiLanguage) { // Cannot change language after optimization if (m_bOptimized) { return( RC_SET( NE_XFLM_Q_ALREADY_OPTIMIZED)); } m_uiLanguage = uiLanguage; return( NE_XFLM_OK); } FINLINE RCODE FLMAPI setCollection( FLMUINT uiCollection ) { // Cannot change collection after optimization if (m_bOptimized) { return( RC_SET( NE_XFLM_Q_ALREADY_OPTIMIZED)); } m_uiCollection = uiCollection; return( NE_XFLM_OK); } FINLINE RCODE FLMAPI setupQueryExpr( IF_Db * pDb, const FLMUNICODE * puzQuery) { return( setupQueryExpr( TRUE, pDb, (void *)puzQuery)); } FINLINE RCODE FLMAPI setupQueryExpr( IF_Db * pDb, const char * pszQuery) { return( setupQueryExpr( FALSE, pDb, (void *)pszQuery)); } RCODE FLMAPI copyCriteria( IF_Query * pSrcQuery); RCODE FLMAPI addXPathComponent( eXPathAxisTypes eXPathAxis, eDomNodeType eNodeType, FLMUINT uiNameId, IF_QueryNodeSource * pNodeSource); RCODE FLMAPI addOperator( eQueryOperators eOperator, FLMUINT uiCompareRules = 0, IF_OperandComparer * pOpComparer = NULL); RCODE FLMAPI addUnicodeValue( const FLMUNICODE * puzVal); RCODE FLMAPI addUTF8Value( const char * pszVal, FLMUINT uiUTF8Len = 0); RCODE FLMAPI addBinaryValue( const void * pvVal, FLMUINT uiValLen); RCODE FLMAPI addUINTValue( FLMUINT uiVal); RCODE FLMAPI addINTValue( FLMINT iVal); RCODE FLMAPI addUINT64Value( FLMUINT64 ui64Val); RCODE FLMAPI addINT64Value( FLMINT64 i64Val); RCODE FLMAPI addBoolean( FLMBOOL bVal, FLMBOOL bUnknown = FALSE); FINLINE RCODE FLMAPI addFunction( eQueryFunctions eFunction) { return( addFunction( eFunction, NULL, FALSE)); } FINLINE RCODE FLMAPI addFunction( IF_QueryValFunc * pFuncObj, FLMBOOL bHasXPathExpr) { // Pass XFLM_FUNC_xxx to private addFunction method - it really // doesn't matter, because it will be ignored. flmAssert( pFuncObj); return( addFunction( XFLM_FUNC_xxx, pFuncObj, bHasXPathExpr)); } RCODE FLMAPI getFirst( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0); RCODE FLMAPI getLast( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0); RCODE FLMAPI getNext( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0, FLMUINT uiNumToSkip = 0, FLMUINT * puiNumSkipped = NULL); RCODE FLMAPI getPrev( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0, FLMUINT uiNumToSkip = 0, FLMUINT * puiNumSkipped = NULL); RCODE FLMAPI getCurrent( IF_Db * pDb, IF_DOMNode ** ppNode); void FLMAPI resetQuery( void); RCODE FLMAPI getStatsAndOptInfo( FLMUINT * puiNumOptInfos, XFLM_OPT_INFO ** ppOptInfo); void FLMAPI freeStatsAndOptInfo( XFLM_OPT_INFO ** ppOptInfo); void FLMAPI setDupHandling( FLMBOOL bRemoveDups); RCODE FLMAPI setIndex( FLMUINT uiIndex); RCODE FLMAPI getIndex( IF_Db * pDb, FLMUINT * puiIndex, FLMBOOL * pbHaveMultiple); RCODE FLMAPI addSortKey( void * pvSortKeyContext, FLMBOOL bChildToContext, FLMBOOL bElement, FLMUINT uiNameId, FLMUINT uiCompareRules, FLMUINT uiLimit, FLMUINT uiKeyComponent, FLMBOOL bSortDescending, FLMBOOL bSortMissingHigh, void ** ppvContext); FINLINE RCODE FLMAPI enablePositioning( void) { if (m_bOptimized) { return( RC_SET( NE_XFLM_ILLEGAL_OP)); } else { m_bPositioningEnabled = TRUE; } return( NE_XFLM_OK); } RCODE FLMAPI positionTo( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiPosition); RCODE FLMAPI positionTo( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, IF_DataVector * pSearchKey, FLMUINT uiFlags); RCODE FLMAPI getPosition( IF_Db * pDb, FLMUINT * puiPosition); RCODE FLMAPI buildResultSet( IF_Db * pDb, FLMUINT uiTimeLimit); void FLMAPI stopBuildingResultSet( void); RCODE FLMAPI getCounts( IF_Db * pDb, FLMUINT uiTimeLimit, FLMBOOL bPartialCountOk, FLMUINT * puiReadCount, FLMUINT * puiPassedCount, FLMUINT * puiPositionableToCount, FLMBOOL * pbDoneBuildingResultSet = NULL); FINLINE void FLMAPI enableResultSetEncryption( void) { m_bEncryptResultSet = TRUE; } FINLINE void FLMAPI setQueryStatusObject( IF_QueryStatus * pQueryStatus) { if (m_pQueryStatus) { m_pQueryStatus->Release(); } if ((m_pQueryStatus = pQueryStatus) != NULL) { m_pQueryStatus->AddRef(); } } FINLINE void FLMAPI setQueryValidatorObject( IF_QueryValidator * pQueryValidator) { if (m_pQueryValidator) { m_pQueryValidator->Release(); } if ((m_pQueryValidator = pQueryValidator) != NULL) { m_pQueryValidator->AddRef(); } } private: RCODE FLMAPI addFunction( eQueryFunctions eFunction, IF_QueryValFunc * pFuncObj, FLMBOOL bHasXPathExpr); FINLINE FLMBOOL timedOut( void) { if (m_uiTimeLimit) { FLMUINT uiCurrTime; uiCurrTime = FLM_GET_TIMER(); if (FLM_ELAPSED_TIME( uiCurrTime, m_uiStartTime) > m_uiTimeLimit) { return( TRUE); } } return( FALSE); } FINLINE RCODE queryStatus( void) { if (timedOut()) { return( RC_SET( NE_XFLM_TIMEOUT)); } if (m_uiBuildThreadId && m_bStopBuildingResultSet) { return( RC_SET( NE_XFLM_USER_ABORT)); } return( (RCODE)(m_pQueryStatus ? m_pQueryStatus->queryStatus( m_pCurrOpt) : (RCODE)NE_XFLM_OK)); } FINLINE RCODE newSource( void) { if (timedOut()) { return( RC_SET( NE_XFLM_TIMEOUT)); } if (m_uiBuildThreadId && m_bStopBuildingResultSet) { return( RC_SET( NE_XFLM_USER_ABORT)); } return( (RCODE)(m_pQueryStatus ? m_pQueryStatus->newSource( m_pCurrOpt) : (RCODE)NE_XFLM_OK)); } FINLINE RCODE incrNodesRead( void) { m_pCurrOpt->ui64NodesRead++; return( queryStatus()); } FINLINE FLMBOOL expectingOperand( void) { return( !m_pCurExprState->bExpectingOperator); } FINLINE FLMBOOL expectingOperator( void) { return( m_pCurExprState->bExpectingOperator); } FINLINE FLMBOOL parsingFunction( void) { return( (FLMBOOL)(m_pCurExprState->pPrev && m_pCurExprState->pQFunction ? TRUE : FALSE)); } FINLINE FLMBOOL parsingXPathExpr( void) { return( (FLMBOOL)(m_pCurExprState->pPrev && m_pCurExprState->pXPathComponent && !m_pCurExprState->pQFunction ? TRUE : FALSE)); } RCODE allocExprState( void); RCODE allocValueNode( FLMUINT uiValLen, eValTypes eValType, FQNODE ** ppQNode); RCODE intersectPredicates( CONTEXT_PATH * pContextPath, FQNODE * pXPathNode, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FQNODE * pContextNode, FLMBOOL bNotted, FQVALUE * pQValue, FLMBOOL * pbClipContext); RCODE unionPredicates( CONTEXT_PATH * pContextPath, FQNODE * pXPathNode, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FQNODE * pContextNode, FLMBOOL bNotted, FQVALUE * pQValue); RCODE addPredicateToContext( OP_CONTEXT * pContext, XPATH_COMPONENT * pXPathComponent, XPATH_COMPONENT * pXPathComp, eQueryOperators eOperator, FLMUINT uiCompareRules, IF_OperandComparer * pOpComparer, FQNODE * pContextNode, FLMBOOL bNotted, FQVALUE * pQValue, FLMBOOL * pbClipContext, FQNODE ** ppQNode); RCODE createOpContext( OP_CONTEXT * pParentContext, FLMBOOL bIntersect, FQNODE * pQRootNode); RCODE getPathPredicates( FQNODE * pParentNode, FQNODE ** ppQNode, XPATH_COMPONENT * pXPathContext); RCODE getPredicates( FQNODE ** ppExpr, FQNODE * pStartNode, XPATH_COMPONENT * pXPathComponent); RCODE optimizePredicate( XPATH_COMPONENT * pXPathComponent, PATH_PRED * pPred); RCODE optimizePath( CONTEXT_PATH * pContextPath, PATH_PRED * pSingleNodeIdPred, FLMBOOL bIntersect); RCODE optimizeContext( OP_CONTEXT * pContext, CONTEXT_PATH * pSingleNodeIdPath, PATH_PRED * pSingleNodeIdPred); RCODE setupIndexScan( void); RCODE checkSortIndex( FLMUINT uiOptIndex); RCODE optimize( void); RCODE setupCurrPredicate( FLMBOOL bForward); RCODE testPassed( IF_DOMNode ** ppNode, FLMBOOL * pbPassed, FLMBOOL * pbEliminatedDup); RCODE nextFromIndex( FLMBOOL bEvalCurrDoc, FLMUINT uiMaxToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode); RCODE prevFromIndex( FLMBOOL bEvalCurrDoc, FLMUINT uiMaxToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode); RCODE getDocFromIndexScan( FLMBOOL bFirstLast, FLMBOOL bForward); RCODE nextFromScan( FLMBOOL bFirstDoc, FLMUINT uiMaxToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode); RCODE prevFromScan( FLMBOOL bLastDoc, FLMUINT uiMaxToSkip, FLMUINT * puiNumSkipped, IF_DOMNode ** ppNode); void useLeafContext( FLMBOOL bGetFirst); FLMBOOL useNextPredicate( void); FLMBOOL usePrevPredicate( void); RCODE getNodeSourceNode( FLMBOOL bForward, IF_QueryNodeSource * pNodeSource, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE getRootAxisNode( IF_DOMNode ** ppCurrNode); RCODE walkDocument( FLMBOOL bForward, FLMBOOL bWalkAttributes, FLMUINT uiAttrNameId, IF_DOMNode ** ppCurrNode); RCODE getChildAxisNode( FLMBOOL bForward, IF_DOMNode * pContextNode, FLMUINT uiChildNameId, IF_DOMNode ** ppCurrNode); RCODE getParentAxisNode( FLMBOOL bForward, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE getAncestorAxisNode( FLMBOOL bForward, FLMBOOL bIncludeSelf, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE getDescendantAxisNode( FLMBOOL bForward, FLMBOOL bIncludeSelf, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE getSibAxisNode( FLMBOOL bForward, FLMBOOL bPrevSibAxis, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE getPrevOrAfterAxisNode( FLMBOOL bForward, FLMBOOL bPrevAxis, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE getAttrAxisNode( FLMBOOL bForward, FLMBOOL bAttrAxis, FLMUINT uiAttrNameId, IF_DOMNode * pContextNode, IF_DOMNode ** ppCurrNode); RCODE verifyOccurrence( FLMBOOL bUseKeyNodes, XPATH_COMPONENT * pXPathComponent, IF_DOMNode * pCurrNode, FLMBOOL * pbPassed); RCODE getXPathComponentFromAxis( IF_DOMNode * pContextNode, FLMBOOL bForward, FLMBOOL bUseKeyNodes, XPATH_COMPONENT * pXPathComponent, IF_DOMNode ** ppCurrNode, eXPathAxisTypes eAxis, FLMBOOL bAxisInverted, FLMBOOL bCountNodes); RCODE getNextXPathValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FLMBOOL bUseKeyNodes, FLMBOOL bXPathIsEntireExpr, FQNODE * pQNode); RCODE getNextFunctionValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FQNODE * pCurrNode, F_DynaBuf * pDynaBuf); RCODE getFuncValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FQNODE ** ppCurrNode, FLMBOOL * pbGetNodeValue, F_DynaBuf * pDynaBuf); RCODE getXPathValue( IF_DOMNode * pContextNode, FLMBOOL bForward, FQNODE ** ppCurrNode, FLMBOOL * pbGetNodeValue, FLMBOOL bUseKeyNodes, FLMBOOL bXPathIsEntireExpr); RCODE setExprReturnValue( FLMBOOL bUseKeyNodes, FQNODE * pQueryExpr, FLMBOOL * pbPassed, IF_DOMNode ** ppNode); RCODE evalExpr( IF_DOMNode * pContextNode, FLMBOOL bForward, FLMBOOL bUseKeyNodes, FQNODE * pQueryExpr, FLMBOOL * pbPassed, IF_DOMNode ** ppNode); RCODE getAppNode( FLMBOOL * pbFirstLast, FLMBOOL bForward, XPATH_COMPONENT * pXPathComp); RCODE testKey( F_DataVector * pKey, PATH_PRED * pPred, FLMBOOL * pbPasses, IF_DOMNode ** ppPassedNode); RCODE getKey( FLMBOOL * pbFirstLast, FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent); RCODE testMetaData( IF_DOMNode * pNode, FLMUINT uiMetaDataType, PATH_PRED * pPred, FLMBOOL * pbPasses); RCODE getANode( FLMBOOL * pbFirstLast, FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent); RCODE getContextNode( FLMBOOL bForward, XPATH_COMPONENT * pXPathComponent); RCODE getNextIndexNode( FLMBOOL * pbFirstLast, FLMBOOL bForward, FQNODE * pExprXPathSource, FLMBOOL bSkipCurrKey); RCODE objectAddRef( F_Object * pObject); RCODE setupQueryExpr( FLMBOOL bUnicode, IF_Db * pDb, const void * pvQuery); RCODE allocDupCheckSet( void); RCODE checkIfDup( IF_DOMNode ** ppNode, FLMBOOL * pbPassed); RCODE copyValue( FQVALUE * pDestValue, FQVALUE * pSrcValue); RCODE copyXPath( XPATH_COMPONENT * pXPathContext, FQNODE * pDestNode, FXPATH ** ppDestXPath, FXPATH * pSrcXPath); RCODE copyFunction( XPATH_COMPONENT * pXPathContext, FQFUNCTION ** ppDestFunc, FQFUNCTION * pSrcFunc); RCODE copyNode( XPATH_COMPONENT * pXPathContext, FQNODE ** ppDestNode, FQNODE * pSrcNode); RCODE copyExpr( XPATH_COMPONENT * pXPathContext, FQNODE ** ppDestExpr, FQNODE * pSrcExpr); void clearQuery( void); void initVars( void); FINLINE RCODE validateNode( IF_DOMNode * pNode, FLMBOOL * pbPassed) { RCODE rc = NE_XFLM_OK; if (*pbPassed && m_pQueryValidator) { if (RC_BAD( rc = m_pQueryValidator->validateNode( (IF_Db *)m_pDb, pNode, pbPassed))) { goto Exit; } if (!(*pbPassed)) { if (!m_pQuery || m_pQuery->eNodeType != FLM_XPATH_NODE || m_bRemoveDups) { m_pCurrOpt->ui64DocsFailedValidation++; } m_pCurrOpt->ui64NodesFailedValidation++; if (RC_BAD( rc = queryStatus())) { goto Exit; } } } Exit: return( rc); } RCODE createResultSet( void); RCODE buildResultSet( IF_Db * pDb, FLMUINT uiTimeLimit, FLMUINT uiNumToWaitFor); void checkResultSetWaiters( RCODE rc); RCODE waitResultSetBuild( IF_Db * pDb, FLMUINT uiTimeLimit, FLMUINT uiNumToWaitFor); RCODE getFirstFromResultSet( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit); RCODE getLastFromResultSet( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit); RCODE getNextFromResultSet( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped); RCODE getPrevFromResultSet( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped); RCODE getCurrentFromResultSet( IF_Db * pDb, IF_DOMNode ** ppNode); RCODE verifySortKeys( void); RCODE addToResultSet( void); RCODE m_rc; FQNODE * m_pQuery; FLMBOOL m_bScan; FLMBOOL m_bScanIndex; FLMBOOL m_bResetAllXPaths; FSIndexCursor * m_pFSIndexCursor; XFLM_OPT_INFO m_scanOptInfo; XFLM_OPT_INFO * m_pCurrOpt; FLMBOOL m_bEmpty; IXD * m_pSortIxd; F_QueryResultSet * m_pSortResultSet; RS_WAITER * m_pFirstWaiter; FLMBOOL m_bStopBuildingResultSet; FLMUINT m_uiBuildThreadId; FLMBOOL m_bPositioningEnabled; FLMBOOL m_bResultSetPopulated; FLMBOOL m_bEntriesAlreadyInOrder; FLMBOOL m_bEncryptResultSet; FLMUINT64 m_ui64RSDocsRead; FLMUINT64 m_ui64RSDocsPassed; EXPR_STATE * m_pCurExprState; F_Pool m_pool; FLMBOOL m_bOptimized; FLMUINT m_uiLanguage; FLMUINT m_uiCollection; IF_DOMNode * m_pCurrDoc; IF_DOMNode * m_pCurrNode; OP_CONTEXT * m_pCurrContext; CONTEXT_PATH * m_pCurrContextPath; PATH_PRED * m_pCurrPred; FQNODE * m_pExprXPathSource; eQueryStates m_eState; IF_QueryStatus * m_pQueryStatus; IF_QueryValidator * m_pQueryValidator; F_Database * m_pDatabase; F_Db * m_pDb; F_Query * m_pNext; // Next query off of database F_Query * m_pPrev; // Prev query off of database F_Object ** m_ppObjectList; FLMUINT m_uiObjectListSize; FLMUINT m_uiObjectCount; FLMBOOL m_bRemoveDups; F_DynSearchSet * m_pDocIdSet; FLMUINT m_uiIndex; FLMBOOL m_bIndexSet; FLMUINT m_uiTimeLimit; FLMUINT m_uiStartTime; friend class F_Db; friend class F_Database; friend class F_Dict; friend class F_IStream; }; /***************************************************************************** Desc: ******************************************************************************/ class F_DbSystem : public IF_DbSystem, public F_OSBase { public: F_DbSystem(); virtual ~F_DbSystem(); virtual FLMINT FLMAPI AddRef( FLMBOOL bSysDataLocked); virtual FLMINT FLMAPI AddRef( void) { return( AddRef( FALSE)); } virtual FLMINT FLMAPI Release( void); virtual FLMINT FLMAPI getRefCount( void) { return( (FLMINT)m_refCnt); } RCODE FLMAPI init( void); RCODE FLMAPI updateIniFile( const char * pszParamName, const char * pszValue); void FLMAPI getFileSystem( IF_FileSystem ** ppFileSystem); RCODE FLMAPI dbCreate( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszDictFileName, const char * pszDictBuf, XFLM_CREATE_OPTS * pCreateOpts, FLMBOOL bTempDb, IF_Db ** ppDb); FINLINE RCODE FLMAPI dbCreate( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszDictFileName, const char * pszDictBuf, XFLM_CREATE_OPTS * pCreateOpts, IF_Db ** ppDb) { return( dbCreate( pszDbFileName, pszDataDir, pszRflDir, pszDictFileName, pszDictBuf, pCreateOpts, FALSE, ppDb)); } FINLINE RCODE FLMAPI dbOpen( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMBOOL bAllowLimited, IF_Db ** ppDb) { FLMUINT uiOpenFlags = bAllowLimited ? XFLM_ALLOW_LIMITED_MODE : 0; return( openDb( pszDbFileName, pszDataDir, pszRflDir, pszPassword, uiOpenFlags, ppDb)); } RCODE FLMAPI dbRebuild( const char * pszSourceDbPath, const char * pszSourceDataDir, const char * pszDestDbPath, const char * pszDestDataDir, const char * pszDestRflDir, const char * pszDictPath, const char * pszPassword, XFLM_CREATE_OPTS * pCreateOpts, FLMUINT64 * pui64TotNodes, FLMUINT64 * pui64NodesRecov, FLMUINT64 * pui64QuarantinedNodes, IF_DbRebuildStatus * pRebuildStatus); RCODE FLMAPI dbCheck( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiFlags, IF_DbInfo ** ppDbInfo, IF_DbCheckStatus * pDbCheck); FINLINE RCODE FLMAPI dbDup( IF_Db * ifpDb, IF_Db ** ppDb) { F_Db * pDb = (F_Db *)ifpDb; return( openDatabase( pDb->m_pDatabase, NULL, NULL, NULL, NULL, 0, FALSE, NULL, NULL, NULL, ppDb)); } FINLINE RCODE FLMAPI setDynamicMemoryLimit( FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave) { return( gv_XFlmSysData.pGlobalCacheMgr->setDynamicMemoryLimit( uiCacheAdjustPercent, uiCacheAdjustMin, uiCacheAdjustMax, uiCacheAdjustMinToLeave)); } FINLINE RCODE FLMAPI setHardMemoryLimit( FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate) { return( gv_XFlmSysData.pGlobalCacheMgr->setHardMemoryLimit( uiPercent, bPercentOfAvail, uiMin, uiMax, uiMinToLeave, bPreallocate)); } // Determine if dyamic cache adjusting is supported. FINLINE FLMBOOL FLMAPI getDynamicCacheSupported( void) { #ifdef FLM_CAN_GET_PHYS_MEM return( TRUE); #else return( FALSE); #endif } FINLINE void FLMAPI getCacheInfo( XFLM_CACHE_INFO * pCacheInfo) { gv_XFlmSysData.pGlobalCacheMgr->getCacheInfo( pCacheInfo); } // Enable/disable cache debugging mode void FLMAPI enableCacheDebug( FLMBOOL bDebug); FLMBOOL FLMAPI cacheDebugEnabled( void); // Clear cache FINLINE RCODE FLMAPI clearCache( IF_Db * pDb) { return( gv_XFlmSysData.pGlobalCacheMgr->clearCache( pDb)); } // Close all files that have not been used for the specified number of // seconds. RCODE FLMAPI closeUnusedFiles( FLMUINT uiSeconds); // Start gathering statistics. void FLMAPI startStats( void); // Stop gathering statistics. void FLMAPI stopStats( void); // Reset statistics. void FLMAPI resetStats( void); RCODE FLMAPI getStats( XFLM_STATS * pFlmStats); void FLMAPI freeStats( XFLM_STATS * pFlmStats); // Set the maximum number of queries to save. void FLMAPI setQuerySaveMax( FLMUINT uiMaxToSave); FLMUINT FLMAPI getQuerySaveMax( void); // Set temporary directory. RCODE FLMAPI setTempDir( const char * pszPath); RCODE FLMAPI getTempDir( char * pszPath); // Maximum seconds between checkpoints. void FLMAPI setCheckpointInterval( FLMUINT uiSeconds); FLMUINT FLMAPI getCheckpointInterval( void); // Set interval for dynamically adjusting cache limit. void FLMAPI setCacheAdjustInterval( FLMUINT uiSeconds); FLMUINT FLMAPI getCacheAdjustInterval( void); // Set interval for dynamically cleaning out old cache blocks and records. void FLMAPI setCacheCleanupInterval( FLMUINT uiSeconds); FLMUINT FLMAPI getCacheCleanupInterval( void); // Set interval for cleaning up unused structures. void FLMAPI setUnusedCleanupInterval( FLMUINT uiSeconds); FLMUINT FLMAPI getUnusedCleanupInterval( void); // Set maximum time for an item to be unused. void FLMAPI setMaxUnusedTime( FLMUINT uiSeconds); FLMUINT FLMAPI getMaxUnusedTime( void); // Specify the logger object void FLMAPI setLogger( IF_LoggerClient * pLogger); // Enable or disable use of ESM void FLMAPI enableExtendedServerMemory( FLMBOOL bEnable); FLMBOOL FLMAPI extendedServerMemoryEnabled( void); void FLMAPI deactivateOpenDb( const char * pszDbFileName, const char * pszDataDir); // Maximum dirty cache. void FLMAPI setDirtyCacheLimits( FLMUINT uiMaxDirty, FLMUINT uiLowDirty); void FLMAPI getDirtyCacheLimits( FLMUINT * puiMaxDirty, FLMUINT * puiLowDirty); RCODE FLMAPI getThreadInfo( IF_ThreadInfo ** ppThreadInfo); RCODE FLMAPI registerForEvent( eEventCategory eCategory, IF_EventClient * pEventClient); void FLMAPI deregisterForEvent( eEventCategory eCategory, IF_EventClient * pEventClient); RCODE FLMAPI getNextMetaphone( IF_IStream * pIStream, FLMUINT * puiMetaphone, FLMUINT * puiAltMetaphone = NULL); RCODE FLMAPI dbCopy( const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, IF_DbCopyStatus * ifpStatus); RCODE FLMAPI dbRemove( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, FLMBOOL bRemoveRflFiles); RCODE FLMAPI dbRename( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, const char * pszNewDbName, FLMBOOL bOverwriteDestOk, IF_DbRenameStatus * ifpStatus); RCODE FLMAPI dbRestore( const char * pszDbPath, const char * pszDataDir, const char * pszRflDir, const char * pszBackupPath, const char * pszPassword, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus); RCODE FLMAPI strCmp( FLMUINT uiCompFlags, FLMUINT uiLanguage, FLMUNICODE * uzStr1, FLMUNICODE * uzStr2, FLMINT * piCmp); FLMBOOL FLMAPI errorIsFileCorrupt( RCODE rc); const char * FLMAPI checkErrorToStr( FLMINT iCheckErrorCode); RCODE FLMAPI openBufferIStream( const char * pucBuffer, FLMUINT uiLength, IF_PosIStream ** ppIStream); RCODE FLMAPI openFileIStream( const char * pszPath, IF_PosIStream ** ppIStream); RCODE FLMAPI openMultiFileIStream( const char * pszDirectory, const char * pszBaseName, IF_IStream ** ppIStream); RCODE FLMAPI openBufferedIStream( IF_IStream * pIStream, FLMUINT uiBufferSize, IF_IStream ** ppIStream); RCODE FLMAPI openUncompressingIStream( IF_IStream * pIStream, IF_IStream ** ppIStream); RCODE FLMAPI openFileOStream( const char * pszFileName, FLMBOOL bTruncateIfExists, IF_OStream ** ppOStream); RCODE FLMAPI openMultiFileOStream( const char * pszDirectory, const char * pszBaseName, FLMUINT uiMaxFileSize, FLMBOOL bOverwrite, IF_OStream ** ppStream); RCODE FLMAPI removeMultiFileStream( const char * pszDirectory, const char * pszBaseName); RCODE FLMAPI openBufferedOStream( IF_OStream * pOStream, FLMUINT uiBufferSize, IF_OStream ** ppOStream); RCODE FLMAPI openCompressingOStream( IF_OStream * pOStream, IF_OStream ** ppOStream); RCODE FLMAPI writeToOStream( IF_IStream * pIStream, IF_OStream * pOStream); RCODE FLMAPI openBase64Encoder( IF_IStream * pInputStream, FLMBOOL bInsertLineBreaks, IF_IStream ** ppEncodedStream); RCODE FLMAPI openBase64Decoder( IF_IStream * pInputStream, IF_IStream ** ppDecodedStream); RCODE FLMAPI createIFDataVector( IF_DataVector ** ifppDV); RCODE FLMAPI createIFResultSet( IF_ResultSet ** ppResultSet); RCODE FLMAPI createIFQuery( IF_Query ** ppQuery); FINLINE void FLMAPI freeMem( void ** ppMem) { f_free( ppMem); } FINLINE RCODE internalDbOpen( F_Database * pDatabase, F_Db ** ppDb) { RCODE rc = NE_XFLM_OK; IF_Db * pDb; if (RC_OK( rc = openDatabase( pDatabase, NULL, NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL, &pDb))) { *ppDb = (F_Db *)pDb; } return( rc); } RCODE openDb( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiOpenFlags, IF_Db ** ppDb); static FINLINE FLMBOOL validBlockSize( FLMUINT uiBlockSize) { if( uiBlockSize == 4096 || uiBlockSize == 8192) { return( TRUE); } return( FALSE); } RCODE FLMAPI compareUTF8Strings( const FLMBYTE * pucLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMBYTE * pucRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI compareUnicodeStrings( const FLMUNICODE * puzLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMUNICODE * puzRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult); RCODE FLMAPI utf8IsSubStr( const FLMBYTE * pszString, const FLMBYTE * pszSubString, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMBOOL * pbExists); FLMBOOL FLMAPI uniIsUpper( FLMUNICODE uzChar); FLMBOOL FLMAPI uniIsLower( FLMUNICODE uzChar); FLMBOOL FLMAPI uniIsAlpha( FLMUNICODE uzChar); FLMBOOL FLMAPI uniIsDecimalDigit( FLMUNICODE uzChar); FLMUNICODE FLMAPI uniToLower( FLMUNICODE uzChar); RCODE FLMAPI nextUCS2Char( const FLMBYTE ** ppszUTF8, const FLMBYTE * pszEndOfUTF8String, FLMUNICODE * puzChar); RCODE FLMAPI numUCS2Chars( const FLMBYTE * pszUTF8, FLMUINT * puiNumChars); RCODE FLMAPI waitToClose( const char * pszDbPath); RCODE FLMAPI createIFNodeInfo( IF_NodeInfo ** ifppNodeInfo); RCODE FLMAPI createIFBTreeInfo( IF_BTreeInfo ** ifppBTreeInfo); private: // Methods RCODE readIniFile( void); RCODE setCacheParams( IF_IniFile * pIniFile); void cleanup( void); FINLINE RCODE internalDbDup( F_Db * pDb, F_Db ** ppDb) { RCODE rc = NE_XFLM_OK; IF_Db * ifpDb; if (RC_OK( rc = openDatabase( pDb->m_pDatabase, NULL, NULL, NULL, NULL, 0, TRUE, NULL, NULL, NULL, &ifpDb))) { *ppDb = (F_Db *)ifpDb; } return( rc); } RCODE allocDb( F_Db ** ppDb, FLMBOOL bInternalOpen); RCODE findDatabase( const char * pszDbPath, const char * pszDataDir, F_Database ** ppDatabase); RCODE checkDatabaseClosed( const char * pszDbName, const char * pszDataDir); RCODE allocDatabase( const char * pszDbPath, const char * pszDataDir, FLMBOOL bTempDb, F_Database ** ppDatabase); RCODE openDatabase( F_Database * pDatabase, const char * pszDbPath, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiOpenFlags, FLMBOOL bInternalOpen, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus, IF_FileHdl * pLockFileHdl, IF_Db ** ppDb); RCODE copyDb( const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, IF_DbCopyStatus * ifpStatus); static RCODE FLMAPI monitorThrd( IF_Thread * pThread); static RCODE FLMAPI cacheCleanupThrd( IF_Thread * pThread); FLMATOMIC m_refCnt; friend class F_Db; friend class F_Database; friend class F_DbRebuild; friend class F_DbCheck; }; void flmGetDbBasePath( char * pszBaseDbName, const char * pszDbName, FLMUINT * puiBaseDbNameLen); // Supported text types typedef enum { XFLM_UNICODE_TEXT = 1, XFLM_UTF8_TEXT } eXFlmTextType; /*------------------------------------------------------ FLAIM Processing Hooks (call-backs) -------------------------------------------------------*/ #define FLM_DATA_LEFT_TRUNCATED 0x10 // Data is left truncated #define FLM_DATA_RIGHT_TRUNCATED 0x20 // Data is right truncated RCODE flmReadStorageAsText( IF_IStream * pIStream, FLMBYTE * pucStorageData, FLMUINT uiDataLen, FLMUINT uiDataType, void * pvBuffer, FLMUINT uiBufLen, eXFlmTextType eTextType, FLMUINT uiMaxCharsToRead, FLMUINT uiCharOffset, FLMUINT * puiCharsRead, FLMUINT * puiBufferBytesUsed); RCODE flmReadStorageAsBinary( IF_IStream * pIStream, void * pvBuffer, FLMUINT uiBufLen, FLMUINT uiByteOffset, FLMUINT * puiBytesRead); RCODE flmReadStorageAsNumber( IF_IStream * pIStream, FLMUINT uiDataType, FLMUINT64 * pui64Number, FLMBOOL * pbNeg); RCODE flmReadLine( IF_IStream * pIStream, FLMBYTE * pucBuffer, FLMUINT * puiSize); #define FLM_ENCRYPT_CHUNK_SIZE 512 /***************************************************************************** Desc: ******************************************************************************/ class F_BTreeIStream : public IF_PosIStream { public: F_BTreeIStream() { m_pucBuffer = NULL; m_pBTree = NULL; m_bReleaseBTree = FALSE; reset(); } virtual ~F_BTreeIStream() { reset(); } FINLINE void reset( void) { m_pNextInPool = NULL; if( m_pBTree && m_bReleaseBTree) { m_pBTree->btClose(); gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pBTree); m_pBTree = NULL; } if( m_pucBuffer != &m_ucBuffer [0]) { f_free( &m_pucBuffer); } m_pDb = NULL; m_uiCollection = 0; m_ui64NodeId = 0; m_pBTree = NULL; m_bReleaseBTree = FALSE; m_uiKeyLength = 0; m_uiStreamSize = 0; m_uiBufferBytes = 0; m_uiBufferOffset = 0; m_uiBufferStartOffset = 0; m_uiBufferSize = sizeof( m_ucBuffer); m_pucBuffer = &m_ucBuffer [0]; m_ui32BlkAddr = 0; m_uiOffsetIndex = 0; m_bDataEncrypted = FALSE; m_bBufferDecrypted = FALSE; m_uiDataLength = 0; m_uiEncDefId = 0; } RCODE openStream( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT32 ui32BlkAddr = 0, FLMUINT uiOffsetIndex = 0); RCODE openStream( F_Db * pDb, F_Btree * pBTree, FLMUINT uiFlags, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT32 ui32BlkAddr = 0, FLMUINT uiOffsetIndex = 0); FINLINE FLMUINT64 FLMAPI totalSize( void) { return( m_uiStreamSize); } FINLINE FLMUINT64 FLMAPI remainingSize( void) { return( m_uiStreamSize - (m_uiBufferStartOffset + m_uiBufferOffset)); } FINLINE RCODE FLMAPI closeStream( void) { reset(); return( NE_XFLM_OK); } RCODE FLMAPI positionTo( FLMUINT64 ui64Position); FINLINE FLMUINT64 FLMAPI getCurrPosition( void) { return( m_uiBufferStartOffset + m_uiBufferOffset); } RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead); FLMINT FLMAPI Release( void); FINLINE FLMUINT32 getBlkAddr( void) { return( m_ui32BlkAddr); } FINLINE FLMUINT getOffsetIndex( void) { return( m_uiOffsetIndex); } private: F_BTreeIStream * m_pNextInPool; F_Db * m_pDb; F_Btree * m_pBTree; FLMUINT m_uiCollection; FLMUINT64 m_ui64NodeId; FLMUINT m_uiStreamSize; FLMUINT m_uiKeyLength; FLMUINT m_uiBufferBytes; FLMUINT m_uiBufferSize; FLMUINT m_uiBufferOffset; FLMUINT m_uiBufferStartOffset; FLMUINT m_uiDataLength; FLMUINT m_uiEncDefId; FLMBYTE m_ucBuffer[ FLM_ENCRYPT_CHUNK_SIZE]; FLMBYTE * m_pucBuffer; FLMUINT m_uiOffsetIndex; FLMUINT32 m_ui32BlkAddr; FLMBOOL m_bReleaseBTree; FLMBOOL m_bDataEncrypted; FLMBOOL m_bBufferDecrypted; FLMBYTE m_ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMBYTE m_ucIV [16]; friend class F_DOMNode; friend class F_CachedNode; friend class F_NodePool; friend class F_Db; }; /***************************************************************************** Desc: ******************************************************************************/ class F_NodeBufferIStream : public IF_PosIStream { public: F_NodeBufferIStream() { m_pCachedNode = NULL; m_pBufferIStream = NULL; reset(); } virtual ~F_NodeBufferIStream() { reset(); } RCODE FLMAPI openStream( const char * pucBuffer, FLMUINT uiLength, char ** ppucAllocatedBuffer = NULL); FINLINE FLMUINT64 FLMAPI totalSize( void) { return( m_pBufferIStream->totalSize()); } FINLINE FLMUINT64 FLMAPI remainingSize( void) { return( m_pBufferIStream->remainingSize()); } FINLINE RCODE FLMAPI closeStream( void) { RCODE rc = NE_FLM_OK; if( m_pBufferIStream) { m_pBufferIStream->Release(); m_pBufferIStream = NULL; } return( rc); } FINLINE RCODE FLMAPI positionTo( FLMUINT64 ui64Position) { return( m_pBufferIStream->positionTo( ui64Position)); } FINLINE FLMUINT64 FLMAPI getCurrPosition( void) { return( m_pBufferIStream->getCurrPosition()); } FINLINE RCODE FLMAPI read( void * pvBuffer, FLMUINT uiBytesToRead, FLMUINT * puiBytesRead) { return( m_pBufferIStream->read( pvBuffer, uiBytesToRead, puiBytesRead)); } FINLINE void FLMAPI truncate( FLMUINT uiOffset) { m_pBufferIStream->truncateStream( uiOffset); } FINLINE void reset( void) { if( m_pCachedNode) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); m_pCachedNode->decrNodeUseCount(); m_pCachedNode->decrStreamUseCount(); f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); m_pCachedNode = NULL; } if( m_pBufferIStream) { m_pBufferIStream->Release(); m_pBufferIStream = NULL; } } F_CachedNode * m_pCachedNode; IF_BufferIStream * m_pBufferIStream; friend class F_CachedNode; }; /***************************************************************************** Desc: ******************************************************************************/ class F_DOMNode : public IF_DOMNode { public: F_DOMNode() { m_pCachedNode = NULL; resetDOMNode( FALSE); } virtual ~F_DOMNode() { resetDOMNode( FALSE); } void resetDOMNode( FLMBOOL bMutexAlreadyLocked) { m_pNextInPool = NULL; m_uiAttrNameId = 0; if (m_pCachedNode) { if( !bMutexAlreadyLocked) { f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); } m_pCachedNode->decrNodeUseCount(); if( !bMutexAlreadyLocked) { f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); } m_pCachedNode = NULL; } } FLMINT FLMAPI Release( void); RCODE FLMAPI createNode( IF_Db * pDb, eDomNodeType eNodeType, FLMUINT uiNameId, eNodeInsertLoc eLocation, IF_DOMNode ** ppNewNode, FLMUINT64 * pui64NodeId = NULL); RCODE FLMAPI createChildElement( IF_Db * pDb, FLMUINT uiChildElementNameId, eNodeInsertLoc eLocation, IF_DOMNode ** ppNewChildElementNode, FLMUINT64 * pui64NodeId = NULL); RCODE FLMAPI deleteNode( IF_Db * pDb); RCODE FLMAPI deleteChildren( IF_Db * pDb, FLMUINT uiNameId = 0); RCODE FLMAPI createAttribute( IF_Db * pDb, FLMUINT uiAttrNameId, IF_DOMNode ** ppAttrNode); RCODE FLMAPI getFirstAttribute( IF_Db * pDb, IF_DOMNode ** ppAttrNode); RCODE FLMAPI getLastAttribute( IF_Db * pDb, IF_DOMNode ** ppAttrNode); FINLINE RCODE FLMAPI getAttribute( IF_Db * pDb, FLMUINT uiAttrNameId, IF_DOMNode ** ppAttrNode) { return( hasAttribute( pDb, uiAttrNameId, ppAttrNode)); } RCODE FLMAPI deleteAttribute( IF_Db * pDb, FLMUINT uiAttrNameId); RCODE FLMAPI hasAttribute( IF_Db * pDb, FLMUINT uiAttrNameId, IF_DOMNode ** ppAttrNode = NULL); RCODE FLMAPI hasAttributes( IF_Db * pDb, FLMBOOL * pbHasAttrs); RCODE FLMAPI hasNextSibling( IF_Db * pDb, FLMBOOL * pbHasNextSibling); RCODE FLMAPI hasPreviousSibling( IF_Db * pDb, FLMBOOL * pbHasPreviousSibling); RCODE FLMAPI hasChildren( IF_Db * pDb, FLMBOOL * pbHasChildren); RCODE FLMAPI isNamespaceDecl( IF_Db * pDb, FLMBOOL * pbIsNamespaceDecl); FINLINE eDomNodeType FLMAPI getNodeType( void) { if( m_uiAttrNameId) { return( ATTRIBUTE_NODE); } else if( m_pCachedNode) { return( m_pCachedNode->getNodeType()); } flmAssert( 0); return( INVALID_NODE); } RCODE FLMAPI getNodeId( IF_Db * pDb, FLMUINT64 * pui64NodeId); RCODE FLMAPI getParentId( IF_Db * pDb, FLMUINT64 * pui64ParentId); RCODE FLMAPI getDocumentId( IF_Db * pDb, FLMUINT64 * pui64DocumentId); RCODE FLMAPI getPrevSibId( IF_Db * pDb, FLMUINT64 * pui64PrevSibId); RCODE FLMAPI getNextSibId( IF_Db * pDb, FLMUINT64 * pui64NextSibId); RCODE FLMAPI getFirstChildId( IF_Db * pDb, FLMUINT64 * pui64FirstChildId); RCODE FLMAPI getLastChildId( IF_Db * pDb, FLMUINT64 * pui64LastChildId); RCODE FLMAPI getNameId( IF_Db * pDb, FLMUINT * puiNameId); virtual RCODE FLMAPI getEncDefId( IF_Db * pDb, FLMUINT * puiEncDefId); RCODE FLMAPI getDataType( IF_Db * pDb, FLMUINT * puiDataType); RCODE FLMAPI getDataLength( IF_Db * pDb, FLMUINT * puiLength); FINLINE RCODE FLMAPI getUINT32( IF_Db * pDb, FLMUINT32 * pui32Value) { RCODE rc; FLMUINT64 ui64Value; if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, NULL))) { return( rc); } return( convertToUINT32( ui64Value, FALSE, pui32Value)); } FINLINE RCODE FLMAPI getUINT( IF_Db * pDb, FLMUINT * puiValue) { RCODE rc; FLMUINT64 ui64Value; if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, NULL))) { return( rc); } return( convertToUINT( ui64Value, FALSE, puiValue)); } FINLINE RCODE FLMAPI getUINT64( IF_Db * pDb, FLMUINT64 * pui64Value) { return( getNumber64( (F_Db *)pDb, pui64Value, NULL)); } FINLINE RCODE FLMAPI getINT32( IF_Db * pDb, FLMINT32 * pi32Value) { RCODE rc; FLMUINT64 ui64Value; FLMBOOL bNeg; if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, &bNeg))) { return( rc); } return( convertToINT32( ui64Value, bNeg, pi32Value)); } FINLINE RCODE FLMAPI getINT( IF_Db * pDb, FLMINT * piValue) { RCODE rc; FLMUINT64 ui64Value; FLMBOOL bNeg; if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, &bNeg))) { return( rc); } return( convertToINT( ui64Value, bNeg, piValue)); } FINLINE RCODE FLMAPI getINT64( IF_Db * pDb, FLMINT64 * pi64Value) { RCODE rc; FLMUINT64 ui64Value; FLMBOOL bNeg; if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, &bNeg))) { return( rc); } return( convertToINT64( ui64Value, bNeg, pi64Value)); } RCODE FLMAPI getMetaValue( IF_Db * pDb, FLMUINT64 * pui64Value); FINLINE RCODE FLMAPI getUnicodeChars( IF_Db * pDb, FLMUINT * puiNumChars) { return( getUnicode( pDb, NULL, 0, 0, FLM_MAX_UINT, puiNumChars)); } RCODE FLMAPI getUnicode( IF_Db * pDb, FLMUNICODE * puzValueBuffer, FLMUINT uiBufferSize, FLMUINT uiCharOffset, FLMUINT uiMaxCharsRequested, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL); RCODE FLMAPI getUnicode( IF_Db * pDb, FLMUNICODE ** ppuzUnicodeValue); RCODE FLMAPI getUnicode( IF_Db * pDb, F_DynaBuf * pDynaBuf); RCODE FLMAPI getUTF8( IF_Db * pDb, FLMBYTE * pszValueBuffer, FLMUINT uiBufferSize, FLMUINT uiCharOffset, FLMUINT uiMaxCharsRequested, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL); RCODE FLMAPI getUTF8( IF_Db * pDb, FLMBYTE ** ppszUTF8Value); RCODE FLMAPI getUTF8( IF_Db * pDb, F_DynaBuf * pDynaBuf); RCODE FLMAPI getBinary( IF_Db * pDb, void * pvValue, FLMUINT uiByteOffset, FLMUINT uiBytesRequested, FLMUINT * puiBytesReturned); RCODE FLMAPI getBinary( IF_Db * pDb, F_DynaBuf * pBuffer); FINLINE RCODE FLMAPI getAttributeValueUINT32( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT32 * pui32Num) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num; FLMBOOL bNeg; if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, uiAttrName, &ui64Num, &bNeg))) { return( rc); } return( convertToUINT32( ui64Num, bNeg, pui32Num)); } FINLINE RCODE FLMAPI getAttributeValueUINT32( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT32 * pui32Num, FLMUINT32 ui32NotFoundDefault) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = getAttributeValueUINT32( pDb, uiAttrName, pui32Num))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { return( rc); } *pui32Num = ui32NotFoundDefault; rc = NE_XFLM_OK; } return( rc); } FINLINE RCODE FLMAPI getAttributeValueUINT( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT * puiNum) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num; FLMBOOL bNeg; if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, uiAttrName, &ui64Num, &bNeg))) { return( rc); } return( convertToUINT( ui64Num, bNeg, puiNum)); } FINLINE RCODE FLMAPI getAttributeValueUINT( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT * puiNum, FLMUINT uiNotFoundDefault) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = getAttributeValueUINT( pDb, uiAttrName, puiNum))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { return( rc); } *puiNum = uiNotFoundDefault; rc = NE_XFLM_OK; } return( rc); } FINLINE RCODE FLMAPI getAttributeValueUINT64( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT64 * pui64Num) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num; FLMBOOL bNeg; if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, uiAttrName, &ui64Num, &bNeg))) { return( rc); } return( convertToUINT64( ui64Num, bNeg, pui64Num)); } FINLINE RCODE FLMAPI getAttributeValueUINT64( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT64 * pui64Num, FLMUINT64 ui64NotFoundDefault) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = getAttributeValueUINT64( pDb, uiAttrName, pui64Num))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { return( rc); } *pui64Num = ui64NotFoundDefault; rc = NE_XFLM_OK; } return( rc); } FINLINE RCODE FLMAPI getAttributeValueINT( IF_Db * pDb, FLMUINT uiAttrName, FLMINT * piNum) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num; FLMBOOL bNeg; if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, uiAttrName, &ui64Num, &bNeg))) { return( rc); } return( convertToINT( ui64Num, bNeg, piNum)); } FINLINE RCODE FLMAPI getAttributeValueINT( IF_Db * pDb, FLMUINT uiAttrName, FLMINT * piNum, FLMINT iNotFoundDefault) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = getAttributeValueINT( pDb, uiAttrName, piNum))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { return( rc); } *piNum = iNotFoundDefault; rc = NE_XFLM_OK; } return( rc); } FINLINE RCODE FLMAPI getAttributeValueINT64( IF_Db * pDb, FLMUINT uiAttrName, FLMINT64 * pi64Num) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num; FLMBOOL bNeg; if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, uiAttrName, &ui64Num, &bNeg))) { return( rc); } return( convertToINT64( ui64Num, bNeg, pi64Num)); } FINLINE RCODE FLMAPI getAttributeValueINT64( IF_Db * pDb, FLMUINT uiAttrName, FLMINT64 * pi64Num, FLMINT64 i64NotFoundDefault) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = getAttributeValueINT64( pDb, uiAttrName, pi64Num))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { return( rc); } *pi64Num = i64NotFoundDefault; rc = NE_XFLM_OK; } return( rc); } FINLINE RCODE FLMAPI getAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrName, FLMUNICODE * puzValueBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL) { return( getAttributeValueText( pDb, uiAttrName, XFLM_UNICODE_TEXT, puzValueBuffer, uiBufferSize, puiCharsReturned, puiBufferBytesUsed)); } RCODE FLMAPI getAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrName, FLMUNICODE ** ppuzValueBuffer); RCODE FLMAPI getAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrName, F_DynaBuf * pDynaBuf); RCODE FLMAPI getAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrName, FLMBYTE * pucValueBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL) { return( getAttributeValueText( pDb, uiAttrName, XFLM_UTF8_TEXT, pucValueBuffer, uiBufferSize, puiCharsReturned, puiBufferBytesUsed)); } RCODE FLMAPI getAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrNameId, FLMBYTE ** ppszValueBuffer); RCODE FLMAPI getAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrName, F_DynaBuf * pDynaBuf); RCODE FLMAPI getAttributeValueBinary( IF_Db * pDb, FLMUINT uiAttrName, void * pvValueBuffer, FLMUINT uiBufferSize, FLMUINT * puiValueLength); RCODE FLMAPI getAttributeValueBinary( IF_Db * pDb, FLMUINT uiAttrName, F_DynaBuf * pDynaBuf); FINLINE RCODE FLMAPI setUINT( IF_Db * pDb, FLMUINT uiValue, FLMUINT uiEncDefId = 0) { return( setNumber64( pDb, 0, uiValue, uiEncDefId)); } FINLINE RCODE FLMAPI setUINT64( IF_Db * pDb, FLMUINT64 ui64Value, FLMUINT uiEncDefId = 0) { return( setNumber64( pDb, 0, ui64Value, uiEncDefId)); } FINLINE RCODE FLMAPI setINT( IF_Db * pDb, FLMINT iValue, FLMUINT uiEncDefId = 0) { return( setNumber64( pDb, iValue, 0, uiEncDefId)); } FINLINE RCODE FLMAPI setINT64( IF_Db * pDb, FLMINT64 i64Value, FLMUINT uiEncDefId = 0) { return( setNumber64( pDb, i64Value, 0, uiEncDefId)); } RCODE FLMAPI setMetaValue( IF_Db * pDb, FLMUINT64 ui64Value); FINLINE RCODE FLMAPI setUnicode( IF_Db * pDb, const FLMUNICODE * puzValue, FLMUINT uiValueLength = 0, FLMBOOL bLast = TRUE, FLMUINT uiEncDefId = 0) { F_Database * pDatabase = ((F_Db *)pDb)->m_pDatabase; if( bLast && !pDatabase->m_pPendingInput) { return( setTextFastPath( (F_Db *)pDb, puzValue, uiValueLength, XFLM_UNICODE_TEXT, uiEncDefId)); } else { return( setTextStreaming( (F_Db *)pDb, puzValue, uiValueLength, XFLM_UNICODE_TEXT, bLast, uiEncDefId)); } } FINLINE RCODE FLMAPI setUTF8( IF_Db * pDb, const FLMBYTE * pszValue, FLMUINT uiValueLength = 0, FLMBOOL bLast = TRUE, FLMUINT uiEncDefId = 0) { F_Database * pDatabase = ((F_Db *)pDb)->m_pDatabase; if( bLast && !pDatabase->m_pPendingInput) { return( setTextFastPath( (F_Db *)pDb, pszValue, uiValueLength, XFLM_UTF8_TEXT, uiEncDefId)); } else { return( setTextStreaming( (F_Db *)pDb, pszValue, uiValueLength, XFLM_UTF8_TEXT, bLast, uiEncDefId)); } } FINLINE RCODE FLMAPI setBinary( IF_Db * pDb, const void * pvValue, FLMUINT uiValueLength, FLMBOOL bLast = TRUE, FLMUINT uiEncDefId = 0) { F_Database * pDatabase = ((F_Db *)pDb)->m_pDatabase; if( bLast && !pDatabase->m_pPendingInput) { return( setBinaryFastPath( (F_Db *)pDb, pvValue, uiValueLength, uiEncDefId)); } else { return( setBinaryStreaming( (F_Db *)pDb, pvValue, uiValueLength, bLast, uiEncDefId)); } } FINLINE RCODE FLMAPI setAttributeValueUINT( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT uiValue, FLMUINT uiEncDefId = 0) { return( setAttributeValueNumber( pDb, uiAttrName, 0, uiValue, uiEncDefId)); } FINLINE RCODE FLMAPI setAttributeValueUINT64( IF_Db * pDb, FLMUINT uiAttrName, FLMUINT64 ui64Value, FLMUINT uiEncDefId = 0) { return( setAttributeValueNumber( pDb, uiAttrName, 0, ui64Value, uiEncDefId)); } FINLINE RCODE FLMAPI setAttributeValueINT( IF_Db * pDb, FLMUINT uiAttrName, FLMINT iValue, FLMUINT uiEncDefId = 0) { return( setAttributeValueNumber( pDb, uiAttrName, iValue, 0, uiEncDefId)); } FINLINE RCODE FLMAPI setAttributeValueINT64( IF_Db * pDb, FLMUINT uiAttrName, FLMINT64 i64Value, FLMUINT uiEncDefId = 0) { return( setAttributeValueNumber( pDb, uiAttrName, i64Value, 0, uiEncDefId)); } RCODE FLMAPI setAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrName, const FLMUNICODE * puzValue, FLMUINT uiEncDefId = 0); RCODE FLMAPI setAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrName, const FLMBYTE * pszValue, FLMUINT uiLength, FLMUINT uiEncDefId = 0); RCODE FLMAPI setAttributeValueBinary( IF_Db * pDb, FLMUINT uiAttrName, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId = 0); RCODE FLMAPI getDocumentNode( IF_Db * pDb, IF_DOMNode ** ppDocument); RCODE FLMAPI getNextDocument( IF_Db * pDb, IF_DOMNode ** ppNextDocument); RCODE FLMAPI getPreviousDocument( IF_Db * pDb, IF_DOMNode ** ppPrevDocument); RCODE FLMAPI getParentNode( IF_Db * pDb, IF_DOMNode ** ppParent); RCODE FLMAPI getFirstChild( IF_Db * pDb, IF_DOMNode ** ppFirstChild); RCODE FLMAPI getLastChild( IF_Db * pDb, IF_DOMNode ** ppLastChild); RCODE FLMAPI getNextSibling( IF_Db * pDb, IF_DOMNode ** ppNextSibling); RCODE FLMAPI getPreviousSibling( IF_Db * pDb, IF_DOMNode ** ppPrevSibling); RCODE FLMAPI getChild( IF_Db * pDb, eDomNodeType eNodeType, IF_DOMNode ** ppChild); RCODE FLMAPI getChildElement( IF_Db * pDb, FLMUINT uiElementNameId, IF_DOMNode ** ppChild, FLMUINT uiFlags = 0); RCODE FLMAPI getSiblingElement( IF_Db * pDb, FLMUINT uiElementNameId, FLMBOOL bNext, IF_DOMNode ** ppSibling); RCODE FLMAPI getAncestorElement( IF_Db * pDb, FLMUINT uiElementNameId, IF_DOMNode ** ppAncestor); RCODE FLMAPI getDescendantElement( IF_Db * pDb, FLMUINT uiElementNameId, IF_DOMNode ** ppDescendant); RCODE FLMAPI insertBefore( IF_Db * pDb, IF_DOMNode * pNewChild, IF_DOMNode * pRefChild); FINLINE RCODE FLMAPI getPrefix( IF_Db * pDb, FLMUNICODE * puzPrefixBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned) { return( getPrefix( TRUE, pDb, (void *)puzPrefixBuffer, uiBufferSize, puiCharsReturned)); } FINLINE RCODE FLMAPI getPrefix( IF_Db * pDb, char * pszPrefixBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned) { return( getPrefix( FALSE, pDb, (void *)pszPrefixBuffer, uiBufferSize, puiCharsReturned)); } RCODE FLMAPI getPrefixId( IF_Db * pDb, FLMUINT * puiPrefixId); FINLINE RCODE FLMAPI setPrefix( IF_Db * pDb, const FLMUNICODE * puzPrefix) { return setPrefix( TRUE, pDb, (void *)puzPrefix); } FINLINE RCODE FLMAPI setPrefix( IF_Db * pDb, const char * pszPrefix) { return setPrefix( FALSE, pDb, (void *)pszPrefix); } RCODE FLMAPI setPrefixId( IF_Db * pDb, FLMUINT uiPrefixId); FINLINE RCODE FLMAPI getNamespaceURI( IF_Db * pDb, FLMUNICODE * puzNamespaceURIBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned) { return getNamespaceURI( TRUE, pDb, (void *)puzNamespaceURIBuffer, uiBufferSize, puiCharsReturned); } FINLINE RCODE FLMAPI getNamespaceURI( IF_Db * pDb, char * pszNamespaceURIBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned) { return getNamespaceURI( FALSE, pDb, (void *)pszNamespaceURIBuffer, uiBufferSize, puiCharsReturned); } FINLINE RCODE FLMAPI getLocalName( IF_Db * pDb, FLMUNICODE * puzLocalNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned) { return( getLocalName( TRUE, pDb, (void *)puzLocalNameBuffer, uiBufferSize, puiCharsReturned)); } FINLINE RCODE FLMAPI getLocalName( IF_Db * pDb, char * pszLocalNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned) { return( getLocalName( FALSE, pDb, (void *)pszLocalNameBuffer, uiBufferSize, puiCharsReturned)); } RCODE FLMAPI getQualifiedName( IF_Db * pDb, FLMUNICODE * puzQualifiedNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned); RCODE FLMAPI getQualifiedName( IF_Db * pDb, char * pszQualifiedNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned); FINLINE RCODE FLMAPI getCollection( IF_Db *, // pDb, FLMUINT * puiCollection) { *puiCollection = m_pCachedNode->getCollection(); return( NE_XFLM_OK); } RCODE FLMAPI createAnnotation( IF_Db * pDb, IF_DOMNode ** ppAnnotation, FLMUINT64 * pui64NodeId = NULL); RCODE FLMAPI getAnnotation( IF_Db * pDb, IF_DOMNode ** ppAnnotation); RCODE FLMAPI getAnnotationId( IF_Db * pDb, FLMUINT64 * pui64AnnotationId); RCODE FLMAPI hasAnnotation( IF_Db * pDb, FLMBOOL * pbHasAnnotation); FINLINE RCODE FLMAPI getIStream( IF_Db * pDb, IF_PosIStream ** ppIStream, FLMUINT * puiDataType = NULL, FLMUINT * puiDataLength = NULL) { return( getIStream( (F_Db *)pDb, NULL, ppIStream, puiDataType, puiDataLength)); } FINLINE RCODE FLMAPI getTextIStream( IF_Db * pDb, IF_PosIStream ** ppIStream, FLMUINT * puiNumChars = NULL) { return( getTextIStream( (F_Db *)pDb, NULL, ppIStream, puiNumChars)); } FLMUINT FLMAPI compareNode( IF_DOMNode * pNode, IF_Db * pDb1, IF_Db * pDb2, char * pszErrBuff, FLMUINT uiErrBuffLen); RCODE FLMAPI isDataLocalToNode( IF_Db * pDb, FLMBOOL * pbDataIsLocal); // Public methods that are not part of the exposed public API in the IF_DOMNode interface FINLINE FLMBOOL isNamespaceDecl( void) { if( getModeFlags() & FDOM_NAMESPACE_DECL) { return( TRUE); } return( FALSE); } RCODE FLMAPI setTextFastPath( F_Db * pDb, const void * pvValue, FLMUINT uiNumBytesInBuffer, eXFlmTextType eTextType, FLMUINT uiEncDefId); RCODE getNodeId( F_Db * pDb, FLMUINT64 * pui64NodeId, FLMUINT * puiAttrNameId); FINLINE FLMUINT64 getNodeId( void) { if( m_uiAttrNameId) { flmAssert( 0); return( 0); } if( m_pCachedNode) { return( m_pCachedNode->getNodeId()); } return( 0); } FINLINE FLMUINT64 getIxNodeId( void) { if( m_pCachedNode) { return( m_pCachedNode->getNodeId()); } return( 0); } FINLINE FLMUINT getCollection( void) { if( m_pCachedNode) { return( m_pCachedNode->getCollection()); } return( 0); } RCODE getIStream( F_Db * pDb, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiDataType = NULL, FLMUINT * puiDataLength = NULL); RCODE getTextIStream( F_Db * pDb, F_NodeBufferIStream * pStackStream, IF_PosIStream ** ppIStream, FLMUINT * puiNumChars = NULL); FINLINE FLMBOOL isQuarantined( void) { return( (getModeFlags() & FDOM_QUARANTINED) ? TRUE : FALSE); } RCODE getAttributeValueNumber( F_Db * pDb, FLMUINT uiAttrName, FLMUINT64 * pui64Num, FLMBOOL * pbNeg); RCODE getAttributeValueText( IF_Db * pDb, FLMUINT uiAttrName, eXFlmTextType eTextType, void * pvBuffer, FLMUINT uiBufSize, FLMUINT * puiCharsReturned, FLMUINT * puiBufferBytesUsed); RCODE setAttributeValueNumber( IF_Db * pDb, FLMUINT uiAttrName, FLMINT64 i64Value, FLMUINT64 ui64Value, FLMUINT uiEncDefId); RCODE deleteAttributes( F_Db * pDb, FLMUINT uiAttrToDelete, FLMUINT uiFlags); private: // Methods RCODE setTextStreaming( F_Db * pDb, const void * pvValue, FLMUINT uiLength, eXFlmTextType eTextType, FLMBOOL bLast, FLMUINT uiEncDefId = 0); RCODE setBinaryStreaming( IF_Db * pDb, const void * pvValue, FLMUINT uiLength, FLMBOOL bLast, FLMUINT uiEncDefId); RCODE setBinaryFastPath( IF_Db * pDb, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId); RCODE clearNodeValue( F_Db * pDb); FINLINE RCODE makeWriteCopy( F_Db * pDb) { return( gv_XFlmSysData.pNodeCacheMgr->makeWriteCopy( pDb, &m_pCachedNode)); } RCODE canSetValue( F_Db * pDb, FLMUINT uiDataType); RCODE isChildTypeValid( eDomNodeType eChildNodeType); RCODE isDescendantOf( F_Db * pDb, F_DOMNode * pAncestor, FLMBOOL * pbDescendant); RCODE getNumber64( F_Db * pDb, FLMUINT64 * pui64Num, FLMBOOL * pbNeg); RCODE storeTextAsNumber( F_Db * pDb, void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId = 0); RCODE storeTextAsBinary( F_Db * pDb, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId = 0); RCODE storeBinaryAsText( F_Db * pDb, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId = 0); FINLINE RCODE syncFromDb( F_Db * pDb) { F_CachedNode * pCachedNode = m_pCachedNode; if( !pCachedNode) { return( RC_SET( NE_XFLM_DOM_NODE_DELETED)); } else if( !pCachedNode->nodeLinkedToDatabase()) { return( _syncFromDb( pDb)); } else if( pDb->m_pDatabase != pCachedNode->getDatabase()) { return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP)); } else if( pCachedNode->isRightVersion( pDb->m_ui64CurrTransID) && !pCachedNode->nodePurged()) { if( m_uiAttrNameId) { if( !pCachedNode->m_uiAttrCount || !pCachedNode->getAttribute( m_uiAttrNameId, NULL)) { return( RC_SET( NE_XFLM_DOM_NODE_DELETED)); } } return( NE_XFLM_OK); } return( _syncFromDb( pDb)); } RCODE _syncFromDb( F_Db * pDb); RCODE unlinkNode( F_Db * pDb, FLMUINT uiFlags); RCODE addModeFlags( F_Db * pDb, FLMUINT uiFlags); RCODE removeModeFlags( F_Db * pDb, FLMUINT uiFlags); FINLINE FLMBOOL canHaveChildren( void) { if( m_pCachedNode) { eDomNodeType eNodeType = getNodeType(); return( (eNodeType == DOCUMENT_NODE || eNodeType == ELEMENT_NODE) ? TRUE : FALSE); } return( FALSE); } RCODE getData( F_Db * pDb, FLMBYTE * pucBuffer, FLMUINT * puiLength); void addNodeToMRUList( F_DOMNode * pNode); void removeNodeFromMRUList( F_DOMNode * pNode); RCODE getNodeFromMRUList( F_Db * pDb, eDomNodeType eNodeType, FLMUINT uiNameId, F_DOMNode ** ppNode); RCODE getNodeFromMRUListById( F_Db * pDb, FLMUINT64 ui64NodeId, F_DOMNode ** ppNode); RCODE getLocalName( FLMBOOL bUnicode, IF_Db * pDb, void * pvLocalName, FLMUINT uiBufSize, FLMUINT * puiCharsReturned); RCODE getPrefix( FLMBOOL bUnicode, IF_Db * pDb, void * pvPrefix, FLMUINT uiBufSize, FLMUINT * puiCharsReturned); RCODE setPrefix( FLMBOOL bUnicode, IF_Db * pDb, void * pvPrefix); FINLINE FLMUINT64 getDocumentId( void) { if( m_pCachedNode) { return( m_pCachedNode->getDocumentId()); } flmAssert( 0); return( 0); } FINLINE FLMBOOL isRootNode( void) { eDomNodeType eNodeType = getNodeType(); if( eNodeType == DOCUMENT_NODE || eNodeType == ELEMENT_NODE) { return( m_pCachedNode->isRootNode()); } return( FALSE); } FINLINE FLMUINT64 getParentId( void) { if( !m_pCachedNode) { return( 0); } if( m_uiAttrNameId) { return( m_pCachedNode->getNodeId()); } return( m_pCachedNode->getParentId()); } FINLINE void setParentId( FLMUINT64 ui64ParentId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setParentId( ui64ParentId); } FINLINE FLMUINT64 getFirstChildId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); if( m_pCachedNode) { return( m_pCachedNode->getFirstChildId()); } return( 0); } FINLINE FLMUINT64 getLastChildId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); if( m_pCachedNode) { return( m_pCachedNode->getLastChildId()); } return( 0); } FINLINE FLMUINT64 getPrevSibId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); if( m_pCachedNode) { return( m_pCachedNode->getPrevSibId()); } return( 0); } FINLINE void setPrevSibId( FLMUINT64 ui64PrevSibId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setPrevSibId( ui64PrevSibId); } FINLINE FLMUINT64 getNextSibId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); if( m_pCachedNode) { return( m_pCachedNode->getNextSibId()); } return( 0); } FINLINE void setNextSibId( FLMUINT64 ui64NextSibId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setNextSibId( ui64NextSibId); } FINLINE FLMUINT getDataChildCount( void) { flmAssert( getNodeType() == ELEMENT_NODE); if( m_pCachedNode) { return( m_pCachedNode->getDataChildCount()); } return( 0); } FINLINE void setDataChildCount( FLMUINT uiDataChildCount) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() == ELEMENT_NODE); m_pCachedNode->setDataChildCount( uiDataChildCount); } FINLINE FLMUINT getModeFlags( void) { if( m_uiAttrNameId) { return( m_pCachedNode->getModeFlags( m_uiAttrNameId)); } else if( m_pCachedNode) { return( m_pCachedNode->getModeFlags()); } return( 0); } RCODE getNamespaceURI( FLMBOOL bUnicode, IF_Db * pDb, void * pvNamespaceURI, FLMUINT uiBufSize, FLMUINT * puiCharsReturned); RCODE setNumber64( IF_Db * pDb, FLMINT64 i64Value, FLMUINT64 ui64Value, FLMUINT uiEncDefId = 0); RCODE setStorageValue( F_Db * pDb, void * pvValue, FLMUINT uiValueLen, FLMUINT uiEncDefId, FLMBOOL bLast); FINLINE void setBlkAddr( FLMUINT32 ui32BlkAddr) { m_pCachedNode->setBlkAddr( ui32BlkAddr); } FINLINE void setOffsetIndex( FLMUINT uiOffsetIndex) { m_pCachedNode->setOffsetIndex( uiOffsetIndex); } FINLINE FLMUINT getOffsetIndex( void) { return( m_pCachedNode->getOffsetIndex()); } FINLINE FLMUINT32 getBlkAddr( void) { return( m_pCachedNode->getBlkAddr()); } FINLINE void unsetNodeDirtyAndNew( F_Db * pDb) { m_pCachedNode->unsetNodeDirtyAndNew( pDb); } FINLINE FLMUINT getPrefixId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getPrefixId()); } FINLINE void setPrefixId( FLMUINT uiPrefixId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setPrefixId( uiPrefixId); } FINLINE FLMUINT getNameId( void) { if( m_uiAttrNameId) { return( m_uiAttrNameId); } return( m_pCachedNode->getNameId()); } FINLINE void setNameId( FLMUINT uiNameId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setNameId( uiNameId); } FINLINE FLMUINT getDataType( void) { if( m_uiAttrNameId) { F_AttrItem * pAttrItem; if( (pAttrItem = m_pCachedNode->getAttribute( m_uiAttrNameId, NULL)) == NULL) { flmAssert( 0); return( XFLM_UNKNOWN_TYPE); } return( pAttrItem->m_uiDataType); } return( m_pCachedNode->getDataType()); } FINLINE void setDataType( FLMUINT uiDataType) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setDataType( uiDataType); } FINLINE FLMUINT getDataLength( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getDataLength()); } FINLINE void setDataLength( FLMUINT uiDataLength) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setDataLength( uiDataLength); } FINLINE FLMBYTE * getDataPtr( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getDataPtr()); } FINLINE FLMBOOL getQuickNumber64( FLMUINT64 * pui64Num, FLMBOOL * pbNeg) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getQuickNumber64( pui64Num, pbNeg)); } FINLINE FLMBOOL nodeIsDirty( void) { return( m_pCachedNode->nodeIsDirty()); } FINLINE FLMBOOL nodeUncommitted( void) { return( m_pCachedNode->nodeUncommitted()); } FINLINE FLMUINT getStreamUseCount( void) { return( m_pCachedNode->getStreamUseCount()); } FINLINE FLMUINT64 getAnnotationId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getAnnotationId()); } FINLINE void setAnnotationId( FLMUINT64 ui64AnnotationId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setAnnotationId( ui64AnnotationId); } FINLINE void setLastChildId( FLMUINT64 ui64LastChildId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setLastChildId( ui64LastChildId); } FINLINE void setFirstChildId( FLMUINT64 ui64FirstChildId) { flmAssert( nodeUncommitted()); flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setFirstChildId( ui64FirstChildId); } FINLINE FLMUINT getEncDefId( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getEncDefId()); } FINLINE F_Database * getDatabase( void) { if( m_pCachedNode) { return( m_pCachedNode->getDatabase()); } return( NULL); } FINLINE RCODE openPendingInput( F_Db * pDb, FLMUINT uiNewDataType) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->openPendingInput( pDb, uiNewDataType)); } FINLINE FLMBOOL findChildElm( FLMUINT uiChildElmNameId, FLMUINT * puiInsertPos) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->findChildElm( uiChildElmNameId, puiInsertPos)); } FINLINE RCODE removeChildElm( FLMUINT uiChildElmOffset) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->removeChildElm( uiChildElmOffset)); } FINLINE FLMUINT getChildElmCount( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getChildElmCount()); } FINLINE FLMUINT64 getChildElmNodeId( FLMUINT uiChildElmOffset) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getChildElmNodeId( uiChildElmOffset)); } FINLINE FLMUINT getChildElmNameId( FLMUINT uiChildElmOffset) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getChildElmNameId( uiChildElmOffset)); } FINLINE FLMBOOL hasAttributes( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->hasAttributes()); } FINLINE void setFlags( FLMUINT uiFlags) { flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setFlags( uiFlags); } FINLINE void unsetFlags( FLMUINT uiFlags) { flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->unsetFlags( uiFlags); } FINLINE void setEncDefId( FLMUINT uiEncDefId) { flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setEncDefId( uiEncDefId); } FINLINE RCODE flushPendingInput( F_Db * pDb, FLMBOOL bLast) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->flushPendingInput( pDb, bLast)); } FINLINE FLMUINT getDataBufSize( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getDataBufSize()); } FINLINE RCODE resizeDataBuffer( FLMUINT uiSize, FLMBOOL bMutexAlreadyLocked) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->resizeDataBuffer( uiSize, bMutexAlreadyLocked)); } FINLINE RCODE headerToBuf( FLMBOOL bFixedSizeHeader, FLMBYTE * pucBuf, FLMUINT * puiHeaderSize, XFLM_NODE_INFO * pNodeInfo, F_Db * pDb) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->headerToBuf( bFixedSizeHeader, pucBuf, puiHeaderSize, pNodeInfo, pDb)); } FINLINE FLMINT64 getQuickINT64( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getQuickINT64()); } FINLINE FLMUINT64 getQuickUINT64( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getQuickUINT64()); } FINLINE void setUINT64( FLMUINT64 ui64Value) { flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setUINT64( ui64Value); } FINLINE void setINT64( FLMINT64 i64Value) { flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setINT64( i64Value); } FINLINE FLMUINT64 getMetaValue( void) { flmAssert( getNodeType() != ATTRIBUTE_NODE); return( m_pCachedNode->getMetaValue()); } FINLINE void setMetaValue( FLMUINT64 ui64Value) { flmAssert( getNodeType() != ATTRIBUTE_NODE); m_pCachedNode->setMetaValue( ui64Value); } FINLINE RCODE checkAttrList( void) { if( !m_pCachedNode) { return( RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND)); } else if( m_pCachedNode->getNodeType() != ELEMENT_NODE) { return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP)); } else if( !m_pCachedNode->m_uiAttrCount) { return( RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND)); } return( NE_XFLM_OK); } // Data F_CachedNode * m_pCachedNode; F_DOMNode * m_pNextInPool; // Valid only if this is an attribute node FLMUINT m_uiAttrNameId; friend class F_Db; friend class F_Database; friend class F_BTreeIStream; friend class F_NodeBufferIStream; friend class F_Dict; friend class F_XMLImport; friend class F_NodePool; friend class F_DbRebuild; friend class F_Query; friend class FSCollectionCursor; friend class F_NodeCacheMgr; friend class F_Rfl; friend class F_CachedNode; friend class F_OldNodeList; friend class F_DbCheck; friend class F_NodeInfo; }; /*=========================================================================== Desc: Pool manager for DOM nodes ===========================================================================*/ class F_NodePool : public F_Object { public: F_NodePool() { m_pFirstBTreeIStream = NULL; m_hMutex = F_MUTEX_NULL; } ~F_NodePool(); RCODE setup( void); RCODE allocBTreeIStream( F_BTreeIStream ** ppBTreeIStream); void insertBTreeIStream( F_BTreeIStream * pBTreeIStream); private: F_BTreeIStream * m_pFirstBTreeIStream; F_MUTEX m_hMutex; friend class F_DOMNode; friend class F_BTreeIStream; friend class F_NodeBufferIStream; }; typedef struct { FLMUINT uiCollectionNum; FLMUINT64 ui64HighestNodeIdFound; FLMUINT64 ui64HighestNextNodeIdFound; FLMUINT uiNumNextNodeIdsFound; FLMUINT uiEncId; } COLLECTION_INFO, * COLLECTION_INFO_p; typedef struct Recov_Dict_Node * RECOV_DICT_NODE_p; typedef struct Recov_Dict_Info * RECOV_DICT_INFO_p; typedef struct Recov_Dict_Node { RECOV_DICT_NODE_p pNext; F_DOMNode * pNode; FLMUINT64 ui64NodeId; FLMUINT uiElmOffset; FLMBOOL bAdded; FLMBOOL bGotFromDataCollection; FLMUINT32 ui32BlkAddress; } RECOV_DICT_NODE; typedef struct Recov_Dict_Info { RECOV_DICT_NODE * pRecovNodes; F_Pool * pPool; } RECOV_DICT_INFO; typedef struct RSIxKeyTag { FLMBYTE pucRSKeyBuf[ XFLM_MAX_KEY_SIZE]; FLMUINT uiRSKeyLen; FLMBYTE pucRSDataBuf[ XFLM_MAX_KEY_SIZE]; FLMUINT uiRSDataLen; } RS_IX_KEY; /****************************************************************************** Desc: ******************************************************************************/ class F_KeyCollector : public F_Object { public: F_KeyCollector( F_DbCheck * pDbCheck) { m_pDbCheck = pDbCheck; m_ui64TotalKeys = 0; } ~F_KeyCollector(){} RCODE addKey( F_Db * pDb, IXD * pIxd, KREF_ENTRY * pKref); FLMUINT64 getTotalKeys() { return m_ui64TotalKeys; } private: F_DbCheck * m_pDbCheck; FLMUINT64 m_ui64TotalKeys; friend class F_DbCheck; }; typedef struct { F_DOMNode * pNode; // DOM Node to examine to see if it contains // dictionary information. FLMUINT uiCollection; // Collection the node came from. FLMUINT64 ui64NodeId; // NodeId of node. } CHK_RECORD, * CHK_RECORD_p; typedef struct { FLMUINT64 ui64BytesUsed; FLMUINT64 ui64ElementCount; FLMUINT64 ui64ContElementCount; FLMUINT64 ui64ContElmBytes; FLMUINT uiBlockCount; FLMINT32 i32ErrCode; FLMUINT uiNumErrors; } BLOCK_INFO; typedef struct { FLMUINT64 ui64KeyCount; BLOCK_INFO BlockInfo; } LEVEL_INFO; typedef struct { FLMUINT uiLfNum; eLFileType eLfType; FLMUINT uiRootBlk; FLMUINT uiNumLevels; LEVEL_INFO * pLevelInfo; } LF_HDR; /****************************************************************************** Desc: ******************************************************************************/ typedef struct State_Info { F_Db * pDb; FLMUINT32 ui32BlkAddress; FLMUINT32 ui32NextBlkAddr; FLMUINT32 ui32PrevBlkAddr; FLMUINT32 ui32LastChildAddr; FLMUINT uiElmLastFlag; FLMUINT64 ui64KeyCount; FLMUINT64 ui64KeyRefs; FLMUINT uiBlkType; FLMUINT uiLevel; FLMUINT uiRootLevel; F_COLLECTION * pCollection; FLMUINT uiElmOffset; FLMBYTE * pucElm; FLMUINT uiElmLen; FLMUINT uiElmKeyLenOffset; // New FLMUINT uiElmKeyOffset; // New FLMBYTE * pucElmKey; FLMUINT uiElmKeyLen; FLMUINT uiCurKeyLen; // Used in Rebuild... FLMUINT uiElmDataLenOffset; // New FLMUINT uiElmDataOffset; // uiElmRecOffset; FLMUINT uiElmDataLen; // uiElmRecLen; FLMBYTE * pucElmData; // pucElmRec; FLMUINT uiElmCounts; // New FLMUINT uiElmCountsOffset; // New FLMUINT uiElmOADataLenOffset; FLMUINT uiElmOADataLen; FLMUINT64 ui64ElmNodeId; FLMBOOL bValidKey; F_BLK_HDR * pBlkHdr; F_NodeVerifier * pNodeVerifier; BLOCK_INFO BlkInfo; F_BtResultSet * pNodeRS; F_BtResultSet * pXRefRS; FLMUINT uiCurrLf; } STATE_INFO; /****************************************************************************** Desc: ******************************************************************************/ class F_NodeVerifier : public F_Object { public: F_NodeVerifier(); ~F_NodeVerifier(); void Reset( STATE_INFO * pStateInfo); RCODE AddData( FLMUINT64 ui64NodeId, void * pucData, FLMUINT uiDataLen); RCODE finalize( F_Db * pDb, F_Dict * pDict, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMBOOL bSkipDOMLinkCheck, FLMINT32 * pi32ElmErrCodeRV); FINLINE void setupNodeRS( F_BtResultSet * pRS) { m_pRS = pRS; } FINLINE void setupXRefRS( F_BtResultSet * pXRefRS) { m_pXRefRS = pXRefRS; } private: RCODE verifyNameId( F_Db * pDb, eDomNodeType eNodeType, FLMUINT uiNameId, F_NameTable * pNameTable, FLMINT32 * pi32ErrCode); RCODE verifyPrefixId( F_Db * pDb, FLMUINT uiPrefixId, F_NameTable * pNameTable, FLMINT32 * pi32ErrCode); RCODE checkForIndexes( F_Db * pDb, F_Dict * pDict, FLMUINT uiCollection); FLMBOOL m_bFinalizeCalled; FLMBYTE * m_pucBuf; FLMBYTE m_ucBuf[ MAX_DOM_HEADER_SIZE]; FLMUINT m_uiBufSize; FLMUINT m_uiBytesInBuf; FLMUINT m_uiOverallLength; F_NODE_INFO m_nodeInfo; F_BtResultSet * m_pRS; F_BtResultSet * m_pXRefRS; }; /****************************************************************************** Desc: ******************************************************************************/ typedef struct { F_NODE_INFO nodeInfo; FLMUINT uiStorageFlags; FLMUINT uiDataBufSize; FLMBYTE * pucData; FLMBOOL bUseFilename; FLMBOOL bGotFromDataCollection; FLMBOOL bProcessed; FLMBYTE ucSortKey; } REBUILD_NODE_INFO; /****************************************************************************** Desc: ******************************************************************************/ class RebuildNodeInfoStack : public F_Object { public: RebuildNodeInfoStack(); ~RebuildNodeInfoStack(); RCODE setup( void); RCODE push( REBUILD_NODE_INFO * pRebuildNodeInfo); RCODE pop( REBUILD_NODE_INFO * pRebuildNodeInfo); void reset( void); private: REBUILD_NODE_INFO * m_pStack; FLMUINT m_uiStackSize; FLMUINT m_uiNextPos; FLMBOOL m_bSetupCalled; }; /****************************************************************************** Desc: ******************************************************************************/ typedef struct { FLMUINT uiStartOfEntry; FLMUINT uiEndOfEntry; } BlkStruct; /****************************************************************************** Desc: DbCheck object for verifying the condition of the database. ******************************************************************************/ class F_DbCheck : public F_Object { public: F_DbCheck( void) { m_bSkipDOMLinkCheck = FALSE; m_pXRefRS = NULL; m_pDb = NULL; m_pIxd = NULL; m_pCollection = NULL; m_pLFile = NULL; m_pDbInfo = NULL; m_pBtPool = NULL; m_pResultSetDb = NULL; m_pRandGen = NULL; m_pDbCheckStatus = NULL; f_memset( m_szResultSetDibName, 0, sizeof( m_szResultSetDibName)); m_bPhysicalCorrupt = FALSE; m_bIndexCorrupt = FALSE; m_LastStatusRc = NE_XFLM_OK; m_uiFlags = 0; m_bStartedUpdateTrans = FALSE; m_puiIxArray = NULL; m_pIxRSet = NULL; m_bGetNextRSKey = FALSE; f_memset( &m_IxKey1, 0, sizeof( m_IxKey1)); f_memset( &m_IxKey2, 0, sizeof( m_IxKey2)); m_pCurrRSKey = NULL; m_pPrevRSKey = NULL; m_pBlkEntries = NULL; m_uiBlkEntryArraySize = 0; } ~F_DbCheck(); RCODE dbCheck( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiFlags, IF_DbInfo ** ppDbInfo, IF_DbCheckStatus * pDbCheck); private: FINLINE RCODE chkCallProgFunc( void) { if (m_pDbCheckStatus && RC_OK( m_LastStatusRc)) { m_LastStatusRc = m_pDbCheckStatus->reportProgress( &m_Progress); } return( m_LastStatusRc); } RCODE chkReportError( FLMINT32 i32ErrCode, FLMUINT32 ui32ErrLocale, FLMUINT32 ui32ErrLfNumber, FLMUINT32 ui32ErrLfType, FLMUINT32 ui32ErrBTreeLevel, FLMUINT32 ui32ErrBlkAddress, FLMUINT32 ui32ErrParentBlkAddress, FLMUINT32 ui32ErrElmOffset, FLMUINT64 ui64ErrNodeId); FINLINE XFLM_PROGRESS_CHECK_INFO * getProgress( void) { return( &m_Progress); } RCODE getBtResultSet( F_BtResultSet ** ppBtRSet); RCODE createAndOpenResultSetDb( void); RCODE closeAndDeleteResultSetDb( void); RCODE getDictInfo( void); RCODE verifyBlkChain( BLOCK_INFO * pBlkInfo, FLMUINT uiLocale, FLMUINT uiFirstBlkAddr, FLMUINT uiBlkType, FLMBOOL * pbStartOverRV); RCODE verifyLFHBlocks( FLMBOOL * pbStartOverRV); RCODE verifyAvailList( FLMBOOL * pbStartOverRV); RCODE blkRead( FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, F_CachedBlock ** ppSCache, FLMINT32 * pi32BlkErrCodeRV); RCODE verifySubTree( STATE_INFO * pParentState, STATE_INFO * pStateInfo, FLMUINT uiBlkAddress, FLMBYTE ** ppucResetKey, FLMUINT uiResetKeyLen, FLMUINT64 ui64ResetNodeId); RCODE buildIndexKeyList( FLMUINT64 * pui64TotalKeys); RCODE verifyBTrees( FLMBOOL * pbStartOverRV); RCODE setupLfTable(); RCODE setupIxInfo( void); RCODE getLfInfo( LF_HDR * pLogicalFile, LFILE * pLFile); RCODE verifyNodePointers( STATE_INFO * pStateInfo, FLMINT32 * pi32ErrCode); RCODE verifyDOChain( STATE_INFO * pParentState, FLMUINT uiBlkAddr, FLMINT32 * pi32ElmErrCode); RCODE chkGetNextRSKey( void); RCODE verifyIXRSet( STATE_INFO * pStateInfo); RCODE resolveIXMissingKey( STATE_INFO * pStateInfo); RCODE verifyComponentInDoc( ICD * pIcd, FLMUINT uiComponent, F_DataVector * pKey, FLMBOOL * pbInDoc); RCODE getKeySource( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL * pbKeyInDoc, FLMBOOL * pbKeyInIndex); RCODE resolveRSetMissingKey( STATE_INFO * pStateInfo); RCODE chkVerifyKeyExists( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL * pbFoundRV); RCODE addDelKeyRef( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bDelete); RCODE reportIxError( STATE_INFO * pStateInfo, FLMINT32 i32ErrCode, FLMBYTE * pucErrKey, FLMUINT uiErrKeyLen, FLMBOOL * pbFixErrRV); RCODE startUpdate( void); RCODE keyToVector( FLMBYTE * pucKey, FLMUINT uiKeyLen, IF_DataVector ** ppKeyRV); RCODE verifyIXRefs( STATE_INFO * pStateInfo, FLMUINT64 ui64ResetNodeId); RCODE verifyBlockStructure( FLMUINT uiBlockSize, F_BTREE_BLK_HDR * pBlkHdr); RCODE chkEndUpdate( void); F_Db * m_pDb; F_COLLECTION * m_pCollection; IXD * m_pIxd; LFILE * m_pLFile; F_DbInfo * m_pDbInfo; FLMBOOL m_bSkipDOMLinkCheck; F_BtResultSet * m_pXRefRS; F_BtPool * m_pBtPool; IF_RandomGenerator * m_pRandGen; char m_szResultSetDibName [F_PATH_MAX_SIZE]; F_Db * m_pResultSetDb; IF_DbCheckStatus * m_pDbCheckStatus; FLMBOOL m_bPhysicalCorrupt; FLMBOOL m_bIndexCorrupt; XFLM_PROGRESS_CHECK_INFO m_Progress; RCODE m_LastStatusRc; FLMUINT m_uiFlags; FLMBOOL m_bStartedUpdateTrans; FLMUINT * m_puiIxArray; F_BtResultSet * m_pIxRSet; FLMBOOL m_bGetNextRSKey; RS_IX_KEY m_IxKey1; RS_IX_KEY m_IxKey2; RS_IX_KEY * m_pCurrRSKey; RS_IX_KEY * m_pPrevRSKey; BlkStruct * m_pBlkEntries; FLMUINT m_uiBlkEntryArraySize; friend class F_DbInfo; friend class F_KeyCollector; }; // Check Error Codes /* ** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE ** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP */ #define FLM_BAD_CHAR 1 #define FLM_BAD_ASIAN_CHAR 2 #define FLM_BAD_CHAR_SET 3 #define FLM_BAD_TEXT_FIELD 4 #define FLM_BAD_NUMBER_FIELD 5 #define FLM_BAD_FIELD_TYPE 6 #define FLM_BAD_IX_DEF 7 #define FLM_MISSING_REQ_KEY_FIELD 8 #define FLM_BAD_TEXT_KEY_COLL_CHAR 9 #define FLM_BAD_TEXT_KEY_CASE_MARKER 10 #define FLM_BAD_NUMBER_KEY 11 #define FLM_BAD_BINARY_KEY 12 #define FLM_BAD_CONTEXT_KEY 13 #define FLM_BAD_KEY_FIELD_TYPE 14 //#define Not_Used_15 15 //#define Not_Used_16 16 //#define Not_Used_17 17 #define FLM_BAD_KEY_LEN 18 #define FLM_BAD_LFH_LIST_PTR 19 #define FLM_BAD_LFH_LIST_END 20 #define FLM_INCOMPLETE_NODE 21 // Not really an error. Part of a field has been split across entries #define FLM_BAD_BLK_END 22 #define FLM_KEY_COUNT_MISMATCH 23 #define FLM_REF_COUNT_MISMATCH 24 #define FLM_BAD_CONTAINER_IN_KEY 25 #define FLM_BAD_BLK_HDR_ADDR 26 #define FLM_BAD_BLK_HDR_LEVEL 27 #define FLM_BAD_BLK_HDR_PREV 28 /* ** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE ** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP */ #define FLM_BAD_BLK_HDR_NEXT 29 #define FLM_BAD_BLK_HDR_TYPE 30 #define FLM_BAD_BLK_HDR_ROOT_BIT 31 #define FLM_BAD_BLK_HDR_BLK_END 32 #define FLM_BAD_BLK_HDR_LF_NUM 33 #define FLM_BAD_AVAIL_LIST_END 34 #define FLM_BAD_PREV_BLK_NEXT 35 #define FLM_BAD_FIRST_ELM_FLAG 36 // NOTE: This is only needed during rebuild #define FLM_BAD_LAST_ELM_FLAG 37 // NOTE: This is only needed during rebuild #define FLM_BAD_LEM 38 #define FLM_BAD_ELM_LEN 39 #define FLM_BAD_ELM_KEY_SIZE 40 #define FLM_BAD_ELM_KEY 41 #define FLM_BAD_ELM_KEY_ORDER 42 #define FLM_BAD_ELM_KEY_COMPRESS 43 // NOTE: 5.x keys are not compressed #define FLM_BAD_CONT_ELM_KEY 44 #define FLM_NON_UNIQUE_FIRST_ELM_KEY 45 #define FLM_BAD_ELM_OFFSET 46 #define FLM_BAD_ELM_INVALID_LEVEL 47 #define FLM_BAD_ELM_FLD_NUM 48 #define FLM_BAD_ELM_FLD_LEN 49 #define FLM_BAD_ELM_FLD_TYPE 50 #define FLM_BAD_ELM_END 51 #define FLM_BAD_PARENT_KEY 52 #define FLM_BAD_ELM_DOMAIN_SEN 53 #define FLM_BAD_ELM_BASE_SEN 54 #define FLM_BAD_ELM_IX_REF 55 #define FLM_BAD_ELM_ONE_RUN_SEN 56 #define FLM_BAD_ELM_DELTA_SEN 57 #define FLM_BAD_ELM_DOMAIN 58 /* ** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE ** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP */ #define FLM_BAD_LAST_BLK_NEXT 59 #define FLM_BAD_FIELD_PTR 60 #define FLM_REBUILD_REC_EXISTS 61 #define FLM_REBUILD_KEY_NOT_UNIQUE 62 #define FLM_NON_UNIQUE_ELM_KEY_REF 63 #define FLM_OLD_VIEW 64 #define FLM_COULD_NOT_SYNC_BLK 65 #define FLM_IX_REF_REC_NOT_FOUND 66 #define FLM_IX_KEY_NOT_FOUND_IN_REC 67 #define FLM_KEY_NOT_IN_KEY_REFSET 68 #define FLM_BAD_BLK_CHECKSUM 69 #define FLM_BAD_LAST_DRN 70 #define FLM_BAD_FILE_SIZE 71 #define FLM_BAD_FIRST_LAST_ELM_FLAG 72 #define FLM_BAD_DATE_FIELD 73 #define FLM_BAD_TIME_FIELD 74 #define FLM_BAD_TMSTAMP_FIELD 75 #define FLM_BAD_DATE_KEY 76 #define FLM_BAD_TIME_KEY 77 #define FLM_BAD_TMSTAMP_KEY 78 #define FLM_BAD_BLOB_FIELD 79 /* ** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE ** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP */ #define FLM_BAD_PCODE_IXD_TBL 80 #define FLM_NODE_QUARANTINED 81 #define FLM_BAD_BLK_TYPE 82 #define FLM_BAD_ELEMENT_CHAIN 83 #define FLM_BAD_ELM_EXTRA_DATA 84 #define FLM_BAD_BLOCK_STRUCTURE 85 #define FLM_BAD_ROOT_PARENT 86 #define FLM_BAD_ROOT_LINK 87 #define FLM_BAD_PARENT_LINK 88 #define FLM_BAD_INVALID_ROOT 89 #define FLM_BAD_FIRST_CHILD_LINK 90 #define FLM_BAD_LAST_CHILD_LINK 91 #define FLM_BAD_PREV_SIBLING_LINK 92 #define FLM_BAD_NEXT_SIBLING_LINK 93 #define FLM_BAD_ANNOTATION_LINK 94 #define FLM_UNSUPPORTED_NODE_TYPE 95 #define FLM_BAD_INVALID_NAME_ID 96 #define FLM_BAD_INVALID_PREFIX_ID 97 #define FLM_BAD_DATA_BLOCK_COUNT 98 #define FLM_BAD_AVAIL_SIZE 99 #define FLM_BAD_NODE_TYPE 100 #define FLM_BAD_CHILD_ELM_COUNT 101 #define FLM_NUM_CORRUPT_ERRORS 101 /* ** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE ** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP */ class F_DbInfo : public IF_DbInfo { public: F_DbInfo() { m_uiLogicalCorruptions = 0; m_uiLogicalRepairs = 0; m_ui64FileSize = 0; m_uiNumIndexes = 0; m_uiNumCollections = 0; m_uiNumLogicalFiles = 0; m_pLogicalFiles = NULL; f_memset( &m_dbHdr, 0, sizeof( m_dbHdr)); f_memset( &m_AvailBlocks, 0, sizeof( m_AvailBlocks)); f_memset( &m_LFHBlocks, 0, sizeof( m_LFHBlocks)); } virtual ~F_DbInfo() { freeLogicalFiles(); } FINLINE void freeLogicalFiles( void) { FLMUINT uiLoop; if (m_pLogicalFiles) { for (uiLoop = 0; uiLoop < m_uiNumLogicalFiles; uiLoop++) { if (m_pLogicalFiles [uiLoop].pLevelInfo) { f_free( &m_pLogicalFiles [uiLoop].pLevelInfo); } } f_free( &m_pLogicalFiles); } m_uiNumLogicalFiles = 0; m_uiNumIndexes = 0; m_uiNumCollections = 0; } FINLINE FLMUINT FLMAPI getNumCollections( void) { return( m_uiNumCollections); } FINLINE FLMUINT FLMAPI getNumIndexes( void) { return( m_uiNumIndexes); } FINLINE FLMUINT FLMAPI getNumLogicalFiles( void) { return( m_uiNumLogicalFiles); } FINLINE FLMUINT64 FLMAPI getFileSize( void) { return( m_ui64FileSize); } FINLINE XFLM_DB_HDR * FLMAPI getDbHdr( void) { return( &m_dbHdr); } FINLINE void FLMAPI getAvailBlockStats( FLMUINT64 * pui64BytesUsed, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors) { *pui64BytesUsed = m_LFHBlocks.ui64BytesUsed; *puiBlockCount = m_AvailBlocks.uiBlockCount; *pi32LastError = m_AvailBlocks.i32ErrCode; *puiNumErrors = m_AvailBlocks.uiNumErrors; } FINLINE void FLMAPI getLFHBlockStats( FLMUINT64 * pui64BytesUsed, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors) { *pui64BytesUsed = m_LFHBlocks.ui64BytesUsed; *puiBlockCount = m_LFHBlocks.uiBlockCount; *pi32LastError = m_LFHBlocks.i32ErrCode; *puiNumErrors = m_LFHBlocks.uiNumErrors; } void FLMAPI getBTreeInfo( FLMUINT uiNthLogicalFile, FLMUINT * puiLfNum, eLFileType * peLfType, FLMUINT * puiRootBlkAddress, FLMUINT * puiNumLevels); void FLMAPI getBTreeBlockStats( FLMUINT uiNthLogicalFile, FLMUINT uiLevel, FLMUINT64 * pui64KeyCount, FLMUINT64 * pui64BytesUsed, FLMUINT64 * pui64ElementCount, FLMUINT64 * pui64ContElementCount, FLMUINT64 * pui64ContElmBytes, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors); private: FLMUINT m_uiLogicalCorruptions; FLMUINT m_uiLogicalRepairs; FLMUINT64 m_ui64FileSize; FLMUINT m_uiNumIndexes; FLMUINT m_uiNumCollections; FLMUINT m_uiNumLogicalFiles; LF_HDR * m_pLogicalFiles; XFLM_DB_HDR m_dbHdr; BLOCK_INFO m_AvailBlocks; BLOCK_INFO m_LFHBlocks; friend class F_DbCheck; }; typedef struct { FLMUINT uiIndexNum; FLMUINT uiCollection; FLMUINT64 ui64DocId; } DOC_IXD_XREF; // Verifier Flags #define CHK_NEXT_SIBLING_VERIFIED 0x0001 #define CHK_PREV_SIBLING_VERIFIED 0x0002 #define CHK_FIRST_CHILD_VERIFIED 0x0004 #define CHK_LAST_CHILD_VERIFIED 0x0008 #define CHK_PARENT_VERIFIED 0x0010 #define CHK_ANNOTATION_VERIFIED 0x0020 #define CHK_ROOT_VERIFIED 0x0040 // Bitmap field order #define CHK_BM_ROOT_ID 0x0001 #define CHK_BM_PARENT_ID 0x0002 #define CHK_BM_PREV_SIBLING 0x0004 #define CHK_BM_NEXT_SIBLING 0x0008 #define CHK_BM_FIRST_CHILD 0x0010 #define CHK_BM_LAST_CHILD 0x0020 #define CHK_BM_ANNOTATION 0x0040 #define CHK_MAX_BM_ENTRIES 8 typedef struct { FLMUINT16 ui16Flags; // Verify Flags to record visits/verifies FLMUINT16 ui16BitMap; // Holds a map of present fields FLMUINT64 ui64NodeId; } NODE_RS_HDR; typedef struct { NODE_RS_HDR hdr; FLMUINT64 ui64FieldArray[ CHK_MAX_BM_ENTRIES]; } NODE_RS_ENTRY; #define REBUILD_BLK_SIZE (1024 * 50) #define REBUILD_RSET_ENTRY_SIZE 21 /*============================================================================= Desc: Class to rebuild a broken database. =============================================================================*/ class F_DbRebuild : public F_Object { public: // Constructor and Destructor F_DbRebuild( void) { m_pDb = NULL; m_pSFileHdl = NULL; } ~F_DbRebuild() { } RCODE dbRebuild( const char * pszSourceDbPath, const char * pszSourceDataDir, const char * pszDestDbPath, const char * pszDestDataDir, const char * pszDestRflDir, const char * pDictPath, const char * pszPassword, XFLM_CREATE_OPTS * pCreateOpts, FLMUINT64 * pui64TotNodes, FLMUINT64 * pui64NodesRecov, FLMUINT64 * pui64QuarantinedNodes, IF_DbRebuildStatus * pRebuildStatus); FINLINE FLMUINT getBlockSize( void) { return( m_dbHdr.ui16BlockSize); } FINLINE RCODE reportStatus( FLMBOOL bForce = FALSE) { RCODE rc = NE_XFLM_OK; if( m_pRebuildStatus) { FLMUINT uiCurrentTime = FLM_GET_TIMER(); FLMUINT uiElapTime = FLM_ELAPSED_TIME( uiCurrentTime, m_uiLastStatusTime); uiElapTime = FLM_TIMER_UNITS_TO_SECS( uiElapTime); if( bForce || uiElapTime >= 1) { m_uiLastStatusTime = uiCurrentTime; m_callbackData.bStartFlag = FALSE; if( RC_BAD( rc = m_pRebuildStatus->reportRebuild( &m_callbackData))) { m_cbrc = rc; goto Exit; } } } Exit: return( rc); } void incrBytesExamined() { m_callbackData.ui64BytesExamined += m_dbHdr.ui16BlockSize; } private: RCODE getDatabaseSize( void); RCODE rebuildDatabase( void); RCODE recoverNodes( FLMBOOL bRecoverDictionary); FINLINE FLMBYTE getRSetPrefix( FLMUINT uiNameId) { if( uiNameId == ELM_PREFIX_TAG) { return( 1); } if( uiNameId == ELM_ATTRIBUTE_TAG) { return( 2); } if( uiNameId == ELM_ELEMENT_TAG) { return( 3); } if( uiNameId == ELM_COLLECTION_TAG) { return( 4); } return( 5); } FINLINE void buildRSetEntry( FLMBYTE ucPrefix, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiBlockAddr, FLMUINT uiElmNumber, FLMBYTE * pucBuffer) { pucBuffer[ 0] = ucPrefix; f_UINT32ToBigEndian( (FLMUINT32)uiCollection, &pucBuffer[ 1]); f_UINT64ToBigEndian( ui64NodeId, &pucBuffer[ 5]); f_UINT32ToBigEndian( (FLMUINT32)uiBlockAddr, &pucBuffer[ 13]); f_UINT32ToBigEndian( (FLMUINT32)uiElmNumber, &pucBuffer[ 17]); } FINLINE void extractRSetEntry( FLMBYTE * pucBuffer, FLMUINT * puiCollection, FLMUINT64 * pui64NodeId, FLMUINT * puiBlockAddr, FLMUINT * puiElmNumber) { if( puiCollection) { *puiCollection = f_bigEndianToUINT32( &pucBuffer[ 1]); } if( pui64NodeId) { *pui64NodeId = f_bigEndianToUINT64( &pucBuffer[ 5]); } if( puiBlockAddr) { *puiBlockAddr = f_bigEndianToUINT32( &pucBuffer[ 13]); } if( puiElmNumber) { *puiElmNumber = f_bigEndianToUINT32( &pucBuffer[ 17]); } } RCODE recoverTree( F_RebuildNodeIStream * pIStream, IF_ResultSet * pNonRootRSet, F_DOMNode * pParentNode, F_CachedNode * pRecovCachedNode, FLMBYTE * pucNodeIV); FINLINE RCODE reportCorruption( FLMINT32 i32ErrCode, FLMUINT uiErrBlkAddress, FLMUINT uiErrElmOffset, FLMUINT64 ui64ErrNodeId) { RCODE rc; if( m_pRebuildStatus) { m_corruptInfo.i32ErrCode = i32ErrCode; m_corruptInfo.ui32ErrBlkAddress = (FLMUINT32)uiErrBlkAddress; m_corruptInfo.ui32ErrElmOffset = (FLMUINT32)uiErrElmOffset; m_corruptInfo.ui64ErrNodeId = ui64ErrNodeId; rc = m_pRebuildStatus->reportRebuildErr( &m_corruptInfo); m_corruptInfo.i32ErrCode = 0; return( rc); } return( NE_XFLM_OK); } RCODE determineBlkSize( FLMUINT * puiBlkSizeRV); F_Db * m_pDb; F_SuperFileHdl * m_pSFileHdl; IF_DbRebuildStatus * m_pRebuildStatus; FLMBOOL m_bBadHeader; FLMUINT m_uiLastStatusTime; XFLM_DB_HDR m_dbHdr; XFLM_CREATE_OPTS m_createOpts; XFLM_REBUILD_INFO m_callbackData; XFLM_CORRUPT_INFO m_corruptInfo; RCODE m_cbrc; friend class F_RebuildNodeIStream; }; RCODE chkBlkRead( F_DbInfo * pDbInfo, FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, F_CachedBlock ** ppSCache, FLMINT * piBlkErrCodeRV); FLMINT flmCompareKeys( FLMBYTE * pBuf1, FLMUINT uiBuf1Len, FLMBYTE * pBuf2, FLMUINT uiBuf2Len); void flmInitReadState( STATE_INFO * pStateInfo, FLMBOOL * pbStateInitialized, FLMUINT uiVersionNum, F_Db * pDb, LF_HDR * pLogicalFile, FLMUINT uiLevel, FLMUINT uiBlkType, FLMBYTE * pucKeyBuffer); FLMINT32 flmVerifyBlockHeader( STATE_INFO * pStateInfo, BLOCK_INFO * pBlockInfoRV, FLMUINT uiBlockSize, FLMUINT uiExpNextBlkAddr, FLMUINT uiExpPrevBlkAddr, FLMBOOL bCheckEOF); RCODE flmVerifyElement( STATE_INFO * pStateInfo, LFILE * pLFile, IXD * pIxd, FLMINT32 * pi32ErrCode); void getEntryInfo( F_BTREE_BLK_HDR * pBlkHdr, FLMUINT uiOffset, FLMBYTE ** ppucElm, FLMUINT * puiElmLen, FLMUINT * puiElmKeyLen, FLMUINT * puiElmDataLen, FLMBYTE ** ppucElmKey, FLMBYTE ** ppucElmData); FINLINE RCODE F_NodeCacheMgr::allocDOMNode( F_DOMNode ** ppDOMNode) { flmAssert( *ppDOMNode == NULL); if( m_pFirstNode) { f_resetStackInfo( m_pFirstNode); *ppDOMNode = m_pFirstNode; m_pFirstNode = m_pFirstNode->m_pNextInPool; (*ppDOMNode)->m_pNextInPool = NULL; } else { if( (*ppDOMNode = f_new F_DOMNode) == NULL) { return( RC_SET( NE_XFLM_MEM)); } } return( NE_XFLM_OK); } FINLINE RCODE F_NodeCacheMgr::makeWriteCopy( F_Db * pDb, F_CachedNode ** ppCachedNode) { F_CachedNode * pCachedNode = *ppCachedNode; if( pCachedNode->getLowTransId() < pDb->m_ui64CurrTransID) { return( gv_XFlmSysData.pNodeCacheMgr->_makeWriteCopy( pDb, ppCachedNode)); } else if( pCachedNode->getStreamUseCount()) { // The only thread that would be using this version of // the node is the updater thread. It would be illegal // for the updater thread to have an input stream going // while trying to update this. return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP)); } return( NE_XFLM_OK); } /**************************************************************************** Desc: *****************************************************************************/ class FLMEXP F_SuperFileClient : public IF_SuperFileClient { public: F_SuperFileClient(); virtual ~F_SuperFileClient(); RCODE setup( const char * pszCFileName, const char * pszDataDir, FLMUINT uiMaxFileSize); FLMUINT FLMAPI getFileNumber( FLMUINT uiBlockAddr); FLMUINT FLMAPI getFileOffset( FLMUINT uiBlockAddr); FLMUINT FLMAPI getBlockAddress( FLMUINT uiFileNumber, FLMUINT uiFileOffset); RCODE FLMAPI getFilePath( FLMUINT uiFileNumber, char * pszPath); FLMUINT64 FLMAPI getMaxFileSize( void); static void bldSuperFileExtension( FLMUINT uiFileNum, char * pszFileExtension); private: char * m_pszCFileName; char * m_pszDataFileBaseName; FLMUINT m_uiExtOffset; FLMUINT m_uiDataExtOffset; FLMUINT m_uiMaxFileSize; }; #endif // FLAIMSYS_H libxflaim-5.1.969/src/nodeinfo.cpp0000644000175000017500000001370710511001742020341 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Class for gathering node information. // // Tabs: 3 // // Copyright (c) 2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: nodeinfo.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /***************************************************************************** Desc: Get node information and add it to the node information object. ******************************************************************************/ RCODE FLMAPI F_NodeInfo::addNodeInfo( IF_Db * ifpDb, IF_DOMNode * pNode, FLMBOOL bDoNodeSubTree, FLMBOOL bDoSelf) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pCurNode = NULL; IF_DOMNode * pTmpNode = NULL; FLMUINT64 ui64MyNodeId; F_CachedNode * pCachedNode = ((F_DOMNode *)pNode)->m_pCachedNode; FLMUINT uiCollection = pCachedNode->getCollection(); FLMBOOL bStartedTrans = FALSE; FLMBOOL bDoNode; F_Db * pDb = (F_Db *)ifpDb; // Start a read transaction, if no other transaction is going. if (ifpDb->getTransType() == XFLM_NO_TRANS) { if (RC_BAD( rc = ifpDb->transBegin( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } pCurNode = pNode; pCurNode->AddRef(); // Need special case handling of attribute nodes, because // the pCachedNode will be pointing to the element node for // the attribute. Furthermore, it will be the only node // we do, because it can have no children to do. if (pCurNode->getNodeType() == ATTRIBUTE_NODE) { if (bDoSelf) { F_AttrItem * pAttrItem = NULL; FLMUINT uiSizeNeeded; if (pCachedNode->m_uiAttrCount) { pAttrItem = pCachedNode->getAttribute( ((F_DOMNode *)pCurNode)->m_uiAttrNameId, NULL); } if (!pAttrItem) { rc = RC_SET( NE_XFLM_DOM_NODE_DELETED); goto Exit; } pAttrItem->getAttrSizeNeeded( pCachedNode->m_ppAttrList [0]->m_uiNameId, &m_nodeInfo, NULL, &uiSizeNeeded); } // There can be no child nodes to do, so we are done. goto Exit; } // Traverse the sub-tree and get info. on all nodes below and including the // node we are starting on. ui64MyNodeId = pCachedNode->getNodeId(); bDoNode = bDoSelf; for (;;) { // Add in statistics for the current node. if (bDoNode) { if (RC_BAD( rc = pCachedNode->headerToBuf( pCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER ? TRUE : FALSE, NULL, NULL, &m_nodeInfo, pDb))) { goto Exit; } } if (!bDoNodeSubTree) { break; } // If the node has an annotation, do that node. if (pCachedNode->getAnnotationId() && bDoNode) { F_CachedNode * pTmpCachedNode; if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getAnnotationId(), &pTmpNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } pTmpCachedNode = ((F_DOMNode *)pTmpNode)->m_pCachedNode; if (RC_BAD( rc = pTmpCachedNode->headerToBuf( pTmpCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER ? TRUE : FALSE, NULL, NULL, &m_nodeInfo, pDb))) { goto Exit; } } // If the node has a child node, go to it. if (pCachedNode->getFirstChildId()) { if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getFirstChildId(), &pCurNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } pCachedNode = ((F_DOMNode *)pCurNode)->m_pCachedNode; bDoNode = TRUE; continue; } for(;;) { // If we are on the node we started on, there is nothing more to do. if (pCachedNode->getNodeId() == ui64MyNodeId) { goto Exit; // Should return NE_XFLM_OK } // If node has a sibling node, go to it. if (pCachedNode->getNextSibId()) { if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getNextSibId(), &pCurNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } pCachedNode = ((F_DOMNode *)pCurNode)->m_pCachedNode; bDoNode = TRUE; break; } // Traverse back up the tree to parent node. // Better be a parent node at this point - because we know we // are not on the node we started on. flmAssert( pCachedNode->getParentId()); if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getParentId(), &pCurNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_DATA_ERROR); } goto Exit; } pCachedNode = ((F_DOMNode *)pCurNode)->m_pCachedNode; } } Exit: if (pCurNode) { pCurNode->Release(); } if (pTmpNode) { pTmpNode->Release(); } if (bStartedTrans) { (void)ifpDb->transAbort(); } return( rc); } /**************************************************************************** Desc: Create an empty node info. object and return it's interface... ****************************************************************************/ RCODE FLMAPI F_DbSystem::createIFNodeInfo( IF_NodeInfo ** ppNodeInfo) { RCODE rc = NE_XFLM_OK; if ((*ppNodeInfo = f_new F_NodeInfo) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } Exit: return( rc); } libxflaim-5.1.969/src/kybuild.cpp0000644000175000017500000021241710511001742020202 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the main routines for building of index keys, // and adding them to the database. // // Tabs: 3 // // Copyright (c) 1990-1992, 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kybuild.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define STACK_DATA_BUF_SIZE 64 typedef struct NodeTraverseTag * NODE_TRAV_p; typedef struct AnchorNodeTag * ANCHOR_NODE_p; typedef struct AnchorNodeTag { FLMUINT64 ui64AnchorNodeId; FLMUINT uiAnchorNameId; eDomNodeType eNodeType; FLMBOOL bSeeIfRepeatingSibs; ANCHOR_NODE_p pNext; } ANCHOR_NODE; typedef struct NodeTraverseTag { ANCHOR_NODE * pAnchorNode; F_DOMNode * pNode; FLMBOOL bInNodeSubtree; ICD * pIcd; FLMUINT uiSibIcdAttrs; FLMUINT uiSibIcdElms; FLMBOOL bTraverseChildren; FLMBOOL bTraverseSibs; NODE_TRAV_p pParent; NODE_TRAV_p pChild; } NODE_TRAV; // Local function prototypes FSTATIC RCODE kyAddIDsToKey( FLMUINT64 ui64DocumentID, IXD * pIxd, CDL_HDR * pCdlTbl, FLMBYTE * pucKeyBuf, FLMUINT uiIDBufSize, FLMUINT * puiIDLen); FSTATIC RCODE kySeeIfRepeatingSibs( F_Db * pDb, F_DOMNode * pNode, FLMBOOL * pbHadRepeatingSib); FSTATIC RCODE kyFindChildNode( F_Db * pDb, F_Pool * pPool, NODE_TRAV ** ppTrav, FLMBOOL * pbGotChild, FLMBOOL * pbHadRepeatingSib); FSTATIC RCODE kyFindSibNode( F_Db * pDb, NODE_TRAV * pTrav, FLMBOOL bTestFirstNode, FLMBOOL * pbGotSib, FLMBOOL * pbHadRepeatingSib); /**************************************************************************** Desc: Append node IDs to the key buffer. ****************************************************************************/ FSTATIC RCODE kyAddIDsToKey( FLMUINT64 ui64DocumentID, IXD * pIxd, CDL_HDR * pCdlTbl, FLMBYTE * pucKeyBuf, FLMUINT uiIDBufSize, FLMUINT * puiIDLen) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucIDs = pucKeyBuf; FLMUINT uiSenLen; FLMUINT uiIDLen = 0; ICD * pIcd; FLMUINT64 ui64Id; FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE]; FLMBYTE * pucTmpSen; // Our ID buffer can never exceed MAX_ID_SIZE bytes (which is always // defined to be 256), because it has a trailing // length byte that we put on it - which can only represent up to // 255 bytes. if (uiIDBufSize > MAX_ID_SIZE) { uiIDBufSize = MAX_ID_SIZE; } // Put document ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (uiIDBufSize - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64DocumentID, &pucIDs); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64DocumentID, &pucTmpSen); if (uiSenLen + uiIDLen > uiIDBufSize) { rc = RC_SET( NE_XFLM_KEY_OVERFLOW); goto Exit; } f_memcpy( pucIDs, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucIDs += uiSenLen; } // Append the key component NODE IDs to the key for (pIcd = pIxd->pFirstKey; pIcd; pIcd = pIcd->pNextKeyComponent) { ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList && pCdlTbl [pIcd->uiCdl].pCdlList->pNode ? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId() : (FLMUINT64)0); // Put node ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (uiIDBufSize - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64Id, &pucIDs); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen); if (uiSenLen + uiIDLen > uiIDBufSize) { rc = RC_SET( NE_XFLM_KEY_OVERFLOW); goto Exit; } f_memcpy( pucIDs, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucIDs += uiSenLen; } } // Append the data NODE IDs to the key for (pIcd = pIxd->pFirstData; pIcd; pIcd = pIcd->pNextDataComponent) { ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList && pCdlTbl [pIcd->uiCdl].pCdlList->pNode ? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId() : (FLMUINT64)0); // Put node ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (uiIDBufSize - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64Id, &pucIDs); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen); if (uiSenLen + uiIDLen > uiIDBufSize) { rc = RC_SET( NE_XFLM_KEY_OVERFLOW); goto Exit; } f_memcpy( pucIDs, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucIDs += uiSenLen; } } // Append the context NODE IDs to the key for (pIcd = pIxd->pFirstContext; pIcd; pIcd = pIcd->pNextKeyComponent) { ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList && pCdlTbl [pIcd->uiCdl].pCdlList->pNode ? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId() : (FLMUINT64)0); // Put node ID into buffer. If there is room for at least nine // bytes, we can encode the ID right into the buffer safely. Otherwise, // we have to use a temporary buffer and see if there is room. if (uiIDBufSize - uiIDLen >= 9) { uiIDLen += f_encodeSEN( ui64Id, &pucIDs); } else { pucTmpSen = &ucTmpSen [0]; uiSenLen = f_encodeSEN( ui64Id, &pucTmpSen); if (uiSenLen + uiIDLen > uiIDBufSize) { rc = RC_SET( NE_XFLM_KEY_OVERFLOW); goto Exit; } f_memcpy( pucIDs, ucTmpSen, uiSenLen); uiIDLen += uiSenLen; pucIDs += uiSenLen; } } *puiIDLen = uiIDLen; Exit: return( rc); } /***************************************************************************** Desc: F_OldNodeList destructor *****************************************************************************/ F_OldNodeList::~F_OldNodeList() { if (m_pNodeList) { f_free( &m_pNodeList); } m_pool.poolFree(); } /***************************************************************************** Desc: Find a node in the old node list. *****************************************************************************/ FLMBOOL F_OldNodeList::findNodeInList( eDomNodeType eNodeType, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiNameId, FLMBYTE ** ppucData, FLMUINT * puiDataLen, FLMUINT * puiInsertPos) { FLMBOOL bFound = FALSE; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; eDomNodeType eTblNodeType; FLMUINT uiTblCollection; FLMUINT uiTblNameId; FLMUINT64 ui64TblNodeId; // Do binary search in the table if ((uiTblSize = m_uiNodeCount) == 0) { *puiInsertPos = 0; goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; eTblNodeType = m_pNodeList[ uiMid].eNodeType; uiTblCollection = m_pNodeList[ uiMid].uiCollection; ui64TblNodeId = m_pNodeList[ uiMid].ui64NodeId; uiTblNameId = m_pNodeList[ uiMid].uiNameId; flmAssert( eTblNodeType != INVALID_NODE); flmAssert( uiTblCollection); flmAssert( ui64TblNodeId); flmAssert( uiTblNameId); if( eTblNodeType == eNodeType && uiTblCollection == uiCollection && ui64TblNodeId == ui64NodeId && uiTblNameId == uiNameId) { bFound = TRUE; *ppucData = m_pNodeList [uiMid].pucData; *puiDataLen = m_pNodeList [uiMid].uiDataLen; *puiInsertPos = uiMid; goto Exit; } // Check if we are done if( uiLow >= uiHigh) { // Done, item not found if( eNodeType >= eTblNodeType) { goto CmpGreaterEq; } if( uiCollection >= uiTblCollection) { goto CmpGreaterEq; } if( ui64NodeId >= ui64TblNodeId) { goto CmpGreaterEq; } if( uiNameId >= uiTblNameId) { CmpGreaterEq: *puiInsertPos = uiMid + 1; goto Exit; } *puiInsertPos = uiMid; goto Exit; } if( eNodeType >= eTblNodeType) { goto CmpGreaterEq2; } if( uiCollection >= uiTblCollection) { goto CmpGreaterEq2; } if( ui64NodeId >= ui64TblNodeId) { goto CmpGreaterEq2; } if( uiNameId >= uiTblNameId) { CmpGreaterEq2: if (uiMid == uiTblSize) { *puiInsertPos = uiMid + 1; goto Exit; } uiLow = uiMid + 1; continue; } if (uiMid == 0) { *puiInsertPos = 0; goto Exit; } uiHigh = uiMid - 1; continue; } Exit: return( bFound); } /***************************************************************************** Desc: Add an old node to the old node list. *****************************************************************************/ RCODE F_OldNodeList::addNodeToList( F_Db * pDb, F_DOMNode * pNode) { RCODE rc = NE_XFLM_OK; FLMUINT uiInsertPos; FLMBYTE * pucData; FLMUINT uiDataType; FLMUINT uiDataLen; FLMUINT uiChars; FLMUINT uiBufSize; FLMUINT uiCollection; FLMUINT uiNameId; FLMUINT64 ui64NodeId; if( RC_BAD( rc = pNode->getCollection( pDb, &uiCollection))) { goto Exit; } if( RC_BAD( rc = pNode->getDataType( pDb, &uiDataType))) { goto Exit; } ui64NodeId = pNode->getIxNodeId(); uiNameId = pNode->getNameId(); // See if the node is already there if( !findNodeInList( pNode->getNodeType(), uiCollection, ui64NodeId, uiNameId, &pucData, &uiDataLen, &uiInsertPos)) { // Expand the size of the table. if (m_uiNodeCount == m_uiListSize) { if (RC_BAD( rc = f_realloc( (m_uiListSize + 20) * sizeof( OLD_NODE_DATA), &m_pNodeList))) { goto Exit; } m_uiListSize += 20; } if (uiInsertPos < m_uiNodeCount) { f_memmove( &m_pNodeList [uiInsertPos + 1], &m_pNodeList [uiInsertPos], sizeof( OLD_NODE_DATA) * (m_uiNodeCount - uiInsertPos)); } m_pNodeList [uiInsertPos].eNodeType = pNode->getNodeType(); m_pNodeList [uiInsertPos].uiCollection = uiCollection; m_pNodeList [uiInsertPos].ui64NodeId = ui64NodeId; m_pNodeList [uiInsertPos].uiNameId = uiNameId; m_uiNodeCount++; // Set up the data - either unicode or binary. if( uiDataType == XFLM_BINARY_TYPE) { // Get the length first. if( RC_BAD( rc = pNode->getDataLength( pDb, &uiBufSize))) { goto Exit; } // Allocate the space needed. if (RC_BAD( rc = m_pool.poolAlloc( uiBufSize, (void **)&m_pNodeList [uiInsertPos].pucData))) { goto Exit; } // Go back again and get the data now. if( RC_BAD( rc = pNode->getBinary( pDb, m_pNodeList [uiInsertPos].pucData, 0, uiBufSize, NULL))) { goto Exit; } m_pNodeList [uiInsertPos].uiDataLen = uiBufSize; } else { flmAssert( uiDataType == XFLM_TEXT_TYPE); // Get the length first. if( RC_BAD( rc = pNode->getUnicodeChars( pDb, &uiChars))) { goto Exit; } // Allocate the space needed. uiBufSize = (uiChars + 1) * sizeof( FLMUNICODE); if (RC_BAD( rc = m_pool.poolAlloc( uiBufSize, (void **)&m_pNodeList [uiInsertPos].pucData))) { goto Exit; } // Go back again and get the data now. if( RC_BAD( rc = pNode->getUnicode( pDb, (FLMUNICODE *)m_pNodeList [uiInsertPos].pucData, uiBufSize, 0, FLM_MAX_UINT))) { goto Exit; } m_pNodeList [uiInsertPos].uiDataLen = uiBufSize; } } Exit: return( rc); } /***************************************************************************** Desc: Release all of the nodes in the list. *****************************************************************************/ void F_OldNodeList::resetList( void) { m_pool.poolReset( NULL); m_uiNodeCount = 0; } /**************************************************************************** Desc: Add an index key to the buffers ****************************************************************************/ RCODE F_Db::addToKrefTbl( FLMUINT uiKeyLen, FLMUINT uiDataLen) { RCODE rc = NE_XFLM_OK; KREF_ENTRY * pKref; FLMUINT uiSizeNeeded; FLMBYTE * pucDest; // If the table is FULL, expand the table if (m_uiKrefCount == m_uiKrefTblSize) { FLMUINT uiAllocSize; FLMUINT uiOrigKrefTblSize = m_uiKrefTblSize; if (m_uiKrefTblSize > 0x8000 / sizeof( KREF_ENTRY *)) { m_uiKrefTblSize += 4096; } else { m_uiKrefTblSize *= 2; } uiAllocSize = m_uiKrefTblSize * sizeof( KREF_ENTRY *); rc = f_realloc( uiAllocSize, &m_pKrefTbl); if (RC_BAD(rc)) { m_uiKrefTblSize = uiOrigKrefTblSize; rc = RC_SET( NE_XFLM_MEM); goto Exit; } } // Allocate memory for the key's KREF and the key itself. // We allocate one extra byte so we can zero terminate the key // below. The extra zero character is to ensure that the compare // in the qsort routine will work. uiSizeNeeded = sizeof( KREF_ENTRY) + uiKeyLen + 1 + uiDataLen; if (RC_BAD( rc = m_pKrefPool->poolAlloc( uiSizeNeeded, (void **)&pKref))) { goto Exit; } m_pKrefTbl [ m_uiKrefCount++] = pKref; m_uiTotalKrefBytes += uiSizeNeeded; // Fill in all of the fields in the KREF structure. pKref->ui16IxNum = (FLMUINT16)m_keyGenInfo.pIxd->uiIndexNum; pKref->bDelete = m_keyGenInfo.bAddKeys ? FALSE : TRUE; pKref->ui16KeyLen = (FLMUINT16)uiKeyLen; pKref->uiSequence = m_uiKrefCount; pKref->uiDataLen = uiDataLen; // Copy the key to just after the KREF structure. pucDest = (FLMBYTE *)(&pKref [1]); f_memcpy( pucDest, m_keyGenInfo.pucKeyBuf, uiKeyLen); // Null terminate the key so compare in qsort will work. pucDest [uiKeyLen] = 0; if (uiDataLen) { f_memcpy( pucDest + uiKeyLen + 1, m_keyGenInfo.pucData, uiDataLen); } Exit: return( rc); } /**************************************************************************** Desc: Verifies that all of the nodes in the CDL list are in contexts that are related according to the index definition. ****************************************************************************/ RCODE F_Db::verifyKeyContext( FLMBOOL * pbVerified) { RCODE rc = NE_XFLM_OK; CDL * pCdl; CDL * pParentCdl; ICD * pIcd; *pbVerified = FALSE; pIcd = m_keyGenInfo.pIxd->pIcdTree; // Do in-order traversal, from leaf ICDs up. while (pIcd->pFirstChild) { pIcd = pIcd->pFirstChild; } for (;;) { if ((pCdl = m_keyGenInfo.pCdlTbl [pIcd->uiCdl].pCdlList) != NULL) { // If this is a "missing" placeholder and the // component is required, we cannot build the key if (!pCdl->pNode && pIcd->uiKeyComponent && (pIcd->uiFlags & ICD_REQUIRED_PIECE)) { goto Exit; } // If the ICD has a parent, see if the parent has the // correct node id. if (pIcd->pParent) { pParentCdl = m_keyGenInfo.pCdlTbl [pIcd->pParent->uiCdl].pCdlList; if( !pParentCdl || !pParentCdl->pNode) { goto Exit; } if( pParentCdl->pNode->getNodeId() != pCdl->ui64ParentId) { goto Exit; } } } else { if (pIcd->uiKeyComponent && (pIcd->uiFlags & ICD_REQUIRED_PIECE)) { // This better already have been checked flmAssert( 0); goto Exit; } } // See if there is a sibling if (pIcd->pNextSibling) { pIcd = pIcd->pNextSibling; while (pIcd->pFirstChild) { pIcd = pIcd->pFirstChild; } } else { if ((pIcd = pIcd->pParent) == NULL) { break; } } } *pbVerified = TRUE; Exit: return( rc); } /**************************************************************************** Desc: Build the data part for a key. Notes: This routine is recursive in nature. Will recurse the number of data components defined in the index. ****************************************************************************/ RCODE F_Db::buildData( ICD * pIcd, FLMUINT uiKeyLen, FLMUINT uiDataLen ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCdl; CDL * pFirstCdl; CDL * pCdl; FLMUINT uiDataComponentLen; F_DOMNode * pNode = NULL; FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE]; FLMBYTE * pucTmpSen; FLMUINT uiSENLen; FLMBOOL bVerified; FLMUINT uiIDLen; uiCdl = pIcd->uiCdl; pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList; pCdl = pFirstCdl; if (!m_keyGenInfo.bUseSubtreeNodes) { // If we are not using nodes in the sub-tree, skip any // that are in the sub-tree. while (pCdl && pCdl->bInNodeSubtree) { pCdl = pCdl->pNext; } } // Go through all of the data CDL - even the ones that are NULL for (;;) { // Data components cannot be root tags. flmAssert( pIcd->uiDictNum != ELM_ROOT_TAG); if (pNode) { pNode->Release(); pNode = NULL; } m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl; if (pCdl) { // NOTE: pNode could be NULL because it is a "missing" placeholder pNode = pCdl->pNode; } if (pNode) { pNode->AddRef(); if (RC_BAD( rc = pNode->getDataLength( this, &uiDataComponentLen))) { goto Exit; } } else { uiDataComponentLen = 0; } // Output the length of the data as a SEN value pucTmpSen = &ucTmpSen [0]; uiSENLen = f_encodeSEN( uiDataComponentLen, &pucTmpSen); if (uiDataComponentLen + uiSENLen + uiDataLen > m_keyGenInfo.uiDataBufSize) { FLMUINT uiNewSize = uiDataComponentLen + uiSENLen + uiDataLen + 512; // Allocate the data buffer if it has not been allocated. Otherwise, // realloc it. if (!m_keyGenInfo.bDataBufAllocated) { FLMBYTE * pucNewData; if (RC_BAD( rc = f_alloc( uiNewSize, &pucNewData))) { goto Exit; } if( uiDataLen) { f_memcpy( pucNewData, m_keyGenInfo.pucData, uiDataLen); } m_keyGenInfo.pucData = pucNewData; m_keyGenInfo.bDataBufAllocated = TRUE; } else { // Reallocate the buffer. if (RC_BAD( rc = f_realloc( uiNewSize, &m_keyGenInfo.pucData))) { goto Exit; } } m_keyGenInfo.uiDataBufSize = uiNewSize; } f_memcpy( m_keyGenInfo.pucData + uiDataLen, ucTmpSen, uiSENLen); if (uiDataComponentLen) { if (RC_BAD( rc = pNode->getData( this, m_keyGenInfo.pucData + uiDataLen + uiSENLen, &uiDataComponentLen))) { goto Exit; } } // If this is the last data CDL, append IDs to the // key and output the key and data to the KREF. // Otherwise, recurse down. if (pIcd->pNextDataComponent) { if (RC_BAD( rc = buildData( pIcd->pNextDataComponent, uiKeyLen, uiDataLen + uiDataComponentLen + uiSENLen))) { goto Exit; } } else if (m_keyGenInfo.pIxd->pFirstContext) { if (RC_BAD( rc = buildContext( m_keyGenInfo.pIxd->pFirstContext, uiKeyLen, uiDataLen + uiDataComponentLen + uiSENLen))) { goto Exit; } } else { if (RC_BAD( rc = verifyKeyContext( &bVerified))) { goto Exit; } if (bVerified) { if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID, m_keyGenInfo.pIxd, m_keyGenInfo.pCdlTbl, &m_keyGenInfo.pucKeyBuf [uiKeyLen], XFLM_MAX_KEY_SIZE - uiKeyLen, &uiIDLen))) { goto Exit; } if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, uiDataLen + uiDataComponentLen + uiSENLen))) { goto Exit; } } } // Get the next CDL, if any if (pCdl) { pCdl = pCdl->pNext; if (!m_keyGenInfo.bUseSubtreeNodes) { // If we are not using nodes in the sub-tree, skip any // that are in the sub-tree. while (pCdl && pCdl->bInNodeSubtree) { pCdl = pCdl->pNext; } } } if (!pCdl) { goto Exit; } } Exit: if (pNode) { pNode->Release(); } m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl; return( rc); } /**************************************************************************** Desc: Go through the context components of a key. Notes: This routine is recursive in nature. Will recurse the number of context components defined in the index. ****************************************************************************/ RCODE F_Db::buildContext( ICD * pIcd, FLMUINT uiKeyLen, FLMUINT uiDataLen ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCdl; CDL * pFirstCdl; CDL * pCdl; FLMUINT uiIDLen; F_DOMNode * pNode = NULL; FLMBOOL bVerified; uiCdl = pIcd->uiCdl; pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList; pCdl = pFirstCdl; if (!m_keyGenInfo.bUseSubtreeNodes) { // If we are not using nodes in the sub-tree, skip any // that are in the sub-tree. while (pCdl && pCdl->bInNodeSubtree) { pCdl = pCdl->pNext; } } // Go through all of the context CDLs - even the ones that are NULL for (;;) { if (pNode) { pNode->Release(); pNode = NULL; } m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl; if (pCdl) { // NOTE: pNode could be NULL because it is a "missing" placeholder if ((pNode = pCdl->pNode) != NULL) { pNode->AddRef(); } } // If this is the last context CDL, append IDs to the // key and output the key and data to the KREF. // Otherwise, recurse down. if (pIcd->pNextKeyComponent) { if (RC_BAD( rc = buildContext( pIcd->pNextKeyComponent, uiKeyLen, uiDataLen))) { goto Exit; } } else { if (RC_BAD( rc = verifyKeyContext( &bVerified))) { goto Exit; } if (bVerified) { if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID, m_keyGenInfo.pIxd, m_keyGenInfo.pCdlTbl, &m_keyGenInfo.pucKeyBuf [uiKeyLen], XFLM_MAX_KEY_SIZE - uiKeyLen, &uiIDLen))) { goto Exit; } if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, uiDataLen))) { goto Exit; } } } // Get the next CDL, if any if (pCdl) { pCdl = pCdl->pNext; if (!m_keyGenInfo.bUseSubtreeNodes) { // If we are not using nodes in the sub-tree, skip any // that are in the sub-tree. while (pCdl && pCdl->bInNodeSubtree) { pCdl = pCdl->pNext; } } } if (!pCdl) { goto Exit; } } Exit: if (pNode) { pNode->Release(); } m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl; return( rc); } /**************************************************************************** Desc: Finish the current key component. If there is a next one call build keys. Otherwise, go on to doing data and context pieces. ****************************************************************************/ RCODE F_Db::finishKeyComponent( ICD * pIcd, FLMUINT uiKeyLen ) { RCODE rc = NE_XFLM_OK; if (pIcd->pNextKeyComponent) { flmAssert( m_keyGenInfo.bIsCompound); if (RC_BAD( rc = buildKeys( pIcd->pNextKeyComponent, uiKeyLen))) { goto Exit; } } else { if (m_keyGenInfo.pIxd->pFirstData) { if (RC_BAD( rc = buildData( m_keyGenInfo.pIxd->pFirstData, uiKeyLen, 0))) { goto Exit; } } else if (m_keyGenInfo.pIxd->pFirstContext) { if (RC_BAD( rc = buildContext( m_keyGenInfo.pIxd->pFirstContext, uiKeyLen, 0))) { goto Exit; } } else { FLMBOOL bVerified; if (RC_BAD( rc = verifyKeyContext( &bVerified))) { goto Exit; } if (bVerified) { FLMUINT uiIDLen; if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID, m_keyGenInfo.pIxd, m_keyGenInfo.pCdlTbl, &m_keyGenInfo.pucKeyBuf [uiKeyLen], XFLM_MAX_KEY_SIZE - uiKeyLen, &uiIDLen))) { goto Exit; } if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, 0))) { goto Exit; } } } } Exit: return( rc); } /**************************************************************************** Desc: Generate the keys for a text component. ****************************************************************************/ RCODE F_Db::genTextKeyComponents( F_DOMNode * pNode, ICD * pIcd, FLMUINT uiKeyLen, FLMBYTE ** ppucTmpBuf, FLMUINT * puiTmpBufSize, void ** ppvMark) { RCODE rc = NE_XFLM_OK; IF_PosIStream * pIStream = NULL; FLMUINT uiNumChars; FLMUINT uiStrBytes; FLMUINT uiSubstrChars; FLMUINT uiMeta; FLMBOOL bEachWord = FALSE; FLMBOOL bMetaphone = FALSE; FLMBOOL bSubstring = FALSE; FLMBOOL bWholeString = FALSE; FLMBOOL bHadAtLeastOneString = FALSE; FLMBOOL bDataTruncated; FLMUINT uiSaveKeyLen; FLMUINT uiElmLen; FLMUINT uiKeyLenPos = uiKeyLen; FLMUINT uiCompareRules = pIcd->uiCompareRules; F_NodeBufferIStream nodeBufferIStream; IF_BufferIStream * pBufferIStream = NULL; uiKeyLen += 2; uiSaveKeyLen = uiKeyLen; if (!pNode) { goto No_Strings; } if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } if (RC_BAD( rc = pNode->getTextIStream( this, &nodeBufferIStream, &pIStream, &uiNumChars))) { goto Exit; } if (!uiNumChars) { No_Strings: // Save the key component length UW2FBA( 0, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); if( pIStream) { pIStream->Release(); pIStream = NULL; } rc = finishKeyComponent( pIcd, uiKeyLen); goto Exit; } if (pIcd->uiFlags & ICD_EACHWORD) { bEachWord = TRUE; // OR in the compressing of spaces, because we only want to treat // spaces as delimiters between words. uiCompareRules |= XFLM_COMP_COMPRESS_WHITESPACE; } else if (pIcd->uiFlags & ICD_METAPHONE) { bMetaphone = TRUE; } else if (pIcd->uiFlags & ICD_SUBSTRING) { bSubstring = TRUE; } else { bWholeString = TRUE; } // Loop on each word or substring in the value for (;;) { uiKeyLen = uiSaveKeyLen; bDataTruncated = FALSE; if (bWholeString) { uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen; if( RC_BAD( rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], &uiElmLen, pIStream, XFLM_TEXT_TYPE, pIcd->uiFlags, pIcd->uiCompareRules, pIcd->uiLimit, NULL, NULL, m_keyGenInfo.pIxd->uiLanguage, FALSE, FALSE, &bDataTruncated, NULL))) { goto Exit; } } else if (bEachWord) { if (*ppucTmpBuf == NULL) { *ppvMark = m_tempPool.poolMark(); *puiTmpBufSize = (FLMUINT)XFLM_MAX_KEY_SIZE + 8; if (RC_BAD( rc = m_tempPool.poolAlloc( *puiTmpBufSize, (void **)ppucTmpBuf))) { goto Exit; } } uiStrBytes = *puiTmpBufSize; if( RC_BAD( rc = KYEachWordParse( pIStream, &uiCompareRules, pIcd->uiLimit, *ppucTmpBuf, &uiStrBytes))) { goto Exit; } if (!uiStrBytes) { if (!bHadAtLeastOneString) { goto No_Strings; } break; } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)*ppucTmpBuf, uiStrBytes))) { goto Exit; } // Pass 0 for compare rules because KYEachWordParse will already // have taken care of them - except for XFLM_COMP_CASE_INSENSITIVE. uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen; rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], &uiElmLen, pBufferIStream, XFLM_TEXT_TYPE, pIcd->uiFlags, pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE, pIcd->uiLimit, NULL, NULL, m_keyGenInfo.pIxd->uiLanguage, FALSE, FALSE, &bDataTruncated, NULL); pBufferIStream->closeStream(); if( RC_BAD( rc)) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } bHadAtLeastOneString = TRUE; } else if (bMetaphone) { FLMBYTE ucStorageBuf[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiStorageLen; if( RC_BAD( rc = f_getNextMetaphone( pIStream, &uiMeta))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; if (!bHadAtLeastOneString) { goto No_Strings; } break; } uiStorageLen = FLM_MAX_NUM_BUF_SIZE; if( RC_BAD( rc = flmNumber64ToStorage( uiMeta, &uiStorageLen, ucStorageBuf, FALSE, FALSE))) { goto Exit; } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)ucStorageBuf, uiStorageLen))) { goto Exit; } // Pass 0 for compare rules - only applies to strings. uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen; rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], &uiElmLen, pBufferIStream, XFLM_NUMBER_TYPE, pIcd->uiFlags, 0, pIcd->uiLimit, NULL, NULL, m_keyGenInfo.pIxd->uiLanguage, FALSE, FALSE, NULL, NULL); pBufferIStream->closeStream(); if( RC_BAD( rc)) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } bHadAtLeastOneString = TRUE; } else { flmAssert( bSubstring); if (*ppucTmpBuf == NULL) { *ppvMark = m_tempPool.poolMark(); *puiTmpBufSize = (FLMUINT)XFLM_MAX_KEY_SIZE + 8; if (RC_BAD( rc = m_tempPool.poolAlloc( *puiTmpBufSize, (void **)ppucTmpBuf))) { goto Exit; } } uiStrBytes = *puiTmpBufSize; if( RC_BAD( rc = KYSubstringParse( pIStream, &uiCompareRules, pIcd->uiLimit, *ppucTmpBuf, &uiStrBytes, &uiSubstrChars))) { goto Exit; } if (!uiStrBytes) { if (!bHadAtLeastOneString) { goto No_Strings; } break; } if (bHadAtLeastOneString && uiSubstrChars == 1 && !m_keyGenInfo.bIsAsia) { break; } if (RC_BAD( rc = pBufferIStream->openStream( (const char *)*ppucTmpBuf, uiStrBytes))) { goto Exit; } // Pass 0 for compare rules, because KYSubstringParse has already // taken care of them, except for XFLM_COMP_CASE_INSENSITIVE uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen; rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], &uiElmLen, pBufferIStream, XFLM_TEXT_TYPE, pIcd->uiFlags, pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE, pIcd->uiLimit, NULL, NULL, m_keyGenInfo.pIxd->uiLanguage, bHadAtLeastOneString ? FALSE : TRUE, FALSE, &bDataTruncated, NULL); pBufferIStream->closeStream(); if( RC_BAD( rc)) { RC_UNEXPECTED_ASSERT( rc); goto Exit; } bHadAtLeastOneString = TRUE; } uiKeyLen += uiElmLen; // Save the key component length if (!bDataTruncated) { UW2FBA( (FLMUINT16)uiElmLen, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); } else { UW2FBA( (FLMUINT16)(uiElmLen | TRUNCATED_FLAG), &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); // If we are deleting, save the node into the list of "old" nodes // We do this so that the compare routine can look for the node // if it has to compare the full value. if (!m_keyGenInfo.bAddKeys) { if (!m_pOldNodeList) { if ((m_pOldNodeList = f_new F_OldNodeList) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } if (RC_BAD( rc = m_pOldNodeList->addNodeToList( this, pNode))) { goto Exit; } } } if (RC_BAD( rc = finishKeyComponent( pIcd, uiKeyLen))) { goto Exit; } if (bWholeString) { break; } } Exit: if( pBufferIStream) { pBufferIStream->Release(); } if (pIStream) { pIStream->Release(); pIStream = NULL; } return( rc); } /**************************************************************************** Desc: Generate the keys for other data types besides text. ****************************************************************************/ RCODE F_Db::genOtherKeyComponent( F_DOMNode * pNode, ICD * pIcd, FLMUINT uiKeyLen) { RCODE rc = NE_XFLM_OK; FLMUINT uiElmLen; FLMUINT uiKeyLenPos = uiKeyLen; FLMBOOL bDataTruncated; IF_PosIStream * pIStream = NULL; F_NodeBufferIStream bufferIStream; uiKeyLen += 2; if (!pNode) { No_Data: // Save the key component length UW2FBA( 0, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); rc = finishKeyComponent( pIcd, uiKeyLen); goto Exit; } if (pIcd->uiFlags & ICD_PRESENCE) { FLMUINT uiNameId = pIcd->uiDictNum; // If we are indexing ELM_ROOT_TAG, we // need to get the name id from the node. if (uiNameId == ELM_ROOT_TAG) { if (RC_BAD( rc = pNode->getNameId( this, &uiNameId))) { goto Exit; } } f_UINT32ToBigEndian( (FLMUINT32)uiNameId, &m_keyGenInfo.pucKeyBuf [uiKeyLen]); uiKeyLen += 4; // Save the key component length. UW2FBA( 4, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); } else { // If it is not a presence index, we cannot be indexing // ELM_ROOT_TAG on an element node. flmAssert( pIcd->uiDictNum != ELM_ROOT_TAG); if (RC_BAD( rc = pNode->getIStream( this, &bufferIStream, &pIStream))) { goto Exit; } if (!pIStream->remainingSize()) { goto No_Data; } // Compute number of bytes left uiElmLen = XFLM_MAX_KEY_SIZE - uiKeyLen; bDataTruncated = FALSE; // Pass zero for compare rules - these are not strings. if( RC_BAD( rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], &uiElmLen, pIStream, icdGetDataType( pIcd), pIcd->uiFlags, 0, pIcd->uiLimit, NULL, NULL, m_keyGenInfo.pIxd->uiLanguage, FALSE, FALSE, &bDataTruncated, NULL))) { goto Exit; } uiKeyLen += uiElmLen; // Save the key component length. if (!bDataTruncated) { UW2FBA( (FLMUINT16)uiElmLen, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); } else { UW2FBA( (FLMUINT16)(uiElmLen | TRUNCATED_FLAG), &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); // If we are deleting, save the node into the list of "old" nodes // We do this so that the compare routine can look for the node // if it has to compare the full value. if (!m_keyGenInfo.bAddKeys) { if (!m_pOldNodeList) { if ((m_pOldNodeList = f_new F_OldNodeList) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } } if (RC_BAD( rc = m_pOldNodeList->addNodeToList( this, pNode))) { goto Exit; } } } // Better have an F_IStream at this point! flmAssert( pIStream); pIStream->Release(); pIStream = NULL; } if (RC_BAD( rc = finishKeyComponent( pIcd, uiKeyLen))) { goto Exit; } Exit: if (pIStream) { pIStream->Release(); } return( rc); } /**************************************************************************** Desc: Build all compound keys from the CDL table. Notes: This routine is recursive in nature. Will recurse the number of key components defined in the index. ****************************************************************************/ RCODE F_Db::buildKeys( ICD * pIcd, FLMUINT uiKeyLen ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCdl = pIcd->uiCdl; CDL * pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList; CDL * pCdl = pFirstCdl; FLMBYTE * pucTmpBuf = NULL; void * pvMark = NULL; FLMUINT uiTmpBufSize = 0; F_DOMNode * pNode = NULL; flmAssert( m_keyGenInfo.bIsCompound || pIcd->uiKeyComponent == 1); // Do each CDL. If there is no CDL for this level, must // still do at least once so that if there are other // components or data after this component, we will // do a recursive call or generate a key. if (!m_keyGenInfo.bUseSubtreeNodes) { // Skip any CDLs in the subtree if we are not using // sub-tree nodes at this point. while (pCdl && pCdl->bInNodeSubtree) { pCdl = pCdl->pNext; } } for (;;) { // Need to set the current CDL into the table so that // when we append the IDs we will have the right ones. // This will be restored to the first item in the // table at Exit. if (pNode) { pNode->Release(); pNode = NULL; } m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl; if (pCdl) { // pNode could be NULL because it is a placeholder for a // "missing" value if ((pNode = pCdl->pNode) != NULL) { pNode->AddRef(); } } // Generate the key component if (icdGetDataType( pIcd) == XFLM_TEXT_TYPE && !(pIcd->uiFlags & ICD_PRESENCE)) { if (RC_BAD( rc = genTextKeyComponents( pNode, pIcd, uiKeyLen, &pucTmpBuf, &uiTmpBufSize, &pvMark))) { goto Exit; } } else { if (RC_BAD( rc = genOtherKeyComponent( pNode, pIcd, uiKeyLen))) { goto Exit; } } // Go to the next CDL, if any if (pCdl) { pCdl = pCdl->pNext; if (!m_keyGenInfo.bUseSubtreeNodes) { // Skip any CDLs in the subtree if we are not using // sub-tree nodes at this point. while (pCdl && pCdl->bInNodeSubtree) { pCdl = pCdl->pNext; } } } if (!pCdl) { break; } } Exit: if (pNode) { pNode->Release(); } if (pvMark) { m_tempPool.poolReset( pvMark); } // Restore the CDL table entry to point to the // beginning of the list m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl; return( rc); } /**************************************************************************** Desc: Build all keys from combinations of CDLs. Add keys to KREF table. ****************************************************************************/ RCODE F_Db::buildKeys( FLMUINT64 ui64DocumentID, IXD * pIxd, CDL_HDR * pCdlTbl, FLMBOOL bUseSubtreeNodes, FLMBOOL bAddKeys) { RCODE rc = NE_XFLM_OK; FLMUINT uiNumKeysHavingCdl; CDL * pCdl; ICD * pIcd; FLMBYTE ucDataBuf [STACK_DATA_BUF_SIZE]; m_keyGenInfo.bDataBufAllocated = FALSE; // Do a quick check to make sure we have all required pieces uiNumKeysHavingCdl = 0; pIcd = pIxd->pFirstKey; while (pIcd) { pCdl = pCdlTbl [pIcd->uiCdl].pCdlList; if (!bUseSubtreeNodes) { // Skip any nodes in the subtree if we are not using // sub-tree nodes at this point. Also skip any // "missing" placeholders. while (pCdl && (pCdl->bInNodeSubtree || !pCdl->pNode)) { pCdl = pCdl->pNext; } } else { // Skip any "missing" placeholders. while (pCdl && !pCdl->pNode) { pCdl = pCdl->pNext; } } if (!pCdl) { if (pIcd->uiFlags & ICD_REQUIRED_PIECE) { goto Exit; // Nothing to generate, a required piece is missing. } } else { uiNumKeysHavingCdl++; } pIcd = pIcd->pNextKeyComponent; } // If none of the key pieces had a CDL, we cannot generate a key either. if (!uiNumKeysHavingCdl) { goto Exit; } // Build all of the keys m_keyGenInfo.ui64DocumentID = ui64DocumentID; m_keyGenInfo.pIxd = pIxd; m_keyGenInfo.bIsAsia = (FLMBOOL)(pIxd->uiLanguage >= FLM_FIRST_DBCS_LANG && pIxd->uiLanguage <= FLM_LAST_DBCS_LANG) ? TRUE : FALSE; m_keyGenInfo.bIsCompound = pIxd->uiNumKeyComponents > 1 ? TRUE : FALSE; m_keyGenInfo.pCdlTbl = pCdlTbl; m_keyGenInfo.bUseSubtreeNodes = bUseSubtreeNodes; m_keyGenInfo.bAddKeys = bAddKeys; m_keyGenInfo.pucKeyBuf = m_pucKrefKeyBuf; m_keyGenInfo.pucData = &ucDataBuf [0]; m_keyGenInfo.uiDataBufSize = sizeof( ucDataBuf); m_keyGenInfo.bDataBufAllocated = FALSE; if (RC_BAD( rc = buildKeys( pIxd->pFirstKey, 0))) { goto Exit; } Exit: if (m_keyGenInfo.bDataBufAllocated) { f_free( &m_keyGenInfo.pucData); m_keyGenInfo.bDataBufAllocated = FALSE; } return( rc); } /**************************************************************************** Desc: See if the passed in node has any other siblings with the same ID. The passed in node must be an element node. ****************************************************************************/ FSTATIC RCODE kySeeIfRepeatingSibs( F_Db * pDb, F_DOMNode * pNode, FLMBOOL * pbHadRepeatingSib) { RCODE rc = NE_XFLM_OK; F_DOMNode * pTmpNode = NULL; FLMUINT uiNameId; FLMUINT uiTmpNameId; flmAssert( pNode->getNodeType() == ELEMENT_NODE); if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId))) { goto Exit; } // Traverse next siblings pTmpNode = pNode; pTmpNode->AddRef(); for (;;) { if( RC_BAD( rc = pTmpNode->getNextSibling( pDb, (IF_DOMNode **)&pTmpNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } if( pTmpNode->getNodeType() == ELEMENT_NODE) { if (RC_BAD( rc = pTmpNode->getNameId( pDb, &uiTmpNameId))) { goto Exit; } if (uiTmpNameId == uiNameId) { *pbHadRepeatingSib = TRUE; goto Exit; } } } // Traverse previous siblings pTmpNode->Release(); pTmpNode = pNode; pTmpNode->AddRef(); for (;;) { if( RC_BAD( rc = pTmpNode->getPreviousSibling( pDb, (IF_DOMNode **)&pTmpNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } if (pTmpNode->getNodeType() == ELEMENT_NODE) { if (RC_BAD( rc = pTmpNode->getNameId( pDb, &uiTmpNameId))) { goto Exit; } if (uiTmpNameId == uiNameId) { *pbHadRepeatingSib = TRUE; goto Exit; } } } Exit: if (pTmpNode) { pTmpNode->Release(); } return( rc); } /**************************************************************************** Desc: Get a child node for current traversal node. ****************************************************************************/ FSTATIC RCODE kyFindChildNode( F_Db * pDb, F_Pool * pPool, NODE_TRAV ** ppTrav, FLMBOOL * pbGotChild, FLMBOOL * pbHadRepeatingSib) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; ICD * pIcd; FLMUINT uiNameId; NODE_TRAV * pTrav = *ppTrav; FLMUINT uiElmIcdCount = 0; FLMUINT uiAttrIcdCount = 0; FLMBOOL bTraverseElms; FLMBOOL bTraverseAttrs; ANCHOR_NODE * pAnchorNode = pTrav->pAnchorNode; FLMUINT uiAttrNameId = 0; if( pAnchorNode && pTrav->pNode->getNodeId() == pAnchorNode->ui64AnchorNodeId) { pAnchorNode = pAnchorNode->pNext; } else { pAnchorNode = NULL; } *pbGotChild = FALSE; // Determine if we need to traverse elements, attributes, // or both. pIcd = pTrav->pIcd->pFirstChild; while (pIcd) { if (pIcd->uiFlags & ICD_IS_ATTRIBUTE) { uiAttrNameId = pIcd->uiDictNum; uiAttrIcdCount++; } else { uiElmIcdCount++; } pIcd = pIcd->pNextSibling; } bTraverseElms = (uiElmIcdCount) ? TRUE : FALSE; bTraverseAttrs = (uiAttrIcdCount) ? TRUE : FALSE; flmAssert( pTrav->pNode->getNodeType() == ELEMENT_NODE); if (bTraverseElms) { bTraverseElms = FALSE; if (RC_BAD( rc = pTrav->pNode->getFirstChild( pDb, (IF_DOMNode **)&pNode)) && rc == NE_XFLM_DOM_NODE_NOT_FOUND) { if (bTraverseAttrs) { bTraverseAttrs = FALSE; rc = pTrav->pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode); } else { rc = NE_XFLM_OK; goto Exit; } } } else { flmAssert( bTraverseAttrs); bTraverseAttrs = FALSE; // If we only have a single attribute we are searching for, // it will be quicker to call getAttribute than to cycle through // all of the attributes. if (uiAttrIcdCount == 1) { rc = pTrav->pNode->getAttribute( (IF_Db *)pDb, uiAttrNameId, (IF_DOMNode **)&pNode); } else { rc = pTrav->pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode); } } if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } // Follow siblings until we have a match to any of the sibling ICDs // Attribute nodes are also considered siblings. for (;;) { eDomNodeType eNodeType = pNode->getNodeType(); // Skip any nodes that are not element or attribute nodes. if( eNodeType != ELEMENT_NODE && eNodeType != ATTRIBUTE_NODE) { goto Next_Node; } if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId))) { goto Exit; } // If the node has the same name id as the anchor node, // we need to skip it, unless it is the anchor node itself if( pAnchorNode && uiNameId == pAnchorNode->uiAnchorNameId && pNode->getNodeType() == pAnchorNode->eNodeType && pNode->getIxNodeId() != pAnchorNode->ui64AnchorNodeId) { // Better not be repeated attribute nodes flmAssert( pAnchorNode->eNodeType != ATTRIBUTE_NODE); if (pAnchorNode->bSeeIfRepeatingSibs) { *pbHadRepeatingSib = TRUE; } pIcd = NULL; } else { pIcd = pTrav->pIcd->pFirstChild; if (pNode->getNodeType() == ELEMENT_NODE) { while (pIcd && (pIcd->uiDictNum != uiNameId || (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } } else // pNode->getNodeType() == ATTRIBUTE_NODE { while (pIcd && (pIcd->uiDictNum != uiNameId || !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } } } if (pIcd) { if (!pTrav->pChild) { NODE_TRAV * pNewTrav; if (RC_BAD( rc = pPool->poolCalloc( sizeof( NODE_TRAV), (void **)&pNewTrav))) { goto Exit; } pNewTrav->pParent = pTrav; pTrav->pChild = pNewTrav; } else { f_memset( pTrav->pChild, 0, sizeof( NODE_TRAV)); pTrav->pChild->pParent = pTrav; } *ppTrav = pTrav = pTrav->pChild; pTrav->pNode = pNode; pTrav->pNode->AddRef(); pTrav->pAnchorNode = pAnchorNode; pTrav->uiSibIcdElms = uiElmIcdCount; pTrav->uiSibIcdAttrs = uiAttrIcdCount; pTrav->pIcd = pIcd; if (pNode->getNodeType() == ATTRIBUTE_NODE) { // There will only be one occurrence of any given // attribute node, so once we have found it, there // is no need to traverse any other siblings if // there are no other sibling ICDs that are // attributes. At this point, the number of ICDs // that are elements is irrelevant, because we would // have already traversed through all of them. pTrav->bTraverseSibs = uiAttrIcdCount > 1 ? TRUE : FALSE; // Attributes don't have children pTrav->bTraverseChildren = FALSE; flmAssert( !pIcd->pFirstChild); } else { FLMBOOL bIsAnchor = FALSE; if( pAnchorNode) { if( pNode->getNodeId() == pAnchorNode->ui64AnchorNodeId) { bIsAnchor = TRUE; } } // If there are attribute ICDs or more than one // element ICD, or there is exactly one element // ICD, but this is the anchor node, then no // need to traverse siblings. if (uiAttrIcdCount || uiElmIcdCount > 1 || !bIsAnchor) { pTrav->bTraverseSibs = TRUE; } else { pTrav->bTraverseSibs = FALSE; // No need to specially traverse siblings searching // for repeating siblings if bTraverseSibs is set // to TRUE. if (bIsAnchor && pAnchorNode->bSeeIfRepeatingSibs && !(*pbHadRepeatingSib)) { if (RC_BAD( rc = kySeeIfRepeatingSibs( pDb, pNode, pbHadRepeatingSib))) { goto Exit; } } } // If we have a child ICD, we need to traverse child nodes. pTrav->bTraverseChildren = pIcd->pFirstChild ? TRUE : FALSE; } *pbGotChild = TRUE; goto Exit; // Will return NE_XFLM_OK } Next_Node: // Try the next sibling node if (RC_BAD( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // If we have not yet traversed attributes, do it now. if (!bTraverseAttrs) { break; } bTraverseAttrs = FALSE; // If there is only one attribute, it is quicker to call // getAttribute than it is to cycle through the attributes. if (uiAttrIcdCount == 1) { rc = pTrav->pNode->getAttribute( (IF_Db *)pDb, uiAttrNameId, (IF_DOMNode **)&pNode); } else { rc = pTrav->pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode); } if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } } } Exit: if (!(*pbGotChild)) { pTrav->bTraverseChildren = FALSE; } if (pNode) { pNode->Release(); } return( rc); } /**************************************************************************** Desc: Get a sibling node for current traversal node. ****************************************************************************/ FSTATIC RCODE kyFindSibNode( F_Db * pDb, NODE_TRAV * pTrav, FLMBOOL bTestFirstNode, FLMBOOL * pbGotSib, FLMBOOL * pbHadRepeatingSib ) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; ICD * pIcd; FLMUINT uiNameId; FLMBOOL bTraverseAttrs = pTrav->uiSibIcdAttrs ? TRUE : FALSE; FLMBOOL bTraverseElms = pTrav->uiSibIcdElms ? TRUE : FALSE; FLMBOOL bGetNextNode; ANCHOR_NODE * pAnchorNode = pTrav->pAnchorNode; eDomNodeType eNodeType; *pbGotSib = FALSE; pNode = pTrav->pNode; pNode->AddRef(); eNodeType = pNode->getNodeType(); if( eNodeType == ELEMENT_NODE) { flmAssert( bTraverseElms); } else { flmAssert( eNodeType != DATA_NODE); flmAssert( bTraverseAttrs); bTraverseAttrs = FALSE; } bTraverseElms = FALSE; // Follow siblings until we have a match to any of the sibling ICDs // Attribute nodes are also considered siblings. bGetNextNode = bTestFirstNode ? FALSE : TRUE; for (;;) { if (bGetNextNode) { if (RC_BAD( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // If we have not yet traversed attributes, do it now. if (!bTraverseAttrs) { break; } bTraverseAttrs = FALSE; if (RC_OK( rc = pTrav->pNode->getParentNode( pDb, (IF_DOMNode **)&pNode))) { rc = pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode); } if (RC_BAD( rc)) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } } } else { // Set to TRUE for next time around. bGetNextNode = TRUE; } // Skip any nodes that are not element or attribute nodes eNodeType = pNode->getNodeType(); if( eNodeType != ELEMENT_NODE && eNodeType != ATTRIBUTE_NODE) { continue; } if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId))) { goto Exit; } // If the node has the same name id as the anchor node, // we need to skip it, unless it is the anchor node itself if (pAnchorNode && eNodeType == pAnchorNode->eNodeType && uiNameId == pAnchorNode->uiAnchorNameId && pNode->getIxNodeId() != pAnchorNode->ui64AnchorNodeId) { // Better not be repeated attribute nodes flmAssert( pAnchorNode->eNodeType != ATTRIBUTE_NODE); if (pAnchorNode->bSeeIfRepeatingSibs) { *pbHadRepeatingSib = TRUE; } pIcd = NULL; } else if( eNodeType == ELEMENT_NODE) { // Search forward for a matching ICD. pIcd = pTrav->pIcd; while (pIcd && (pIcd->uiDictNum != uiNameId || (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } // If didn't find a matching ICD in the forward direction, // search backward. if (!pIcd) { pIcd = pTrav->pIcd->pPrevSibling; while (pIcd && (pIcd->uiDictNum != uiNameId || (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pPrevSibling; } } } else { flmAssert( eNodeType == ATTRIBUTE_NODE); // Search forward for a matching ICD. pIcd = pTrav->pIcd; while (pIcd && (pIcd->uiDictNum != uiNameId || !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pNextSibling; } // If didn't find a matching ICD in the forward direction, // search backward. if (!pIcd) { pIcd = pTrav->pIcd->pPrevSibling; while (pIcd && (pIcd->uiDictNum != uiNameId || !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) { pIcd = pIcd->pPrevSibling; } } } if (pIcd) { pTrav->pNode->Release(); pTrav->pNode = pNode; pTrav->pNode->AddRef(); pTrav->pIcd = pIcd; if( eNodeType == ATTRIBUTE_NODE) { // Attributes don't have children pTrav->bTraverseChildren = FALSE; flmAssert( !pIcd->pFirstChild); } else { // If we have a child ICD, we need to traverse child nodes. pTrav->bTraverseChildren = pIcd->pFirstChild ? TRUE : FALSE; } *pbGotSib = TRUE; goto Exit; } } Exit: if (!(*pbGotSib)) { pTrav->bTraverseSibs = FALSE; } if (pNode) { pNode->Release(); } return( rc); } /**************************************************************************** Desc: Release all nodes a CDL table is pointing to. ****************************************************************************/ void kyReleaseCdls( IXD * pIxd, CDL_HDR * pCdlTbl ) { FLMUINT uiLoop; CDL * pCdl; if (pCdlTbl) { for (uiLoop = 0; uiLoop < pIxd->uiNumIcds; uiLoop++) { pCdl = pCdlTbl [uiLoop].pCdlList; while (pCdl) { // NOTE: pNode can be NULL because it may be a // placeholder for a "missing" component if (pCdl->pNode) { pCdl->pNode->Release(); } pCdl = pCdl->pNext; } pCdlTbl [uiLoop].pCdlList = NULL; } } } /**************************************************************************** Desc: Generate index keys from a given node for a particular index. ****************************************************************************/ RCODE F_Db::genIndexKeys( FLMUINT64 ui64DocumentID, F_DOMNode * pIxNode, IXD * pIxd, ICD * pIcd, IxAction eAction) { RCODE rc = NE_XFLM_OK; ICD * pTmpIcd; ICD * pChildIcd; CDL_HDR * pCdlTbl = NULL; CDL * pCdl; ANCHOR_NODE * pAnchorNodeList; ANCHOR_NODE * pAnchorNode; NODE_TRAV * pTrav = NULL; FLMBOOL bGotNode; F_DOMNode * pParent = NULL; F_DOMNode * pTmpNode = NULL; FLMUINT uiParentNameId; FLMUINT uiIxNodeName; FLMBOOL bInNodeSubtree; FLMBOOL bHadRepeatingSib = FALSE; eDomNodeType eIxNodeType = pIxNode->getNodeType(); FLMUINT64 ui64IxNodeId = pIxNode->getIxNodeId(); pAnchorNodeList = NULL; if( RC_BAD( rc = pIxNode->getNameId( this, &uiIxNodeName))) { goto Exit; } // Follow links back up to highest parent. If the path doesn't // match what we have in the index definition, then this node // is irrelevant to generating keys in this index. if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( ANCHOR_NODE), (void **)&pAnchorNode))) { goto Exit; } pAnchorNode->eNodeType = eIxNodeType; pAnchorNode->ui64AnchorNodeId = ui64IxNodeId; pAnchorNode->uiAnchorNameId = uiIxNodeName; // If the action is link/unlink, and the node is an element node // because it is possible for elements to be repeated, we need // to see if there is another sibling node of this same name. // If there is another sibling with this same name, we do not // need to add the "without" keys back in on the unlink action. // Nor do we need to delete the "without" keys on the link // action. if ((eAction == IX_UNLINK_NODE || eAction == IX_LINK_NODE) && eIxNodeType == ELEMENT_NODE) { pAnchorNode->bSeeIfRepeatingSibs = TRUE; } pAnchorNodeList = pAnchorNode; pTmpIcd = pIcd; pParent = pIxNode; pParent->AddRef(); while (pTmpIcd->pParent) { pTmpIcd = pTmpIcd->pParent; if (RC_BAD( rc = pParent->getParentNode( this, (IF_DOMNode **)&pParent))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } if (RC_BAD( rc = pParent->getNameId( this, &uiParentNameId))) { goto Exit; } if (pTmpIcd->uiDictNum == ELM_ROOT_TAG) { // If this node has a parent, it is not the root node, so // we don't match the ICD. if (pParent->getParentId()) { goto Exit; // Will return NE_XFLM_OK } // Better not be any more parent ICDs. flmAssert( !pTmpIcd->pParent); } else { if (pTmpIcd->uiDictNum != uiParentNameId) { goto Exit; // Will return NE_XFLM_OK } } if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( ANCHOR_NODE), (void **)&pAnchorNode))) { goto Exit; } pAnchorNode->eNodeType = pParent->getNodeType(); flmAssert( pAnchorNode->eNodeType == ELEMENT_NODE); pAnchorNode->ui64AnchorNodeId = pParent->getNodeId(); pAnchorNode->uiAnchorNameId = uiParentNameId; pAnchorNode->pNext = pAnchorNodeList; pAnchorNodeList = pAnchorNode; } // Allocate a CDL table for the index. if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( CDL_HDR) * pIxd->uiNumIcds, (void **)&pCdlTbl))) { goto Exit; } // Create a traversal node for the root node we arrived at. if (RC_BAD( rc = m_tempPool.poolCalloc( sizeof( NODE_TRAV), (void **)&pTrav))) { goto Exit; } pTrav->pNode = pParent; pTrav->pNode->AddRef(); pTrav->pIcd = pTmpIcd; pTrav->pAnchorNode = pAnchorNodeList; // Count the number of sibling ICDs that are attributes versus elements while (pTmpIcd) { if (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE) { pTrav->uiSibIcdAttrs++; } else { pTrav->uiSibIcdElms++; } pTmpIcd = pTmpIcd->pNextSibling; } pTmpIcd = pTrav->pIcd->pPrevSibling; while (pTmpIcd) { if (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE) { pTrav->uiSibIcdAttrs++; } else { pTrav->uiSibIcdElms++; } pTmpIcd = pTmpIcd->pPrevSibling; } #ifdef FLM_DEBUG if (pTrav->pIcd->uiDictNum == ELM_ROOT_TAG) { flmAssert( !pTrav->uiSibIcdAttrs && pTrav->uiSibIcdElms == 1); flmAssert( pTrav->pNode->getNodeType() == ELEMENT_NODE); } #endif // See if we need to do this node's siblings. If it is an // attribute node and there are no other attribute ICDs and // no element ICDs, or if it is an element node and there are // no other element ICDs and no attribute ICDs, we don't need // to do the node's siblings. Otherwise, we do, so we will // go up to the node's parent and back down to the first // child element or first attribute. pTrav->bTraverseSibs = FALSE; if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE && (pTrav->uiSibIcdAttrs > 1 || pTrav->uiSibIcdElms)) || (pTrav->pNode->getNodeType() == ELEMENT_NODE && (pTrav->uiSibIcdAttrs || pTrav->uiSibIcdElms > 1))) { if (RC_BAD( rc = pTrav->pNode->getParentNode( this, (IF_DOMNode **)&pTmpNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE && pTrav->uiSibIcdElms) || (pTrav->pNode->getNodeType() == ELEMENT_NODE && pTrav->uiSibIcdElms > 1)) { if (RC_BAD( rc = pTmpNode->getFirstChild( this, (IF_DOMNode **)&pTmpNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // No sibling elements, do we need to do attributes? if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE && pTrav->uiSibIcdAttrs > 1) || (pTrav->pNode->getNodeType() == ELEMENT_NODE && pTrav->uiSibIcdAttrs)) { goto Get_First_Attribute; } } else { pTrav->pNode->Release(); pTrav->pNode = pTmpNode; pTrav->pNode->AddRef(); pTmpNode->Release(); pTmpNode = NULL; pTrav->bTraverseSibs = TRUE; } } else { Get_First_Attribute: if (RC_BAD( rc = pTmpNode->getFirstAttribute( this, (IF_DOMNode **)&pTmpNode))) { if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } else { pTrav->pNode->Release(); pTrav->pNode = pTmpNode; pTrav->pNode->AddRef(); pTmpNode->Release(); pTmpNode = NULL; pTrav->bTraverseSibs = TRUE; } } } // Position to a sibling node to start on. At this point, we should // be guaranteed to at least find the node we started on if (pTrav->bTraverseSibs) { if (RC_BAD( rc = kyFindSibNode( this, pTrav, TRUE, &bGotNode, &bHadRepeatingSib))) { goto Exit; } flmAssert( bGotNode); } else if (pTrav->pNode->getNodeType() == ATTRIBUTE_NODE) { pTrav->bTraverseChildren = FALSE; } else // ELEMENT_NODE { // If we have a child ICD, we need to traverse child nodes. pTrav->bTraverseChildren = pTrav->pIcd->pFirstChild ? TRUE : FALSE; // If this is our primary anchor node, we need to see // if there are repeating siblings. No need to do it // in the other above cases, because if bTraversSibs is // TRUE, it will happen inside kyFindSibNode, and if // the node type is an attribute node, they should never // have repeated siblings. if (pTrav->pAnchorNode && pTrav->pAnchorNode->bSeeIfRepeatingSibs) { if (RC_BAD( rc = kySeeIfRepeatingSibs( this, pTrav->pNode, &bHadRepeatingSib))) { goto Exit; } } } // Follow all links from the current node and ICD, // populating CDL tables as we go. if( pTrav->pNode->getNodeType() == eIxNodeType && pTrav->pNode->getNameId() == uiIxNodeName && pTrav->pNode->getIxNodeId() == ui64IxNodeId) { pTrav->bInNodeSubtree = TRUE; } for (;;) { pCdl = pCdlTbl [pTrav->pIcd->uiCdl].pCdlList; if (pCdl && !pCdl->pNode && pTrav->pNode->getParentId() && pCdl->ui64ParentId == pTrav->pNode->getParentId()) { pCdl->pNode = pTrav->pNode; pCdl->bInNodeSubtree = pTrav->bInNodeSubtree; pCdl->pNode->AddRef(); } else { if (RC_BAD( rc = m_tempPool.poolAlloc( sizeof( CDL), (void **)&pCdl))) { goto Exit; } pCdl->pNode = pTrav->pNode; pCdl->ui64ParentId = pTrav->pNode->getParentId(); pCdl->bInNodeSubtree = pTrav->bInNodeSubtree; pCdl->pNode->AddRef(); pCdl->pNext = pCdlTbl [pTrav->pIcd->uiCdl].pCdlList; pCdlTbl [pTrav->pIcd->uiCdl].pCdlList = pCdl; } // Add "missing" place-holders for any child ICDs pChildIcd = pTrav->pIcd->pFirstChild; while (pChildIcd) { if (RC_BAD( rc = m_tempPool.poolAlloc( sizeof( CDL), (void **)&pCdl))) { goto Exit; } pCdl->pNode = NULL; pCdl->bInNodeSubtree = pTrav->bInNodeSubtree; pCdl->ui64ParentId = pTrav->pNode->getNodeId(); pCdl->pNext = pCdlTbl [pChildIcd->uiCdl].pCdlList; pCdlTbl [pChildIcd->uiCdl].pCdlList = pCdl; pChildIcd = pChildIcd->pNextSibling; } Next_Node: if (pTrav->bTraverseChildren) { bInNodeSubtree = pTrav->bInNodeSubtree; if (RC_BAD( rc = kyFindChildNode( this, &m_tempPool, &pTrav, &bGotNode, &bHadRepeatingSib))) { goto Exit; } if (!bGotNode) { goto Next_Node; } if( bInNodeSubtree || (pTrav->pNode->getNodeType() == eIxNodeType && pTrav->pNode->getNameId() == uiIxNodeName && pTrav->pNode->getIxNodeId() == ui64IxNodeId)) { pTrav->bInNodeSubtree = TRUE; } } else if (pTrav->bTraverseSibs) { // Here we are using bInNodeSubtree to indicate whether the entire // sibling list is in the node's subtree. It could be that the // current node is the subtree, but that does not mean that the // entire sibling list is in the sub-tree. if (!pTrav->bInNodeSubtree) { bInNodeSubtree = FALSE; } else { // Is this sibling list in the node subtree? // If bInNodeSubtree is TRUE, and this is not the original // sub-tree node, then this sibling list has to be subordinate // to the original sub-tree node. if( pTrav->pNode->getNodeType() != eIxNodeType || pTrav->pNode->getNameId() != uiIxNodeName || pTrav->pNode->getIxNodeId() != ui64IxNodeId) { bInNodeSubtree = TRUE; } else { bInNodeSubtree = FALSE; } } if (RC_BAD( rc = kyFindSibNode( this, pTrav, FALSE, &bGotNode, &bHadRepeatingSib))) { goto Exit; } if (!bGotNode) { goto Next_Node; } // If the entire sibling list was in the node sub-tree or this // node is the subtree, then we are in the subtree. if( bInNodeSubtree || (pTrav->pNode->getNodeType() == eIxNodeType && pTrav->pNode->getNameId() == uiIxNodeName && pTrav->pNode->getIxNodeId() == ui64IxNodeId)) { pTrav->bInNodeSubtree = TRUE; } else { pTrav->bInNodeSubtree = FALSE; } } else { if (pTrav->pParent) { pTrav->pNode->Release(); pTrav->pNode = NULL; pTrav = pTrav->pParent; pTrav->bTraverseChildren = FALSE; goto Next_Node; } else { break; } } } // Generate the keys from the combinations of CDLs. switch (eAction) { case IX_UNLINK_NODE: { if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, TRUE, FALSE))) { goto Exit; } if (!bHadRepeatingSib) { if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, FALSE, TRUE))) { goto Exit; } } break; } case IX_LINK_NODE: { if( !bHadRepeatingSib) { if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, FALSE, FALSE))) { goto Exit; } } if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, TRUE, TRUE))) { goto Exit; } break; } case IX_DEL_NODE_VALUE: { if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, TRUE, FALSE))) { goto Exit; } break; } case IX_ADD_NODE_VALUE: { if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, TRUE, TRUE))) { goto Exit; } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } } Exit: // Release the nodes CDL table kyReleaseCdls( pIxd, pCdlTbl); // Release any nodes in the traversal list. if (pTrav) { // Go to top of list while (pTrav->pParent) { pTrav = pTrav->pParent; } // Visit each NODE_TRAV node and release whatever // DOM node it is pointing to. while (pTrav) { if (pTrav->pNode) { pTrav->pNode->Release(); pTrav->pNode = NULL; } pTrav = pTrav->pChild; } } if (pParent) { pParent->Release(); } return( rc); } /**************************************************************************** Desc: Routine that is called before and after changing a node - so we can delete and add any keys from the database. ****************************************************************************/ RCODE F_Db::updateIndexKeys( FLMUINT uiCollectionNum, F_DOMNode * pIxNode, IxAction eAction, FLMBOOL bStartOfUpdate, FLMBOOL * pbIsIndexed) { RCODE rc = NE_XFLM_OK; FLMBOOL bIsRoot; FLMBOOL bIsIndexed = FALSE; ICD * pIcd; FLMUINT uiIcdDictNum; IXD * pIxd; void * pvMark = NULL; FLMUINT64 ui64DocumentID; FLMUINT uiNameId; F_DOMNode * pNode = NULL; F_AttrElmInfo defInfo; IxAction eTmpAction; if( bStartOfUpdate) { if (RC_BAD( rc = krefCntrlCheck())) { goto Exit; } if (m_pOldNodeList) { m_pOldNodeList->resetList(); } } if( RC_BAD( rc = pIxNode->getNameId( this, &uiNameId))) { goto Exit; } if( !uiNameId) { goto Exit; } pNode = pIxNode; pNode->AddRef(); // Lookup the node's ICD list switch( pNode->getNodeType()) { case ELEMENT_NODE: { if (RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) { goto Exit; } break; } case ATTRIBUTE_NODE: { if (RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo))) { goto Exit; } break; } case DATA_NODE: { // Need to operate on the parent node, which should be an element // node. if (RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) { goto Exit; } if (RC_BAD( rc = pNode->getParentNode( this, (IF_DOMNode **)&pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Data node not yet linked to a parent node, so // it won't affect indexing. rc = NE_XFLM_OK; } goto Exit; } // Name ID of element better be the same as what we found in the // data node, and node type better be an element node. flmAssert( pNode->getNameId() == uiNameId); flmAssert( pNode->getNodeType() == ELEMENT_NODE); // Action cannot be link/unlink on data nodes. Higher level needs // to take care of. They should first delete keys on the // parent node, then unlink/link the node, then call add keys on // the parent node. flmAssert( eAction != IX_LINK_NODE && eAction != IX_UNLINK_NODE); break; } default: { // If the node is not an element or an attribute, it will not have // any effect on indexing. goto Exit; } } bIsRoot = pNode->isRootNode(); if( (pIcd = defInfo.m_pFirstIcd) == NULL) { if( !bIsRoot || !m_pDict->m_pRootIcdList) { goto Exit; } else { pIcd = m_pDict->m_pRootIcdList; } } // The node may be indexed so we need to process the ICD list pvMark = m_tempPool.poolMark(); bIsIndexed = TRUE; ui64DocumentID = pNode->getDocumentId(); // Process each index for (;;) { pIxd = pIcd->pIxd; uiIcdDictNum = pIcd->uiDictNum; // See if this ICD's index is on the collection we are doing. // Also, make sure if we are doing a specific index, that this is // that index. if (pIxd->uiCollectionNum != uiCollectionNum) { goto Next_Index; } // See if this document has been indexed for this index. if (ui64DocumentID > pIxd->ui64LastDocIndexed) { goto Next_Index; } if ((eTmpAction = eAction) == IX_LINK_AND_ADD_NODE) { if (!pIcd->pParent && !pIcd->pNextSibling && !pIcd->pPrevSibling) { if (!pIcd->uiKeyComponent && !pIcd->uiDataComponent) { // If this ICD is not a key or data component, it has no // effect on index keys if we are only modifying its value. goto Next_Index; } else { eTmpAction = IX_ADD_NODE_VALUE; } } else { eTmpAction = IX_LINK_NODE; } } else if (eTmpAction == IX_LINK_NODE || eTmpAction == IX_UNLINK_NODE) { // IX_LINK_NODE and IX_UNLINK_NODE are called when a node is linked // to its parent. Thus, if this ICD is the root ICD, it will have // no effect on index keys. if (!pIcd->pParent && !pIcd->pNextSibling && !pIcd->pPrevSibling) { goto Next_Index; } } else { if (!pIcd->uiKeyComponent && !pIcd->uiDataComponent) { // If this ICD is not a key or data component, it has no // effect on index keys if we are only modifying its value. goto Next_Index; } } // Generate index keys for this index. if (RC_BAD( rc = genIndexKeys( ui64DocumentID, pNode, pIxd, pIcd, eTmpAction))) { goto Exit; } Next_Index: m_tempPool.poolReset( pvMark); if ((pIcd = pIcd->pNextInChain) == NULL) { if (!bIsRoot || uiIcdDictNum == ELM_ROOT_TAG) { break; } // When done processing regular ICDs, process the // root ICD list, if this node is a root node. if ((pIcd = m_pDict->m_pRootIcdList) == NULL) { break; } } } Exit: if( pvMark) { m_tempPool.poolReset( pvMark); } if (pNode) { pNode->Release(); } if( pbIsIndexed) { *pbIsIndexed = bIsIndexed; } return( rc); } libxflaim-5.1.969/src/flsweep.cpp0000644000175000017500000005576010511001742020212 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the code to F_Db::sweep method // // Tabs: 3 // // Copyright (c) 1996-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flsweep.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC ELM_ATTR_STATE_INFO * sweepFindState( ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT uiNumItems, FLMUINT uiDictType, FLMUINT uiDictNum, FLMUINT * puiTblSlot); /**************************************************************************** Desc: Provides the ability to scan a FLAIM database to delete or check for usage of elements and attributes. ****************************************************************************/ RCODE F_Db::sweep( IF_Thread * pThread) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; ELM_ATTR_STATE_INFO * pStateTbl = NULL; FLMUINT uiNumItems = 0; F_COLLECTION * pCollection; FLMUINT uiCollection; F_Btree * pbtree = NULL; FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen = 0; FLMUINT64 ui64NodeId; FLMBOOL bNeg; FLMUINT uiBytesProcessed; FLMUINT64 ui64SavedTransId; F_DOMNode * pNode = NULL; eDomNodeType eNodeType; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // Must not be a transaction going. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // Start a read transaction if (RC_BAD( rc = beginTrans( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { goto Exit; } bStartedTrans = TRUE; // Determine which elements and attributes have been marked for // purging or checking. if (RC_BAD( rc = sweepGatherList( &pStateTbl, &uiNumItems))) { goto Exit; } // If there were no items to check or purge, we are done if (!uiNumItems) { goto Exit; // Will return NE_XFLM_OK } // Walk through every node in the database. uiCollection = 0; pCollection = NULL; for (;;) { if( pThread->getShutdownFlag()) { goto Exit; } // Get the next collection if necessary. if (!pCollection) { pCollection = m_pDict->getNextCollection( uiCollection, TRUE); if (!pCollection) { break; } uiCollection = pCollection->lfInfo.uiLfNum; if (pbtree) { pbtree->btClose(); } else { // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } } if (RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } uiKeyLen = sizeof( ucKey); if (RC_BAD( rc = flmNumber64ToStorage( 1, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pbtree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; // Go to the next collection, if any. pCollection = NULL; continue; } goto Exit; } } // Get the node ID if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, &ui64NodeId, &bNeg, &uiBytesProcessed))) { goto Exit; } if (RC_BAD( rc = getNode( uiCollection, ui64NodeId, &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { // Better be able to find the node at this point! rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } ui64SavedTransId = getTransID(); eNodeType = pNode->getNodeType(); if( eNodeType == ELEMENT_NODE) { if( RC_BAD( rc = sweepCheckElementState( pNode, pStateTbl, &uiNumItems, &bStartedTrans))) { goto Exit; } } if( !uiNumItems) { goto Exit; } // If the transaction changed, it was due to a dictionary update. // Need to refresh the b-tree because it is using an out-of-date // lfile. if( getTransID() != ui64SavedTransId) { if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) { if( rc == NE_XFLM_BAD_COLLECTION) { rc = NE_XFLM_OK; // Go to the next collection, if any. pCollection = NULL; continue; } goto Exit; } pbtree->btClose(); if( RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } if( RC_BAD( rc = pbtree->btLocateEntry( ucKey, sizeof( ucKey), &uiKeyLen, XFLM_EXCL))) { if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; pCollection = NULL; continue; } goto Exit; } } else { if (RC_BAD( rc = pbtree->btNextEntry( ucKey, sizeof( ucKey), &uiKeyLen))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; pCollection = NULL; continue; } goto Exit; } } } // Now go through all of the items we gathered at the beginning and // if they are still in the state we first looked at them, remove // them. if( RC_BAD( rc = sweepFinalizeStates( pStateTbl, uiNumItems, &bStartedTrans))) { goto Exit; } Exit: if( bStartedTrans) { (void)abortTrans(); } if( pNode) { pNode->Release(); } if( pStateTbl) { f_free( &pStateTbl); } if( pbtree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); } return( rc); } /**************************************************************************** Desc: Gather the list of elements and attributes that are marked as needed to be checked or purged. This routine assumes that a read transaction is already going. ****************************************************************************/ RCODE F_Db::sweepGatherList( ELM_ATTR_STATE_INFO ** ppStateTbl, FLMUINT * puiNumItems) { RCODE rc = NE_XFLM_OK; FLMUINT uiDictType; FLMUINT uiDictNum; FLMUINT uiStateTblSize = 0; ELM_ATTR_STATE_INFO * pStateInfo; F_DOMNode * pDictDoc = NULL; F_AttrElmInfo defInfo; flmAssert( *puiNumItems == 0); flmAssert( *ppStateTbl == NULL); // Gather the elements and attributes that have been marked for // purging or checking. uiDictType = ELM_ELEMENT_TAG; uiDictNum = 0; for (;;) { if (uiDictType == ELM_ELEMENT_TAG) { if (RC_BAD( rc = m_pDict->getNextElement( this, &uiDictNum, &defInfo))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; uiDictNum = 0; uiDictType = ELM_ATTRIBUTE_TAG; continue; } goto Exit; } } else { if (RC_BAD( rc = m_pDict->getNextAttribute( this, &uiDictNum, &defInfo))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; break; } goto Exit; } } if (defInfo.m_uiState == ATTR_ELM_STATE_CHECKING || defInfo.m_uiState == ATTR_ELM_STATE_PURGE) { // Add to the state table, increase table size if needed. if (*puiNumItems == uiStateTblSize) { ELM_ATTR_STATE_INFO * pNewTbl; FLMUINT uiNewSize; // Increase by 100 at a time - should be plenty, because // applications are not going to be checking 100s of // elements or attributes at a time. uiNewSize = uiStateTblSize + 100; if (RC_BAD( rc = f_calloc( uiNewSize * sizeof( ELM_ATTR_STATE_INFO), &pNewTbl))) { goto Exit; } if (uiStateTblSize) { f_memcpy( pNewTbl, *ppStateTbl, sizeof( ELM_ATTR_STATE_INFO) * uiStateTblSize); f_free( ppStateTbl); } *ppStateTbl = pNewTbl; uiStateTblSize = uiNewSize; } pStateInfo = &((*ppStateTbl)[*puiNumItems]); pStateInfo->uiDictType = uiDictType; pStateInfo->uiDictNum = uiDictNum; pStateInfo->uiState = defInfo.m_uiState; // Read the dictionary item and get its state change count. if (RC_BAD( rc = getDictionaryDef( uiDictType, uiDictNum, (IF_DOMNode **)&pDictDoc))) { goto Exit; } if (RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, ATTR_STATE_CHANGE_COUNT_TAG, &pStateInfo->ui64StateChangeCount))) { goto Exit; } (*puiNumItems)++; } defInfo.resetInfo(); } Exit: if (pDictDoc) { pDictDoc->Release(); } return( rc); } /**************************************************************************** Desc: Find an element or attribute's state. ****************************************************************************/ FSTATIC ELM_ATTR_STATE_INFO * sweepFindState( ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT uiNumItems, FLMUINT uiDictType, FLMUINT uiDictNum, FLMUINT * puiTblSlot ) { ELM_ATTR_STATE_INFO * pStateInfo = NULL; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblDictType; FLMUINT uiTblDictNum; FLMINT iCmp; // Do binary search in the table if ((uiTblSize = uiNumItems) == 0) { goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblDictType = pStateTbl [uiMid].uiDictType; uiTblDictNum = pStateTbl [uiMid].uiDictNum; if (uiDictType == uiTblDictType) { if (uiDictNum == uiTblDictNum) { // Found Match pStateInfo = &pStateTbl [uiMid]; *puiTblSlot = uiMid; goto Exit; } else if (uiDictNum < uiTblDictNum) { iCmp = -1; } else { iCmp = 1; } } else if (uiDictType < uiTblDictType) { iCmp = -1; } else { iCmp = 1; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found goto Exit; } if (iCmp < 0) { if (uiMid == 0) { goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { goto Exit; } uiLow = uiMid + 1; } } Exit: return( pStateInfo); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_Db::sweepCheckElementState( F_DOMNode * pElementNode, ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT * puiNumItems, FLMBOOL * pbStartedTrans) { RCODE rc = NE_XFLM_OK; ELM_ATTR_STATE_INFO * pStateInfo; FLMUINT uiNameId; FLMUINT uiTblSlot; F_DOMNode * pDictDoc = NULL; FLMUINT64 ui64StateChangeCount; F_AttrElmInfo defInfo; if( RC_BAD( rc = pElementNode->getNameId( this, &uiNameId))) { goto Exit; } if( !uiNameId) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } pStateInfo = sweepFindState( pStateTbl, *puiNumItems, ELM_ELEMENT_TAG, uiNameId, &uiTblSlot); if( pStateInfo) { // Stop the read transaction and start an update // transaction. if( RC_BAD( rc = abortTrans())) { goto Exit; } *pbStartedTrans = FALSE; if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } *pbStartedTrans = TRUE; // Get the current state to see if it has changed. if( RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) { if( rc != NE_XFLM_BAD_ELEMENT_NUM) { goto Exit; } rc = NE_XFLM_OK; defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } // Read the dictionary item and get its state change count. if( RC_BAD( rc = getDictionaryDef( ELM_ELEMENT_TAG, uiNameId, (IF_DOMNode **)&pDictDoc))) { goto Exit; } if( RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, ATTR_STATE_CHANGE_COUNT_TAG, &ui64StateChangeCount))) { goto Exit; } if( ui64StateChangeCount != pStateInfo->ui64StateChangeCount) { defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } // If the item's state is still 'checking' set it to // active. if( pStateInfo->uiState == ATTR_ELM_STATE_CHECKING) { if( defInfo.m_uiState == ATTR_ELM_STATE_CHECKING) { if( RC_BAD( rc = changeItemState( ELM_ELEMENT_TAG, uiNameId, XFLM_ACTIVE_OPTION_STR))) { goto Exit; } defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } } else { // If the state is not still purge, don't do anything more // on this element - set state to active, so no more purges // will take place. if( defInfo.m_uiState != ATTR_ELM_STATE_PURGE) { defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } else { if( RC_BAD( rc = pElementNode->deleteNode( this))) { if( rc != NE_XFLM_DOM_NODE_DELETED) { goto Exit; } rc = NE_XFLM_OK; } pElementNode = NULL; } } // Commit the transaction *pbStartedTrans = FALSE; if( RC_BAD( rc = commitTrans( 0, FALSE))) { goto Exit; } // If the state got changed to active, remove the thing from the // array and decrement the item count. It means we have stopped // processing this item. if( pStateInfo->uiState != defInfo.m_uiState) { if( uiTblSlot < *puiNumItems - 1) { f_memmove( &pStateTbl [uiTblSlot], &pStateTbl [uiTblSlot + 1], sizeof( ELM_ATTR_STATE_INFO) * (*puiNumItems - 1 - uiTblSlot)); } (*puiNumItems)--; } // Restart the read transaction. if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { goto Exit; } *pbStartedTrans = TRUE; } // Check the element's attributes if( pElementNode) { if( RC_BAD( rc = sweepCheckAttributeStates( pElementNode, pStateTbl, puiNumItems, pbStartedTrans))) { goto Exit; } } Exit: if( pDictDoc) { pDictDoc->Release(); } if( RC_BAD( rc) && *pbStartedTrans) { abortTrans(); *pbStartedTrans = FALSE; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_Db::sweepCheckAttributeStates( F_DOMNode * pElementNode, ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT * puiNumItems, FLMBOOL * pbStartedTrans) { RCODE rc = NE_XFLM_OK; ELM_ATTR_STATE_INFO * pStateInfo; FLMUINT uiTblSlot; FLMUINT uiNameId; F_DOMNode * pDictDoc = NULL; IF_DOMNode * pAttrNode = NULL; IF_DOMNode * pNextAttrNode = NULL; FLMUINT64 ui64StateChangeCount; F_AttrElmInfo defInfo; FLMBOOL bModifiedDatabase = FALSE; flmAssert( pElementNode->getNodeType() == ELEMENT_NODE); if( !pElementNode->hasAttributes()) { goto Exit; } if( RC_BAD( rc = pElementNode->getFirstAttribute( this, &pAttrNode))) { flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); goto Exit; } for( ;;) { if( RC_BAD( rc = pAttrNode->getNameId( this, &uiNameId))) { goto Exit; } pStateInfo = sweepFindState( pStateTbl, *puiNumItems, ELM_ATTRIBUTE_TAG, uiNameId, &uiTblSlot); // No need to do anything if there is no state info. if( !pStateInfo) { if( RC_BAD( rc = pAttrNode->getNextSibling( this, &pAttrNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; break; } continue; } // Stop the read transaction and start an update // transaction. if( getTransType() != XFLM_UPDATE_TRANS) { if( RC_BAD( rc = abortTrans())) { goto Exit; } *pbStartedTrans = FALSE; if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } *pbStartedTrans = TRUE; bModifiedDatabase = TRUE; } // Get the current state to see if it has changed. if( RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo))) { if( rc != NE_XFLM_BAD_ATTRIBUTE_NUM) { goto Exit; } rc = NE_XFLM_OK; defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } // Read the dictionary item and get its state change count. if( RC_BAD( rc = getDictionaryDef( ELM_ATTRIBUTE_TAG, uiNameId, (IF_DOMNode **)&pDictDoc))) { goto Exit; } if( RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, ATTR_STATE_CHANGE_COUNT_TAG, &ui64StateChangeCount))) { goto Exit; } if( ui64StateChangeCount != pStateInfo->ui64StateChangeCount) { defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } // Get the next attribute before doing anything to our // current attribute - because we may end up deleting // pAttrNode below. if( RC_BAD( rc = pAttrNode->getNextSibling( this, &pNextAttrNode))) { if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; } // If the item's state is still 'checking' set it to // active. if( pStateInfo->uiState == ATTR_ELM_STATE_CHECKING) { if( defInfo.m_uiState == ATTR_ELM_STATE_CHECKING) { if( RC_BAD( rc = changeItemState( ELM_ATTRIBUTE_TAG, uiNameId, XFLM_ACTIVE_OPTION_STR))) { goto Exit; } defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } } else { // If the state is not still purge, don't do anything more // on this attribute - set state to active, so no more purges // will take place. if( defInfo.m_uiState != ATTR_ELM_STATE_PURGE) { defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } else { if( RC_BAD( rc = pAttrNode->deleteNode( this))) { if( rc != NE_XFLM_DOM_NODE_DELETED) { goto Exit; } rc = NE_XFLM_OK; } } } // If the state got changed to active, remove the thing from the // array and decrement the item count. It means we have stopped // processing this item. if( pStateInfo->uiState != defInfo.m_uiState) { if( uiTblSlot < *puiNumItems - 1) { f_memmove( &pStateTbl [uiTblSlot], &pStateTbl [uiTblSlot + 1], sizeof( ELM_ATTR_STATE_INFO) * (*puiNumItems - 1 - uiTblSlot)); } (*puiNumItems)--; if( *puiNumItems == 0) { break; } } pAttrNode->Release(); pAttrNode = NULL; // Point pAttrNode to pNextAttrNode and steal its AddRef() if( (pAttrNode = pNextAttrNode) == NULL) { break; } pNextAttrNode = NULL; } if( bModifiedDatabase) { // Commit the transaction *pbStartedTrans = FALSE; if( RC_BAD( rc = commitTrans( 0, FALSE))) { goto Exit; } // Restart the read transaction. if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) { goto Exit; } *pbStartedTrans = TRUE; } Exit: if( pDictDoc) { pDictDoc->Release(); } if( pAttrNode) { pAttrNode->Release(); } if( pNextAttrNode) { pNextAttrNode->Release(); } if( RC_BAD( rc) && *pbStartedTrans) { abortTrans(); *pbStartedTrans = FALSE; } return( rc); } /**************************************************************************** Desc: Go through items in the element/attribute table and finalize the state for each item. ****************************************************************************/ RCODE F_Db::sweepFinalizeStates( ELM_ATTR_STATE_INFO * pStateTbl, FLMUINT uiNumItems, FLMBOOL * pbStartedTrans) { RCODE rc = NE_XFLM_OK; ELM_ATTR_STATE_INFO * pStateInfo; F_DOMNode * pNode = NULL; F_DOMNode * pDictDoc = NULL; FLMUINT uiLoop; FLMUINT64 ui64StateChangeCount; F_AttrElmInfo defInfo; m_bItemStateUpdOk = TRUE; // Stop the read transaction and start an update transaction. abortTrans(); *pbStartedTrans = FALSE; if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } *pbStartedTrans = TRUE; // Check the state of all items in the table. for (uiLoop = 0, pStateInfo = pStateTbl; uiLoop < uiNumItems; uiLoop++, pStateInfo++) { if (pStateInfo->uiDictType == ELM_ELEMENT_TAG) { if (RC_BAD( rc = m_pDict->getElement( this, pStateInfo->uiDictNum, &defInfo))) { // Element has gone away. if( rc != NE_XFLM_BAD_ELEMENT_NUM) { goto Exit; } rc = NE_XFLM_OK; defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } } else { if (RC_BAD( rc = m_pDict->getAttribute( this, pStateInfo->uiDictNum, &defInfo))) { // Attribute has gone away. if( rc != NE_XFLM_BAD_ATTRIBUTE_NUM) { goto Exit; } rc = NE_XFLM_OK; defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; } } // Read the dictionary item and get its state change count. if (RC_BAD( rc = getDictionaryDef( pStateInfo->uiDictType, pStateInfo->uiDictNum, (IF_DOMNode **)&pDictDoc))) { goto Exit; } if (RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, ATTR_STATE_CHANGE_COUNT_TAG, &ui64StateChangeCount))) { goto Exit; } // If the state is unchanged, purge the definition document if (defInfo.m_uiState == pStateInfo->uiState && ui64StateChangeCount == pStateInfo->ui64StateChangeCount) { // First make sure the element or attribute is not // referenced from an index definition. if (pStateInfo->uiDictType == ELM_ELEMENT_TAG) { if( RC_BAD( rc = m_pDict->checkElementReferences( pStateInfo->uiDictNum))) { if( rc != NE_XFLM_CANNOT_DEL_ELEMENT) { goto Exit; } rc = NE_XFLM_OK; pStateInfo->uiState = ATTR_ELM_STATE_ACTIVE; } } else { if( RC_BAD( rc = m_pDict->checkAttributeReferences( pStateInfo->uiDictNum))) { if( rc != NE_XFLM_CANNOT_DEL_ATTRIBUTE) { goto Exit; } rc = NE_XFLM_OK; pStateInfo->uiState = ATTR_ELM_STATE_ACTIVE; } } if( pStateInfo->uiState == ATTR_ELM_STATE_ACTIVE) { // Change the state to active since it is referenced // from an index definition if (RC_BAD( rc = changeItemState( pStateInfo->uiDictType, pStateInfo->uiDictNum, XFLM_ACTIVE_OPTION_STR))) { goto Exit; } } else { F_DataVector srchKey; F_DataVector foundKey; // Find and purge the definition document. if (RC_BAD( rc = srchKey.setUINT( 0, pStateInfo->uiDictType))) { goto Exit; } if (RC_BAD( rc = srchKey.setUINT( 1, pStateInfo->uiDictNum))) { goto Exit; } if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, &srchKey, XFLM_EXACT, &foundKey))) { if (rc == NE_XFLM_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, foundKey.getDocumentID(), &pNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); } goto Exit; } if (RC_BAD( rc = pNode->deleteNode( this))) { goto Exit; } } } defInfo.resetInfo(); } // Commit the transaction. *pbStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, FALSE))) { goto Exit; } Exit: if( RC_BAD( rc) && *pbStartedTrans) { abortTrans(); *pbStartedTrans = FALSE; } m_bItemStateUpdOk = FALSE; if (pNode) { pNode->Release(); } if (pDictDoc) { pDictDoc->Release(); } return( rc); } libxflaim-5.1.969/src/fntable.cpp0000644000175000017500000016740610511001742020161 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Class for managing a name table. // // Tabs: 3 // // Copyright (c) 1992, 1994-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fntable.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define MAX_ELEMENTS_TO_LOAD 0xFFFF #define MAX_ATTRIBUTES_TO_LOAD 0xFFFF typedef FLMINT (* TAG_COMPARE_FUNC)( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); FSTATIC FLMINT tagNameCompare( const FLMUNICODE * puzName1, const char * pszName1, const FLMUNICODE * puzName2); FSTATIC FLMINT compareTagTypeAndName( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); FSTATIC FLMINT compareTagTypeAndNum( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2); FSTATIC RCODE findTagName( F_Db * pDb, FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, F_DataVector * pSrchKey, FLMUINT * puiDictNum, FLMUINT64 * pui64DocumentID); FLMUNICODE gv_uzXFLAIMNamespace[] = // http://www.novell.com/XMLDatabase/Schema { 'h','t','t','p',':','/','/','w','w','w','.','n','o','v','e','l','l','.','c','o','m','/', 'X','M','L','D','a','t','a','b','a','s','e','/','S','c','h','e','m','a', 0}; RESERVED_TAG_NAME FlmReservedElementTags[] = { {ELM_ELEMENT_TAG_NAME, ELM_ELEMENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ATTRIBUTE_TAG_NAME, ELM_ATTRIBUTE_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_INDEX_TAG_NAME, ELM_INDEX_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ELEMENT_COMPONENT_TAG_NAME, ELM_ELEMENT_COMPONENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ATTRIBUTE_COMPONENT_TAG_NAME, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_COLLECTION_TAG_NAME, ELM_COLLECTION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_PREFIX_TAG_NAME, ELM_PREFIX_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_NEXT_DICT_NUMS_TAG_NAME, ELM_NEXT_DICT_NUMS_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_DOCUMENT_TITLE_TAG_NAME, ELM_DOCUMENT_TITLE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ELM_INVALID_TAG_NAME, ELM_INVALID_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_QUARANTINED_TAG_NAME, ELM_QUARANTINED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ELM_ALL_TAG_NAME, ELM_ALL_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ANNOTATION_TAG_NAME, ELM_ANNOTATION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ANY_TAG_NAME, ELM_ANY_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ATTRIBUTE_GROUP_TAG_NAME, ELM_ATTRIBUTE_GROUP_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_CHOICE_TAG_NAME, ELM_CHOICE_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_COMPLEX_CONTENT_TAG_NAME, ELM_COMPLEX_CONTENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_COMPLEX_TYPE_TAG_NAME, ELM_COMPLEX_TYPE_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_DOCUMENTATION_TAG_NAME, ELM_DOCUMENTATION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_ENUMERATION_TAG_NAME, ELM_ENUMERATION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_EXTENSION_TAG_NAME, ELM_EXTENSION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, {ELM_DELETE_TAG_NAME, ELM_DELETE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ELM_BLOCK_CHAIN_TAG_NAME, ELM_BLOCK_CHAIN_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ELM_ENCDEF_TAG_NAME, ELM_ENCDEF_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ELM_SWEEP_TAG_NAME, ELM_SWEEP_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {NULL, 0, 0, NULL} }; RESERVED_TAG_NAME FlmReservedAttributeTags[] = { {ATTR_DICT_NUMBER_TAG_NAME, ATTR_DICT_NUMBER_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_COLLECTION_NUMBER_TAG_NAME, ATTR_COLLECTION_NUMBER_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_COLLECTION_NAME_TAG_NAME, ATTR_COLLECTION_NAME_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NAME_TAG_NAME, ATTR_NAME_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_TARGET_NAMESPACE_TAG_NAME, ATTR_TARGET_NAMESPACE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_TYPE_TAG_NAME, ATTR_TYPE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_STATE_TAG_NAME, ATTR_STATE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_LANGUAGE_TAG_NAME, ATTR_LANGUAGE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_INDEX_OPTIONS_TAG_NAME, ATTR_INDEX_OPTIONS_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_INDEX_ON_TAG_NAME, ATTR_INDEX_ON_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_REQUIRED_TAG_NAME, ATTR_REQUIRED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_LIMIT_TAG_NAME, ATTR_LIMIT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_COMPARE_RULES_TAG_NAME, ATTR_COMPARE_RULES_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_KEY_COMPONENT_TAG_NAME, ATTR_KEY_COMPONENT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_DATA_COMPONENT_TAG_NAME, ATTR_DATA_COMPONENT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_LAST_DOC_INDEXED_TAG_NAME, ATTR_LAST_DOC_INDEXED_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NEXT_ELEMENT_NUM_TAG_NAME, ATTR_NEXT_ELEMENT_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NEXT_ATTRIBUTE_NUM_TAG_NAME, ATTR_NEXT_ATTRIBUTE_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NEXT_INDEX_NUM_TAG_NAME, ATTR_NEXT_INDEX_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NEXT_COLLECTION_NUM_TAG_NAME, ATTR_NEXT_COLLECTION_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NEXT_PREFIX_NUM_TAG_NAME, ATTR_NEXT_PREFIX_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_SOURCE_TAG_NAME, ATTR_SOURCE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_STATE_CHANGE_COUNT_TAG_NAME, ATTR_STATE_CHANGE_COUNT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_XMLNS_TAG_NAME, ATTR_XMLNS_TAG, XFLM_TEXT_TYPE, NULL}, {ATTR_ABSTRACT_TAG_NAME, ATTR_ABSTRACT_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_BASE_TAG_NAME, ATTR_BASE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_BLOCK_TAG_NAME, ATTR_BLOCK_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_DEFAULT_TAG_NAME, ATTR_DEFAULT_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_FINAL_TAG_NAME, ATTR_FINAL_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_FIXED_TAG_NAME, ATTR_FIXED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_ITEM_TYPE_TAG_NAME, ATTR_ITEM_TYPE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_MEMBER_TYPES_TAG_NAME, ATTR_MEMBER_TYPES_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_MIXED_TAG_NAME, ATTR_MIXED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NILLABLE_TAG_NAME, ATTR_NILLABLE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_REF_TAG_NAME, ATTR_REF_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_USE_TAG_NAME, ATTR_USE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_VALUE_TAG_NAME, ATTR_VALUE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_ADDRESS_TAG_NAME, ATTR_ADDRESS_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {ATTR_XMLNS_XFLAIM_TAG_NAME, ATTR_XMLNS_XFLAIM_TAG, XFLM_TEXT_TYPE, NULL}, {ATTR_ENCRYPTION_KEY_TAG_NAME, ATTR_ENCRYPTION_KEY_TAG, XFLM_BINARY_TYPE, gv_uzXFLAIMNamespace}, {ATTR_TRANSACTION_TAG_NAME, ATTR_TRANSACTION_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_NEXT_ENCDEF_NUM_TAG_NAME, ATTR_NEXT_ENCDEF_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_ENCRYPTION_ID_TAG_NAME, ATTR_ENCRYPTION_ID_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_ENCRYPTION_KEY_SIZE_TAG_NAME, ATTR_ENCRYPTION_KEY_SIZE_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, {ATTR_UNIQUE_SUB_ELEMENTS_TAG_NAME, ATTR_UNIQUE_SUB_ELEMENTS_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, {NULL, 0, 0, NULL} }; FSTATIC void sortTagTbl( FLM_TAG_INFO * * ppTagInfoTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds, TAG_COMPARE_FUNC fnTagCompare); /**************************************************************************** Desc: Constructor ****************************************************************************/ F_NameTable::F_NameTable() { m_pool.poolInit( 1024); m_uiMemoryAllocated = 0; m_ppSortedByTagTypeAndName = NULL; m_ppSortedByTagTypeAndNum = NULL; m_uiTblSize = 0; m_uiNumTags = 0; m_bTablesSorted = FALSE; // The m_bLoadedAllElements will be set to FALSE if we call addTag and // there is no more room for elements. Same for m_bLoadedAllAttributes // with respect to attributes. m_bLoadedAllElements = TRUE; m_bLoadedAllAttributes = TRUE; m_uiNumElementsLoaded = 0; m_uiNumAttributesLoaded = 0; m_ppuzNamespaces = NULL; m_uiNamespaceTblSize = 0; m_uiNumNamespaces = 0; } /**************************************************************************** Desc: Destructor ****************************************************************************/ F_NameTable::~F_NameTable() { clearTable( 0); } /**************************************************************************** Desc: Free everything in the table ****************************************************************************/ void F_NameTable::clearTable( FLMUINT uiPoolBlkSize) { m_pool.poolFree(); if (uiPoolBlkSize) { m_pool.poolInit( uiPoolBlkSize); } m_uiMemoryAllocated = 0; // NOTE: Only one allocation is used for m_ppSortedByTagTypeAndName and // m_ppSortedByTagTypeAndNum - there is no // need to free m_ppSortedByTagTypeAndNum if (m_ppSortedByTagTypeAndName) { f_free( &m_ppSortedByTagTypeAndName); m_ppSortedByTagTypeAndNum = NULL; m_uiTblSize = 0; m_uiNumTags = 0; } if (m_ppuzNamespaces) { f_free( &m_ppuzNamespaces); m_ppuzNamespaces = NULL; m_uiNamespaceTblSize = 0; m_uiNumNamespaces = 0; } m_bTablesSorted = FALSE; m_bLoadedAllElements = TRUE; m_bLoadedAllAttributes = TRUE; m_uiNumElementsLoaded = 0; m_uiNumAttributesLoaded = 0; } /**************************************************************************** Desc: Compare two tag names. Name1 can be NATIVE or UNICODE. If a non-NULL UNICODE string is passed, it will be used. Otherwise, the NATIVE string will be used. Note: Comparison is case sensitive. Either or both names may be NULL. Empty strings and NULL pointers are considered to be equal. ****************************************************************************/ FSTATIC FLMINT tagNameCompare( const FLMUNICODE * puzName1, // If NULL, use pszName1 for comparison const char * pszName1, const FLMUNICODE * puzName2) { FLMUNICODE uzChar1; FLMUNICODE uzChar2; FLMUNICODE uzLowerChar1; FLMUNICODE uzLowerChar2; if (puzName1) { if (!puzName2) { if (*puzName1) { return( 1); } else { return( 0); } } for (;;) { uzChar1 = *puzName1; uzChar2 = *puzName2; if (!uzChar1) { if (!uzChar2) { return( 0); } else { return( -1); } } else if (!uzChar2) { return( 1); } else if (uzChar1 != uzChar2) { Test_Case: uzLowerChar1 = f_uniToLower( uzChar1); uzLowerChar2 = f_uniToLower( uzChar2); if (uzLowerChar1 < uzLowerChar2) { return( -1); } else if (uzLowerChar1 > uzLowerChar2) { return( 1); } else if (uzLowerChar1 != uzChar1) { // Char1 is uppercase, char2 is lowercase // Uppercase sorts before lowercase return( -1); } else { // Char1 is lowercase, char2 is uppercase // Lowercase sorts after uppercase return( 1); } } puzName1++; puzName2++; } } else if (pszName1) { if (!puzName2) { if (*pszName1) { return( 1); } else { return( 0); } } for (;;) { uzChar1 = (FLMUNICODE)*pszName1; uzChar2 = *puzName2; if (!uzChar1) { if (!uzChar2) { return( 0); } else { return( -1); } } else if (!uzChar2) { return( 1); } else if (uzChar1 != uzChar2) { goto Test_Case; } pszName1++; puzName2++; } } else if (puzName2) { // Both name1's are NULL. if (*puzName2) { return( -1); } else { return( 0); } } else { // Both name1 and name2 are NULL. return( 0); } } /**************************************************************************** Desc: Lookup a tag by type and tag number. ****************************************************************************/ FLM_TAG_INFO * F_NameTable::findTagByTypeAndNum( FLMUINT uiType, FLMUINT uiTagNum, FLMUINT * puiInsertPos) { FLM_TAG_INFO * pTagInfo = NULL; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMUINT uiTblTagNum; FLMUINT uiTblType; if (!m_bTablesSorted) { sortTags(); } // Do binary search in the table if ((uiTblSize = m_uiNumTags) == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblType = m_ppSortedByTagTypeAndNum [uiMid]->uiType; uiTblTagNum = m_ppSortedByTagTypeAndNum [uiMid]->uiTagNum; if (uiTblType == uiType && uiTagNum == uiTblTagNum) { // Found Match pTagInfo = m_ppSortedByTagTypeAndNum [uiMid]; if (puiInsertPos) { *puiInsertPos = uiMid; } goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (uiType < uiTblType || (uiType == uiTblType && uiTagNum < uiTblTagNum)) ? uiMid : uiMid + 1; } goto Exit; } if (uiType < uiTblType || (uiType == uiTblType && uiTagNum < uiTblTagNum)) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( pTagInfo); } /**************************************************************************** Desc: Lookup a tag by tag type and tag name. Tag name is passed in as a UNICODE string or a NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ FLM_TAG_INFO * F_NameTable::findTagByTypeAndName( FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, FLMBOOL bMatchNamespace, const FLMUNICODE * puzNamespace, FLMBOOL * pbAmbiguous, FLMUINT * puiInsertPos) { FLM_TAG_INFO * pTagInfo = NULL; FLMUINT uiTblType; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMINT iCmp; if (!m_bTablesSorted) { sortTags(); } // Do binary search in the table *pbAmbiguous = FALSE; if ((uiTblSize = m_uiNumTags) == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; uiTblType = m_ppSortedByTagTypeAndName [uiMid]->uiType; if (uiType < uiTblType) { iCmp = -1; } else if (uiType > uiTblType) { iCmp = 1; } else if ((iCmp = tagNameCompare( puzTagName, pszTagName, m_ppSortedByTagTypeAndName [uiMid]->puzTagName)) == 0) { // Elements and attributes need to also compare namespace if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) { // If the bMatchNamespace flag is FALSE, only search on // name. If there are multiple elements or attributes // with the same name, return an ambiguous flag. if (!bMatchNamespace) { // Better not be trying to insert a new one in this case! flmAssert( puiInsertPos == NULL); if ((uiMid && tagNameCompare( puzTagName, pszTagName, m_ppSortedByTagTypeAndName [uiMid-1]->puzTagName) == 0) || (uiMid < m_uiNumTags - 1 && tagNameCompare( puzTagName, pszTagName, m_ppSortedByTagTypeAndName [uiMid+1]->puzTagName) == 0)) { *pbAmbiguous = TRUE; } } else { iCmp = tagNameCompare( puzNamespace, NULL, m_ppSortedByTagTypeAndName [uiMid]->puzNamespace); } } if (iCmp == 0) { // Found Match pTagInfo = m_ppSortedByTagTypeAndName [uiMid]; if (puiInsertPos) { *puiInsertPos = uiMid; } goto Exit; } } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (iCmp < 0) ? uiMid : uiMid + 1; } goto Exit; } if (iCmp < 0) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( pTagInfo); } /**************************************************************************** Desc: Copy a tag name to a UNICODE or NATIVE buffer. Truncate if necessary. If a non-NULL UNICODE string is passed in, it will be populated. Otherwise, the NATIVE string will be populated. ****************************************************************************/ RCODE F_NameTable::copyTagName( FLMUNICODE * puzDestTagName, char * pszDestTagName, FLMUINT * puiDestBufSize, // Bytes, must be enough for null terminator FLMUNICODE * puzSrcTagName, // May be NULL FLMBOOL bTruncatedNamesOk ) { RCODE rc = NE_XFLM_OK; FLMUINT uiDestCharCnt = *puiDestBufSize; FLMUINT uiCharCnt; if (puzDestTagName) { // Decrement name buffer size by sizeof( FLMUNICODE) to allow for a // terminating NULL character. uiDestChars better be at least big // enough for a null terminating character. flmAssert( uiDestCharCnt >= sizeof( FLMUNICODE)); uiDestCharCnt /= sizeof( FLMUNICODE); uiDestCharCnt--; if (puzSrcTagName) { // Copy the name to the UNICODE buffer. uiCharCnt = 0; while (uiCharCnt < uiDestCharCnt && *puzSrcTagName) { *puzDestTagName++ = *puzSrcTagName; uiCharCnt++; puzSrcTagName++; } *puzDestTagName = 0; *puiDestBufSize = uiCharCnt; if (!bTruncatedNamesOk && *puzSrcTagName) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } } else { *puzDestTagName = 0; *puiDestBufSize = 0; } } else { // Decrement name buffer size by one to allow for a terminating // 0 character. uiDestCharCnt better be at list big // enough for a 0 terminating character. flmAssert( uiDestCharCnt); uiDestCharCnt--; if (puzSrcTagName) { // Copy the name to the NATIVE buffer. Non-Ascii UNICODE characters // will cause NE_XFLM_CONV_ILLEGAL to be returned. uiCharCnt = 0; while (uiCharCnt < uiDestCharCnt && *puzSrcTagName) { if (*puzSrcTagName <= 0xFF) { *pszDestTagName++ = (char)f_tonative( (FLMBYTE)*puzSrcTagName); uiCharCnt++; puzSrcTagName++; } else { rc = RC_SET( NE_XFLM_CONV_ILLEGAL); goto Exit; } } *pszDestTagName = 0; *puiDestBufSize = uiCharCnt; if (!bTruncatedNamesOk && *puzSrcTagName) { rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); goto Exit; } } else { *pszDestTagName = 0; *puiDestBufSize = 0; } } Exit: return( rc); } /*************************************************************************** Desc: Swap two entries in tag info table during sort. *****************************************************************************/ FINLINE void tagInfoSwap( FLM_TAG_INFO * * ppTagInfoTbl, FLMUINT uiPos1, FLMUINT uiPos2 ) { FLM_TAG_INFO * pTmpTagInfo = ppTagInfoTbl [uiPos1]; ppTagInfoTbl [uiPos1] = ppTagInfoTbl [uiPos2]; ppTagInfoTbl [uiPos2] = pTmpTagInfo; } /*************************************************************************** Desc: Comparison function for sorting by tag type and name. ****************************************************************************/ FSTATIC FLMINT compareTagTypeAndName( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2 ) { FLMINT iCmp; if (pTagInfo1->uiType < pTagInfo2->uiType) { return( -1); } else if (pTagInfo1->uiType > pTagInfo2->uiType) { return( 1); } else if ((iCmp = tagNameCompare( pTagInfo1->puzTagName, NULL, pTagInfo2->puzTagName)) != 0) { return( iCmp); } else if (pTagInfo1->uiType == ELM_ELEMENT_TAG || pTagInfo1->uiType == ELM_ATTRIBUTE_TAG) { return( tagNameCompare( pTagInfo1->puzNamespace, NULL, pTagInfo2->puzNamespace)); } else { return( 0); } } /*************************************************************************** Desc: Comparison function for sorting by tag type and number. ****************************************************************************/ FSTATIC FLMINT compareTagTypeAndNum( FLM_TAG_INFO * pTagInfo1, FLM_TAG_INFO * pTagInfo2 ) { if (pTagInfo1->uiType < pTagInfo2->uiType) { return( -1); } else if (pTagInfo1->uiType > pTagInfo2->uiType) { return( 1); } else if (pTagInfo1->uiTagNum < pTagInfo2->uiTagNum) { return( -1); } else if (pTagInfo1->uiTagNum > pTagInfo2->uiTagNum) { return( 1); } else { return( 0); } } /*************************************************************************** Desc: Sort an array of SCACHE pointers by their block address. ****************************************************************************/ FSTATIC void sortTagTbl( FLM_TAG_INFO * * ppTagInfoTbl, FLMUINT uiLowerBounds, FLMUINT uiUpperBounds, TAG_COMPARE_FUNC fnTagCompare ) { FLMUINT uiLBPos; FLMUINT uiUBPos; FLMUINT uiMIDPos; FLMUINT uiLeftItems; FLMUINT uiRightItems; FLM_TAG_INFO * pCurTagInfo; FLMINT iCompare; Iterate_Larger_Half: uiUBPos = uiUpperBounds; uiLBPos = uiLowerBounds; uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; pCurTagInfo = ppTagInfoTbl [uiMIDPos ]; for (;;) { while (uiLBPos == uiMIDPos || // Don't compare with target ((iCompare = fnTagCompare( ppTagInfoTbl [uiLBPos], pCurTagInfo)) < 0)) { if (uiLBPos >= uiUpperBounds) { break; } uiLBPos++; } while (uiUBPos == uiMIDPos || // Don't compare with target (((iCompare = fnTagCompare( pCurTagInfo, ppTagInfoTbl [uiUBPos])) < 0))) { if (!uiUBPos) { break; } uiUBPos--; } if (uiLBPos < uiUBPos ) // Interchange and continue loop. { // Exchange [uiLBPos] with [uiUBPos]. tagInfoSwap( ppTagInfoTbl, uiLBPos, uiUBPos); uiLBPos++; // Scan from left to right. uiUBPos--; // Scan from right to left. } else // Past each other - done { break; } } // Check for swap( LB, MID ) - cases 3 and 4 if( uiLBPos < uiMIDPos ) { // Exchange [uiLBPos] with [uiMIDPos] tagInfoSwap( ppTagInfoTbl, uiMIDPos, uiLBPos); uiMIDPos = uiLBPos; } else if( uiMIDPos < uiUBPos ) { // Exchange [uUBPos] with [uiMIDPos] tagInfoSwap( ppTagInfoTbl, uiMIDPos, uiUBPos); uiMIDPos = uiUBPos; } // Check the left piece. uiLeftItems = (uiLowerBounds + 1 < uiMIDPos ) ? uiMIDPos - uiLowerBounds // 2 or more : 0; uiRightItems = (uiMIDPos + 1 < uiUpperBounds ) ? uiUpperBounds - uiMIDPos // 2 or more : 0; if( uiLeftItems < uiRightItems ) { // Recurse on the LEFT side and goto the top on the RIGHT side. if (uiLeftItems ) { sortTagTbl( ppTagInfoTbl, uiLowerBounds, uiMIDPos - 1, fnTagCompare); } uiLowerBounds = uiMIDPos + 1; goto Iterate_Larger_Half; } else if (uiLeftItems ) // Compute a truth table to figure out this check. { // Recurse on the RIGHT side and goto the top for the LEFT side. if (uiRightItems ) { sortTagTbl( ppTagInfoTbl, uiMIDPos + 1, uiUpperBounds, fnTagCompare); } uiUpperBounds = uiMIDPos - 1; goto Iterate_Larger_Half; } } /**************************************************************************** Desc: Lookup a namespace - so we can reuse the memory. ****************************************************************************/ FLMUNICODE * F_NameTable::findNamespace( FLMUNICODE * puzNamespace, FLMUINT * puiInsertPos ) { FLMUNICODE * puzFoundNamespace = NULL; FLMUINT uiTblSize; FLMUINT uiLow; FLMUINT uiMid; FLMUINT uiHigh; FLMINT iCmp; // Do binary search in the table if ((uiTblSize = m_uiNumNamespaces) == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = --uiTblSize; uiLow = 0; for (;;) { uiMid = (uiLow + uiHigh) / 2; if ((iCmp = tagNameCompare( puzNamespace, NULL, m_ppuzNamespaces [uiMid])) == 0) { // Found Match puzFoundNamespace = m_ppuzNamespaces [uiMid]; if (puiInsertPos) { *puiInsertPos = uiMid; } goto Exit; } // Check if we are done if (uiLow >= uiHigh) { // Done, item not found if (puiInsertPos) { *puiInsertPos = (iCmp < 0) ? uiMid : uiMid + 1; } goto Exit; } if (iCmp < 0) { if (uiMid == 0) { if (puiInsertPos) { *puiInsertPos = 0; } goto Exit; } uiHigh = uiMid - 1; } else { if (uiMid == uiTblSize) { if (puiInsertPos) { *puiInsertPos = uiMid + 1; } goto Exit; } uiLow = uiMid + 1; } } Exit: return( puzFoundNamespace); } /**************************************************************************** Desc: Insert a tag info structure into the sorted tables at the specified positions. ****************************************************************************/ RCODE F_NameTable::insertNamespace( FLMUNICODE * puzNamespace, FLMUINT uiInsertPos ) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; // See if we need to resize the table. Add 32 each time. There // should not be that many different namespaces. if (m_uiNumNamespaces == m_uiNamespaceTblSize) { FLMUINT uiNewSize = m_uiNamespaceTblSize + 32; FLMUNICODE ** ppNewTbl; if( RC_BAD( rc = f_alloc( sizeof( FLMUNICODE *) * uiNewSize, &ppNewTbl))) { goto Exit; } // Copy the old tables into the new. if (m_uiNumNamespaces) { f_memcpy( ppNewTbl, m_ppuzNamespaces, sizeof( FLMUNICODE *) * m_uiNumNamespaces); f_free( &m_ppuzNamespaces); } m_ppuzNamespaces = ppNewTbl; m_uiNamespaceTblSize = uiNewSize; } // Insert the new namespace into the table. uiLoop = m_uiNumNamespaces; while (uiLoop > uiInsertPos) { m_ppuzNamespaces [uiLoop] = m_ppuzNamespaces [uiLoop - 1]; uiLoop--; } m_ppuzNamespaces [uiInsertPos] = puzNamespace; // Increment the total number of namespaces m_uiNumNamespaces++; Exit: return( rc); } /**************************************************************************** Desc: Allocate a new tag info structure and set it up. ****************************************************************************/ RCODE F_NameTable::allocTag( FLMUINT uiType, FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiDataType, FLMUNICODE * puzNamespace, FLM_TAG_INFO ** ppTagInfo) { RCODE rc = NE_XFLM_OK; void * pvMark; FLMUINT uiSaveMemoryAllocated; FLM_TAG_INFO * pTagInfo; FLMUINT uiNameSize; FLMUNICODE * puzTmp; FLMUNICODE * puzTblNamespace; FLMUINT uiNamespaceInsertPos; // Create a new tag info structure. pvMark = m_pool.poolMark(); uiSaveMemoryAllocated = m_uiMemoryAllocated; if (RC_BAD( rc = m_pool.poolCalloc( sizeof( FLM_TAG_INFO), (void **)&pTagInfo))) { goto Exit; } m_uiMemoryAllocated += sizeof( FLM_TAG_INFO); // Allocate the space for the tag name. if (puzTagName) { uiNameSize = (f_unilen( puzTagName) + 1) * sizeof( FLMUNICODE); if (RC_BAD( rc = m_pool.poolAlloc( uiNameSize, (void **)&pTagInfo->puzTagName))) { goto Exit; } m_uiMemoryAllocated += uiNameSize; f_memcpy( pTagInfo->puzTagName, puzTagName, uiNameSize); } else { uiNameSize = (f_strlen( pszTagName) + 1) * sizeof( FLMUNICODE); if (RC_BAD( rc = m_pool.poolAlloc( uiNameSize, (void **)&pTagInfo->puzTagName))) { goto Exit; } m_uiMemoryAllocated += uiNameSize; puzTmp = pTagInfo->puzTagName; while (*pszTagName) { *puzTmp++ = (FLMUNICODE)*pszTagName; pszTagName++; } *puzTmp = 0; } pTagInfo->uiType = uiType; pTagInfo->uiTagNum = uiTagNum; // If this is an element or attribute, set namespace and data type if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) { pTagInfo->uiDataType = uiDataType; if (puzNamespace && *puzNamespace) { // See if we have already allocated the namespace. If so, just // point to it. Otherwise, allocate it and add it to the // namespace table and then point to it. if ((puzTblNamespace = findNamespace( puzNamespace, &uiNamespaceInsertPos)) == NULL) { uiNameSize = (f_unilen( puzNamespace) + 1) * sizeof( FLMUNICODE); if (RC_BAD( rc = m_pool.poolAlloc( uiNameSize, (void **)&puzTblNamespace))) { goto Exit; } m_uiMemoryAllocated += uiNameSize; f_memcpy( puzTblNamespace, puzNamespace, uiNameSize); if (RC_BAD( rc = insertNamespace( puzTblNamespace, uiNamespaceInsertPos))) { goto Exit; } // Need to re-mark the pool after this point, because // we can now not afford to lose the namespace that was // allocated if the pool is reset at Exit due to a later // error. pvMark = m_pool.poolMark(); uiSaveMemoryAllocated = m_uiMemoryAllocated; } pTagInfo->puzNamespace = puzTblNamespace; } } Exit: if (RC_BAD( rc)) { m_pool.poolReset( pvMark); m_uiMemoryAllocated = uiSaveMemoryAllocated; pTagInfo = NULL; } *ppTagInfo = pTagInfo; return( rc); } /**************************************************************************** Desc: Allocate the sort tables. ****************************************************************************/ RCODE F_NameTable::reallocSortTables( FLMUINT uiNewTblSize ) { RCODE rc = NE_XFLM_OK; FLM_TAG_INFO ** ppNewTbl; if( RC_BAD( f_alloc( sizeof( FLM_TAG_INFO *) * uiNewTblSize * 2, &ppNewTbl))) { goto Exit; } // Copy the old tables into the new. if (m_uiNumTags) { f_memcpy( ppNewTbl, m_ppSortedByTagTypeAndName, sizeof( FLM_TAG_INFO *) * m_uiNumTags); f_memcpy( &ppNewTbl [uiNewTblSize], m_ppSortedByTagTypeAndNum, sizeof( FLM_TAG_INFO *) * m_uiNumTags); f_free( &m_ppSortedByTagTypeAndName); } m_ppSortedByTagTypeAndName = ppNewTbl; m_ppSortedByTagTypeAndNum = &ppNewTbl [uiNewTblSize]; m_uiTblSize = uiNewTblSize; Exit: return( rc); } /**************************************************************************** Desc: Add the reserved dictionary tags to the name table. ****************************************************************************/ RCODE F_NameTable::addReservedDictTags( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; for (uiLoop = 0; FlmReservedElementTags [uiLoop].pszTagName; uiLoop++) { if (RC_BAD( rc = addTag( ELM_ELEMENT_TAG, NULL, FlmReservedElementTags [uiLoop].pszTagName, FlmReservedElementTags [uiLoop].uiTagNum, FlmReservedElementTags [uiLoop].uiDataType, FlmReservedElementTags [uiLoop].puzNamespace, FALSE, FALSE))) { goto Exit; } } for (uiLoop = 0; FlmReservedAttributeTags [uiLoop].pszTagName; uiLoop++) { if (RC_BAD( rc = addTag( ELM_ATTRIBUTE_TAG, NULL, FlmReservedAttributeTags [uiLoop].pszTagName, FlmReservedAttributeTags [uiLoop].uiTagNum, FlmReservedAttributeTags [uiLoop].uiDataType, FlmReservedAttributeTags [uiLoop].puzNamespace, FALSE, FALSE))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Get a tag name, number, etc. using type + tag number ordering. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ RCODE F_NameTable::getNextTagTypeAndNumOrder( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName, // May be NULL char * pszTagName, // May be NULL FLMUINT uiNameBufSize, FLMUINT * puiTagNum, // May be NULL FLMUINT * puiDataType, // May be NULL FLMUNICODE * puzNamespace, // May be NULL FLMUINT uiNamespaceBufSize, FLMBOOL bTruncatedNamesOk ) { RCODE rc = NE_XFLM_OK; FLM_TAG_INFO * pTagInfo = NULL; FLMBOOL bFound = FALSE; if (!m_bTablesSorted) { sortTags(); } while (*puiNextPos < m_uiNumTags) { pTagInfo = m_ppSortedByTagTypeAndNum [*puiNextPos]; if (pTagInfo->uiType == uiType) { bFound = TRUE; if (puiTagNum) { *puiTagNum = pTagInfo->uiTagNum; } if( puzTagName || pszTagName) { if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, &uiNameBufSize, pTagInfo->puzTagName, bTruncatedNamesOk))) { goto Exit; } } if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) { if (puiDataType) { *puiDataType = pTagInfo->uiDataType; } if (puzNamespace) { if (RC_BAD( rc = copyTagName( puzNamespace, NULL, &uiNamespaceBufSize, pTagInfo->puzNamespace, bTruncatedNamesOk))) { goto Exit; } } } else { flmAssert( !puiDataType && !puzNamespace); } // Returned *puiNextPos should be the next one to retrieve. (*puiNextPos)++; break; } else if (pTagInfo->uiType > uiType) { break; } else { (*puiNextPos)++; } } if (!bFound) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Get a tag name, number, etc. using type + tag name ordering. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ RCODE F_NameTable::getNextTagTypeAndNameOrder( FLMUINT uiType, FLMUINT * puiNextPos, FLMUNICODE * puzTagName, // May be NULL char * pszTagName, // May be NULL FLMUINT uiNameBufSize, FLMUINT * puiTagNum, // May be NULL FLMUINT * puiDataType, // May be NULL FLMUNICODE * puzNamespace, // May be NULL FLMUINT uiNamespaceBufSize, FLMBOOL bTruncatedNamesOk ) { RCODE rc = NE_XFLM_OK; FLM_TAG_INFO * pTagInfo = NULL; FLMBOOL bFound = FALSE; if (!m_bTablesSorted) { sortTags(); } while (*puiNextPos < m_uiNumTags) { pTagInfo = m_ppSortedByTagTypeAndName [*puiNextPos]; if (pTagInfo->uiType == uiType) { bFound = TRUE; if (puiTagNum) { *puiTagNum = pTagInfo->uiTagNum; } if (puzTagName || pszTagName) { if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, &uiNameBufSize, pTagInfo->puzTagName, bTruncatedNamesOk))) { goto Exit; } } if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) { if (puiDataType) { *puiDataType = pTagInfo->uiDataType; } if (puzNamespace) { if (RC_BAD( rc = copyTagName( puzNamespace, NULL, &uiNamespaceBufSize, pTagInfo->puzNamespace, bTruncatedNamesOk))) { goto Exit; } } } else { flmAssert( !puiDataType && !puzNamespace); } // Returned *puiNextPos should be the next one to retrieve. (*puiNextPos)++; break; } else if (pTagInfo->uiType > uiType) { break; } else { (*puiNextPos)++; } } if (!bFound) { rc = RC_SET( NE_XFLM_EOF_HIT); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Get a tag name from its tag type and number. Tag name is returned as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ RCODE F_NameTable::getFromTagTypeAndNum( F_Db * pDb, FLMUINT uiType, FLMUINT uiTagNum, FLMUNICODE * puzTagName, // May be NULL char * pszTagName, // May be NULL FLMUINT * puiNameBufSize, // May be NULL, returns # characters FLMUINT * puiDataType, // May be NULL FLMUNICODE * puzNamespace, // May be NULL char * pszNamespace, // May be NULL FLMUINT * puiNamespaceBufSize, // May be NULL, returns # characters FLMBOOL bTruncatedNamesOk ) { RCODE rc = NE_XFLM_OK; FLM_TAG_INFO * pTagInfo; FLMUNICODE * puzTmpNamespace = NULL; FLMUNICODE * puzTmpName = NULL; if ((pTagInfo = findTagByTypeAndNum( uiType, uiTagNum)) != NULL) { if( puzTagName || pszTagName) { if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, puiNameBufSize, pTagInfo->puzTagName, bTruncatedNamesOk))) { goto Exit; } } else if (puiNameBufSize) { *puiNameBufSize = f_unilen( pTagInfo->puzTagName); } if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) { if (puiDataType) { *puiDataType = pTagInfo->uiDataType; } if (puzNamespace || pszNamespace) { if (RC_BAD( rc = copyTagName( puzNamespace, pszNamespace, puiNamespaceBufSize, pTagInfo->puzNamespace, bTruncatedNamesOk))) { goto Exit; } } else if (puiNamespaceBufSize) { *puiNamespaceBufSize = f_unilen( pTagInfo->puzNamespace); } } else { flmAssert( !puiDataType && !puzNamespace && !pszNamespace); } } else { // If we do not have all of the tags cached, and this tag number is // outside the range of tag numbers we read in, read the database // definition document to get the information. if (pDb && ((uiType == ELM_ELEMENT_TAG && !m_bLoadedAllElements) || (uiType == ELM_ATTRIBUTE_TAG && !m_bLoadedAllAttributes))) { F_DataVector searchKey; F_DataVector foundKey; F_AttrElmInfo defInfo; // Need to lookup the document's ID, using the tag number if (RC_BAD( rc = searchKey.setUINT( 0, uiType))) { goto Exit; } if (RC_BAD( rc = searchKey.setUINT( 1, uiTagNum))) { goto Exit; } if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } if (RC_BAD( rc = pDb->getElmAttrInfo( uiType, foundKey.getDocumentID(), &defInfo, TRUE, FALSE))) { goto Exit; } if( RC_BAD( rc = defInfo.m_pNameAttr->getUnicode( pDb, &puzTmpName))) { goto Exit; } if( defInfo.m_pTargetNamespaceAttr) { if( RC_BAD( rc = defInfo.m_pTargetNamespaceAttr->getUnicode( pDb, &puzTmpNamespace))) { goto Exit; } } if( puiDataType) { *puiDataType = defInfo.m_uiDataType; } if (puzTagName || pszTagName) { if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, puiNameBufSize, puzTmpName, bTruncatedNamesOk))) { goto Exit; } } else if (puiNameBufSize) { *puiNameBufSize = f_unilen( puzTmpName); } if (puzNamespace || pszNamespace) { if (RC_BAD( rc = copyTagName( puzNamespace, pszNamespace, puiNamespaceBufSize, puzTmpNamespace, bTruncatedNamesOk))) { goto Exit; } } else if (puiNamespaceBufSize) { *puiNamespaceBufSize = f_unilen( puzTmpNamespace); } } else { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } } Exit: if (puzTmpName) { f_free( &puzTmpName); } if (puzTmpNamespace) { f_free( &puzTmpNamespace); } return( rc); } /**************************************************************************** Desc: Find a name of a particular type and determine if it is ambiguous. ****************************************************************************/ FSTATIC RCODE findTagName( F_Db * pDb, FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, F_DataVector * pSrchKey, FLMUINT * puiDictNum, FLMUINT64 * pui64DocumentID) { RCODE rc = NE_XFLM_OK; F_DataVector foundKey; F_DataVector foundKey2; FLMUINT uiFoundType; FLMUNICODE * puzTmpName = NULL; // Node type and namespace are unknown, find the first name // that matches. if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, pSrchKey, XFLM_INCL, &foundKey))) { if (rc == NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_NOT_FOUND); } goto Exit; } if (RC_BAD( rc = foundKey.getUINT( 0, &uiFoundType))) { goto Exit; } // Make sure we are still on the right kind of definition. if (uiFoundType != uiType) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } // Verify that we landed on a key that has the name we // were searching for. Name is in component [1] of the key. if (RC_BAD( rc = foundKey.getUnicode( 1, &puzTmpName))) { goto Exit; } if (tagNameCompare( puzTagName, pszTagName, puzTmpName) != 0) { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } // Get the document ID *pui64DocumentID = foundKey.getDocumentID(); // Get the dictionary number from the data part of the key if (RC_BAD( rc = foundKey.getUINT( 3, puiDictNum))) { goto Exit; } // Determine if there is more than one of that key. if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &foundKey, XFLM_EXCL | XFLM_MATCH_IDS, &foundKey2))) { if (rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { goto Exit; } } else { if (RC_BAD( rc = foundKey2.getUINT( 0, &uiFoundType))) { if (rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } // Make sure we are still on the right kind of definition. if (uiFoundType != uiType) { goto Exit; // will return NE_XFLM_OK } // Verify that we landed on a key that has a different name than we // were searching for. Name is in component [1] of the key. if (RC_BAD( rc = foundKey2.getUnicode( 1, &puzTmpName))) { if (rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; } goto Exit; } if (tagNameCompare( puzTagName, pszTagName, puzTmpName) != 0) { goto Exit; // will return NE_XFLM_OK } rc = RC_SET( NE_XFLM_MULTIPLE_MATCHES); goto Exit; } Exit: if (puzTmpName) { f_free( &puzTmpName); } return( rc); } /**************************************************************************** Desc: Get a tag number from its tag name and type. Tag name is passed in as a UNICODE or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ RCODE F_NameTable::getFromTagTypeAndName( F_Db * pDb, FLMUINT uiType, const FLMUNICODE * puzTagName, const char * pszTagName, FLMBOOL bMatchNamespace, const FLMUNICODE * puzNamespace, FLMUINT * puiTagNum, // May be NULL FLMUINT * puiDataType) // May be NULL { RCODE rc = NE_XFLM_OK; FLM_TAG_INFO * pTagInfo; FLMUINT uiDictNum; FLMBOOL bAmbiguous; FLMUINT uiTmpDictNum; FLMUINT64 ui64DocumentID; F_DataVector searchKey; F_DataVector foundKey; if ((pTagInfo = findTagByTypeAndName( uiType, puzTagName, pszTagName, bMatchNamespace, puzNamespace, &bAmbiguous)) != NULL) { if (puiTagNum) { *puiTagNum = pTagInfo->uiTagNum; } if (puiDataType) { *puiDataType = (FLMUINT)(uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG ? pTagInfo->uiDataType : 0); } if (bAmbiguous) { rc = RC_SET( NE_XFLM_MULTIPLE_MATCHES); goto Exit; } else if (pDb && !bMatchNamespace && ((uiType == ELM_ELEMENT_TAG && !m_bLoadedAllElements) || (uiType == ELM_ATTRIBUTE_TAG && !m_bLoadedAllAttributes))) { // Must see if it is ambiguous - create a search key if (RC_BAD( rc = searchKey.setUINT( 0, uiType))) { goto Exit; } // Put name into the key. if (puzTagName) { if (RC_BAD( rc = searchKey.setUnicode( 1, puzTagName))) { goto Exit; } } else { if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszTagName))) { goto Exit; } } if (RC_BAD( rc = findTagName( pDb, uiType, puzTagName, pszTagName, &searchKey, &uiTmpDictNum, &ui64DocumentID))) { // Better be found at this point because it was in the // name table! Error may be NE_XFLM_MULTIPLE_MATCHES. flmAssert( rc != NE_XFLM_NOT_FOUND); goto Exit; } // What we found in the index better match what we found in // the table! flmAssert( uiTmpDictNum == *puiTagNum); } } else { // If we do not have all of the tags cached, and we did not read in // all of the elements or attributes, read the dictionary name index // to see if we can find this tag name. NOTE that if bMatchNamespace == // FALSE we will simply find the first tag name that // matches and not worry about matching the namespace. if (pDb && ((uiType == ELM_ELEMENT_TAG && !m_bLoadedAllElements) || (uiType == ELM_ATTRIBUTE_TAG && !m_bLoadedAllAttributes))) { F_AttrElmInfo defInfo; // Create a search key if (RC_BAD( rc = searchKey.setUINT( 0, uiType))) { goto Exit; } if (puzTagName) { if (RC_BAD( rc = searchKey.setUnicode( 1, puzTagName))) { goto Exit; } } else { if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszTagName))) { goto Exit; } } if (!bMatchNamespace) { if (RC_BAD( rc = findTagName( pDb, uiType, puzTagName, pszTagName, &searchKey, &uiDictNum, &ui64DocumentID))) { goto Exit; } } else { // Add the namespace as a child node. if (puzNamespace) { if (RC_BAD( rc = searchKey.setUnicode( 2, puzNamespace))) { goto Exit; } } // Search for this exact key. if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &searchKey, XFLM_EXACT, &foundKey))) { goto Exit; } ui64DocumentID = foundKey.getDocumentID(); // Data component [0]'s value will be the dictionary number if (RC_BAD( rc = foundKey.getUINT( 3, &uiDictNum))) { if (rc == NE_XFLM_NOT_FOUND) { flmAssert( 0); uiDictNum = 0; rc = NE_XFLM_OK; } else { goto Exit; } } } if (RC_BAD( rc = pDb->getElmAttrInfo( uiType, ui64DocumentID, &defInfo, TRUE, FALSE))) { goto Exit; } if( puiDataType) { *puiDataType = defInfo.m_uiDataType; } if (puiTagNum) { *puiTagNum = uiDictNum; } } else { rc = RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Insert a tag info structure into the sorted tables at the specified positions. ****************************************************************************/ RCODE F_NameTable::insertTagInTables( FLM_TAG_INFO * pTagInfo, FLMUINT uiTagTypeAndNameTblInsertPos, FLMUINT uiTagTypeAndNumTblInsertPos ) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; // See if we need to resize the tables. Start at 256. Double each // time up to 2048. Then just add 2048 at a time. if (m_uiNumTags == m_uiTblSize) { FLMUINT uiNewSize; if (!m_uiTblSize) { uiNewSize = 256; } else if (m_uiTblSize < 2048) { uiNewSize = m_uiTblSize * 2; } else { uiNewSize = m_uiTblSize + 2048; } if (RC_BAD( rc = reallocSortTables( uiNewSize))) { goto Exit; } } // Insert into the sorted-by-tag-type-and-name table uiLoop = m_uiNumTags; while (uiLoop > uiTagTypeAndNameTblInsertPos) { m_ppSortedByTagTypeAndName [uiLoop] = m_ppSortedByTagTypeAndName [uiLoop - 1]; uiLoop--; } m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblInsertPos] = pTagInfo; // Insert into the sorted-by-tag-type-and-num table uiLoop = m_uiNumTags; while (uiLoop > uiTagTypeAndNumTblInsertPos) { m_ppSortedByTagTypeAndNum [uiLoop] = m_ppSortedByTagTypeAndNum [uiLoop - 1]; uiLoop--; } m_ppSortedByTagTypeAndNum [uiTagTypeAndNumTblInsertPos] = pTagInfo; // Increment the total number of tags m_uiNumTags++; Exit: return( rc); } /**************************************************************************** Desc: Add a tag to the table. Tag name is passed in as a UNICODE string or NATIVE string. If a non-NULL UNICODE string is passed in, it will be used. Otherwise, the NATIVE string will be used. ****************************************************************************/ RCODE F_NameTable::addTag( FLMUINT uiType, FLMUNICODE * puzTagName, const char * pszTagName, FLMUINT uiTagNum, FLMUINT uiDataType, FLMUNICODE * puzNamespace, FLMBOOL bCheckDuplicates, FLMBOOL bLimitNumToLoad) { RCODE rc = NE_XFLM_OK; FLMUINT uiTagTypeAndNameTblInsertPos; FLMUINT uiTagTypeAndNumTblInsertPos; FLM_TAG_INFO * pTagInfo; FLMBOOL bAmbiguous; // Must have a non-NULL tag name. Use UNICODE string if it is // non-NULL. Otherwise, use NATIVE string. if (puzTagName && *puzTagName) { pszTagName = NULL; } else if (pszTagName && *pszTagName) { puzTagName = NULL; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } // Tag number of zero not allowed. if (!uiTagNum) { rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); goto Exit; } // Tables must be sorted in order for this to work if (bCheckDuplicates) { // Make sure that the tag type + name is not already used. if (findTagByTypeAndName( uiType, puzTagName, pszTagName, TRUE, puzNamespace, &bAmbiguous, &uiTagTypeAndNameTblInsertPos)) { rc = RC_SET( NE_XFLM_EXISTS); goto Exit; } // Make sure that the tag type + number is not already used. if (findTagByTypeAndNum( uiType, uiTagNum, &uiTagTypeAndNumTblInsertPos)) { rc = RC_SET( NE_XFLM_EXISTS); goto Exit; } } else { uiTagTypeAndNameTblInsertPos = uiTagTypeAndNumTblInsertPos = m_uiNumTags; m_bTablesSorted = FALSE; } if (uiType == ELM_ELEMENT_TAG) { if (m_uiNumElementsLoaded >= MAX_ELEMENTS_TO_LOAD && bLimitNumToLoad) { // We purposely limit the number of elements that can be // loaded into the table. m_bLoadedAllElements = FALSE; goto Exit; // Will return NE_XFLM_OK } } else if (uiType == ELM_ATTRIBUTE_TAG) { if (m_uiNumAttributesLoaded >= MAX_ATTRIBUTES_TO_LOAD && bLimitNumToLoad) { // We purposely limit the number of elements that can be // loaded into the table. m_bLoadedAllAttributes = FALSE; goto Exit; // Will return NE_XFLM_OK } } // Create a new tag info structure. if (RC_BAD( rc = allocTag( uiType, puzTagName, pszTagName, uiTagNum, uiDataType, puzNamespace, &pTagInfo))) { goto Exit; } // Insert the tag structure into the appropriate places in the // sorted tables. if (RC_BAD( rc = insertTagInTables( pTagInfo, uiTagTypeAndNameTblInsertPos, uiTagTypeAndNumTblInsertPos))) { goto Exit; } if (uiType == ELM_ELEMENT_TAG) { m_uiNumElementsLoaded++; } else if (uiType == ELM_ATTRIBUTE_TAG) { m_uiNumAttributesLoaded++; } Exit: return( rc); } /**************************************************************************** Desc: Sort the tag tables according to their respective criteria. ****************************************************************************/ void F_NameTable::sortTags( void) { if (!m_bTablesSorted && m_uiNumTags > 1) { sortTagTbl( m_ppSortedByTagTypeAndName, 0, m_uiNumTags - 1, compareTagTypeAndName); sortTagTbl( m_ppSortedByTagTypeAndNum, 0, m_uiNumTags - 1, compareTagTypeAndNum); } m_bTablesSorted = TRUE; } /**************************************************************************** Desc: Remove a tag from the table ****************************************************************************/ void F_NameTable::removeTag( FLMUINT uiType, FLMUINT uiTagNum) { FLM_TAG_INFO * pTagInfo; FLMUINT uiTagTypeAndNameTblPos; FLMUINT uiTagTypeAndNumTblPos; FLMBOOL bAmbiguous; FLMBOOL bMatchNamespace; FLMUNICODE * puzNamespace; if ((pTagInfo = findTagByTypeAndNum( uiType, uiTagNum, &uiTagTypeAndNumTblPos)) != NULL) { if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) { puzNamespace = pTagInfo->puzNamespace; bMatchNamespace = TRUE; } else { bMatchNamespace = FALSE; // Really doesn't matter puzNamespace = NULL; } if (findTagByTypeAndName( uiType, pTagInfo->puzTagName, NULL, bMatchNamespace, puzNamespace, &bAmbiguous, &uiTagTypeAndNameTblPos) == NULL) { // It should have been in the name table too! flmAssert( 0); } // Shift everything in the sorted number table that is above // the found position down one. if (uiTagTypeAndNumTblPos < m_uiNumTags - 1) { f_memmove( &m_ppSortedByTagTypeAndNum [uiTagTypeAndNumTblPos], &m_ppSortedByTagTypeAndNum [uiTagTypeAndNumTblPos + 1], sizeof( FLM_TAG_INFO *) * (m_uiNumTags - 1 - uiTagTypeAndNumTblPos)); } // Shift everything in the sorted name table that is above // the found position down one. if (uiTagTypeAndNameTblPos < m_uiNumTags - 1) { f_memmove( &m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblPos], &m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblPos + 1], sizeof( FLM_TAG_INFO *) * (m_uiNumTags - 1 - uiTagTypeAndNameTblPos)); } m_uiNumTags--; } } /**************************************************************************** Desc: Clone a name table from another one ****************************************************************************/ RCODE F_NameTable::cloneNameTable( F_NameTable * pSrcNameTable ) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLM_TAG_INFO * pTagInfo; FLMUINT uiPoolBlkSize; // Set the pool size to be as optimal as possible. uiPoolBlkSize = pSrcNameTable->m_uiMemoryAllocated / 8; if (uiPoolBlkSize < 1024) { uiPoolBlkSize = 1024; } else if (uiPoolBlkSize > 65536) { uiPoolBlkSize = 65536; } clearTable( uiPoolBlkSize); // Pre-allocate exactly enough table space if (RC_BAD( rc = reallocSortTables( pSrcNameTable->m_uiNumTags))) { goto Exit; } // Add all of the tags for (uiLoop = 0; uiLoop < pSrcNameTable->m_uiNumTags; uiLoop++) { pTagInfo = pSrcNameTable->m_ppSortedByTagTypeAndNum [uiLoop]; if (pTagInfo->uiType == ELM_ELEMENT_TAG || pTagInfo->uiType == ELM_ATTRIBUTE_TAG) { if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, pTagInfo->uiTagNum, pTagInfo->uiDataType, pTagInfo->puzNamespace, FALSE))) { goto Exit; } } else { if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, pTagInfo->uiTagNum, 0, NULL, FALSE))) { goto Exit; } } } sortTags(); Exit: return( rc); } /**************************************************************************** Desc: Copy a name table from another one. This differs from cloneNameTable in that it doesn't clear the name table, it just copies the names. Thus, the destination name table may already have names in it, and the names from the source table will just be added. ****************************************************************************/ RCODE F_NameTable::importFromNameTable( F_NameTable * pSrcNameTable ) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; FLM_TAG_INFO * pTagInfo; // Pre-allocate exactly enough table space if (RC_BAD( rc = reallocSortTables( m_uiNumTags + pSrcNameTable->m_uiNumTags))) { goto Exit; } // Add all of the tags from the source table for (uiLoop = 0; uiLoop < pSrcNameTable->m_uiNumTags; uiLoop++) { pTagInfo = pSrcNameTable->m_ppSortedByTagTypeAndNum [uiLoop]; if (pTagInfo->uiType == ELM_ELEMENT_TAG || pTagInfo->uiType == ELM_ATTRIBUTE_TAG) { if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, pTagInfo->uiTagNum, pTagInfo->uiDataType, pTagInfo->puzNamespace, FALSE))) { if (rc != NE_XFLM_EXISTS) { goto Exit; } else { rc = NE_XFLM_OK; } } } else { if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, pTagInfo->uiTagNum, 0, NULL, FALSE))) { if (rc != NE_XFLM_EXISTS) { goto Exit; } else { rc = NE_XFLM_OK; } } } } sortTags(); Exit: return( rc); } /**************************************************************************** Desc: Increment use count on this object. ****************************************************************************/ FLMINT FLMAPI F_NameTable::AddRef( void) { return( f_atomicInc( &m_refCnt)); } /**************************************************************************** Desc: Decrement the use count and delete if use count goes to zero. ****************************************************************************/ FLMINT FLMAPI F_NameTable::Release( void) { FLMINT iRefCnt; if ((iRefCnt = f_atomicDec( &m_refCnt)) == 0) { delete this; } return( iRefCnt); } /**************************************************************************** Desc: Get the name table for an FDB, if any. If it has no current dictionary, get the name table for the FDB's FFILE's first dictionary, if any. Increment the use count on any name table returned. It is the caller's responsibility to release the name table when it is done with it. ****************************************************************************/ RCODE F_Db::getNameTable( F_NameTable ** ppNameTable) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; *ppNameTable = NULL; if (m_pDict) { if ((*ppNameTable = m_pDict->getNameTable()) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_NAME_TABLE); goto Exit; } (*ppNameTable)->AddRef(); } else { m_pDatabase->lockMutex(); bMutexLocked = TRUE; if (m_pDatabase && m_pDatabase->m_pDictList) { if ((*ppNameTable = m_pDatabase->m_pDictList->getNameTable()) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_NO_NAME_TABLE); goto Exit; } (*ppNameTable)->AddRef(); } else { rc = RC_SET( NE_XFLM_NO_NAME_TABLE); goto Exit; } } Exit: if (bMutexLocked) { m_pDatabase->unlockMutex(); } return( rc); } /**************************************************************************** Desc: Get the name for a dictionary item. ****************************************************************************/ RCODE FLMAPI F_Db::getDictionaryName( FLMUINT uiDictType, FLMUINT uiDictNumber, char * pszName, FLMUINT * puiNameBufSize, char * pszNamespace, FLMUINT * puiNamespaceBufSize ) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; if (RC_BAD(rc = getNameTable( &pNameTable))) { goto Exit; } if (pszNamespace && (uiDictType == ELM_ELEMENT_TAG || uiDictType == ELM_ATTRIBUTE_TAG)) { flmAssert( puiNamespaceBufSize); } else { flmAssert( !pszNamespace && !puiNamespaceBufSize); pszNamespace = NULL; puiNamespaceBufSize = NULL; } if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( this, uiDictType, uiDictNumber, NULL, pszName, puiNameBufSize, NULL, NULL, pszNamespace, puiNamespaceBufSize, TRUE))) { goto Exit; } Exit: if (pNameTable) { pNameTable->Release(); } return( rc); } /**************************************************************************** Desc: Get the name for a dictionary item. ****************************************************************************/ RCODE FLMAPI F_Db::getDictionaryName( FLMUINT uiDictType, FLMUINT uiDictNumber, FLMUNICODE * puzName, FLMUINT * puiNameBufSize, FLMUNICODE * puzNamespace, FLMUINT * puiNamespaceBufSize ) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; if (RC_BAD(rc = getNameTable( &pNameTable))) { goto Exit; } if (uiDictType == ELM_ELEMENT_TAG || uiDictType == ELM_ATTRIBUTE_TAG) { flmAssert( puiNamespaceBufSize); } else { flmAssert( !puiNamespaceBufSize); puiNamespaceBufSize = NULL; } if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( this, uiDictType, uiDictNumber, puzName, NULL, puiNameBufSize, NULL, puzNamespace, NULL, puiNamespaceBufSize, TRUE))) { goto Exit; } Exit: if (pNameTable) { pNameTable->Release(); } return( rc); } libxflaim-5.1.969/src/f_btree.h0000644000175000017500000005773510511001742017624 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Header file for the B-Tree class definitions // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: f_btree.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef F_BTREE_H #define F_BTREE_H // Represent the maximum size for data & key before needing two bytes to // store the length. #define ONE_BYTE_SIZE 0xFF // Flag definitions - BT_LEAF_DATA #define BTE_LEAF_DATA_OVHD 7 // Offset (2) Flags (1) OA Data (4) #define BTE_FLAG 0 // Offset to the FLAGS field #define BTE_FLAG_LAST_ELEMENT 0x04 #define BTE_FLAG_FIRST_ELEMENT 0x08 #define BTE_FLAG_DATA_BLOCK 0x10 // Data is stored in a Data-only Block #define BTE_FLAG_OA_DATA_LEN 0x20 // Overall data length #define BTE_FLAG_DATA_LEN 0x40 #define BTE_FLAG_KEY_LEN 0x80 // BT_LEAF (no data) #define BTE_LEAF_OVHD 4 // Offset (2) KeyLen (2) #define BTE_KEY_LEN 0 #define BTE_KEY_START 2 // BT_NON_LEAF_DATA #define BTE_NON_LEAF_OVHD 8 // Offset (2) Child Blk Addr (4) KeyLen (2) #define BTE_NL_CHILD_BLOCK_ADDR 0 #define BTE_NL_KEY_LEN 4 #define BTE_NL_KEY_START 6 // BT_NON_LEAF_COUNTS #define BTE_NON_LEAF_COUNTS_OVHD 12 // Offset (2) Child Blk Addr (4) Counts (4) KeyLen (2) #define BTE_NLC_CHILD_BLOCK_ADDR 0 #define BTE_NLC_COUNTS 4 #define BTE_NLC_KEY_LEN 8 #define BTE_NLC_KEY_START 10 // Low water mark for coalescing blocks (as a percentage) #define BT_LOW_WATER_MARK 65 FINLINE FLMBOOL bteKeyLenFlag( FLMBYTE * pucEntry) { return( (pucEntry[ BTE_FLAG] & BTE_FLAG_KEY_LEN) ? TRUE : FALSE); } FINLINE FLMBOOL bteDataLenFlag( FLMBYTE * pucEntry) { return( (pucEntry[ BTE_FLAG] & BTE_FLAG_DATA_LEN) ? TRUE : FALSE); } FINLINE FLMBOOL bteOADataLenFlag( FLMBYTE * pucEntry) { return( (pucEntry[ BTE_FLAG] & BTE_FLAG_OA_DATA_LEN) ? TRUE : FALSE); } FINLINE FLMBOOL bteDataBlockFlag( FLMBYTE * pucEntry) { return( (pucEntry[ BTE_FLAG] & BTE_FLAG_DATA_BLOCK) ? TRUE : FALSE); } FINLINE FLMBOOL bteFirstElementFlag( FLMBYTE * pucEntry) { return( (pucEntry[ BTE_FLAG] & BTE_FLAG_FIRST_ELEMENT) ? TRUE : FALSE); } FINLINE FLMBOOL bteLastElementFlag( FLMBYTE * pucEntry) { return( (pucEntry[ BTE_FLAG] & BTE_FLAG_LAST_ELEMENT) ? TRUE : FALSE); } FINLINE FLMUINT32 bteGetBlkAddr( const FLMBYTE * pucEntry) { return( FB2UD( pucEntry)); } FINLINE void bteSetEntryOffset( FLMUINT16 * pui16OffsetArray, FLMUINT uiOffsetIndex, FLMUINT16 ui16Offset) { UW2FBA( ui16Offset, (FLMBYTE *)&pui16OffsetArray[ uiOffsetIndex]); } FINLINE FLMUINT16 bteGetEntryOffset( const FLMUINT16 * pui16OffsetArray, FLMUINT uiOffsetIndex) { return( FB2UW( (FLMBYTE *)&pui16OffsetArray[ uiOffsetIndex])); } typedef struct { F_BTREE_BLK_HDR * pBlkHdr; F_CachedBlock * pSCache; const FLMBYTE * pucKeyBuf; FLMUINT uiKeyLen; FLMUINT uiCurOffset; FLMUINT uiLevel; FLMUINT16 * pui16OffsetArray; FLMUINT32 ui32BlkAddr; } F_BTSK, * F_BTSK_p; typedef enum { ELM_INSERT_DO, ELM_INSERT, ELM_REPLACE_DO, ELM_REPLACE, ELM_REMOVE, ELM_BLK_MERGE, ELM_DONE } F_ELM_UPD_ACTION; FINLINE FLMBYTE * BtEntry( FLMBYTE * pBlk, FLMUINT uiIndex) { FLMBYTE * pucOffsetArray = pBlk + sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlk) + (uiIndex * 2); // 2 byte offset entries. return( pBlk + FB2UW( pucOffsetArray)); } FINLINE FLMBYTE * BtLastEntry( FLMBYTE * pBlk ) { return BtEntry( pBlk, ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys - 1); } FINLINE FLMUINT getBlkType( FLMBYTE * pBlk) { return (FLMUINT)((F_BLK_HDR *)pBlk)->ui8BlkType; } FINLINE FLMUINT16 * BtOffsetArray( FLMBYTE * pBlk, FLMUINT uiIndex) { return (FLMUINT16 *) (pBlk + sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlk) + (uiIndex * sizeof( FLMUINT16))); } // Returns the address of the first entry in the block. i.e. the first non-blank // address after the offset array. FINLINE FLMBYTE * getBlockEnd( F_BTREE_BLK_HDR * pBlkHdr) { return ((FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr) + (pBlkHdr->ui16NumKeys * 2) + pBlkHdr->ui16HeapSize); } // This inline function takes the parameter returned from getEntrySize which // adds 2 for the offset. FINLINE FLMUINT actualEntrySize( FLMUINT uiEntrySize) { return uiEntrySize - 2; } // Error information returned by btCheck() enum BTREE_ERR_TYPE { NO_ERR = 0, // FYI: Visual Studio already defines NOERROR BT_HEADER, KEY_ORDER, DUPLICATE_KEYS, INFINITY_MARKER, CHILD_BLOCK_ADDRESS, SCA_GET_BLOCK_FAILED, MISSING_OVERALL_DATA_LENGTH, NOT_DATA_ONLY_BLOCK, BAD_DO_BLOCK_LENGTHS, BAD_COUNTS, CATASTROPHIC_FAILURE = 999 }; typedef struct { FLMUINT uiKeyCnt; FLMUINT uiFirstKeyCnt; FLMUINT uiBlkCnt; FLMUINT uiBytesUsed; FLMUINT uiDOBlkCnt; FLMUINT uiDOBytesUsed; } BTREE_LEVEL_STATS; typedef struct { FLMUINT uiBlkAddr; FLMUINT uiBlockSize; FLMUINT uiBlocksChecked; FLMUINT uiAvgFreeSpace; FLMUINT uiLevels; FLMUINT uiNumKeys; FLMUINT64 ui64FreeSpace; BTREE_LEVEL_STATS LevelStats[ BH_MAX_LEVELS]; char szMsg[ 64]; BTREE_ERR_TYPE type; } BTREE_ERR_STRUCT; typedef struct { FLMUINT uiParentLevel; FLMUINT uiParentKeyLen; FLMUINT uiParentChildBlkAddr; FLMUINT uiNewKeyLen; FLMUINT uiChildBlkAddr; FLMUINT uiCounts; void * pPrev; FLMBYTE pucParentKey[ XFLM_MAX_KEY_SIZE]; FLMBYTE pucNewKey[ XFLM_MAX_KEY_SIZE]; } BTREE_REPLACE_STRUCT; class F_BtPool; class F_Btree : public F_Object { public: F_Btree( void); ~F_Btree( void); RCODE btCreate( F_Db * pDb, LFILE * pLFile, FLMBOOL bCounts, FLMBOOL bData); RCODE btOpen( F_Db * pDb, LFILE * pLFile, FLMBOOL bCounts, FLMBOOL bData, IF_ResultSetCompare * pCompare = NULL); void btClose( void); RCODE btDeleteTree( IF_DeleteStatus * ifpDeleteStatus); RCODE btGetBlockChains( FLMUINT * puiBlockChains, FLMUINT * puiNumLevels); RCODE btRemoveEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen); RCODE btInsertEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bFirst, FLMBOOL bLast, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btReplaceEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bFirst, FLMBOOL bLast, FLMBOOL bTruncate = TRUE, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btLocateEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT uiMatch, FLMUINT * puiPosition = NULL, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btGetEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT uiKeyLen, FLMBYTE * pucData, FLMUINT uiDataBufSize, FLMUINT * puiDataLen); RCODE btNextEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btPrevEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btFirstEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btLastEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMUINT * puiDataLength = NULL, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE btSetReadPosition( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiPosition); RCODE btGetReadPosition( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT * puiPosition); RCODE btPositionTo( FLMUINT uiPosition, FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen); RCODE btGetPosition( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiPosition); RCODE btCheck( BTREE_ERR_STRUCT * pErrStruct); RCODE btRewind( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen); FINLINE void btGetTransInfo( FLMUINT64 * pui64LowTransId, FLMBOOL * pbMostCurrent) { *pui64LowTransId = m_ui64LowTransId; *pbMostCurrent = m_bMostCurrent; } FINLINE void btRelease( void) { releaseBlocks( TRUE); } FINLINE void btResetBtree( void) { releaseBlocks( TRUE); m_bSetupForRead = FALSE; m_bSetupForWrite = FALSE; m_bSetupForReplace = FALSE; m_bOrigInDOBlocks = FALSE; m_bDataOnlyBlock = FALSE; m_ui32PrimaryBlkAddr = 0; m_ui32CurBlkAddr = 0; m_uiPrimaryOffset = 0; m_uiCurOffset = 0; m_uiDataLength = 0; m_uiPrimaryDataLen = 0; m_uiOADataLength = 0; m_uiDataRemaining = 0; m_uiOADataRemaining = 0; m_uiOffsetAtStart = 0; m_ui64CurrTransID = 0; m_ui64LastBlkTransId = 0; m_ui64PrimaryBlkTransId = 0; m_uiBlkChangeCnt = 0; m_uiSearchLevel = BH_MAX_LEVELS; } RCODE btComputeCounts( F_Btree * pUntilBtree, FLMUINT * puiBlkCount, FLMUINT * puiKeyCount, FLMBOOL * pbTotalsEstimated, FLMUINT uiAvgBlkFullness); FINLINE void btSetSearchLevel( FLMUINT uiSearchLevel) { flmAssert( uiSearchLevel <= BH_MAX_LEVELS); btResetBtree(); m_uiSearchLevel = uiSearchLevel; } RCODE btMoveBlock( FLMUINT32 ui32FromBlkAddr, FLMUINT32 ui32ToBlkAddr); FINLINE FLMBOOL btHasCounts( void) { return m_bCounts; } FINLINE FLMBOOL btHasData( void) { return m_bData; } FINLINE FLMBOOL btDbIsOpen( void) { return m_bOpened; } FINLINE FLMBOOL btIsSetupForRead( void) { return m_bSetupForRead; } FINLINE FLMBOOL btIsSetupForWrite( void) { return m_bSetupForWrite; } FINLINE FLMBOOL btIsSetupForReplace( void) { return m_bSetupForReplace; } private: FINLINE FLMUINT calcEntrySize( FLMUINT uiBlkType, FLMUINT uiFlags, FLMUINT uiKeyLen, FLMUINT uiDataLen, FLMUINT uiOADataLen) { switch( uiBlkType) { case BT_LEAF: { return( uiKeyLen + 2); } case BT_LEAF_DATA: { return( 1 + // Flags (uiKeyLen > ONE_BYTE_SIZE ? 2 : 1) + // KeyLen (uiDataLen > ONE_BYTE_SIZE ? 2 : 1) + // DataLen (uiOADataLen && // OA DataLen (uiFlags & BTE_FLAG_FIRST_ELEMENT) ? 4 : 0) + uiKeyLen + uiDataLen); } case BT_NON_LEAF: case BT_NON_LEAF_COUNTS: { return( 4 + // Child block address (uiBlkType == BT_NON_LEAF_COUNTS ? 4 : 0) + // Counts 2 + // Key length uiKeyLen); } } return( 0); } RCODE computeCounts( F_BTSK_p pFromStack, F_BTSK_p pUntilStack, FLMUINT * puiBlockCount, FLMUINT * puiKeyCount, FLMBOOL * pbTotalsEstimated, FLMUINT uiAvgBlkFullness); RCODE blockCounts( F_BTSK_p pStack, FLMUINT uiFirstOffset, FLMUINT uiLastOffset, FLMUINT * puiKeyCount, FLMUINT * puiElementCount); RCODE getStoredCounts( F_BTSK_p pFromStack, F_BTSK_p pUntilStack, FLMUINT * puiBlockCount, FLMUINT * puiKeyCount, FLMBOOL * pbTotalsEstimated, FLMUINT uiAvgBlkFullness); RCODE getCacheBlocks( F_BTSK_p pStack1, F_BTSK_p pStack2); FINLINE FLMUINT getAvgKeyCount( F_BTSK_p pFromStack, F_BTSK_p pUntilStack, FLMUINT uiAvgBlkFullness); FINLINE void updateTransInfo( FLMUINT64 ui64LowTransID, FLMUINT64 ui64HighTransID ) { if (m_ui64LowTransId > ui64LowTransID) { m_ui64LowTransId = ui64LowTransID; } if (!m_bMostCurrent) { m_bMostCurrent = (ui64HighTransID == FLM_MAX_UINT64) ? TRUE : FALSE; } } FINLINE FLMUINT getBlkEntryCount( FLMBYTE * pBlk ) { return ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys; } FINLINE FLMUINT getBlkAvailSpace( FLMBYTE * pBlk ) { return ((F_BLK_HDR *)pBlk)->ui16BlkBytesAvail; } FLMUINT getEntryKeyLength( FLMBYTE * pucEntry, FLMUINT uiBlockType, const FLMBYTE ** ppucKeyRV); FLMUINT getEntrySize( FLMBYTE * pBlk, FLMUINT uiOffset, FLMBYTE ** ppucEntry = NULL); RCODE calcNewEntrySize( FLMUINT uiKeyLen, FLMUINT uiDataLen, FLMUINT * puiEntrySize, FLMBOOL * pbHaveRoom, FLMBOOL * pbDefragBlk); RCODE extractEntryData( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBYTE * pucBuffer, FLMUINT uiBufSiz, FLMUINT * puiDataLen); RCODE updateEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, F_ELM_UPD_ACTION eAction, FLMBOOL bTruncate = TRUE); RCODE insertEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction); RCODE storeEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiOADataLen, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, FLMUINT uiEntrySize, FLMBOOL * pbLastEntry); RCODE removeEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, FLMBOOL * pbMoreToRemove, F_ELM_UPD_ACTION * peAction); RCODE remove( FLMBOOL bDeleteDOBlocks); RCODE removeRange( FLMUINT uiStartElm, FLMUINT uiEndElm, FLMBOOL bDeleteDOBlocks); RCODE findEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiMatch, FLMUINT * puiPosition = NULL, FLMUINT32 * pui32BlkAddr = NULL, FLMUINT * puiOffsetIndex = NULL); RCODE findInBlock( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT uiMatch, FLMUINT * uiPosition, FLMUINT32 * ui32BlkAddr, FLMUINT * uiOffsetIndex); RCODE scanBlock( F_BTSK_p pStack, FLMUINT uiMatch); RCODE compareKeys( const FLMBYTE * pucKey1, FLMUINT uiKeyLen1, const FLMBYTE * pucKey2, FLMUINT uiKeyLen2, FLMINT * piCompare); FINLINE RCODE compareBlkKeys( const FLMBYTE * pucBlockKey, FLMUINT uiBlockKeyLen, const FLMBYTE * pucTargetKey, FLMUINT uiTargetKeyLen, FLMINT * piCompare) { flmAssert( uiBlockKeyLen); if( !m_pCompare && uiBlockKeyLen == uiTargetKeyLen) { *piCompare = f_memcmp( pucBlockKey, pucTargetKey, uiBlockKeyLen); return( NE_XFLM_OK); } return( compareKeys( pucBlockKey, uiBlockKeyLen, pucTargetKey, uiTargetKeyLen, piCompare)); } RCODE positionToEntry( FLMUINT uiPosition); RCODE searchBlock( F_BTREE_BLK_HDR * pBlkHdr, FLMUINT * puiPrevCounts, FLMUINT uiPosition, FLMUINT * puiOffset); RCODE defragmentBlock( F_CachedBlock ** ppSCache); RCODE advanceToNextElement( FLMBOOL bAdvanceStack); RCODE backupToPrevElement( FLMBOOL bBackupStack); RCODE replaceEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction, FLMBOOL bTruncate = TRUE); RCODE replaceOldEntry( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiOADataLen, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction, FLMBOOL bTruncate = TRUE); RCODE replaceByInsert( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucDataValue, FLMUINT uiDataLen, FLMUINT uiOADataLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction); RCODE replace( FLMBYTE * pucEntry, FLMUINT uiEntrySize, FLMBOOL * pbLastEntry); RCODE buildAndStoreEntry( FLMUINT uiBlkType, FLMUINT uiFlags, const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMUINT uiOADataLen, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, FLMBYTE * pucBuffer, FLMUINT uiBufferSize, FLMUINT * puiEntrySize); RCODE moveEntriesToPrevBlk( FLMUINT uiNewEntrySize, F_CachedBlock ** ppPrevSCache, FLMBOOL * pbEntriesWereMoved); RCODE moveEntriesToNextBlk( FLMUINT uiEntrySize, FLMBOOL * pbEntriesWereMoved); RCODE splitBlock( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiOADataLen, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, FLMBOOL * pbBlockSplit); RCODE createNewLevel( void); RCODE storeDataOnlyBlocks( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bSaveKey, const FLMBYTE * pucData, FLMUINT uiDataLen); RCODE replaceDataOnlyBlocks( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bSaveKey, const FLMBYTE * pucData, FLMUINT uiDataLen, FLMBOOL bLast, FLMBOOL bTruncate = TRUE); RCODE moveToPrev( FLMUINT uiStart, FLMUINT uiFinish, F_CachedBlock ** ppPrevSCache); RCODE moveToNext( FLMUINT uiStart, FLMUINT uiFinish, F_CachedBlock ** ppNextSCache); RCODE updateParentCounts( F_CachedBlock * pChildSCache, F_CachedBlock ** ppParentSCache, FLMUINT uiParentElm); FLMUINT countKeys( FLMBYTE * pBlk); FLMUINT countRangeOfKeys( F_BTSK_p pFromStack, FLMUINT uiFromOffset, FLMUINT uiUntilOffset); RCODE moveStackToPrev( F_CachedBlock * pPrevSCache); RCODE moveStackToNext( F_CachedBlock * pSCache, FLMBOOL bReleaseCurrent = TRUE); RCODE calcOptimalDataLength( FLMUINT uiKeyLen, FLMUINT uiDataLen, FLMUINT uiBytesAvail, FLMUINT * puiNewDataLen); // Performs an integrity check on a chain of data-only blocks RCODE verifyDOBlkChain( FLMUINT uiDOAddr, FLMUINT uiDataLength, BTREE_ERR_STRUCT * localErrStruct); // Performs a check to verify that the counts in the DB match. RCODE verifyCounts( BTREE_ERR_STRUCT * pErrStruct); void releaseBlocks( FLMBOOL bResetStack); void releaseBtree( void); RCODE saveReplaceInfo( const FLMBYTE * pucKey, FLMUINT uiKeyLen); RCODE restoreReplaceInfo( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts); FINLINE RCODE setReturnKey( FLMBYTE * pucEntry, FLMUINT uiBlockType, FLMBYTE * pucKey, FLMUINT * puiKeyLen, FLMUINT uiKeyBufSize); RCODE setupReadState( F_BLK_HDR * pBlkHdr, FLMBYTE * pucEntry); RCODE removeRemainingEntries( const FLMBYTE * pucKey, FLMUINT uiKeyLen); RCODE deleteEmptyBlock( void); RCODE removeDOBlocks( FLMUINT32 ui32OrigDOAddr); RCODE replaceMultiples( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucDataValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction); RCODE replaceMultiNoTruncate( const FLMBYTE ** ppucKey, FLMUINT * puiKeyLen, const FLMBYTE * pucDataValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT * puiChildBlkAddr, FLMUINT * puiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, F_ELM_UPD_ACTION * peAction); FINLINE RCODE getNextBlock( F_CachedBlock ** ppSCache); FINLINE RCODE getPrevBlock( F_CachedBlock ** ppSCache); FLMBOOL checkContinuedEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL * pbLastElement, FLMBYTE * pucEntry, FLMUINT uiBlkType); RCODE updateCounts( void); RCODE storePartialEntry( const FLMBYTE * pucKey, FLMUINT uiKeyLen, const FLMBYTE * pucValue, FLMUINT uiLen, FLMUINT uiFlags, FLMUINT uiChildBlkAddr, FLMUINT uiCounts, const FLMBYTE ** ppucRemainingValue, FLMUINT * puiRemainingLen, FLMBOOL bNewBlock = FALSE); RCODE mergeBlocks( FLMBOOL bLastEntry, FLMBOOL * pbMergedWithPrev, FLMBOOL * pbMergedWithNext, F_ELM_UPD_ACTION * peAction); RCODE merge( F_CachedBlock ** ppFromSCache, F_CachedBlock ** ppToSCache); RCODE checkDownLinks( void); RCODE verifyChildLinks( F_CachedBlock * pParentSCache); RCODE combineEntries( F_BTREE_BLK_HDR * pSrcBlkHdr, FLMUINT uiSrcOffset, F_BTREE_BLK_HDR * pDstBlkHdr, FLMUINT uiDstOffset, FLMBOOL * pbEntriesCombined, FLMUINT * puiEntrySize); RCODE moveBtreeBlock( FLMUINT32 ui32FromBlkAddr, FLMUINT32 ui32ToBlkAddr); RCODE moveDOBlock( FLMUINT32 ui32FromBlkAddr, FLMUINT32 ui32ToBlkAddr); // Member variables FLMBOOL m_bCounts; // BT_NON_LEAF_COUNTS FLMBOOL m_bData; // BT_LEAF_DATA FLMBOOL m_bSetupForRead; FLMBOOL m_bSetupForWrite; FLMBOOL m_bSetupForReplace; FLMBOOL m_bOpened; FLMBOOL m_bMostCurrent; FLMBOOL m_bDataOnlyBlock; FLMBOOL m_bOrigInDOBlocks; FLMBOOL m_bFirstRead; FLMBOOL m_bStackSetup; LFILE * m_pLFile; F_Db * m_pDb; FLMBOOL m_bTempDb; F_BTSK_p m_pStack; // Used for traversing the B-Tree FLMBYTE * m_pucTempBlk; FLMBYTE * m_pucTempDefragBlk; BTREE_REPLACE_STRUCT * m_pReplaceInfo; BTREE_REPLACE_STRUCT * m_pReplaceStruct; const FLMBYTE * m_pucDataPtr; F_CachedBlock * m_pSCache; F_BTREE_BLK_HDR * m_pBlkHdr; FLMBYTE * m_pucBuffer; // Buffer used during moves FLMUINT m_uiBufferSize; // Size of the buffer FLMUINT m_uiBlockSize; FLMUINT m_uiDefragThreshold; FLMUINT m_uiOverflowThreshold; FLMUINT m_uiStackLevels; FLMUINT m_uiRootLevel; FLMUINT m_uiReplaceLevels; FLMUINT m_uiBlkChangeCnt; FLMUINT m_uiDataLength; FLMUINT m_uiPrimaryDataLen; FLMUINT m_uiOADataLength; FLMUINT m_uiDataRemaining; FLMUINT m_uiOADataRemaining; FLMUINT m_uiPrimaryOffset; // Offset into primary block FLMUINT m_uiCurOffset; // Offset into current block FLMUINT m_uiSearchLevel; FLMUINT m_uiOffsetAtStart; // Offset into the current // element at the beginning of // the entry. An element may // span multiple entries. FLMUINT32 m_ui32PrimaryBlkAddr;// Primary block address FLMUINT32 m_ui32DOBlkAddr; // Address of first DO Block FLMUINT32 m_ui32CurBlkAddr; // Current block being read FLMUINT64 m_ui64LowTransId; FLMUINT64 m_ui64LastBlkTransId; FLMUINT64 m_ui64PrimaryBlkTransId; FLMUINT64 m_ui64CurrTransID; F_BTSK m_Stack[ BH_MAX_LEVELS]; // The m_pNext field is only used by the btPool object. F_Btree * m_pNext; IF_ResultSetCompare * m_pCompare; friend class F_BtPool; friend class F_Db; friend class F_Rfl; }; RCODE btFreeBlockChain( F_Db * pDb, LFILE * pLFile, FLMUINT uiStartAddr, FLMUINT uiBlocksToFree, FLMUINT * puiBlocksFreed, FLMUINT * puiEndAddr, IF_DeleteStatus * ifpDeleteStatus); #endif libxflaim-5.1.969/src/fqsort.cpp0000644000175000017500000017265210511001742020063 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the methods for doing sorting in the F_Query class. // // Tabs: 3 // // Copyright (c) 2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fqsort.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "fquery.h" FSTATIC RCODE fqGetDocId( IXD * pIxd, const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT64 * pui64DocId); /*************************************************************************** Desc: Add a sort key to the query. ***************************************************************************/ RCODE FLMAPI F_Query::addSortKey( void * pvSortKeyContext, FLMBOOL bChildToContext, FLMBOOL bElement, FLMUINT uiDictNum, FLMUINT uiCompareRules, FLMUINT uiLimit, FLMUINT uiKeyComponent, FLMBOOL bSortDescending, FLMBOOL bSortMissingHigh, void ** ppvContext) { RCODE rc = NE_XFLM_OK; ICD * pSortIcd; ICD * pSortIcdContext; ICD * pTmpIcd; // If an error has already occurred, cannot add more to query. if (RC_BAD( rc = m_rc)) { goto Exit; } if (m_bOptimized) { rc = RC_SET( NE_XFLM_Q_TOO_LATE_TO_ADD_SORT_KEYS); goto Exit; } // Verify that the sort key component number is legal if (uiKeyComponent > XFLM_MAX_SORT_KEYS) { rc = RC_SET( NE_XFLM_Q_INVALID_SORT_KEY_COMPONENT); goto Exit; } // If we have not created an IXD, create one now. if (!m_pSortIxd) { if (RC_BAD( rc = m_pool.poolCalloc( sizeof( IXD), (void **)&m_pSortIxd))) { goto Exit; } // Assume single path - can be unset later on. m_pSortIxd->uiFlags |= IXD_SINGLE_PATH; m_pSortIxd->uiCollectionNum = m_uiCollection; } // Allocate an ICD structure. if (RC_BAD( rc = m_pool.poolCalloc( sizeof( ICD), (void **)&pSortIcd))) { goto Exit; } pSortIcd->uiCdl = m_pSortIxd->uiNumIcds; m_pSortIxd->uiNumIcds++; pSortIcd->pIxd = m_pSortIxd; pSortIcd->uiIndexNum = m_pSortIxd->uiIndexNum; pSortIcd->uiDictNum = uiDictNum; if (!bElement) { pSortIcd->uiFlags |= ICD_IS_ATTRIBUTE; } if ((pSortIcd->uiKeyComponent = uiKeyComponent) > 0) { pSortIcd->uiFlags |= (ICD_VALUE | ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET); pSortIcd->uiCompareRules = uiCompareRules; if (bSortDescending) { pSortIcd->uiFlags |= ICD_DESCENDING; } if (bSortMissingHigh) { pSortIcd->uiFlags |= ICD_MISSING_HIGH; } if (!uiLimit) { pSortIcd->uiLimit = ICD_DEFAULT_LIMIT; } else { pSortIcd->uiLimit = uiLimit; } m_pSortIxd->uiNumKeyComponents++; // Link into list of key components where it goes. pTmpIcd = m_pSortIxd->pFirstKey; while (pTmpIcd && pSortIcd->uiKeyComponent > pTmpIcd->uiKeyComponent) { pTmpIcd = pTmpIcd->pNextKeyComponent; } // Cannot have two components with the same value. if (pTmpIcd && pSortIcd->uiKeyComponent == pTmpIcd->uiKeyComponent) { rc = RC_SET( NE_XFLM_Q_DUPLICATE_SORT_KEY_COMPONENT); goto Exit; } if ((pSortIcd->pNextKeyComponent = pTmpIcd) == NULL) { // Link at end of list. if ((pSortIcd->pPrevKeyComponent = m_pSortIxd->pLastKey) != NULL) { m_pSortIxd->pLastKey->pNextKeyComponent = pSortIcd; } else { m_pSortIxd->pFirstKey = pSortIcd; } m_pSortIxd->pLastKey = pSortIcd; } else { // Link in front of pTmpIcd flmAssert( pSortIcd->uiKeyComponent < pTmpIcd->uiKeyComponent); if ((pSortIcd->pPrevKeyComponent = pTmpIcd->pPrevKeyComponent) == NULL) { m_pSortIxd->pFirstKey = pSortIcd; } else { pTmpIcd->pPrevKeyComponent->pNextKeyComponent = pSortIcd; } pTmpIcd->pPrevKeyComponent = pSortIcd; } } else { m_pSortIxd->uiNumContextComponents++; if ((pSortIcd->pPrevKeyComponent = m_pSortIxd->pLastContext) == NULL) { m_pSortIxd->pFirstContext = pSortIcd; } m_pSortIxd->pLastContext = pSortIcd; } if ((pSortIcdContext = (ICD *)(pvSortKeyContext)) != NULL) { flmAssert( m_pSortIxd->pIcdTree); if (bChildToContext) { // Attributes cannot be parents to another sort ICD. if (pSortIcdContext->uiFlags & ICD_IS_ATTRIBUTE) { rc = RC_SET( NE_XFLM_Q_SORT_KEY_CONTEXT_MUST_BE_ELEMENT); goto Exit; } pSortIcd->pParent = pSortIcdContext; if ((pSortIcd->pNextSibling = pSortIcdContext->pFirstChild) != NULL) { pSortIcd->pNextSibling->pPrevSibling = pSortIcd; } pSortIcdContext->pFirstChild = pSortIcd; if (pSortIcdContext->pNextSibling || pSortIcd->pPrevSibling) { m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); } } else { pSortIcd->pParent = pSortIcdContext->pParent; pSortIcd->pPrevSibling = pSortIcdContext; if ((pSortIcd->pNextSibling = pSortIcdContext->pNextSibling) != NULL) { pSortIcd->pNextSibling->pPrevSibling = pSortIcd; } pSortIcdContext->pNextSibling = pSortIcd; if (pSortIcdContext->pFirstChild) { m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); } } } else if (m_pSortIxd->pIcdTree) { pSortIcdContext = m_pSortIxd->pIcdTree; while (pSortIcdContext->pNextSibling) { if (pSortIcdContext->pFirstChild) { m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); } pSortIcdContext = pSortIcdContext->pNextSibling; } if (pSortIcdContext->pFirstChild) { m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); } pSortIcdContext->pNextSibling = pSortIcd; pSortIcd->pPrevSibling = pSortIcdContext; } else { m_pSortIxd->pIcdTree = pSortIcd; } if (ppvContext) { *ppvContext = (void *)pSortIcd; } Exit: m_rc = rc; return( rc); } /*************************************************************************** Desc: Verify the sort keys, if any. This method is called during optimization. ***************************************************************************/ RCODE F_Query::verifySortKeys( void) { RCODE rc = NE_XFLM_OK; ICD * pSortIcd; FLMUINT uiExpectedKeyComponent; // This routine should not be called if there were no sort keys specified. flmAssert( m_pSortIxd); if ((pSortIcd = m_pSortIxd->pFirstKey) == NULL) { // No sort keys were really specified. The user called addSortKey // without ever setting one of the sort key components. m_pSortIxd = NULL; rc = RC_SET( NE_XFLM_Q_NO_SORT_KEY_COMPONENTS_SPECIFIED); goto Exit; } // Set the language for the sort keys m_pSortIxd->uiLanguage = m_pDb->m_pDatabase->m_uiDefaultLanguage; // Verify that we don't have any missing sort key components, // and that we can get all of their data types. uiExpectedKeyComponent = 1; while (pSortIcd) { if (pSortIcd->uiKeyComponent != uiExpectedKeyComponent) { rc = RC_SET( NE_XFLM_Q_MISSING_SORT_KEY_COMPONENT); goto Exit; } // Verify the dictionary number and get its data type. if (pSortIcd->uiDictNum != ELM_ROOT_TAG) { F_AttrElmInfo info; if (!(pSortIcd->uiFlags & ICD_IS_ATTRIBUTE)) { if (RC_BAD( rc = m_pDb->m_pDict->getElement( m_pDb, pSortIcd->uiDictNum, &info))) { if (rc == NE_XFLM_BAD_ELEMENT_NUM) { rc = RC_SET( NE_XFLM_Q_INVALID_ELEMENT_NUM_IN_SORT_KEYS); } goto Exit; } } else { if (RC_BAD( rc = m_pDb->m_pDict->getAttribute( m_pDb, pSortIcd->uiDictNum, &info))) { if (rc == NE_XFLM_BAD_ATTRIBUTE_NUM) { rc = RC_SET( NE_XFLM_Q_INVALID_ATTR_NUM_IN_SORT_KEYS); } goto Exit; } } icdSetDataType( pSortIcd, info.m_uiDataType); } // Go to the next sort key component pSortIcd = pSortIcd->pNextKeyComponent; uiExpectedKeyComponent++; } Exit: return( rc); } /*************************************************************************** Desc: Add the current document to the result set. ***************************************************************************/ RCODE F_Query::addToResultSet( void) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_SORT_KEYS * 3 + 20]; FLMBYTE * pucTmp; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiIDLen; if (m_pSortIxd) { // This routine should only be called if we have sort keys. m_pSortResultSet->setIxInfo( m_pDb, m_pSortIxd); // Call index document to generate the sort keys for the document. if (RC_BAD( rc = m_pDb->indexDocument( m_pSortIxd, (F_DOMNode *)m_pCurrDoc))) { goto Exit; } // Take only the lowest sort key for the document. if (m_pDb->m_uiKrefCount) { KREF_ENTRY * pKref; pKref = m_pDb->m_pKrefTbl [0]; if (RC_BAD( rc = m_pSortResultSet->addEntry( (FLMBYTE *)&pKref [1], (FLMUINT)pKref->ui16KeyLen, TRUE))) { goto Exit; } } else { uiKeyLen = 2 * m_pSortIxd->uiNumKeyComponents; // Generate an empty key and add to the result set. f_memset( ucKey, 0, uiKeyLen); // Put in document ID - so we can pull it out when traversing // through the result set. if (RC_BAD( rc = m_pCurrDoc->getDocumentId( (IF_Db *)m_pDb, &ui64DocId))) { goto Exit; } pucTmp = &ucKey [uiKeyLen]; uiIDLen = f_encodeSEN( ui64DocId, &pucTmp); // Output a zero SEN (which is one byte) for all of the other node IDs. f_memset( &ucKey [uiKeyLen + uiIDLen], 0, m_pSortIxd->uiNumKeyComponents); uiIDLen += m_pSortIxd->uiNumKeyComponents; uiKeyLen += uiIDLen; if (RC_BAD( rc = m_pSortResultSet->addEntry( ucKey, uiKeyLen, TRUE))) { goto Exit; } } // Empty the table out for next document. m_pDb->m_pKrefPool->poolReset( NULL, TRUE); m_pDb->m_uiKrefCount = 0; m_pDb->m_uiTotalKrefBytes = 0; } else { FLMUINT32 ui32Count = (FLMUINT32)m_pSortResultSet->getCount(); // If there is no sort key, the m_bPositioningEnabled flag better // be set - meaning we are just keeping a result set with the objects // in the order we found them. flmAssert( m_bPositioningEnabled); ui32Count++; f_UINT32ToBigEndian( ui32Count, ucKey); pucTmp = &ucKey [4]; if (RC_BAD( rc = m_pCurrDoc->getDocumentId( (IF_Db *)m_pDb, &ui64DocId))) { goto Exit; } uiIDLen = f_encodeSEN( ui64DocId, &pucTmp); if (RC_BAD( rc = m_pSortResultSet->addEntry( ucKey, uiIDLen + 4, TRUE))) { goto Exit; } } m_ui64RSDocsPassed++; Exit: return( rc); } /*************************************************************************** Desc: Destructor for query result set. ***************************************************************************/ F_QueryResultSet::~F_QueryResultSet() { if (m_pBTree) { m_pBTree->btClose(); m_pBTree->Release(); } if (m_pResultSetDb) { if (m_pResultSetDb->getTransType() != XFLM_NO_TRANS) { m_pResultSetDb->transAbort(); } m_pResultSetDb->Release(); m_pResultSetDb = NULL; gv_pXFlmDbSystem->dbRemove( m_szResultSetDibName, NULL, NULL, TRUE); } if (m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /*************************************************************************** Desc: Initialize a query result set. ***************************************************************************/ RCODE F_QueryResultSet::initResultSet( FLMBOOL bUseIxCompareObj, FLMBOOL bEnableEncryption) { RCODE rc = NE_XFLM_OK; XFLM_CREATE_OPTS createOpts; FLMUINT uiNum = (FLMUINT)this; flmAssert( !m_pResultSetDb); // Create a mutex. if (RC_BAD( f_mutexCreate( &m_hMutex))) { goto Exit; } f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); for (;;) { // Generate a random file name f_sprintf( m_szResultSetDibName, "%x.db", (unsigned)uiNum); if (RC_OK( rc = gv_pXFlmDbSystem->dbCreate( m_szResultSetDibName, NULL, NULL, NULL, NULL, &createOpts, TRUE, (IF_Db **)&m_pResultSetDb))) { break; } if (rc == NE_XFLM_FILE_EXISTS || rc == NE_FLM_IO_ACCESS_DENIED) { // Try again with a slightly altered number. uiNum -= 10; rc = NE_XFLM_OK; } else { goto Exit; } } // Shouldn't have an RFL object - don't want anything // logged by this database. flmAssert( !m_pResultSetDb->getDatabase()->m_pRfl); // Create a b-tree that will hold our result set data. // Will always be #1. // Should be one that keeps counts so we can do absolute positioning. if (RC_BAD( rc = m_pResultSetDb->m_pDatabase->lFileCreate( m_pResultSetDb, &m_LFile, NULL, 1, XFLM_LF_INDEX, TRUE, FALSE, (FLMUINT)(bEnableEncryption ? 1 : 0)))) { goto Exit; } // Open a b-tree object to read from and write to the btree. if ((m_pBTree = f_new F_Btree) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } // Open the btree and use the specified collection. if (RC_BAD( rc = m_pBTree->btOpen( m_pResultSetDb, &m_LFile, TRUE, FALSE, bUseIxCompareObj ? &m_compareObj : (IF_ResultSetCompare *)NULL))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Add an entry to the result set. ***************************************************************************/ RCODE F_QueryResultSet::addEntry( FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; // May need to lock the mutex when adding an entry - this will hold off // any other thread that may be trying to read the result set at the // same time. if (bLockMutex) { lockMutex(); } m_pBTree->btResetBtree(); if (RC_BAD( rc = m_pBTree->btInsertEntry( pucKey, uiKeyLen, NULL, 0, TRUE, TRUE))) { goto Exit; } m_uiCount++; m_bPositioned = FALSE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Get the first entry in the result set. ***************************************************************************/ RCODE F_QueryResultSet::getFirst( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; if (bLockMutex) { lockMutex(); } if (RC_BAD( rc = m_pBTree->btFirstEntry( pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } // Remember our current position, so we can reposition there if necessary. if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) { goto Exit; } m_bPositioned = TRUE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Get the last entry in the result set. ***************************************************************************/ RCODE F_QueryResultSet::getLast( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; if (bLockMutex) { lockMutex(); } if (RC_BAD( rc = m_pBTree->btLastEntry( pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } // Remember our current position, so we can reposition there if necessary. if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) { goto Exit; } m_bPositioned = TRUE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Get the next entry from the current position in the result set. ***************************************************************************/ RCODE F_QueryResultSet::getNext( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; if (bLockMutex) { lockMutex(); } if (m_uiCurrPos == FLM_MAX_UINT) { if (RC_BAD( rc = getFirst( pucKey, uiKeyBufSize, puiKeyLen, FALSE))) { goto Exit; } } else { // m_bPositioned will be set to FALSE if addEntry is called - because // that changes the positioning of the b-tree. If that happens, we need // to reposition before calling btNextEntry. if (!m_bPositioned) { if (RC_BAD( rc = m_pBTree->btPositionTo( m_uiCurrPos, pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } } if (RC_BAD( rc = m_pBTree->btNextEntry( pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } // Remember our current position, so we can reposition there if necessary. if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) { goto Exit; } } m_bPositioned = TRUE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Get the previous entry from the current position in the result set. ***************************************************************************/ RCODE F_QueryResultSet::getPrev( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; if (bLockMutex) { lockMutex(); } if (m_uiCurrPos == FLM_MAX_UINT) { if (RC_BAD( rc = getLast( pucKey, uiKeyBufSize, puiKeyLen, FALSE))) { goto Exit; } } else { // m_bPositioned will be set to FALSE if addEntry is called - because // that changes the positioning of the b-tree. If that happens, we need // to reposition before calling btPrevEntry. if (!m_bPositioned) { if (RC_BAD( rc = m_pBTree->btPositionTo( m_uiCurrPos, pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } } if (RC_BAD( rc = m_pBTree->btPrevEntry( pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } // Remember our current position, so we can reposition there if necessary. if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) { goto Exit; } } m_bPositioned = TRUE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Get the entry from the current position in the result set. ***************************************************************************/ RCODE F_QueryResultSet::getCurrent( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; if (bLockMutex) { lockMutex(); } if (m_uiCurrPos == FLM_MAX_UINT) { rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } else { if (RC_BAD( rc = m_pBTree->btPositionTo( m_uiCurrPos, pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } } m_bPositioned = TRUE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Position to a particular entry in the result set. ***************************************************************************/ RCODE F_QueryResultSet::positionToEntry( FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, F_DataVector * pSearchKey, FLMUINT uiFlags, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; FLMBYTE ucSearchKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiSearchKeyLen = 0; FLMUINT uiOriginalFlags; FLMUINT uiIdMatchFlags = uiFlags & (XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID); FLMBOOL bCompareDocId = FALSE; FLMBOOL bCompareNodeIds = FALSE; FLMUINT uiPos; if (bLockMutex) { lockMutex(); } if (uiFlags & XFLM_FIRST || (!pSearchKey && !(uiFlags & XFLM_FIRST) && !(uiFlags & XFLM_LAST))) { uiOriginalFlags = uiFlags = XFLM_FIRST; } else if (uiFlags & XFLM_LAST) { uiOriginalFlags = uiFlags = XFLM_LAST; } else { uiOriginalFlags = uiFlags; if( !(uiIdMatchFlags & XFLM_MATCH_IDS)) { flmAssert( !(uiFlags & XFLM_KEY_EXACT)); if (uiFlags & XFLM_EXCL) { uiFlags = XFLM_EXCL; } else if (uiFlags & XFLM_EXACT) { uiOriginalFlags = XFLM_EXACT | XFLM_KEY_EXACT; uiFlags = XFLM_INCL; } else { uiFlags = XFLM_INCL; } } else { if (uiFlags & XFLM_EXACT) { flmAssert( !(uiFlags & XFLM_KEY_EXACT)); uiFlags = XFLM_EXACT; } else if (uiFlags & XFLM_EXCL) { uiFlags = XFLM_EXCL; } else { uiFlags = XFLM_INCL; } } if (RC_BAD( rc = pSearchKey->outputKey( m_pIxd, uiIdMatchFlags, ucSearchKey, sizeof( ucSearchKey), &uiSearchKeyLen, SEARCH_KEY_FLAG))) { goto Exit; } // If we are not matching on the IDs and this is an XFLM_EXCL // search, tack on a 0xFF for the IDs, which should get us past // all keys that match. We need to turn on the match IDs flags // in this case so that the comparison routine will match on the // 0xFF. if (!uiIdMatchFlags && (uiFlags & XFLM_EXCL)) { ucSearchKey [uiSearchKeyLen++] = 0xFF; bCompareDocId = TRUE; bCompareNodeIds = TRUE; } else { if (uiIdMatchFlags & XFLM_MATCH_IDS) { bCompareNodeIds = TRUE; bCompareDocId = TRUE; } else if (uiIdMatchFlags & XFLM_MATCH_DOC_ID) { bCompareDocId = TRUE; } } } m_compareObj.setCompareNodeIds( bCompareNodeIds); m_compareObj.setCompareDocId( bCompareDocId); m_compareObj.setSearchKey( pSearchKey); // Search the for the key if (uiSearchKeyLen) { f_memcpy( pucKey, ucSearchKey, uiSearchKeyLen); } *puiKeyLen = uiSearchKeyLen; // NOTE: We don't pass m_uiCurrPos into btLocateEntry, because we may have to // test below to see if we really got to something we can stay on. // m_uiCurrPos should remain unchanged if we did not. if (RC_BAD( rc = m_pBTree->btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, uiFlags, &uiPos, NULL, NULL, NULL))) { if (rc == NE_XFLM_EOF_HIT && uiOriginalFlags & XFLM_EXACT) { rc = RC_SET( NE_XFLM_NOT_FOUND); } goto Exit; } // See if we are in the same key if (uiOriginalFlags & XFLM_KEY_EXACT) { FLMINT iTmpCmp; if (RC_BAD( rc = ixKeyCompare( m_pSrcDb, m_pIxd, pSearchKey, NULL, NULL, (uiIdMatchFlags == XFLM_MATCH_DOC_ID) ? TRUE : FALSE, FALSE, pucKey, *puiKeyLen, ucSearchKey, uiSearchKeyLen, &iTmpCmp))) { goto Exit; } if (iTmpCmp != 0) { rc = (uiOriginalFlags & (XFLM_INCL | XFLM_EXCL)) ? RC_SET( NE_XFLM_EOF_HIT) : RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } } m_bPositioned = TRUE; m_uiCurrPos = uiPos; Exit: m_compareObj.setSearchKey( NULL); m_compareObj.setCompareNodeIds( FALSE); m_compareObj.setCompareDocId( FALSE); if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Position to a particular entry in the result set. ***************************************************************************/ RCODE F_QueryResultSet::positionToEntry( FLMUINT uiPosition, FLMBYTE * pucKey, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen, FLMBOOL bLockMutex) { RCODE rc = NE_XFLM_OK; if (bLockMutex) { lockMutex(); } if (uiPosition >= m_uiCount) { rc = RC_SET( NE_XFLM_Q_INVALID_POSITION); goto Exit; } if (RC_BAD( rc = m_pBTree->btPositionTo( uiPosition, pucKey, uiKeyBufSize, puiKeyLen))) { goto Exit; } m_uiCurrPos = uiPosition; m_bPositioned = TRUE; Exit: if (bLockMutex) { unlockMutex(); } return( rc); } /*************************************************************************** Desc: Determine how much time is remaining on timer. ***************************************************************************/ FINLINE RCODE getRemainingTimeMilli( FLMUINT uiStartTimeTU, FLMUINT uiTimeLimitTU, FLMUINT * puiRemainingTimeMilli) { RCODE rc = NE_XFLM_OK; FLMUINT uiCurrTimeTU; FLMUINT uiElapsedTimeTU; FLMUINT uiRemainingTimeTU; uiCurrTimeTU = FLM_GET_TIMER(); uiElapsedTimeTU = FLM_ELAPSED_TIME( uiCurrTimeTU, uiStartTimeTU); if (uiElapsedTimeTU >= uiTimeLimitTU) { rc = RC_SET( NE_XFLM_TIMEOUT); goto Exit; } else { uiRemainingTimeTU = uiTimeLimitTU - uiElapsedTimeTU; *puiRemainingTimeMilli = FLM_TIMER_UNITS_TO_MILLI( uiRemainingTimeTU); } Exit: return( rc); } /*************************************************************************** Desc: Create and initialize the query result set. ***************************************************************************/ RCODE F_Query::createResultSet( void) { RCODE rc = NE_XFLM_OK; // Create a result set for the sort keys if ((m_pSortResultSet = f_new F_QueryResultSet) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = m_pSortResultSet->initResultSet( m_pSortIxd ? TRUE : FALSE, m_bEncryptResultSet))) { goto Exit; } m_ui64RSDocsRead = 0; m_ui64RSDocsPassed = 0; if (!m_pSortIxd) { m_bEntriesAlreadyInOrder = TRUE; } Exit: return( rc); } /*************************************************************************** Desc: Build the query result set. ***************************************************************************/ RCODE F_Query::buildResultSet( IF_Db * pDb, FLMUINT uiTimeLimit, FLMUINT uiNumToWaitFor) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; RS_WAITER * pWaiter; FLMBOOL bMutexLocked = FALSE; FLMBOOL bDoneBuilding = FALSE; FLMBOOL bNotifyWaiters = FALSE; // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } m_pSortResultSet->lockMutex(); bMutexLocked = TRUE; // See if we have already built enough of the result set to get what // we need from it. if (m_pSortResultSet->getCount() >= uiNumToWaitFor || m_bResultSetPopulated) { goto Exit; } // Only one thread at a time can be actually be building the result set. // This for loop waits for such a thread to build what it needs, or, if // there is no thread running and the result set is not populated yet, // it will build the result set out to the point it needs. for (;;) { // If no other thread is building the result set, we can take over and // do it. Otherwise, we will wait for that thread. if (!m_uiBuildThreadId) { m_uiBuildThreadId = f_threadId(); bNotifyWaiters = TRUE; break; } // Thread is currently building the result set. It cannot // be our thread. flmAssert( m_uiBuildThreadId != f_threadId()); // Just need to wait now. if (RC_BAD( rc = waitResultSetBuild( pDb, uiTimeLimit, uiNumToWaitFor))) { goto Exit; } // See if the other thread built enough of the result set to get what // we need from it. if (m_pSortResultSet->getCount() >= uiNumToWaitFor || m_bResultSetPopulated) { goto Exit; } // At this point, we know that the result set is not built out far enough // for us to get what we need, so we need to adjust our timeout. if (uiTimeLimit) { if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, &uiRemainingTimeMilli))) { goto Exit; } } } if (bMutexLocked) { m_pSortResultSet->unlockMutex(); bMutexLocked = FALSE; } for (;;) { if (m_bStopBuildingResultSet) { bDoneBuilding = TRUE; rc = RC_SET( NE_XFLM_USER_ABORT); break; } if (RC_BAD( rc = getNext( pDb, &pNode, uiRemainingTimeMilli, 0, NULL))) { if (rc == NE_XFLM_EOF_HIT) { bDoneBuilding = TRUE; rc = NE_XFLM_OK; break; } goto Exit; } // See how much time is left, if we are operating with a time limit. if (uiTimeLimit) { if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, &uiRemainingTimeMilli))) { goto Exit; } } // See if any of the waiters timed out. checkResultSetWaiters( NE_XFLM_OK); if (m_pSortResultSet->getCount() >= uiNumToWaitFor) { break; } } Exit: if (pNode) { pNode->Release(); } if (!bMutexLocked) { m_pSortResultSet->lockMutex(); bMutexLocked = TRUE; } if (!m_bResultSetPopulated && bDoneBuilding) { m_bResultSetPopulated = TRUE; m_bPositioningEnabled = TRUE; if (RC_OK( rc)) { if (m_pQueryStatus) { flmAssert( m_ui64RSDocsPassed == (FLMUINT64)m_pSortResultSet->getCount()); rc = m_pQueryStatus->resultSetComplete( m_ui64RSDocsRead, m_ui64RSDocsPassed); } } } if (bNotifyWaiters) { // Notify any waiters that are waiting. pWaiter = m_pFirstWaiter; m_pFirstWaiter = NULL; while (pWaiter) { F_SEM hESem = pWaiter->hESem; // Set waiter's return code to whatever we are returning. *(pWaiter->pRc) = rc; // Must get next waiter before signaling semaphore. pWaiter = pWaiter->pNext; f_semSignal( hESem); } } // Must set m_uiBuildThreadId to 0 inside the // mutex lock, because stopResultSetBuild() locks the mutex // one last time to ensure that this method is no longer // accessing anything inside the F_Query object. That is // because it is fair game to destroy the F_Query object upon // returning from this method. // IMPORTANT NOTE: NOTHING SHOULD BE ACCESSED INSIDE THE F_Query // OBJECT AFTER THE MUTEX IS UNLOCKED. m_uiBuildThreadId = 0; if (bMutexLocked) { m_pSortResultSet->unlockMutex(); } return( rc); } /*************************************************************************** Desc: Stop building the result set. ***************************************************************************/ void FLMAPI F_Query::stopBuildingResultSet( void) { if (m_pSortResultSet) { m_pSortResultSet->lockMutex(); // If we have a thread currently building the // result set, signal it to stop. if (m_uiBuildThreadId) { m_bStopBuildingResultSet = TRUE; (void)waitResultSetBuild( m_pDb, 0, FLM_MAX_UINT); // waitResultSetBuild may temporarily unlock the // mutex, but it will always be relocked upon // returning. When we return this time, // m_uiBuildThreadId should be zero. flmAssert( !m_uiBuildThreadId); } else { m_bResultSetPopulated = TRUE; } m_pSortResultSet->unlockMutex(); } } /*************************************************************************** Desc: Build the query result set. This is the method that applications can call. It implies enabling of positioning. ***************************************************************************/ RCODE FLMAPI F_Query::buildResultSet( IF_Db * pDb, FLMUINT uiTimeLimit) { RCODE rc = NE_XFLM_OK; m_pDb = (F_Db *)pDb; if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { m_bPositioningEnabled = TRUE; if (RC_BAD( rc = optimize())) { goto Exit; } } else if (!m_pSortResultSet) { // Cannot build a result set for a query that was not // set up to use result sets. rc = RC_SET( NE_XFLM_ILLEGAL_OP); goto Exit; } // If the query can never evaluate to TRUE, return EOF without // doing anything. if (m_bEmpty) { m_eState = XFLM_QUERY_AT_BOF; rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } // At this point, we should have a result set created. flmAssert( m_pSortResultSet); // Build the entire result set. if (RC_BAD( rc = buildResultSet( pDb, uiTimeLimit, FLM_MAX_UINT))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Extract the document ID from the key. ***************************************************************************/ FSTATIC RCODE fqGetDocId( IXD * pIxd, const FLMBYTE * pucKey, FLMUINT uiKeyLen, FLMUINT64 * pui64DocId) { RCODE rc = NE_XFLM_OK; const FLMBYTE * pucKeyEnd = pucKey + uiKeyLen; if (pIxd) { ICD * pIcd = pIxd->pFirstKey; FLMUINT uiComponentLen; // Skip past all key components. Document ID will be right after them. while (pIcd && pucKey < pucKeyEnd) { if (pucKey + 2 > pucKeyEnd) { rc = RC_SET( NE_XFLM_BAD_COLLATED_KEY); goto Exit; } uiComponentLen = getKeyComponentLength( pucKey); pucKey += (2 + uiComponentLen); pIcd = pIcd->pNextKeyComponent; } if (pucKey >= pucKeyEnd) { rc = RC_SET( NE_XFLM_BAD_COLLATED_KEY); goto Exit; } // Should be positioned on document ID if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, pui64DocId))) { goto Exit; } } else { // If no sort index was defined, the key is a 4 byte sequence number, // followed by the document id stored as a SEN. This is done so that // it can be sorted with a memcmp. pucKey += 4; if (pucKey >= pucKeyEnd) { rc = RC_SET( NE_XFLM_BAD_COLLATED_KEY); goto Exit; } if (RC_BAD( rc = f_decodeSEN64( &pucKey, pucKeyEnd, pui64DocId))) { goto Exit; } } Exit: return( rc); } /*************************************************************************** Desc: Check result set waiters to see if any should be notified that they are done waiting or that they timed out or that there was an error. ***************************************************************************/ void F_Query::checkResultSetWaiters( RCODE rc) { RS_WAITER * pWaiter; FLMUINT uiCurrTime; F_SEM hESem; if (m_pSortResultSet && !m_bResultSetPopulated && m_pFirstWaiter) { uiCurrTime = FLM_GET_TIMER(); m_pSortResultSet->lockMutex(); pWaiter = m_pFirstWaiter; while (pWaiter) { // First see if their count has been satisfied. if (RC_BAD( rc) || m_pSortResultSet->getCount() >= pWaiter->uiNumToWaitFor) { hESem = pWaiter->hESem; *(pWaiter->pRc) = rc; // Unlink the waiter from the list before signaling. if (pWaiter->pNext) { pWaiter->pNext->pPrev = pWaiter->pPrev; } if (pWaiter->pPrev) { pWaiter->pPrev->pNext = pWaiter->pNext; } else { m_pFirstWaiter = pWaiter->pNext; } pWaiter = pWaiter->pNext; f_semSignal( hESem); } else if (pWaiter->uiTimeLimit && FLM_ELAPSED_TIME( uiCurrTime, pWaiter->uiWaitStartTime) > pWaiter->uiTimeLimit) { hESem = pWaiter->hESem; *(pWaiter->pRc) = RC_SET( NE_XFLM_TIMEOUT); // Unlink the waiter from the list before signaling. if (pWaiter->pNext) { pWaiter->pNext->pPrev = pWaiter->pPrev; } if (pWaiter->pPrev) { pWaiter->pPrev->pNext = pWaiter->pNext; } else { m_pFirstWaiter = pWaiter->pNext; } pWaiter = pWaiter->pNext; f_semSignal( hESem); } else { pWaiter = pWaiter->pNext; } } m_pSortResultSet->unlockMutex(); } } /*************************************************************************** Desc: Wait for the result set to get completely built. This routine assumes that the result set mutex has already been locked. ***************************************************************************/ RCODE F_Query::waitResultSetBuild( IF_Db * pDb, FLMUINT uiTimeLimit, FLMUINT uiNumToWaitFor) { RCODE rc = NE_XFLM_OK; RCODE TempRc; RS_WAITER waiter; FLMBOOL bMutexLocked = TRUE; // See if we are still building the result set, or if our // count has been reached. if (m_pSortResultSet->getCount() >= uiNumToWaitFor || m_bResultSetPopulated) { goto Exit; } // This should only be called if there is actually a thread currently // building the result set. flmAssert( m_uiBuildThreadId && m_uiBuildThreadId != f_threadId()); waiter.uiThreadId = f_threadId(); waiter.hESem = ((F_Db *)pDb)->m_hWaitSem; waiter.pRc = &rc; if (uiTimeLimit) { waiter.uiWaitStartTime = FLM_GET_TIMER(); waiter.uiTimeLimit = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); } else { waiter.uiWaitStartTime = 0; waiter.uiTimeLimit = 0; } waiter.uiNumToWaitFor = uiNumToWaitFor; waiter.pPrev = NULL; if ((waiter.pNext = m_pFirstWaiter) != NULL) { m_pFirstWaiter->pPrev = &waiter; } m_pFirstWaiter = &waiter; rc = NE_XFLM_FAILURE; m_pSortResultSet->unlockMutex(); bMutexLocked = FALSE; if (RC_BAD( TempRc = f_semWait( waiter.hESem, F_WAITFOREVER))) { flmAssert( 0); rc = TempRc; } else { // Process that signaled us better set the rc to something // besides NE_XFLM_FAILURE. if( rc == NE_XFLM_FAILURE) { flmAssert( 0); } } Exit: if (!bMutexLocked) { m_pSortResultSet->lockMutex(); } return( rc); } /*************************************************************************** Desc: Get the first item in the result set. ***************************************************************************/ RCODE F_Query::getFirstFromResultSet( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiNumToWaitFor = 1; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } for (;;) { // If we are in the middle of populating the result set, we may need to wait // for entries to come into the result set. if (!m_bResultSetPopulated) { // If the entries are not in order, we must wait for the entire result // set to be populated. NOTE: If we are not sorting, they are, // by definition, in order - we order them with a sequence number. if (!m_bEntriesAlreadyInOrder) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, FLM_MAX_UINT))) { goto Exit; } } // If there are no entries in the result set yet, in order to get // the first entry we must at least wait for the first one to // appear. else if (m_pSortResultSet->getCount() < uiNumToWaitFor) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, uiNumToWaitFor))) { goto Exit; } } } if (uiNumToWaitFor == 1) { // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (RC_BAD( rc = m_pSortResultSet->getFirst( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } } else { if (RC_BAD( rc = m_pSortResultSet->getNext( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { break; } if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // See how much time is left, if we are operating with a time limit. if (uiTimeLimit) { if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, &uiRemainingTimeMilli))) { goto Exit; } } uiNumToWaitFor++; } Exit: return( rc); } /*************************************************************************** Desc: Get the last item in the result set. ***************************************************************************/ RCODE F_Query::getLastFromResultSet( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; FLMBOOL bGetLast = TRUE; // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } for (;;) { // If we are in the middle of populating the result set, we may need to wait // for entries to come into the result set. if (!m_bResultSetPopulated) { // When getting the last entry, we must always wait for the result set // to become fully populated. if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, FLM_MAX_UINT))) { goto Exit; } } // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (bGetLast) { if (RC_BAD( rc = m_pSortResultSet->getLast( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } } else { if (RC_BAD( rc = m_pSortResultSet->getPrev( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { break; } if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // See how much time is left, if we are operating with a time limit. if (uiTimeLimit) { if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, &uiRemainingTimeMilli))) { goto Exit; } } bGetLast = FALSE; } Exit: return( rc); } /*************************************************************************** Desc: Get the next item from the result set. ***************************************************************************/ RCODE F_Query::getNextFromResultSet( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; FLMUINT uiNumSkipped; // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } if (!puiNumSkipped) { // puiNumSkipped has to be non-NULL so it can be incremented only // if uiNumToSkip > 1 if (uiNumToSkip > 1) { uiNumSkipped = 0; puiNumSkipped = &uiNumSkipped; } } else { *puiNumSkipped = 0; } for (;;) { // If we are in the middle of populating the result set, we may need to wait // for entries to come into the result set. if (!m_bResultSetPopulated) { // If the entries are not in order, we must wait for the entire result // set to be populated. NOTE: If we are not sorting, they are, // by definition, in order - we order them with a sequence number. if (!m_bEntriesAlreadyInOrder) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, FLM_MAX_UINT))) { goto Exit; } } else { FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); // See if we have enough entries in the result set to position // to the next entry. // If we are not yet positioned (uiCurrPos == FLM_MAX_UINT), // we need to have at least one item in the result set. // Otherwise, we need to have one more beyond uiCurrPos - which // is (uiCurrPos + 1) + 1 --> uiCurrPos + 2. if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, (uiCurrPos == FLM_MAX_UINT) ? 1 : uiCurrPos + 2))) { goto Exit; } } } // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (RC_BAD( rc = m_pSortResultSet->getNext( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } if (puiNumSkipped) { (*puiNumSkipped)++; } if (uiNumToSkip > 1) { // puiNumSkipped will always be non-NULL in the case // where uiNumToSkip > 1 flmAssert( puiNumSkipped); if (*puiNumSkipped < uiNumToSkip) { continue; } } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { break; } if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // See how much time is left, if we are operating with a time limit. if (uiTimeLimit) { if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, &uiRemainingTimeMilli))) { goto Exit; } } } Exit: return( rc); } /*************************************************************************** Desc: Get the previous item in the result set. ***************************************************************************/ RCODE F_Query::getPrevFromResultSet( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiNumToSkip, FLMUINT * puiNumSkipped) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; FLMUINT uiNumSkipped; if (!puiNumSkipped) { // puiNumSkipped has to be non-NULL so it can be incremented only // if uiNumToSkip > 1 if (uiNumToSkip > 1) { uiNumSkipped = 0; puiNumSkipped = &uiNumSkipped; } } else { *puiNumSkipped = 0; } // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } // If we are in the middle of populating the result set, we may need to wait // for entries to come into the result set. if (!m_bResultSetPopulated) { // If the entries are not in order, we must wait for the entire result // set to be populated. NOTE: If we are not sorting, they are, // by definition, in order - we order them with a sequence number. if (!m_bEntriesAlreadyInOrder) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, FLM_MAX_UINT))) { goto Exit; } } // If there are no entries in the result set yet, in order to get // the first entry we must at least wait for the first one to // appear. else { FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); // See if we have enough entries in the result set to position // to the previous entry. We should never be positioned beyond // the number that are currently in there. So, at most, we should // only have to wait for one to appear. if (uiCurrPos == FLM_MAX_UINT) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, 1))) { goto Exit; } } else { // Better never be beyond the current count. flmAssert( uiCurrPos < m_pSortResultSet->getCount()); } } } for (;;) { // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (RC_BAD( rc = m_pSortResultSet->getPrev( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } if (puiNumSkipped) { (*puiNumSkipped)++; } if (uiNumToSkip > 1) { // puiNumSkipped will always be non-NULL in the case // where uiNumToSkip > 1 flmAssert( puiNumSkipped); if (*puiNumSkipped < uiNumToSkip) { continue; } } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { break; } if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) { goto Exit; } rc = NE_XFLM_OK; // See how much time is left, if we are operating with a time limit. if (uiTimeLimit) { if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, &uiRemainingTimeMilli))) { goto Exit; } } } Exit: return( rc); } /*************************************************************************** Desc: Get the current item in the result set. ***************************************************************************/ RCODE F_Query::getCurrentFromResultSet( IF_Db * ifpDb, IF_DOMNode ** ppNode) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; // If we are in the middle of populating the result set, we may need to wait // for entries to come into the result set. if (!m_bResultSetPopulated) { // If the entries are not in order, we must wait for the entire result // set to be populated. But that means that we have never positioned // anywhere yet, so we should return an error. if (!m_bEntriesAlreadyInOrder) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); goto Exit; } // If there are no entries in the result set yet, in order to get // the first entry we must at least wait for the first one to // appear. else { FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); // It is an error to call this if we have not yet positioned // anywhere in the result set. if (uiCurrPos == FLM_MAX_UINT) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); goto Exit; } else { // Better never be beyond the current count. flmAssert( uiCurrPos < m_pSortResultSet->getCount()); } } } // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (RC_BAD( rc = m_pSortResultSet->getCurrent( ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_BAD( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); } goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get previous node/document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::positionTo( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiPosition ) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; // If we have a result set, or are in the middle of creating it, // we don't want to change the member variables because a background // thread may be using them. if (!m_pSortResultSet) { // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { if (RC_BAD( rc = optimize())) { goto Exit; } } // If we are not creating a result set, this is not a positionable // query. if (!m_pSortResultSet) { rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); goto Exit; } } // If we are in the middle of populating the result set, we may need to wait // for entries to come into the result set. if (!m_bResultSetPopulated) { // If the entries are not in order, we must wait for the entire result // set to be populated before we can determine what will be // the nth entry. NOTE: If we are not sorting, they are, // by definition, in order - we order them with a sequence number. if (!m_bEntriesAlreadyInOrder) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, FLM_MAX_UINT))) { goto Exit; } } else { FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); // See if we have the nth entry yet. if (uiCurrPos == FLM_MAX_UINT || m_pSortResultSet->getCount() < uiPosition + 1) { // Must wait for uiPosition + 1, because uiPosition is zero based. if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, uiPosition + 1))) { goto Exit; } } } } if (m_pSortIxd) { m_pSortResultSet->setIxInfo( m_pDb, m_pSortIxd); } // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (RC_BAD( rc = m_pSortResultSet->positionToEntry( uiPosition, ucKey, sizeof( ucKey), &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_BAD( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); } goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get previous node/document that passes query expression. ***************************************************************************/ RCODE FLMAPI F_Query::positionTo( IF_Db * ifpDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, IF_DataVector * pSearchKey, FLMUINT uiFlags ) { RCODE rc = NE_XFLM_OK; FLMBYTE ucKey [XFLM_MAX_KEY_SIZE]; FLMUINT uiKeyLen; FLMUINT64 ui64DocId; FLMUINT uiStartTimeTU = 0; FLMUINT uiRemainingTimeMilli = 0; FLMUINT uiTimeLimitTU = 0; // If we have a result set, or are in the middle of creating it, // we don't want to change the member variables because a background // thread may be using them. if (!m_pSortResultSet) { // NOTE: uiTimeLimit will always be zero when building the result // set in another thread. if (uiTimeLimit) { uiTimeLimitTU = FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit); uiStartTimeTU = FLM_GET_TIMER(); uiRemainingTimeMilli = uiTimeLimit; } m_pDb = (F_Db *)ifpDb; if (ppNode && *ppNode) { (*ppNode)->Release(); *ppNode = NULL; } if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { if (RC_BAD( rc = optimize())) { goto Exit; } } // If no sort keys were specified, we cannot do this kind of // positioning. if (!m_pSortIxd) { rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); goto Exit; } if (!m_pSortResultSet) { //visit: Need to allow something here for indexes where we really don't //care about a result set. Positioning to a particular key is still possible //in that case. rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); goto Exit; } } // If we are in the middle of populating the result set, and the entries // are not in order, we need to wait for all entries to // come into the result set. //VISIT: Should we just wait for the entire result set even if the entries //are in order? if (!m_bResultSetPopulated && !m_bEntriesAlreadyInOrder) { if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, FLM_MAX_UINT))) { goto Exit; } } // Need to set the result set's index and db. m_pSortResultSet->setIxInfo( m_pDb, m_pSortIxd); // If the result set is not yet populated, we need to lock the mutex // before accessing it - because another thread is trying to populate it. if (RC_BAD( rc = m_pSortResultSet->positionToEntry( ucKey, sizeof( ucKey), &uiKeyLen, (F_DataVector *)pSearchKey, uiFlags, m_bResultSetPopulated ? FALSE : TRUE))) { goto Exit; } if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) { goto Exit; } if (RC_BAD( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) { if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); } goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get current position. ***************************************************************************/ RCODE FLMAPI F_Query::getPosition( IF_Db * ifpDb, FLMUINT * puiPosition ) { RCODE rc = NE_XFLM_OK; // If we do not have a result set, we will not be positioned anywhere. if (!m_pSortResultSet) { m_pDb = (F_Db *)ifpDb; if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { if (RC_BAD( rc = optimize())) { goto Exit; } } if (!m_pSortResultSet) { rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); goto Exit; } *puiPosition = 0; rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } if ((*puiPosition = m_pSortResultSet->getCurrPos()) == FLM_MAX_UINT) { *puiPosition = 0; rc = RC_SET( NE_XFLM_BOF_HIT); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Get count. Only works for queries that are building result sets. ***************************************************************************/ RCODE FLMAPI F_Query::getCounts( IF_Db * ifpDb, FLMUINT uiTimeLimit, FLMBOOL bPartialCountOk, FLMUINT * puiReadCount, FLMUINT * puiPassedCount, FLMUINT * puiPositionableToCount, FLMBOOL * pbDoneBuildingResultSet ) { RCODE rc = NE_XFLM_OK; // If we do not have a result set, we will not have a count. if (!m_pSortResultSet) { m_pDb = (F_Db *)ifpDb; if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) { // Make sure the passed in F_Db matches the one associated with // the query. rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); goto Exit; } // See if the database is being forced to close if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_pDb->m_eTransType == XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); goto Exit; } // See if we have a transaction going which should be aborted. if (RC_BAD( m_pDb->m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } if (!m_bOptimized) { if (RC_BAD( rc = optimize())) { goto Exit; } } if (!m_pSortResultSet) { rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); goto Exit; } } // If the result set is not yet populated, we need to let it become // completely populated before we return a count. if (!m_bResultSetPopulated) { if (bPartialCountOk) { if (m_bEntriesAlreadyInOrder) { *puiPositionableToCount = *puiPassedCount = m_pSortResultSet->getCount(); } else { *puiPassedCount = m_pSortResultSet->getCount(); *puiPositionableToCount = 0; } if (pbDoneBuildingResultSet) { *pbDoneBuildingResultSet = FALSE; } } else { if (RC_BAD( rc = buildResultSet( ifpDb, uiTimeLimit, FLM_MAX_UINT))) { goto Exit; } *puiPositionableToCount = *puiPassedCount = m_pSortResultSet->getCount(); if (pbDoneBuildingResultSet) { *pbDoneBuildingResultSet = TRUE; } } } else { *puiPositionableToCount = *puiPassedCount = m_pSortResultSet->getCount(); if (pbDoneBuildingResultSet) { *pbDoneBuildingResultSet = TRUE; } } // Always set *puiReadCount last, in case it is being incremented on // another thread. It will be ok for it to advance a little after having // set the other two counters. It would look weird, however, if it was // set first, and the other two counters ended up being greater than // this one. *puiReadCount = (FLMUINT)m_ui64RSDocsRead; Exit: return( rc); } libxflaim-5.1.969/src/fldbglog.cpp0000644000175000017500000001634510511001742020321 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the functions for debug logging. // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fldbglog.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #ifdef FLM_DBG_LOG // Local prototypes FSTATIC void _flmDbgLogFlush( void); FSTATIC void _flmDbgOutputMsg( char * pszMsg); // Global data F_MUTEX g_hDbgLogMutex = F_MUTEX_NULL; IF_FileHdl * g_pLogFile = NULL; char * g_pszLogBuf = NULL; FLMUINT g_uiLogBufOffset = 0; FLMUINT g_uiLogFileOffset = 0; FLMBOOL g_bDbgLogEnabled = TRUE; #define DBG_LOG_BUFFER_SIZE ((FLMUINT)512000) /**************************************************************************** Desc: ****************************************************************************/ void flmDbgLogInit( void) { char szLogPath[ 256]; RCODE rc = NE_XFLM_OK; flmAssert( g_hDbgLogMutex == F_MUTEX_NULL); // Allocate a buffer for the log if (RC_BAD( rc = f_alloc( DBG_LOG_BUFFER_SIZE + 1024, &g_pszLogBuf))) { goto Exit; } // Create the mutex if (RC_BAD( rc = f_mutexCreate( &g_hDbgLogMutex))) { goto Exit; } // Build the file path #ifdef FLM_NLM f_strcpy( szLogPath, "SYS:\\FLMDBG.LOG"); #else f_sprintf( szLogPath, "FLMDBG.LOG"); #endif // Create the file - truncate if it exists already. if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->createFile( szLogPath, gv_XFlmSysData.uiFileCreateFlags, &g_pLogFile))) { goto Exit; } Exit: flmAssert( RC_OK( rc)); } /**************************************************************************** Desc: ****************************************************************************/ void flmDbgLogExit( void) { if( g_bDbgLogEnabled) { // Output "Log End" message f_mutexLock( g_hDbgLogMutex); _flmDbgOutputMsg( "--- LOG END ---"); f_mutexUnlock( g_hDbgLogMutex); // Flush the log flmDbgLogFlush(); } // Free all resources if( g_hDbgLogMutex != F_MUTEX_NULL) { f_mutexDestroy( &g_hDbgLogMutex); } if( g_pszLogBuf) { f_free( &g_pszLogBuf); } if( g_pLogFile) { g_pLogFile->Truncate( g_uiLogFileOffset + g_uiLogBufOffset); g_pLogFile->Close(); g_pLogFile->Release(); g_pLogFile = NULL; } g_bDbgLogEnabled = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ void flmDbgLogMsg( char * pszMsg) { if (!g_bDbgLogEnabled) return; f_mutexLock( g_hDbgLogMutex); _flmDbgOutputMsg( pszMsg); f_mutexUnlock( g_hDbgLogMutex); } /**************************************************************************** Desc: ****************************************************************************/ void flmDbgLogFlush( void) { f_mutexLock( g_hDbgLogMutex); _flmDbgLogFlush(); f_mutexUnlock( g_hDbgLogMutex); } /**************************************************************************** Desc: ****************************************************************************/ FSTATIC void _flmDbgLogFlush( void) { FLMUINT uiBytesToWrite; FLMUINT uiBytesWritten; char * pszBufPtr = g_pszLogBuf; FLMUINT uiTotalToWrite = g_uiLogBufOffset; RCODE rc = NE_XFLM_OK; FLMUINT uiBufferSize = DBG_LOG_BUFFER_SIZE + 1024; while( uiTotalToWrite) { if( uiTotalToWrite > 0xFE00) { uiBytesToWrite = 0xFE00; } else { uiBytesToWrite = uiTotalToWrite; } if( RC_BAD( rc = g_pLogFile->SectorWrite( g_uiLogFileOffset, uiBytesToWrite, pszBufPtr, uiBufferSize, NULL, &uiBytesWritten, FALSE))) { goto Exit; } flmAssert( uiBytesToWrite == uiBytesWritten); g_uiLogFileOffset += uiBytesWritten; pszBufPtr += uiBytesWritten; uiBufferSize -= uiBytesWritten; uiTotalToWrite -= uiBytesWritten; } if (g_uiLogBufOffset & 0x1FF) { if (g_uiLogBufOffset > 512) { f_memcpy( g_pszLogBuf, &g_pszLogBuf [g_uiLogBufOffset & 0xFFFFFE00], 512); g_uiLogBufOffset &= 0x1FF; } g_uiLogFileOffset -= g_uiLogBufOffset; } else { g_uiLogBufOffset = 0; } Exit: flmAssert( RC_OK( rc)); } /**************************************************************************** Desc: ****************************************************************************/ void _flmDbgOutputMsg( char * pszMsg) { char * pszBufPtr = (char *)(&(g_pszLogBuf[ g_uiLogBufOffset])); f_sprintf( (char *)pszBufPtr, "%s\n", pszMsg); g_uiLogBufOffset += f_strlen( pszBufPtr); if( g_uiLogBufOffset >= DBG_LOG_BUFFER_SIZE) { _flmDbgLogFlush(); } } /**************************************************************************** Desc: ****************************************************************************/ void flmDbgLogWrite( F_Database * pDatabase, FLMUINT uiBlkAddress, FLMUINT uiWriteAddress, FLMUINT64 ui64TransId, char * pszEvent) { char pszTmpBuf[ 256]; if( !g_bDbgLogEnabled) return; if( !uiWriteAddress) { f_sprintf( (char *)pszTmpBuf, "d%X b=%X t%I64u %s", (unsigned)((FLMUINT)pDatabase), (unsigned)uiBlkAddress, ui64TransId, pszEvent); } else { f_sprintf( (char *)pszTmpBuf, "d%X b=%X a=%X t%I64u %s", (unsigned)((FLMUINT)pDatabase), (unsigned)uiBlkAddress, (unsigned)uiWriteAddress, ui64TransId, pszEvent); } f_mutexLock( g_hDbgLogMutex); _flmDbgOutputMsg( pszTmpBuf); f_mutexUnlock( g_hDbgLogMutex); } /**************************************************************************** Desc: ****************************************************************************/ void flmDbgLogUpdate( F_Database * pDatabase, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId, RCODE rc, char * pszEvent) { char pszTmpBuf[ 256]; char szErr [12]; if (!g_bDbgLogEnabled) { return; } if (RC_BAD( rc)) { f_sprintf( szErr, " RC=%04X", (unsigned)rc); } else { szErr [0] = 0; } if (uiCollection) { f_sprintf( (char *)pszTmpBuf, "d%X t%I64u c%u n%I64u %s%s", (unsigned)((FLMUINT)pDatabase), ui64TransId, (unsigned)uiCollection, ui64NodeId, pszEvent, szErr); } else { f_sprintf( (char *)pszTmpBuf, "d%X t%I64u %s%s", (unsigned)((FLMUINT)pDatabase), ui64TransId, pszEvent, szErr); } f_mutexLock( g_hDbgLogMutex); _flmDbgOutputMsg( pszTmpBuf); f_mutexUnlock( g_hDbgLogMutex); } #endif // FLM_DBG_LOG /**************************************************************************** Desc: ****************************************************************************/ #ifndef FLM_DBG_LOG void fldbglog_dummy() { } #endif libxflaim-5.1.969/src/fstructs.h0000644000175000017500000012537610511001742020070 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Various structures and classes used internally by FLAIM. // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fstructs.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FSTRUCTS_H #define FSTRUCTS_H #if defined( FLM_WIN) || defined( FLM_NLM) || defined( FLM_LINUX) #pragma pack( push, 1) #else #pragma pack( 1) #endif class HRequest; class F_BtPool; class F_XMLImport; class F_XMLExport; class F_Rfl; class F_SuperFileHdl; class F_Btree; class F_DbRebuild; class F_DbCheck; class F_Cursor; class F_MultiAlloc; class F_CachedNode; class F_CachedBlock; class F_BlockCacheMgr; class F_NodeCacheMgr; class F_GlobalCacheMgr; class F_NodePool; class F_BTreeInfo; class F_NodeRelocator; class F_NodeDataRelocator; class F_NodeListRelocator; class F_AttrItemRelocator; class F_AttrBufferRelocator; /**************************************************************************** Desc: Tests to see if database is NOT in native platform format. ****************************************************************************/ FINLINE FLMBOOL hdrIsNonNativeFormat( XFLM_DB_HDR * pDbHdr) { return( (FLMBOOL)(pDbHdr->ui8IsLittleEndian == XFLM_NATIVE_IS_LITTLE_ENDIAN ? (FLMBOOL)FALSE : (FLMBOOL)TRUE)); } /**************************************************************************** Desc: Block header - on-disk format. ****************************************************************************/ typedef struct FlmBlockHdrTag { FLMUINT32 ui32BlkAddr; // BH_ADDR FLMUINT32 ui32PrevBlkInChain; // BH_PREV_BLK FLMUINT32 ui32NextBlkInChain; // BH_NEXT_BLK FLMUINT32 ui32PriorBlkImgAddr; // BH_PREV_BLK_ADDR FLMUINT64 ui64TransID; // BH_TRANS_ID FLMUINT32 ui32BlkCRC; // Block CRC FLMUINT16 ui16BlkBytesAvail; // BH_BLK_END, BH_ELM_END FLMUINT8 ui8BlkFlags; // Flags for the block #define BLK_FORMAT_IS_LITTLE_ENDIAN 0x01 #define BLK_IS_BEFORE_IMAGE 0x02 // This bit gets ORed into type if the // block is a Before Image block that // should be restored on transaction // abort. This is only set when a block // is written to the log, so it only // needs to be unset when the block is // read back from the log. #define BLK_IS_ENCRYPTED 0x04 FLMUINT8 ui8BlkType; // BH_TYPE #define BT_FREE 0 // Free block - avail list #define BT_LFH_BLK 1 // LFH Header block #define BT_LEAF 2 // New B-Tree Leaf block #define BT_NON_LEAF 3 // New B-Tree Non-leaf block block - fixed key size #define BT_NON_LEAF_COUNTS 4 // New B-Tree Non-leaf index with counts #define BT_LEAF_DATA 5 // New B-Tree Leaf block with Data #define BT_DATA_ONLY 6 // Data-only block // NOTE: IF adding more types, may need to modify the blkIsNewBTree function // below. // IMPORTANT NOTE: If anything is changed in here, need to make // corresponding changes to convertBlkHdr routine and // flmVerifyDiskStructOffsets routine. #define F_BLK_HDR_ui32BlkAddr_OFFSET 0 #define F_BLK_HDR_ui32PrevBlkInChain_OFFSET 4 #define F_BLK_HDR_ui32NextBlkInChain_OFFSET 8 #define F_BLK_HDR_ui32PriorBlkImgAddr_OFFSET 12 #define F_BLK_HDR_ui64TransID_OFFSET 16 #define F_BLK_HDR_ui32BlkCRC_OFFSET 24 #define F_BLK_HDR_ui16BlkBytesAvail_OFFSET 28 #define F_BLK_HDR_ui8BlkFlags_OFFSET 30 #define F_BLK_HDR_ui8BlkType_OFFSET 31 } F_BLK_HDR; #define SIZEOF_STD_BLK_HDR sizeof( F_BLK_HDR) /**************************************************************************** Desc: Test to see if block is in non-native format. ****************************************************************************/ FINLINE FLMBOOL blkIsNonNativeFormat( F_BLK_HDR * pBlkHdr) { #if XFLM_NATIVE_IS_LITTLE_ENDIAN return( (pBlkHdr->ui8BlkFlags & BLK_FORMAT_IS_LITTLE_ENDIAN) ? FALSE : TRUE); #else return( (pBlkHdr->ui8BlkFlags & BLK_FORMAT_IS_LITTLE_ENDIAN) ? TRUE : FALSE); #endif } /**************************************************************************** Desc: Set block flags to indicate it is in native format. ****************************************************************************/ FINLINE void blkSetNativeFormat( F_BLK_HDR * pBlkHdr) { #if XFLM_NATIVE_IS_LITTLE_ENDIAN pBlkHdr->ui8BlkFlags |= BLK_FORMAT_IS_LITTLE_ENDIAN; #else pBlkHdr->ui8BlkFlags &= ~(BLK_FORMAT_IS_LITTLE_ENDIAN); #endif } /**************************************************************************** Desc: Test to see if a block type is a B-Tree block type. ****************************************************************************/ FINLINE FLMBOOL blkIsBTree( F_BLK_HDR * pBlkHdr) { return( (pBlkHdr->ui8BlkType != BT_FREE && pBlkHdr->ui8BlkType != BT_LFH_BLK) ? TRUE : FALSE); } /**************************************************************************** Desc: Test to see if a block type is a NEW B-Tree block type. ****************************************************************************/ FINLINE FLMBOOL blkIsNewBTree( F_BLK_HDR * pBlkHdr) { return( (pBlkHdr->ui8BlkType >= BT_LEAF) ? TRUE : FALSE); } /**************************************************************************** Desc: Determine where the block ends. ****************************************************************************/ FINLINE FLMUINT blkGetEnd( FLMUINT uiBlockSize, FLMUINT uiBlkHdrSize, F_BLK_HDR * pBlkHdr) { return( (FLMUINT)(blkIsNewBTree( pBlkHdr) ? uiBlockSize : (FLMUINT)(pBlkHdr->ui16BlkBytesAvail > uiBlockSize - uiBlkHdrSize ? uiBlkHdrSize : uiBlockSize - (FLMUINT)pBlkHdr->ui16BlkBytesAvail))); } FINLINE void setBlockEncrypted( F_BLK_HDR * pBlkHdr) { pBlkHdr->ui8BlkFlags |= BLK_IS_ENCRYPTED; } FINLINE void unsetBlockEncrypted( F_BLK_HDR * pBlkHdr) { pBlkHdr->ui8BlkFlags &= (~(BLK_IS_ENCRYPTED)); } FINLINE FLMBOOL isEncryptedBlk( F_BLK_HDR * pBlkHdr) { return( (pBlkHdr->ui8BlkFlags & BLK_IS_ENCRYPTED) ? TRUE : FALSE); } /**************************************************************************** Desc: B-Tree block header - on-disk format. ****************************************************************************/ typedef struct FlmBTreeBlkHdr { F_BLK_HDR stdBlkHdr; // Standard block header FLMUINT16 ui16LogicalFile; // BH_LOG_FILE_NUM FLMUINT16 ui16NumKeys; // Number of keys FLMUINT8 ui8BlkLevel; // BH_LEVEL #define BH_MAX_LEVELS 8 // Max allowable b-tree levels #define MAX_LEVELS BH_MAX_LEVELS FLMUINT8 ui8BTreeFlags; // Flags for BTree #define BLK_IS_ROOT 0x01 #define BLK_IS_INDEX 0x02 FLMUINT16 ui16HeapSize; // Contiguous available space #define F_BTREE_BLK_HDR_stdBlkHdr_OFFSET 0 #define F_BTREE_BLK_HDR_ui16LogicalFile_OFFSET 32 #define F_BTREE_BLK_HDR_ui16NumKeys_OFFSET 34 #define F_BTREE_BLK_HDR_ui8BlkLevel_OFFSET 36 #define F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET 37 #define F_BTREE_BLK_HDR_ui16HeapSize_OFFSET 38 } F_BTREE_BLK_HDR; /**************************************************************************** Desc: Encrypted B-Tree block header - on-disk format. ****************************************************************************/ typedef struct { F_BTREE_BLK_HDR btree; FLMUINT64 ui64Reserved; // Reserving 8 bytes to ensure the data segment // of the block is sized to an even 16 byte boundary // as needed by encryption. } F_ENC_BTREE_BLK_HDR; FINLINE FLMUINT sizeofBTreeBlkHdr( F_BTREE_BLK_HDR * pBlkHdr) { if (!isEncryptedBlk( (F_BLK_HDR *)pBlkHdr)) { return sizeof( F_BTREE_BLK_HDR); } else { return sizeof( F_ENC_BTREE_BLK_HDR); } } /**************************************************************************** Desc: Encrypted Data-only block header - on-disk format. ****************************************************************************/ typedef struct { F_BLK_HDR blk; FLMUINT32 ui32EncId; FLMBYTE ucReserved[12]; // Reserving 12 bytes to ensure the data segment // of the block is sized to an even 16 byte boundary // as needed by encryption. } F_ENC_DO_BLK_HDR; FINLINE FLMUINT sizeofDOBlkHdr( F_BLK_HDR * pBlkHdr ) { if (!isEncryptedBlk( pBlkHdr)) { return sizeof( F_BLK_HDR); } else { return sizeof( F_ENC_DO_BLK_HDR); } } FINLINE FLMBOOL isRootBlk( F_BTREE_BLK_HDR * pBlkHdr ) { return( (pBlkHdr->ui8BTreeFlags & BLK_IS_ROOT) ? TRUE : FALSE); } FINLINE void setRootBlk( F_BTREE_BLK_HDR * pBlkHdr ) { pBlkHdr->ui8BTreeFlags |= BLK_IS_ROOT; } FINLINE void unsetRootBlk( F_BTREE_BLK_HDR * pBlkHdr ) { pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_ROOT)); } FINLINE FLMBOOL isIndexBlk( F_BTREE_BLK_HDR * pBlkHdr ) { return( (pBlkHdr->ui8BTreeFlags & BLK_IS_INDEX) ? TRUE : FALSE); } FINLINE eLFileType getBlkLfType( F_BTREE_BLK_HDR * pBlkHdr ) { return( (eLFileType)((pBlkHdr->ui8BTreeFlags & BLK_IS_INDEX) ? (eLFileType)XFLM_LF_INDEX : (eLFileType)XFLM_LF_COLLECTION)); } FINLINE void setBlkLfType( F_BTREE_BLK_HDR * pBlkHdr, FLMUINT uiLfType ) { if (uiLfType == XFLM_LF_COLLECTION) { pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_INDEX)); } else { pBlkHdr->ui8BTreeFlags |= BLK_IS_INDEX; } } FINLINE FLMBOOL isContainerBlk( F_BTREE_BLK_HDR * pBlkHdr ) { return( (pBlkHdr->ui8BTreeFlags & BLK_IS_INDEX) ? FALSE : TRUE); } FINLINE void setIndexBlk( F_BTREE_BLK_HDR * pBlkHdr ) { pBlkHdr->ui8BTreeFlags |= BLK_IS_INDEX; } FINLINE void setContainerBlk( F_BTREE_BLK_HDR * pBlkHdr ) { pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_INDEX)); } /**************************************************************************** Desc: Get block header size. ****************************************************************************/ FINLINE FLMUINT blkHdrSize( F_BLK_HDR * pBlkHdr ) { return( (pBlkHdr->ui8BlkType != BT_FREE && pBlkHdr->ui8BlkType != BT_LFH_BLK) ? (pBlkHdr->ui8BlkType == BT_DATA_ONLY ? sizeofDOBlkHdr( pBlkHdr) : sizeofBTreeBlkHdr((F_BTREE_BLK_HDR *)pBlkHdr)) : SIZEOF_STD_BLK_HDR); } /**************************************************************************** Desc: This is a union of all block header types - so that we can have something that gives us the largest block header type in one structure. Reduce uses this. ****************************************************************************/ typedef struct FlmLargestBlkHdr { union { F_BLK_HDR stdBlkHdr; F_BTREE_BLK_HDR BTreeBlkHdr; } all; } F_LARGEST_BLK_HDR; #define SIZEOF_LARGEST_BLK_HDR sizeof( F_LARGEST_BLK_HDR) /**************************************************************************** Desc: Logical File (b-tree) header - on-disk format. ****************************************************************************/ typedef struct FlmLfHdrTag { FLMUINT32 ui32LfNumber; FLMUINT32 ui32LfType; FLMUINT32 ui32RootBlkAddr; FLMUINT32 ui32EncId; FLMUINT64 ui64NextNodeId; FLMUINT64 ui64FirstDocId; FLMUINT64 ui64LastDocId; FLMBYTE ucZeroes[ 24]; // Reserve 24 bytes of zero. // IMPORTANT NOTE: If anything is changed in here, need to make // corresponding changes to convertLfHdr routine and // flmVerifyDiskStructOffsets routine. #define F_LF_HDR_ui32LfNumber_OFFSET 0 #define F_LF_HDR_ui32LfType_OFFSET 4 #define F_LF_HDR_ui32RootBlkAddr_OFFSET 8 #define F_LF_HDR_ui32EncId_OFFSET 12 #define F_LF_HDR_ui64NextNodeId_OFFSET 16 #define F_LF_HDR_ui64FirstDocId_OFFSET 24 #define F_LF_HDR_ui64LastDocId_OFFSET 32 #define F_LF_HDR_ucZeroes_OFFSET 40 } F_LF_HDR; typedef struct { FLMUINT uiCollection; FLMUINT64 ui64Document; FLMUINT64 ui64NodeId; } NODE_LIST_ITEM; /**************************************************************************** Desc: Node list ****************************************************************************/ class F_NodeList : public F_Object { public: F_NodeList() { m_pNodeTbl = NULL; m_uiNodeTblSize = 0; clearNodes(); } ~F_NodeList() { if (m_pNodeTbl) { f_free( &m_pNodeTbl); } } FINLINE void clearNodes( void) { m_uiNumNodes = 0; m_uiLastPosition = 0; m_uiLastCollection = 0; m_ui64LastDocument = 0; m_ui64LastNodeId = 0; } RCODE addNode( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId); FINLINE void removeNode( FLMUINT uiPosition) { if( uiPosition < m_uiNumNodes) { if( uiPosition < m_uiNumNodes - 1) { f_memmove( &m_pNodeTbl[ uiPosition], &m_pNodeTbl[ uiPosition + 1], sizeof( NODE_LIST_ITEM) * (m_uiNumNodes - uiPosition)); } m_uiNumNodes--; } m_uiLastPosition = 0; m_uiLastCollection = 0; m_ui64LastDocument = 0; m_ui64LastNodeId = 0; } void removeNode( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId); FINLINE FLMBOOL isNodeInList( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId) { FLMUINT uiPos; return( findNode( uiCollection, ui64Document, ui64NodeId, &uiPos)); } FLMBOOL findNode( FLMUINT uiCollection, FLMUINT64 ui64Document, FLMUINT64 ui64NodeId, FLMUINT * puiPos); FINLINE void getNode( FLMUINT uiPosition, FLMUINT * puiCollection, FLMUINT64 * pui64Document, FLMUINT64 * pui64NodeId) { if( uiPosition < m_uiNumNodes) { *puiCollection = m_pNodeTbl[ uiPosition].uiCollection; *pui64Document = m_pNodeTbl[ uiPosition].ui64Document; *pui64NodeId = m_pNodeTbl[ uiPosition].ui64NodeId; } else { *puiCollection = 0; *pui64Document = 0; *pui64NodeId = 0; } } private: NODE_LIST_ITEM * m_pNodeTbl; FLMUINT m_uiNodeTblSize; FLMUINT m_uiNumNodes; FLMUINT m_uiLastPosition; FLMUINT m_uiLastCollection; FLMUINT64 m_ui64LastDocument; FLMUINT64 m_ui64LastNodeId; friend class F_Db; friend class F_Database; friend class F_Dict; }; // Flags for the uiKrAction parameter - used in sorting/indexing. #define KREF_DEL_KEYS 0x01 #define KREF_ADD_KEYS 0x02 #define KREF_INDEXING_ONLY 0x04 #define KREF_IN_MODIFY 0x10 #define KREF_MISSING_KEYS_OK 0x20 // Maximum length of a key. #define MAX_ID_SIZE 256 // Cannot be more than 256 because // we can only use one byte to // represent the total ID size. #define DEFAULT_KREF_TBL_SIZE 4096 #define DEFAULT_KREF_POOL_BLOCK_SIZE 8192 /**************************************************************************** Desc: This structure is used to sort keys before the keys are actually added to an index. ****************************************************************************/ typedef struct Kref_Entry { FLMBOOL bDelete; // Delete the key if TRUE FLMUINT uiSequence; // Sequence of updates within trans. FLMUINT uiDataLen; // Data length for this entry. The // data, if any, comes after the key. // Note that there will be a null // terminating byte between the key and // the data. // Note: used uint16 below to reduce memory allocations. FLMUINT16 ui16IxNum; // Index number FLMUINT16 ui16KeyLen; // Key Length for this entry. The key // comes immediately after this structure. } KREF_ENTRY; /**************************************************************************** Desc: This is a temporary structure that is used when building compound keys. ****************************************************************************/ typedef struct Cdl { F_DOMNode * pNode; // Node to be included in a compound key. // May be NULL. FLMUINT64 ui64ParentId; // If pNode is NULL, this is a place // holder for a "missing" child node. We // just need to remember the parent node id. FLMBOOL bInNodeSubtree; // Is this node subordinate to the original // node we are indexing on? Cdl * pNext; // Pointer to the next CDL entry. } CDL; /**************************************************************************** Desc: This is a temporary structure that is used when building compound keys. ****************************************************************************/ typedef struct CdlHdrTag { CDL * pCdlList; FLMUINT64 ui64NodeId; } CDL_HDR; /**************************************************************************** Desc: This is a temporary structure that is used when building compound keys. ****************************************************************************/ typedef struct IxContextTag { F_Pool * pPool; CDL_HDR * pCdlTbl; CDL * pCdlList; IxContextTag * pNext; IxContextTag * pPrev; } IX_CONTEXT; typedef struct IxdFixupTag { FLMUINT uiIndexNum; FLMUINT64 ui64LastDocIndexed; IxdFixupTag * pNext; } IXD_FIXUP; #define FTHREAD_ACTION_IDLE 0 #define FTHREAD_ACTION_INDEX_OFFLINE 1 /*************************************************************************** Desc: Contains elements for passing parms into the background thread. ***************************************************************************/ typedef struct F_BkgndIx { F_Database * pDatabase; FLMUINT uiIndexingAction; XFLM_INDEX_STATUS indexStatus; F_BkgndIx * pPrev; F_BkgndIx * pNext; } F_BKGND_IX; /**************************************************************************** Desc: Structure used to pass information to the checkpoint thread for 3.x databases. ****************************************************************************/ typedef struct { F_Database * pDatabase; F_SuperFileHdl * pSFileHdl; F_SEM hWaitSem; XFLM_STATS Stats; FLMBOOL bStatsInitialized; FLMBOOL bShuttingDown; FLMBOOL bDoingCheckpoint; FLMUINT uiStartTime; FLMBOOL bForcingCheckpoint; FLMUINT uiForceCheckpointStartTime; FLMINT iForceCheckpointReason; FLMUINT uiLogBlocksWritten; FLMBOOL bWritingDataBlocks; FLMUINT uiDataBlocksWritten; FLMUINT uiStartWaitTruncateTime; } CP_INFO; #define MAX_WRITE_BUFFER_BYTES (4 * 1024 * 1024) #define MAX_PENDING_WRITES (MAX_WRITE_BUFFER_BYTES / 4096) #define MAX_LOG_BUFFER_SIZE (256 * 1024) typedef struct Tmp_Read_Stats { XFLM_DISKIO_STAT BlockReads; // Statistics on block reads. XFLM_DISKIO_STAT OldViewBlockReads; // Statistics on old view block // reads. FLMUINT uiBlockChkErrs; // Number of times we had // check errors reading blocks. FLMUINT uiOldViewBlockChkErrs; // Number of times we had // check errors reading an // old view of a block. } TMP_READ_STATS, * TMP_READ_STATS_p; // Flags for F_Database->m_uiFlags #define DBF_BEING_OPENED 0x01 // Flag indicating whether this database is // in the process of being opened. #define DBF_BEING_CLOSED 0x02 // Database is being closed - cannot open. /***************************************************************************** Desc: Shared database object - only to be used internally *****************************************************************************/ class F_Database : public F_Object { public: F_Database( FLMBOOL bTempDb); ~F_Database(); void freeDatabase( void); RCODE setupDatabase( const char * pszDbPath, const char * pszDataDir); FINLINE RCODE dbWriteLock( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats = NULL, FLMUINT uiTimeout = FLM_NO_TIMEOUT) { return( m_pWriteLockObj->lock( hWaitSem, TRUE, uiTimeout, 0, pDbStats ? &pDbStats->LockStats : NULL)); } FINLINE void dbWriteUnlock( void) { (void)m_pWriteLockObj->unlock(); } void shutdownDatabaseThreads( void); RCODE linkToBucket( void); // was flmLinkFileToBucket void setMustCloseFlags( // was flmSetMustCloseFlags RCODE rcMustClose, FLMBOOL bMutexLocked); FINLINE F_NameTable * getNameTable( void) { if( m_pDictList) { return( m_pDictList->getNameTable()); } return( NULL); } FINLINE RCODE checkState( const char * pszFileName, FLMINT iLineNumber) { RCODE rc = NE_XFLM_OK; if (m_bMustClose) { logMustCloseReason( pszFileName, iLineNumber); rc = RC_SET( NE_XFLM_MUST_CLOSE_DATABASE); } return( rc); } RCODE startCPThread( void); FLMBOOL tryCheckpoint( IF_Thread * pThread, CP_INFO * pCPInfo); void newDatabaseFinish( RCODE OpenRc); RCODE getExclAccess( const char * pszFilePath); RCODE verifyOkToUse( FLMBOOL * pbWaited); RCODE writeDbHdr( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, XFLM_DB_HDR * pDbHdr, XFLM_DB_HDR * pCPDbHdr, FLMBOOL bIsCheckpoint); FINLINE char * getDbNamePtr( void) { return m_pszDbPath; } FINLINE XFLM_DB_HDR * getUncommittedDbHdr( void) { return &m_uncommittedDbHdr; } FINLINE FLMUINT getBlockSize( void) { return( m_uiBlockSize); } FINLINE FLMUINT getMaxFileSize( void) { return( m_uiMaxFileSize); } FINLINE FLMUINT getSigBitsInBlkSize( void) { return m_uiSigBitsInBlkSize; } FINLINE F_CachedBlock * getTransLogList( void) { return m_pTransLogList; } void releaseLogBlocks( void); FINLINE FLMUINT getDirtyCacheCount( void) { return m_uiDirtyCacheCount; } FINLINE void incrementDirtyCacheCount( void) { m_uiDirtyCacheCount++; } FINLINE void decrementDirtyCacheCount( void) { m_uiDirtyCacheCount--; } FLMBOOL neededByReadTrans( FLMUINT64 ui64LowTransId, FLMUINT64 ui64HighTransId); RCODE getBlock( F_Db * pDb, LFILE * pLFile, FLMUINT uiBlkAddress, FLMUINT * puiNumLooks, F_CachedBlock ** ppSCache); RCODE logPhysBlk( F_Db * pDb, F_CachedBlock ** ppSCache, F_CachedBlock ** ppOldCache = NULL); RCODE createBlock( F_Db * pDb, F_CachedBlock ** ppSCache); RCODE blockUseNextAvail( F_Db * pDb, F_CachedBlock ** ppSCache); RCODE blockFree( F_Db * pDb, F_CachedBlock * pSCache); RCODE moveBtreeBlk( F_Db * pDb, FLMUINT uiBlkAddr, FLMUINT uiLfNumber, eLFileType eLfType); RCODE freeAvailBlk( F_Db * pDb, FLMUINT uiBlkAddr); RCODE moveLFHBlk( F_Db * pDb, FLMUINT uiBlkAddr); void unlinkTransLogBlocks( void); void freeBlockCache( void); void freeNodeCache( void); void freeModifiedBlocks( FLMUINT64 ui64CurrTransId); void freeModifiedNodes( F_Db * pDb, FLMUINT64 ui64OlderTransId); void commitNodeCache( void); void getCPInfo( XFLM_CHECKPOINT_INFO * pCheckpointInfo); FINLINE FLMUINT getFlags( void) { return( m_uiFlags); } // NOTE: This routine expects that the global mutex is locked when // it is called. FINLINE void incrOpenCount( void) { m_uiOpenIFDbCount++; } // NOTE: This routine expects that the global mutex is locked when // it is called. It may temporarily unlock the mutex (when it // calls freeDatabase), but the mutex will be locked when it returns. FINLINE void decrOpenCount( void) { flmAssert( m_uiOpenIFDbCount); m_uiOpenIFDbCount--; if (!m_uiOpenIFDbCount) { freeDatabase(); } } RCODE startMaintThread( void); FINLINE FLMBOOL inLimitedMode() { return m_bInLimitedMode; } RCODE encryptBlock( F_Dict * pDict, FLMBYTE * pucBuffer); RCODE decryptBlock( F_Dict * pDict, FLMBYTE * pucBuffer); FINLINE void lockMutex( void) { f_mutexLock( m_hMutex); } FINLINE void unlockMutex( void) { f_mutexUnlock( m_hMutex); } private: void logMustCloseReason( const char * pszFileName, FLMINT iLineNumber); RCODE readDbHdr( const char * pszDbPath, XFLM_DB_STATS * pDbStats, FLMBYTE * pszPassword, FLMBOOL bAllowLimited); RCODE physOpen( F_Db * pDb, const char * pszFilePath, const char * pszRflDir, const char * pszPassword, FLMUINT uiOpenFlags, FLMBOOL bNewDatabase, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus); RCODE doRecover( F_Db * pDb, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus); RCODE outputNode( FLMBYTE * pucKeyBuf, F_DOMNode * pNode, FLMBOOL bAdd, F_Btree * pBTree); RCODE readTheBlock( F_Db * pDb, TMP_READ_STATS * pTmpReadStats, F_BLK_HDR * pBlkHdr, FLMUINT uiFilePos, FLMUINT uiBlkAddress); RCODE readBlock( F_Db * pDb, LFILE * pLFile, FLMUINT uiFilePos, FLMUINT uiBlkAddress, FLMUINT64 ui64NewerBlkLowTransID, F_CachedBlock * pSCache, FLMBOOL * pbFoundVerRV, FLMBOOL * pbDiscardRV); RCODE readIntoCache( F_Db * pDb, LFILE * pLFile, FLMUINT uiBlkAddress, F_CachedBlock * pPrevInVerList, F_CachedBlock * pNextInVerList, F_CachedBlock ** ppSCacheRV, FLMBOOL * pbGotFromDisk); void setBlkDirty( F_CachedBlock * pSCache); RCODE allocBlocksArray( FLMUINT uiNewSize, FLMBOOL bOneArray); RCODE flushLogBlocks( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMBOOL bIsCPThread, FLMUINT uiMaxDirtyCache, FLMBOOL * pbForceCheckpoint, FLMBOOL * pbWroteAll); RCODE reduceNewBlocks( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMUINT * puiBlocksFlushed); RCODE writeSortedBlocks( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMUINT uiMaxDirtyCache, FLMUINT * puiDirtyCacheLeft, FLMBOOL * pbForceCheckpoint, FLMBOOL bIsCPThread, FLMUINT uiNumSortedBlocks, FLMBOOL * pbWroteAll); RCODE flushDirtyBlocks( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMUINT uiMaxDirtyCache, FLMBOOL bForceCheckpoint, FLMBOOL bIsCPThread, FLMBOOL * pbWroteAll); RCODE reduceDirtyCache( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl); RCODE finishCheckpoint( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMBOOL bDoTruncate, FLMUINT uiCPFileNum, FLMUINT uiCPOffset, FLMUINT uiCPStartTime, FLMUINT uiTotalToWrite); RCODE doCheckpoint( F_SEM hWaitSem, XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, FLMBOOL bDoTruncate, FLMBOOL bForceCheckpoint, FLMINT iForceReason, FLMUINT uiCPFileNum, FLMUINT uiCPOffset); RCODE lgFlushLogBuffer( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl); RCODE lgOutputBlock( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, F_CachedBlock * pLogBlock, F_BLK_HDR * pBlkHdr, FLMUINT * puiLogEofRV); FLMUINT lFileFindEmpty( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr); RCODE lFileRead( F_Db * pDb, LFILE * pLFile, F_COLLECTION * pCollection); RCODE lFileWrite( F_Db * pDb, F_COLLECTION * pCollection, LFILE * pLFile); RCODE lFileCreate( F_Db * pDb, LFILE * pLFile, F_COLLECTION * pCollection, FLMUINT uiLfNum, eLFileType eLfType, FLMBOOL bCounts, FLMBOOL bHaveData, FLMUINT uiEncId = 0); RCODE startPendingInput( FLMUINT uiPendingType, F_CachedNode * pPendingNode); void endPendingInput( void); RCODE lFileDelete( F_Db * pDb, F_COLLECTION * pCollection, LFILE * pLFile, FLMBOOL bCounts, FLMBOOL bHaveData); static RCODE FLMAPI maintenanceThread( IF_Thread * pThread); F_Database * m_pNext; // Next F_Database structure in in name hash // bucket, dependent store hash // bucket, or avail list. F_Database * m_pPrev; // Previous F_Database structure in name hash // bucket or dependent store hash // bucket. F_Query * m_pFirstQuery; // First query currently linked to this DB F_Query * m_pLastQuery; // Last query currently linked to this DB FLMUINT m_uiBlockSize; // Block size for database FLMUINT m_uiDefaultLanguage; // Default language for database. FLMUINT m_uiMaxFileSize; // Maximum file size for the database. FLMUINT m_uiOpenIFDbCount; // Number of IF_Dbs currently using this // database. Does NOT count internal uses FLMBOOL m_bTempDb; // Is this a temporary database? If so, // minimize writing out to disk. F_Db * m_pFirstDb; // List of ALL F_Db's associated with // this database. char * m_pszDbPath; // Database file name. char * m_pszDataDir; // Path for data files. F_Database * m_pNextNUDatabase; // Next F_Database structure in list of // unused databases. When use count goes // to zero, the structure is linked // into a list of unused databases off of // the FSYSDATA structure. F_Database * m_pPrevNUDatabase; // Previous F_Database structure in list of // unused databases. F_CachedBlock * m_pSCacheList; // This is a pointer to a linked list // of all shared cache blocks // belonging to this database. F_CachedNode * m_pFirstNode; // First cached DOM node that belongs // to this database. Also points to // the first dirty DOM node, if any. F_CachedNode * m_pLastNode; // Last cached DOM node that belongs // to this database. F_CachedNode * m_pLastDirtyNode; // Last dirty DOM node that belongs // to this database. F_CachedBlock * m_pPendingWriteList; // This is a pointer to a linked list // of all shared cache blocks // that are in the pending-write state. F_CachedBlock * m_pLastDirtyBlk; // Pointer to last dirty block in the // list. F_CachedBlock * m_pFirstInLogList; // First block that needs to be logged F_CachedBlock * m_pLastInLogList; // Last block that needs to be logged FLMUINT m_uiLogListCount; // Number of items in the log list F_CachedBlock * m_pFirstInNewList; // First new block that is dirty F_CachedBlock * m_pLastInNewList; // Last new block that is dirty FLMUINT m_uiNewCount; // Number of items in new list FLMUINT m_uiDirtyCacheCount; // Number of dirty blocks FLMUINT m_uiLogCacheCount; // Log blocks needing to be written. F_CachedBlock ** m_ppBlocksDone; // List of blocks to be written to rollback // log or database. FLMUINT m_uiBlocksDoneArraySize;// Size of ppBlocksDone array. FLMUINT m_uiBlocksDone; // Number of blocks currently in the // ppBlocksDone array. F_CachedBlock * m_pTransLogList; // This is a pointer to a linked list // of all shared cache blocks // belonging to this database that need // to be logged to the rollback log // for the current transaction. F_NOTIFY_LIST_ITEM * m_pOpenNotifies; // Pointer to a list of notifies to // perform when this database is finally // opened (points to a linked list of // F_NOTIFY_LIST_ITEM structures). F_NOTIFY_LIST_ITEM * m_pCloseNotifies; // Pointer to a list of notifies to // perform when this database is finally // closed (points to a linked list of // F_NOTIFY_LIST_ITEM structures). F_Dict * m_pDictList; // Pointer to linked list of // dictionaries currently being used // for this database. The linked list // is a list of versions of the // dictionary. When a version is no // longer used, it is removed from the // list. Hence, the list is usually // has only one member. FLMBOOL m_bMustClose; // The database is being forced to close // because of a critical error. RCODE m_rcMustClose; // Return code that caused bMustClose to // be set. F_Pool m_krefPool; // Kref pool to be used during update // transactions. FLMUINT m_uiSigBitsInBlkSize;// Significant bits in the database's // block size. FLMUINT m_uiFileExtendSize; // Bytes to extend files by. F_Rfl * m_pRfl; // Pointer RFL object. XFLM_DB_HDR m_lastCommittedDbHdr;// This is the last committed DBheader. XFLM_DB_HDR m_checkpointDbHdr; // This is the DB header as of the start // of the last checkpoint. XFLM_DB_HDR m_uncommittedDbHdr; // This is the uncommitted DB header. // It is used by the current update // transaction. IF_IOBufferMgr * m_pBufferMgr; IF_IOBuffer * m_pCurrLogBuffer; FLMUINT m_uiCurrLogWriteOffset; // Offset in current write buffer FLMUINT m_uiCurrLogBlkAddr; // Address of first block in the current // buffer. XFLM_DB_HDR * m_pDbHdrWriteBuf; // Aligned buffer for writing the DB header. FLMBYTE * m_pucUpdBuffer; // Buffer for writing out records. FLMUINT m_uiUpdBufferSize; // Size of update buffer. FLMBYTE m_ucIV [16]; // Used when outputting encrypted data. F_CachedNode * m_pPendingInput; // Node that is having its value // set across multiple calls. F_Btree * m_pPendingBTree; // B-Tree used by node that is having // its value set across multiple calls FLMBOOL m_bUpdFirstBuf; FLMUINT m_uiUpdByteCount; FLMUINT m_uiUpdCharCount; FLMUINT m_uiPendingType; FLMBYTE * m_pucBTreeTmpBlk; // Temporary buffer for F_Btree object // to use during a call that updates // a B-Tree - btInsertEntry, // btReplaceEntry, etc. SHOULD NOT // be used in read/find operations! FLMBYTE * m_pucBTreeTmpDefragBlk; FLMBYTE * m_pucEntryArray; // Temporary buffer for F_Btree object // to use during update operations. FLMBYTE * m_pucSortedArray; // Temporary buffer for F_Btree object // to use during update operations. FLMBYTE * m_pucBtreeBuffer; // Buffer used by the Btree during moves // between blocks. FLMBYTE * m_pucReplaceStruct; // Buffer used by the Btree to hold additional // replace information during updates *only*. IF_LockObject * m_pDatabaseLockObj; // Object for locking the database. IF_LockObject * m_pWriteLockObj; // Object for locking to do writing. IF_FileHdl * m_pLockFileHdl; // Lock file handle. F_NOTIFY_LIST_ITEM * m_pLockNotifies; // Pointer to a list of notifies to // perform when this database is finally // locked (points to a linked list of // F_NOTIFY_LIST_ITEM structures). FLMBOOL m_bBeingLocked; // Flag indicating whether or not this // database is in the process of being // locked for exclusive access. F_Db * m_pFirstReadTrans; // Pointer to first read transaction for // this database. F_Db * m_pLastReadTrans; // Pointer to last read transaction for // this database. F_Db * m_pFirstKilledTrans; // List of read transactions that have // been killed. FLMUINT m_uiFirstLogBlkAddress; // Address of first block logged for the // current update transaction. FLMUINT m_uiFirstLogCPBlkAddress; // Address of first block logged for the // current checkpoint. FLMUINT m_uiLastCheckpointTime; // Last time we successfully completed a // checkpoint. IF_Thread * m_pCPThrd; // Checkpoint thread. CP_INFO * m_pCPInfo; // Pointer to checkpoint thread's // information buffer - used for // communicating information to the // checkpoint thread. RCODE m_CheckpointRc; // Return code from last checkpoint // that was attempted. FLMUINT m_uiBucket; // Hash bucket this database is in. // 0xFFFF means it is not currently // in a bucket. FLMUINT m_uiFlags; // Flags for this database. FLMBOOL m_bBackupActive; // Backup is currently being run against the // database. F_NodeList m_DocumentList; // List of documents modified in a transaction IF_Thread * m_pMaintThrd; // Background maintenance thread F_SEM m_hMaintSem; // Maintenance thread "work-to-do" semaphore FLMBYTE * m_pszDbPasswd; // The database encryption password IF_CCS * m_pWrappingKey; // The database wrapping key FLMBOOL m_bHaveEncKey; // FLMBOOL m_bAllowLimitedMode; // Is this database allowed to be opened in limited mode? FLMBOOL m_bInLimitedMode; // Has this database been opened in limited mode? RCODE m_rcLimitedCode; F_MUTEX m_hMutex; // Database mutex. friend class F_Db; friend class F_Rfl; friend class F_Btree; friend class F_Dict; friend class F_DbSystem; friend class F_Backup; friend class FFileItemId; friend class F_DOMNode; friend class F_BTreeIStream; friend class F_DbRebuild; friend class F_DbCheck; friend class F_Query; friend class F_BtResultSet; friend class F_BtRSFactory; friend class FSIndexCursor; friend class FSCollectionCursor; friend class F_CachedNode; friend class F_CachedBlock; friend class F_BlockCacheMgr; friend class F_NodeCacheMgr; friend class F_GlobalCacheMgr; friend class F_QueryResultSet; friend class F_BTreeInfo; friend class F_NodeRelocator; friend class F_NodeDataRelocator; friend class F_NodeListRelocator; friend class F_AttrItemRelocator; friend class F_AttrBufferRelocator; friend class F_BlockRelocator; }; typedef struct QueryHdrTag { F_Query * pQuery; QueryHdrTag * pNext; QueryHdrTag * pPrev; } QUERY_HDR; /*************************************************************************** Desc: This is the FLAIM Event Structure. It keeps track of a registered event callback function that has been registered for a particular event category. ***************************************************************************/ typedef struct F_Event { IF_EventClient * pEventClient; F_Event * pNext; F_Event * pPrev; } FEVENT; /*************************************************************************** Desc: This is the FLAIM Event Header Structure. It is the header for the list of events that have been registered for a particular event category. ***************************************************************************/ typedef struct F_Event_Hdr { FEVENT * pEventCBList; // List of registered event callbacks. F_MUTEX hMutex; // Mutex to control access to the // the event list. } FEVENT_HDR; typedef struct node_loc { FLMUINT32 ui32DatabaseId; FLMUINT32 ui32BlockAddr; FLMUINT32 ui32OffsetIndex; FLMUINT32 ui32Collection; FLMUINT64 ui64NodeId; node_loc * pPrevInBucket; node_loc * pNextInBucket; } F_NODE_LOCATION; /*************************************************************************** Desc: This is the FLAIM Shared System Data Structure. It is the anchor for all of the other shared structures. ***************************************************************************/ typedef struct FlmSystemData { F_BUCKET * pDatabaseHashTbl; // Database name hash table (array of FBUCKET). #define FILE_HASH_ENTRIES 256 F_MUTEX hShareMutex; // Mutex for controlling access to // various global items. F_MUTEX hNodeCacheMutex; // Mutex for controlling access to // node cache. F_MUTEX hBlockCacheMutex; // Mutex for controlling access to // block cache. FLMBOOL bTempDirSet; // TRUE if temporary directory has been set FLMBOOL bOkToDoAsyncWrites; // OK To do async writes, if available. FLMBOOL bOkToUseESM; // OK to use Extended Server Memory, // if available FLMUINT uiMaxCPInterval; // Maximum number of seconds to allow between // checkpoints F_GlobalCacheMgr * pGlobalCacheMgr; F_BlockCacheMgr * pBlockCacheMgr; F_NodeCacheMgr * pNodeCacheMgr; // Node cache manager FLMUINT uiRehashAfterFailureBackoffTime; // Amount of time to wait after trying // to reallocate a cache manager's hash // table before trying again. F_NodePool * pNodePool; // Pool of nodes that can be re-used IF_Thread * pMonitorThrd; // Monitor thread IF_Thread * pCacheCleanupThrd; XFLM_STATS Stats; // Statistics structure F_MUTEX hStatsMutex; // Mutex for statistics structure F_MUTEX hQueryMutex; // Mutex for managing query list QUERY_HDR * pNewestQuery; // Head of query list (newest) QUERY_HDR * pOldestQuery; // Tail of query list (oldest) FLMUINT uiQueryCnt; // Number of queries in the list FLMUINT uiMaxQueries; // Maximum number of queries to keep around FLMBOOL bNeedToUnsetMaxQueries; // When TRUE, indicates that a call to stop // statistics should also stop saving // queries. FLMBOOL bStatsInitialized; // Has statistics structure been // initialized? char szTempDir[ F_PATH_MAX_SIZE]; // Temporary working directory for // ResultSets, RecordCache // and other sub-systems that need // temporary files. This is aligned // on a 4-byte boundary FLMUINT uiMaxUnusedTime; // Maximum number of timer units to keep // unused structures in memory before // freeing them. FEVENT_HDR EventHdrs [XFLM_MAX_EVENT_CATEGORIES]; F_Pool * pKRefPool; // Memory Pool that is only used by // record updaters for key building FLMUINT uiMaxFileSize; IF_LoggerClient * pLogger; FLMUINT uiPendingLogMessages; F_MUTEX hLoggerMutex; #ifdef FLM_DEBUG // Variables for memory allocation tracking. FLMBOOL bTrackLeaks; FLMBOOL bLogLeaks; FLMBOOL bStackWalk; FLMBOOL bMemTrackingInitialized; FLMUINT uiInitThreadId; F_MUTEX hMemTrackingMutex; void ** ppvMemTrackingPtrs; FLMUINT uiMemTrackingPtrArraySize; FLMUINT uiMemNumPtrs; FLMUINT uiMemNextPtrSlotToUse; FLMUINT uiAllocCnt; #endif F_MUTEX hIniMutex; F_MUTEX hHttpSessionMutex; F_BtPool * pBtPool; IF_XML * pXml; IF_FileSystem * pFileSystem; IF_ThreadMgr * pThreadMgr; IF_FileHdlCache * pFileHdlCache; FLMUINT uiIndexingThreadGroup; FLMUINT uiDbThreadGroup; FLMUINT uiCheckpointThreadGroup; FLMUINT uiFileOpenFlags; FLMUINT uiFileCreateFlags; } FLMSYSDATA; #ifndef ALLOCATE_SYS_DATA extern FLMSYSDATA gv_XFlmSysData; #else #ifdef FLM_SOLARIS #pragma align 8 (gv_XFlmSysData) #endif FLMSYSDATA gv_XFlmSysData; #endif #if defined( FLM_WIN) || defined( FLM_NLM) || defined( FLM_LINUX) #pragma pack(pop) #else #pragma pack() #endif #endif // FSTRUCTS_H libxflaim-5.1.969/src/flopen.cpp0000644000175000017500000013332310511001742020020 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the F_DbSystem::openDb method. // // Tabs: 3 // // Copyright (c) 1990-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flopen.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define MAX_DIRTY_PERCENT 70 FSTATIC void flmFreeCPInfo( CP_INFO ** ppCPInfoRV); FSTATIC RCODE FLMAPI flmCPThread( IF_Thread * pThread); /*************************************************************************** Desc: Does most of the actual work of opening an existing database, but doesn't provide COM interfaces... ****************************************************************************/ RCODE F_DbSystem::openDb( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiOpenFlags, IF_Db ** ppDb) { RCODE rc = NE_XFLM_OK; *ppDb = NULL; if (!pszDbFileName || *pszDbFileName == 0) { rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); goto Exit; } // Open the file if (RC_BAD( rc = openDatabase( NULL, pszDbFileName, pszDataDir, pszRflDir, pszPassword, uiOpenFlags, FALSE, NULL, NULL, NULL, ppDb))) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: Constructor for F_Db object. ****************************************************************************/ F_Db::F_Db( FLMBOOL bInternalOpen) { m_pDatabase = NULL; m_pDict = NULL; m_pNextForDatabase = NULL; m_pPrevForDatabase = NULL; m_pvAppData = NULL; m_uiThreadId = 0; m_bMustClose = FALSE; m_pSFileHdl = NULL; m_uiFlags = bInternalOpen ? FDB_INTERNAL_OPEN : 0; m_uiTransCount = 0; m_eTransType = XFLM_NO_TRANS; m_AbortRc = NE_XFLM_OK; m_ui64CurrTransID = 0; m_uiFirstAvailBlkAddr = 0; m_uiLogicalEOF = 0; m_uiUpgradeCPFileNum = 0; m_uiUpgradeCPOffset = 0; m_uiTransEOF = 0; f_memset( &m_TransStartTime, 0, sizeof( m_TransStartTime)); m_bHadUpdOper = FALSE; m_uiBlkChangeCnt = 0; m_pIxdFixups = NULL; m_pNextReadTrans = NULL; m_pPrevReadTrans = NULL; m_uiInactiveTime = 0; m_uiKilledTime = 0; m_bItemStateUpdOk = FALSE; m_pDeleteStatus = NULL; m_pIxClient = NULL; m_pIxStatus = NULL; m_pCommitClient = NULL; m_pStats = NULL; m_pDbStats = NULL; m_pLFileStats = NULL; m_uiLFileAllocSeq = 0; f_memset( &m_Stats, 0, sizeof( m_Stats)); m_bStatsInitialized = TRUE; m_pIxStartList = NULL; m_pIxStopList = NULL; m_pCachedBTree = NULL; m_pKeyColl = NULL; m_pOldNodeList = NULL; m_uiDirtyNodeCount = 0; m_hWaitSem = F_SEM_NULL; m_bKrefSetup = FALSE; m_pKrefTbl = NULL; m_uiKrefTblSize = 0; m_uiKrefCount = 0; m_uiTotalKrefBytes = 0; m_pucKrefKeyBuf = NULL; m_pKrefPool = NULL; m_bReuseKrefPool = FALSE; m_bKrefCompoundKey = FALSE; m_pKrefReset = NULL; m_tmpKrefPool.poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE); m_tempPool.poolInit( XFLM_MAX_KEY_SIZE * 4); } /*************************************************************************** Desc: Allocates and initializes an F_Db object for a database which is to be opened or created. ****************************************************************************/ RCODE F_DbSystem::allocDb( F_Db ** ppDb, FLMBOOL bInternalOpen) { RCODE rc = NE_XFLM_OK; F_Db * pDb = NULL; *ppDb = NULL; // Allocate the F_Db object. if ((pDb = f_new F_Db( bInternalOpen)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = f_semCreate( &pDb->m_hWaitSem))) { goto Exit; } *ppDb = pDb; pDb = NULL; Exit: if (pDb) { pDb->Release(); } return( rc); } /**************************************************************************** Desc: This routine performs all of the necessary steps to complete a create or open of a database, including notifying other threads waiting for the open or create to complete. NOTE: If RC_BAD( rc), this routine will delete the F_Db object. ****************************************************************************/ void F_Db::completeOpenOrCreate( RCODE rc, FLMBOOL bNewDatabase ) { if (RC_OK( rc)) { // If this is a newly created F_Database, we need to notify any // threads waiting for the database to be created or opened that // the create or open is now complete. if (bNewDatabase) { f_mutexLock( gv_XFlmSysData.hShareMutex); m_pDatabase->newDatabaseFinish( NE_XFLM_OK); f_mutexUnlock( gv_XFlmSysData.hShareMutex); } } else { F_Database * pDatabase = m_pDatabase; // Temporarily increment the open count on the F_Database structure // so that it will NOT be freed when pDb is freed below. if (bNewDatabase) { f_mutexLock( gv_XFlmSysData.hShareMutex); pDatabase->m_uiOpenIFDbCount++; f_mutexUnlock( gv_XFlmSysData.hShareMutex); } // NOTE: Cannot access this F_Db object after this! // Must do this before potentially deleting the F_Database object // below, so that the F_Db object will unlink itself from // the F_Database object. Release(); // If we allocated the F_Database object, notify any // waiting threads. if (bNewDatabase) { f_mutexLock( gv_XFlmSysData.hShareMutex); // Decrement the use count to compensate for the increment // that occurred above. pDatabase->m_uiOpenIFDbCount--; // If this is a newly created F_Database, we need to notify any // threads waiting for the database to be created or opened that // the create or open is now complete. pDatabase->newDatabaseFinish( rc); pDatabase->freeDatabase(); f_mutexUnlock ( gv_XFlmSysData.hShareMutex); } } } /**************************************************************************** Desc: Returns the length of the base part of a database name. If the name ends with a '.' or ".db", this will not be included in the returned length. ****************************************************************************/ void flmGetDbBasePath( char * pszBaseDbName, const char * pszDbName, FLMUINT * puiBaseDbNameLen) { FLMUINT uiBaseLen = f_strlen( pszDbName); if( uiBaseLen <= 3 || f_stricmp( &pszDbName[ uiBaseLen - 3], ".db") != 0) { if( pszDbName[ uiBaseLen - 1] == '.') { uiBaseLen--; } } else { uiBaseLen -= 3; } f_memcpy( pszBaseDbName, pszDbName, uiBaseLen); pszBaseDbName[ uiBaseLen] = 0; if( puiBaseDbNameLen) { *puiBaseDbNameLen = uiBaseLen; } } /**************************************************************************** Desc: This routine will open a database, returning a pointer to an IF_Db object that can be used to access it. ****************************************************************************/ RCODE F_DbSystem::openDatabase( F_Database * pDatabase, const char * pszDbPath, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiOpenFlags, FLMBOOL bInternalOpen, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus, IF_FileHdl * pLockFileHdl, IF_Db ** ppDb) { RCODE rc = NE_XFLM_OK; FLMBOOL bNewDatabase = FALSE; FLMBOOL bMutexLocked = FALSE; F_Db * pDb = NULL; FLMBOOL bNeedToOpen = FALSE; // Allocate and initialize an F_Db object. if (RC_BAD( rc = allocDb( &pDb, bInternalOpen))) { goto Exit; } f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; // Look up the file using findDatabase to see if we already // have the file open. if (!pDatabase) { bNeedToOpen = TRUE; // May unlock and re-lock the global mutex. if (RC_BAD( rc = findDatabase( pszDbPath, pszDataDir, &pDatabase))) { goto Exit; } } if (pDatabase) { if( RC_BAD( rc = pDatabase->checkState( __FILE__, __LINE__))) { goto Exit; } } if (!pDatabase) { if (RC_BAD( rc = allocDatabase( pszDbPath, pszDataDir, FALSE, &pDatabase))) { goto Exit; } flmAssert( !pLockFileHdl); bNewDatabase = TRUE; } else if( pLockFileHdl) { flmAssert( pDatabase); flmAssert( !pDatabase->m_uiOpenIFDbCount); flmAssert( pDatabase->m_uiFlags & DBF_BEING_OPENED); pDatabase->m_pLockFileHdl = pLockFileHdl; // Set to NULL to prevent lock file from being released below pLockFileHdl = NULL; bNewDatabase = TRUE; bNeedToOpen = TRUE; } else { FLMBOOL bWaited = FALSE; flmAssert( !pLockFileHdl); if (RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited))) { goto Exit; } if (bWaited) { bNewDatabase = FALSE; bNeedToOpen = FALSE; } } // Link the F_Db object to the F_Database object. rc = pDb->linkToDatabase( pDatabase); f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; if (RC_BAD(rc)) { goto Exit; } (void)flmStatGetDb( &pDb->m_Stats, pDatabase, 0, &pDb->m_pDbStats, NULL, NULL); if (bNeedToOpen) { if (RC_BAD( rc = pDatabase->physOpen( pDb, pszDbPath, pszRflDir, pszPassword, uiOpenFlags, bNewDatabase, pRestoreObj, pRestoreStatus))) { goto Exit; } } // Start a checkpoint thread if( bNewDatabase && !(uiOpenFlags & XFLM_DONT_REDO_LOG)) { flmAssert( !pDatabase->m_pCPThrd); flmAssert( !pDatabase->m_pMaintThrd); if( RC_BAD( rc = pDatabase->startCPThread())) { goto Exit; } if( !(uiOpenFlags & XFLM_DONT_RESUME_THREADS)) { if( RC_BAD( rc = pDb->startBackgroundIndexing())) { goto Exit; } if( RC_BAD( rc = pDatabase->startMaintThread())) { goto Exit; } } } Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } if (pLockFileHdl) { pLockFileHdl->Release(); } if (pDb) { // completeOpenOrCreate will delete pDb if RC_BAD( rc) pDb->completeOpenOrCreate( rc, bNewDatabase); if (RC_BAD( rc)) { pDb = NULL; } } *ppDb = (IF_Db *)pDb; return( rc); } /**************************************************************************** Desc: This routine checks to see if it is OK for another F_Db to use an F_Database object. If so, it increments the database's use counter. NOTE: This routine assumes that the calling routine has locked the global mutex. ****************************************************************************/ RCODE F_Database::verifyOkToUse( FLMBOOL * pbWaited) { RCODE rc = NE_XFLM_OK; F_SEM hWaitSem = F_SEM_NULL; // Can't open the database if it is being closed by someone else. if (m_uiFlags & DBF_BEING_CLOSED) { rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); goto Exit; } // If the file is in the process of being opened by another // thread, wait for the open to complete. if (m_uiFlags & DBF_BEING_OPENED) { if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } *pbWaited = TRUE; if( RC_BAD( rc = f_notifyWait( gv_XFlmSysData.hShareMutex, hWaitSem, NULL,&m_pOpenNotifies))) { // If f_notifyWait returns a bad RC, assume that the other // thread will unlock and free the F_Database object. This // routine should only unlock the object if an error occurs at // some other point. goto Exit; } } else { *pbWaited = FALSE; } Exit: if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } return( rc); } /**************************************************************************** Desc: This routine obtains exclusive access to a database by creating a .lck file. FLAIM holds the .lck file open as long as the database is open. When the database is finally closed, it deletes the .lck file. This is only used for 3.x databases. ****************************************************************************/ RCODE flmCreateLckFile( const char * pszFilePath, IF_FileHdl ** ppLockFileHdlRV) { RCODE rc = NE_XFLM_OK; char szLockPath [F_PATH_MAX_SIZE]; char szDbBaseName [F_FILENAME_SIZE]; char * pszFileExt; IF_FileHdl * pLockFileHdl = NULL; char szFilePathStr[ F_PATH_MAX_SIZE]; if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( pszFilePath, szFilePathStr))) { goto Exit; } // Extract the 8.3 name and put a .lck extension on it to create // the full path for the .lck file. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( szFilePathStr, szLockPath, szDbBaseName))) { goto Exit; } pszFileExt = &szDbBaseName [0]; while ((*pszFileExt) && (*pszFileExt != '.')) pszFileExt++; f_strcpy( pszFileExt, ".lck"); if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( szLockPath, szDbBaseName))) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->createLockFile( szLockPath, &pLockFileHdl))) { goto Exit; } *ppLockFileHdlRV = (IF_FileHdl *)pLockFileHdl; pLockFileHdl = NULL; Exit: if (pLockFileHdl) { (void)pLockFileHdl->closeFile(); pLockFileHdl->Release(); pLockFileHdl = NULL; } return( rc); } /**************************************************************************** Desc: This routine obtains exclusive access to a database by creating a .lck file. FLAIM holds the .lck file open as long as the database is open. When the database is finally closed, it deletes the .lck file. This is only used for 3.x databases. ****************************************************************************/ RCODE F_Database::getExclAccess( const char * pszFilePath) { RCODE rc = NE_XFLM_OK; FLMBOOL bNotifyWaiters = FALSE; FLMBOOL bMutexLocked = FALSE; F_SEM hWaitSem = F_SEM_NULL; // If m_pLockFileHdl is non-NULL, it means that we currently // have the database locked with a lock file. There is no need to make // this test inside a mutex lock, because the lock file handle can only // be set to NULL when the use count goes to zero, meaning that the thread // that sets it to NULL will be the only thread accessing it. // However, it is possible that two or more threads will simultaneously // test m_pLockFileHdl and discover that it is NULL. In that case, // we allow one thread to proceed and attempt to get a lock on the database // while the other threads wait to be notified of the results of the // attempt to lock the database. if (m_pLockFileHdl) { goto Exit; } lockMutex(); bMutexLocked = TRUE; if (m_bBeingLocked) { // If the database is in the process of being locked by another // thread, wait for the lock to complete. NOTE: f_notifyWait will // re-lock the mutex before returning. if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } rc = f_notifyWait( m_hMutex, hWaitSem, NULL, &m_pLockNotifies); goto Exit; } // No other thread was attempting to lock the database, so // set this thread up to make the attempt. Other threads // coming in at this point will be required to wait and // be notified of the results. m_bBeingLocked = TRUE; bNotifyWaiters = TRUE; unlockMutex(); bMutexLocked = FALSE; if (RC_BAD( rc = flmCreateLckFile( pszFilePath, &m_pLockFileHdl))) { goto Exit; } Exit: if (bNotifyWaiters) { F_NOTIFY_LIST_ITEM * pNotify; F_SEM hSem; // Notify any thread waiting on the lock what its status is. if( !bMutexLocked) { lockMutex(); bMutexLocked = TRUE; } pNotify = m_pLockNotifies; while (pNotify) { *(pNotify->pRc) = rc; hSem = pNotify->hSem; pNotify = pNotify->pNext; f_semSignal( hSem); } m_bBeingLocked = FALSE; m_pLockNotifies = NULL; unlockMutex(); bMutexLocked = FALSE; } if( bMutexLocked) { unlockMutex(); } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } return( rc); } /**************************************************************************** Desc: This routine checks to see if it is OK for another FDB to use a file. If so, it increments the file's use counter. NOTE: This routine assumes that the global mutex is NOT locked. ****************************************************************************/ RCODE F_Database::physOpen( F_Db * pDb, const char * pszFilePath, const char * pszRflDir, const char * pszPassword, FLMUINT uiOpenFlags, FLMBOOL bNewDatabase, // Is this a new F_Database object? IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus) { RCODE rc = NE_XFLM_OK; // See if we need to read in the database header. If the database was // already open (bNewDatabase == FALSE), we don't need to again. if( bNewDatabase) { // Read in the database header. if (RC_BAD( rc = readDbHdr( pszFilePath, pDb->m_pDbStats, (FLMBYTE *)pszPassword, (uiOpenFlags & XFLM_ALLOW_LIMITED_MODE) ? TRUE : FALSE))) { goto Exit; } // Allocate the pRfl object. Could not do this until this point // because we need to have the version number, block size, etc. // setup in the database header. flmAssert( !m_pRfl); if ((m_pRfl = f_new F_Rfl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = m_pRfl->setup( this, pszRflDir))) { goto Exit; } } // We must have exclusive access. Create a lock file for that // purpose, if there is not already a lock file. if (!m_pLockFileHdl) { if (RC_BAD( rc = getExclAccess( pszFilePath))) { goto Exit; } } // Do a recovery to ensure a consistent database // state before going any further. The FO_DONT_REDO_LOG // flag is used ONLY by the VIEW program. if (bNewDatabase && !(uiOpenFlags & XFLM_DONT_REDO_LOG)) { if (RC_BAD( rc = doRecover( pDb, pRestoreObj, pRestoreStatus))) { goto Exit; } } Exit: if (RC_BAD( rc)) { (void)pDb->m_pSFileHdl->releaseFiles(); } return( rc); } /**************************************************************************** Desc: This routine finishes up after creating a new F_Database object. It will notify any other threads waiting for the operation to complete of the status of the operation. ****************************************************************************/ void F_Database::newDatabaseFinish( RCODE OpenRc) // Return code to send to other threads that are // waiting for the open to complete. { F_NOTIFY_LIST_ITEM * pNotify; F_SEM hSem; // Notify anyone waiting on the operation what its status is. pNotify = m_pOpenNotifies; while (pNotify) { *(pNotify->pRc) = OpenRc; hSem = pNotify->hSem; pNotify = pNotify->pNext; f_semSignal( hSem); } m_pOpenNotifies = NULL; m_uiFlags &= (~(DBF_BEING_OPENED)); } /**************************************************************************** Desc: This routine is used to see if a file is already in use somewhere. This is only called for files which are opened directly by the application. Notes: This routine assumes that the global mutex is locked, but it may unlock and re-lock the mutex if needed. ****************************************************************************/ RCODE F_DbSystem::findDatabase( const char * pszDbPath, const char * pszDataDir, F_Database ** ppDatabase) { RCODE rc = NE_XFLM_OK; F_BUCKET * pBucket; FLMUINT uiBucket; FLMBOOL bMutexLocked = TRUE; F_Database * pDatabase; char szDbPathStr1 [F_PATH_MAX_SIZE]; char szDbPathStr2 [F_PATH_MAX_SIZE]; F_SEM hWaitSem = F_SEM_NULL; *ppDatabase = NULL; // Normalize the path to a string before looking for it. // NOTE: On non-UNIX, non-WIN platforms, this will basically convert // the string to upper case. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( pszDbPath, szDbPathStr1))) { goto Exit; } Retry: *ppDatabase = NULL; if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } pBucket = gv_XFlmSysData.pDatabaseHashTbl; uiBucket = f_strHashBucket( szDbPathStr1, pBucket, FILE_HASH_ENTRIES); pDatabase = (F_Database *)pBucket [uiBucket].pFirstInBucket; while (pDatabase) { // Compare the strings. On non-Unix platforms we must use // f_stricmp, because case does not matter for file names // on those platforms. #ifdef FLM_UNIX if( f_strcmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) #else if( f_stricmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) #endif { // Make sure data paths match. if (pszDataDir && *pszDataDir) { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( pszDataDir, szDbPathStr2))) { goto Exit; } if (pDatabase->m_pszDataDir) { // f_stricmp must be used on non-unix platforms because file // names are case insensitive on those platforms. #ifdef FLM_UNIX if (f_strcmp( pDatabase->m_pszDataDir, szDbPathStr2) != 0) #else if (f_stricmp( pDatabase->m_pszDataDir, szDbPathStr2) != 0) #endif { rc = RC_SET( NE_XFLM_DATA_PATH_MISMATCH); goto Exit; } } else { rc = RC_SET( NE_XFLM_DATA_PATH_MISMATCH); goto Exit; } } else if (pDatabase->m_pszDataDir) { rc = RC_SET( NE_XFLM_DATA_PATH_MISMATCH); goto Exit; } *ppDatabase = pDatabase; break; } pDatabase = pDatabase->m_pNext; } if (*ppDatabase && pDatabase->m_uiFlags & DBF_BEING_CLOSED) { if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } // Put ourselves into the notify list and then re-try // the lookup when we wake up if (RC_BAD( rc = f_notifyWait( gv_XFlmSysData.hShareMutex, hWaitSem, NULL, &pDatabase->m_pCloseNotifies))) { goto Exit; } f_semDestroy( &hWaitSem); // The mutex will be locked at this point. Re-try the lookup. // IMPORTANT NOTE: pDatabase will have been destroyed by this // time. DO NOT use it for anything! goto Retry; } Exit: if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } // Make sure the global mutex is re-locked before exiting if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); } return( rc); } /**************************************************************************** Desc: Make sure a database is NOT open. If it is, return an error. ****************************************************************************/ RCODE F_DbSystem::checkDatabaseClosed( const char * pszDbName, const char * pszDataDir) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase; f_mutexLock( gv_XFlmSysData.hShareMutex); rc = findDatabase( pszDbName, pszDataDir, &pDatabase); f_mutexUnlock( gv_XFlmSysData.hShareMutex); if (RC_BAD( rc)) { goto Exit; } if (pDatabase) { rc = RC_SET( NE_XFLM_DATABASE_OPEN); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Constructor for F_Database object. ****************************************************************************/ F_Database::F_Database( FLMBOOL bTempDb) { m_krefPool.poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE * 8); m_pNext = NULL; m_pPrev = NULL; m_pFirstQuery = NULL; m_pLastQuery = NULL; m_uiBlockSize = 0; m_uiDefaultLanguage = 0; m_uiMaxFileSize = 0; m_uiOpenIFDbCount = 0; m_bTempDb = bTempDb; m_pFirstDb = NULL; m_pszDbPath = NULL; m_pszDataDir = NULL; m_pSCacheList = NULL; m_pFirstNode = NULL; m_pLastNode = NULL; m_pLastDirtyNode = NULL; m_pPendingWriteList = NULL; m_pLastDirtyBlk = NULL; m_pFirstInLogList = NULL; m_pLastInLogList = NULL; m_uiLogListCount = 0; m_pFirstInNewList = NULL; m_pLastInNewList = NULL; m_uiNewCount = 0; m_uiDirtyCacheCount = 0; m_uiLogCacheCount = 0; m_ppBlocksDone = NULL; m_uiBlocksDoneArraySize = 0; m_uiBlocksDone = 0; m_pTransLogList = NULL; m_pOpenNotifies = NULL; m_pCloseNotifies = NULL; m_pDictList = NULL; m_bMustClose = FALSE; m_rcMustClose = NE_XFLM_OK; m_uiSigBitsInBlkSize = 0; if (!bTempDb) { m_uiFileExtendSize = XFLM_DEFAULT_FILE_EXTEND_SIZE; } else { m_uiFileExtendSize = 65536; } m_pRfl = NULL; f_memset( &m_lastCommittedDbHdr, 0, sizeof( m_lastCommittedDbHdr)); f_memset( &m_checkpointDbHdr, 0, sizeof( m_checkpointDbHdr)); f_memset( &m_uncommittedDbHdr, 0, sizeof( m_uncommittedDbHdr)); m_pBufferMgr = NULL; m_pCurrLogBuffer = NULL; m_uiCurrLogWriteOffset = 0; m_uiCurrLogBlkAddr = 0; m_pDbHdrWriteBuf = NULL; m_pucUpdBuffer = NULL; m_uiUpdBufferSize = 0; m_uiUpdByteCount = 0; m_uiUpdCharCount = 0; m_uiPendingType = XFLM_NODATA_TYPE; m_pPendingInput = NULL; m_pPendingBTree = NULL; m_pucBTreeTmpBlk = NULL; m_pucBTreeTmpDefragBlk = NULL; m_pucEntryArray = NULL; m_pucSortedArray = NULL; m_pucBtreeBuffer = NULL; m_pucReplaceStruct = NULL; m_pDatabaseLockObj = NULL; m_pWriteLockObj = NULL; m_pLockFileHdl = NULL; m_pLockNotifies = NULL; m_bBeingLocked = FALSE; m_pFirstReadTrans = NULL; m_pLastReadTrans = NULL; m_pFirstKilledTrans = NULL; m_uiFirstLogBlkAddress = 0; m_uiFirstLogCPBlkAddress = 0; m_uiLastCheckpointTime = 0; m_pCPThrd = NULL; m_pCPInfo = NULL; m_CheckpointRc = NE_XFLM_OK; m_uiBucket = 0; m_uiFlags = 0; m_bBackupActive = FALSE; m_pMaintThrd = NULL; m_hMaintSem = F_SEM_NULL; m_bAllowLimitedMode = FALSE; m_bInLimitedMode = FALSE; m_pszDbPasswd = NULL; m_pWrappingKey = NULL; m_bHaveEncKey = FALSE; m_rcLimitedCode = NE_XFLM_OK; m_hMutex = F_MUTEX_NULL; } /**************************************************************************** Desc: This destructor frees all of the structures associated with an F_Database object. Whoever called this routine has already determined that it is safe to do so. Notes: The global mutex is assumed to be locked when entering the routine. It may be unlocked and re-locked before the routine exits, however. ****************************************************************************/ F_Database::~F_Database() { F_NOTIFY_LIST_ITEM * pCloseNotifies; F_Dict * pDict; F_Dict * pTmpDict; // At this point, the use count better be zero flmAssert( !m_uiOpenIFDbCount); // Shut down all background threads before shutting down the CP thread. shutdownDatabaseThreads(); if (m_pRfl) { m_pRfl->closeFile(); } // Shouldn't have any pending input at this point flmAssert( !m_pPendingInput); // At this point, the use count better be zero flmAssert( !m_uiOpenIFDbCount); // Unlock the mutex f_mutexUnlock( gv_XFlmSysData.hShareMutex); // Shut down the checkpoint thread if( m_pCPThrd) { m_pCPThrd->stopThread(); m_pCPThrd->Release(); m_pCPThrd = NULL; } // Unlink all of the F_Dict objects that are connected to the // database. lockMutex(); while (m_pDictList) { m_pDictList->unlinkFromDatabase(); } unlockMutex(); // Take the file out of its name hash bucket, if any. if (m_uiBucket != 0xFFFF) { f_mutexLock( gv_XFlmSysData.hShareMutex); if (m_pPrev) { m_pPrev->m_pNext = m_pNext; } else { gv_XFlmSysData.pDatabaseHashTbl[ m_uiBucket].pFirstInBucket = m_pNext; } if (m_pNext) { m_pNext->m_pPrev = m_pPrev; } m_uiBucket = 0xFFFF; // After this point, we should not need to keep the global mutex locked // because the F_Database is no longer visible to any thread to find in // the hash table. f_mutexUnlock( gv_XFlmSysData.hShareMutex); } // Shouldn't have any queries at this point. But we will be nice in case // we do and will unlink the queries from the list flmAssert( !m_pFirstQuery); while (m_pFirstQuery) { F_Query * pQuery = m_pFirstQuery; m_pFirstQuery = m_pFirstQuery->m_pNext; pQuery->m_pPrev = NULL; pQuery->m_pNext = NULL; pQuery->m_pDatabase = NULL; } // Free the RFL data, if any. if (m_pRfl) { m_pRfl->Release(); m_pRfl = NULL; } flmAssert( m_pOpenNotifies == NULL); m_pOpenNotifies = NULL; // Save pCloseNotifies -- we will notify any waiters once the // F_Database has been freed. pCloseNotifies = m_pCloseNotifies; // Free any dictionary usage structures associated with the database. pDict = m_pDictList; while (pDict) { pTmpDict = pDict; pDict = pDict->getNext(); pTmpDict->Release(); } m_pDictList = NULL; // Free any shared cache associated with the database. // IMPORTANT NOTE: // Cannot have the global mutex locked when these are called because // these routines lock the block cache mutex and the node cache mutex. // If both the global mutex and the block or node cache mutexes are to be // locked, the rule is that the block or node cache mutex must be locked // before locking the global mutex. This is because neededByReadTrans // will end up doing it in this order - when neededByReadTrans is called // either the block or node cache mutex is already locked, and it will // additionally lock the global mutex. Since that order is already // required, we cannot have anyone else attempting to lock the mutexes // in a different order. freeBlockCache(); freeNodeCache(); // Release the lock objects. if (m_pWriteLockObj) { m_pWriteLockObj->Release(); m_pWriteLockObj = NULL; } if (m_pDatabaseLockObj) { m_pDatabaseLockObj->Release(); m_pDatabaseLockObj = NULL; } // Close and delete the lock file. if (m_pLockFileHdl) { (void)m_pLockFileHdl->closeFile(); m_pLockFileHdl->Release(); m_pLockFileHdl = NULL; } // Free the write buffer managers. if (m_pBufferMgr) { m_pBufferMgr->Release(); m_pBufferMgr = NULL; } // Free the log header write buffer if (m_pDbHdrWriteBuf) { f_freeAlignedBuffer( (void **)&m_pDbHdrWriteBuf); } // Free the update buffer if (m_pucUpdBuffer) { f_free( &m_pucUpdBuffer); m_uiUpdBufferSize = 0; } m_krefPool.poolFree(); if (m_ppBlocksDone) { f_free( &m_ppBlocksDone); m_uiBlocksDoneArraySize = 0; } // Notify waiters that the F_Database is gone while (pCloseNotifies) { F_SEM hSem; *(pCloseNotifies->pRc) = NE_XFLM_OK; hSem = pCloseNotifies->hSem; pCloseNotifies = pCloseNotifies->pNext; f_semSignal( hSem); } f_free( &m_pszDbPath); // Encryption stuff if (m_pszDbPasswd) { f_free( &m_pszDbPasswd); } if (m_pWrappingKey) { m_pWrappingKey->Release(); m_pWrappingKey = NULL; } flmAssert( !m_pFirstNode && !m_pLastNode && !m_pLastDirtyNode); if (m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } // Global mutex is still expected to be locked at this point f_mutexLock( gv_XFlmSysData.hShareMutex); } /**************************************************************************** Desc: This frees an F_Database object. Note: The global mutex is assumed to be locked when entering the routine. It may be unlocked and re-locked during the destructor, however. For this reason, this routine should be called instead of directly deleting a database object - i.e., delete pDatabase. ****************************************************************************/ void F_Database::freeDatabase( void) { // See if another thread is in the process of freeing // this F_Database. It is possible for this to happen, since // the monitor thread may have selected this F_Database to be // freed because it has been unused for a period of time. // At the same time, a foreground thread could have called // FlmConfig to close all unused F_Databases. Since the // destructor for the F_Database object // may unlock and re-lock the mutex, there is a small window // of opportunity for both threads to try to free the same // F_Database. -- Therefore, we must do this check while the // mutex is still locked. if (m_uiFlags & DBF_BEING_CLOSED) { return; } // Set the DBF_BEING_CLOSED flag m_uiFlags |= DBF_BEING_CLOSED; Release(); } /**************************************************************************** Desc: This routine sets up a new F_Database object, allocating member variables, linking into lists, etc. NOTE: This routine assumes that the global mutex has already been locked. It may unlock it temporarily if there is an error, but will always relock it before exiting. ****************************************************************************/ RCODE F_Database::setupDatabase( const char * pszDbPath, const char * pszDataDir) { RCODE rc = NE_XFLM_OK; FLMUINT uiAllocLen; FLMUINT uiDbNameLen; FLMUINT uiDirNameLen; char szDbPathStr[ F_PATH_MAX_SIZE]; char szDataDirStr[ F_PATH_MAX_SIZE]; if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( pszDbPath, szDbPathStr))) { goto Exit; } uiDbNameLen = f_strlen( szDbPathStr) + 1; if( pszDataDir && *pszDataDir) { if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( pszDataDir, szDataDirStr))) { goto Exit; } uiDirNameLen = f_strlen( szDataDirStr) + 1; } else { szDataDirStr[0] = 0; uiDirNameLen = 0; } if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } uiAllocLen = (FLMUINT)(uiDbNameLen + uiDirNameLen); if (RC_BAD( rc = f_alloc( uiAllocLen, &m_pszDbPath))) { goto Exit; } // Allocate a buffer for writing the DB header // If we are a temporary database, there is no need // for this allocation. if (!m_bTempDb) { if( RC_BAD( rc = f_allocAlignedBuffer( XFLM_MAX_BLOCK_SIZE, (void **)&m_pDbHdrWriteBuf))) { goto Exit; } } // Setup the write buffer managers. if( RC_BAD( rc = FlmAllocIOBufferMgr( MAX_PENDING_WRITES, MAX_WRITE_BUFFER_BYTES, FALSE, &m_pBufferMgr))) { goto Exit; } // Initialize members of F_Database object. m_uiBucket = 0xFFFF; m_uiFlags = DBF_BEING_OPENED; // Copy the database name and directory. // NOTE: uiDbNameLen includes the null terminating byte. // and uiDirNameLen includes the null terminating byte. f_memcpy( m_pszDbPath, szDbPathStr, uiDbNameLen); if (uiDirNameLen) { m_pszDataDir = m_pszDbPath + uiDbNameLen; f_memcpy( m_pszDataDir, szDataDirStr, uiDirNameLen); } // Link the file into the various lists it needs to be linked into. if (RC_BAD( rc = linkToBucket())) { goto Exit; } // Allocate a lock object for write locking. if( RC_BAD( rc = FlmAllocLockObject( &m_pWriteLockObj))) { goto Exit; } // Allocate a lock object for file locking. if( RC_BAD( rc = FlmAllocLockObject( &m_pDatabaseLockObj))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: This routine allocates a new F_Database object and links it into its hash buckets. NOTE: This routine assumes that the global mutex has already been locked. It may unlock it temporarily if there is an error, but will always relock it before exiting. ****************************************************************************/ RCODE F_DbSystem::allocDatabase( const char * pszDbPath, const char * pszDataDir, FLMBOOL bTempDb, F_Database ** ppDatabase) { RCODE rc = NE_XFLM_OK; F_Database * pDatabase = NULL; if ((pDatabase = f_new F_Database( bTempDb)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = pDatabase->setupDatabase( pszDbPath, pszDataDir))) { goto Exit; } *ppDatabase = pDatabase; Exit: if (RC_BAD( rc)) { if (pDatabase) { pDatabase->freeDatabase(); } } return( rc); } /*************************************************************************** Desc: This routine reads the header information for an existing flaim database and makes sure we have a valid database. *****************************************************************************/ RCODE F_Database::readDbHdr( const char * pszDbPath, XFLM_DB_STATS * pDbStats, FLMBYTE * pszPassword, FLMBOOL bAllowLimited) { RCODE rc = NE_XFLM_OK; IF_FileHdl * pFileHdl = NULL; if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( pszDbPath, gv_XFlmSysData.uiFileOpenFlags, &pFileHdl))) { goto Exit; } // Read and verify the database header. if (RC_BAD( rc = flmReadAndVerifyHdrInfo( pDbStats, pFileHdl, &m_lastCommittedDbHdr))) { goto Exit; } m_uiBlockSize = (FLMUINT)m_lastCommittedDbHdr.ui16BlockSize; m_uiDefaultLanguage = (FLMUINT)m_lastCommittedDbHdr.ui8DefaultLanguage; m_uiMaxFileSize = (FLMUINT)m_lastCommittedDbHdr.ui32MaxFileSize; m_uiSigBitsInBlkSize = calcSigBits( m_uiBlockSize); // Initialize the master database key from the database header m_bAllowLimitedMode = bAllowLimited; if (pszPassword && *pszPassword) { if (m_pszDbPasswd) { f_free( &m_pszDbPasswd); } if ( RC_BAD( rc = f_alloc( (f_strlen( (const char *)pszPassword) + 1), &m_pszDbPasswd))) { goto Exit; } f_strcpy( (char *)m_pszDbPasswd, (const char *)pszPassword); } if( RC_BAD( rc = flmAllocCCS( &m_pWrappingKey))) { goto Exit; } if( RC_OK( rc = m_pWrappingKey->init( TRUE, FLM_NICI_AES))) { // If the key was encrypted in a password, then the pszPassword parameter better // be the key used to encrypt it. If the key was not encrypted in a password, // then pszPassword parameter should be NULL. rc = m_pWrappingKey->setKeyFromStore( m_lastCommittedDbHdr.DbKey, pszPassword, NULL); } if( RC_BAD( rc)) { if ((rc == NE_XFLM_ENCRYPTION_UNAVAILABLE) || bAllowLimited) { m_bInLimitedMode = TRUE; rc = NE_XFLM_OK; } else { goto Exit; } } // Note that we might still end up in limited mode if we can't verify all the keys // that are stored in the dictionary. Exit: if( pFileHdl) { pFileHdl->Release(); } return( rc); } /*************************************************************************** Desc: This routine frees a CP_INFO structure and all associated data. *****************************************************************************/ FSTATIC void flmFreeCPInfo( CP_INFO ** ppCPInfoRV) { CP_INFO * pCPInfo; if ((pCPInfo = *ppCPInfoRV) != NULL) { if (pCPInfo->pSFileHdl) { pCPInfo->pSFileHdl->Release(); } if (pCPInfo->bStatsInitialized) { flmStatFree( &pCPInfo->Stats); } if( pCPInfo->hWaitSem != F_SEM_NULL) { f_semDestroy( &pCPInfo->hWaitSem); } f_free( ppCPInfoRV); } } /*************************************************************************** Desc: This routine begins a thread that will do checkpoints for the passed in database. It gives the thread its own FLAIM session and its own handle to the database. *****************************************************************************/ RCODE F_Database::startCPThread( void) { RCODE rc = NE_XFLM_OK; CP_INFO * pCPInfo = NULL; char szThreadName[ F_PATH_MAX_SIZE]; char szBaseName[ 32]; F_SuperFileClient * pSFileClient = NULL; // Allocate a CP_INFO structure that will be passed into the // thread when it is created. if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( CP_INFO)), &pCPInfo))) { goto Exit; } pCPInfo->pDatabase = this; // Create a "wait" semaphore if( RC_BAD( rc = f_semCreate( &pCPInfo->hWaitSem))) { goto Exit; } // Allocate a super file handle. if( (pCPInfo->pSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( (pSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pSFileClient->setup( m_pszDbPath, m_pszDataDir, m_uiMaxFileSize))) { goto Exit; } // Set up the super file if( RC_BAD( rc = pCPInfo->pSFileHdl->setup( pSFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { goto Exit; } f_memset( &pCPInfo->Stats, 0, sizeof( XFLM_STATS)); pCPInfo->bStatsInitialized = TRUE; // Generate the thread name if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( m_pszDbPath, szThreadName, szBaseName))) { goto Exit; } f_sprintf( (char *)szThreadName, "Checkpoint (%s)", (char *)szBaseName); // Start the checkpoint thread. if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->createThread( &m_pCPThrd, flmCPThread, szThreadName, gv_XFlmSysData.uiCheckpointThreadGroup, 0, pCPInfo, NULL, 32000))) { goto Exit; } m_pCPInfo = pCPInfo; pCPInfo = NULL; Exit: if( pCPInfo) { flmFreeCPInfo( &pCPInfo); } if( pSFileClient) { pSFileClient->Release(); } return( rc); } /**************************************************************************** Desc: Try to perform a checkpoint on the database. Returns TRUE if we need to terminate. ****************************************************************************/ FLMBOOL F_Database::tryCheckpoint( IF_Thread * pThread, CP_INFO * pCPInfo) { RCODE rc = NE_XFLM_OK; FLMBOOL bTerminate = FALSE; FLMBOOL bForceCheckpoint; FLMINT iForceReason; FLMUINT uiCurrTime; XFLM_DB_STATS * pDbStats; // See if we should terminate the thread. if (pThread->getShutdownFlag()) { // Set terminate flag to TRUE and then see if // we have been set up to do one final checkpoint // to flush dirty buffers to disk. bTerminate = TRUE; } // Determine if we need to force a checkpoint. bForceCheckpoint = FALSE; iForceReason = 0; uiCurrTime = (FLMUINT)FLM_GET_TIMER(); if (bTerminate) { bForceCheckpoint = TRUE; iForceReason = XFLM_CP_SHUTTING_DOWN_REASON; } else if (!m_pRfl->seeIfRflVolumeOk() || RC_BAD( m_CheckpointRc)) { bForceCheckpoint = TRUE; iForceReason = XFLM_CP_RFL_VOLUME_PROBLEM; } else if ((FLM_ELAPSED_TIME( uiCurrTime, m_uiLastCheckpointTime) >= gv_XFlmSysData.uiMaxCPInterval) || (!gv_XFlmSysData.uiMaxCPInterval)) { bForceCheckpoint = TRUE; iForceReason = XFLM_CP_TIME_INTERVAL_REASON; } if (gv_XFlmSysData.Stats.bCollectingStats) { // Statistics are being collected for the system. Therefore, // if we are not currently collecting statistics in the // start. If we were collecting statistics, but the // start time was earlier than the start time in the system // statistics structure, reset the statistics. if (!pCPInfo->Stats.bCollectingStats) { flmStatStart( &pCPInfo->Stats); } else if (pCPInfo->Stats.uiStartTime < gv_XFlmSysData.Stats.uiStartTime) { flmStatReset( &pCPInfo->Stats, FALSE); } (void)flmStatGetDb( &pCPInfo->Stats, this, 0, &pDbStats, NULL, NULL); } else { pDbStats = NULL; } // Lock write object - If we are forcing a checkpoint // wait until we get the lock. Otherwise, if we can't get // the lock without waiting, don't do anything. if (bForceCheckpoint || (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache && (m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize > gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache)) { if (RC_BAD( rc = dbWriteLock( pCPInfo->hWaitSem, pDbStats))) { // THIS SHOULD NEVER HAPPEN BECAUSE dbWriteLock will // wait forever for the lock! RC_UNEXPECTED_ASSERT( rc); goto Exit; } pThread->setThreadStatusStr( "Forcing checkpoint"); // Must wait for any RFL writes to complete. (void)m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, TRUE); } else { if (RC_BAD( dbWriteLock( pCPInfo->hWaitSem, pDbStats, 0))) { goto Exit; } pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); // See if we actually need to do the checkpoint. If the // current transaction ID and the last checkpoint transaction // ID are the same, no updates have occurred that would require // a checkpoint to take place. if (m_lastCommittedDbHdr.ui64RflLastCPTransID == m_lastCommittedDbHdr.ui64CurrTransID || !m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, FALSE)) { dbWriteUnlock(); goto Exit; } } // Do the checkpoint. (void)doCheckpoint( pCPInfo->hWaitSem, pDbStats, pCPInfo->pSFileHdl, FALSE, bForceCheckpoint, iForceReason, 0, 0); if (pDbStats) { (void)flmStatUpdate( &pCPInfo->Stats); } dbWriteUnlock(); // Set the thread's status pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING); Exit: return( bTerminate); } /**************************************************************************** Desc: This routine functions as a thread. It monitors open files and frees up files which have been closed longer than the maximum close time. ****************************************************************************/ FSTATIC RCODE FLMAPI flmCPThread( IF_Thread * pThread) { CP_INFO * pCPInfo = (CP_INFO *)pThread->getParm1(); F_Database * pDatabase = pCPInfo->pDatabase; pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING); for (;;) { f_sleep( 1000); if (pDatabase->tryCheckpoint( pThread, pCPInfo)) { break; } } pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); flmFreeCPInfo( &pCPInfo); return( NE_XFLM_OK); } /**************************************************************************** Desc: Recover a database on startup. ****************************************************************************/ RCODE F_Database::doRecover( F_Db * pDb, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus) { RCODE rc = NE_XFLM_OK; XFLM_DB_HDR * pLastCommittedDbHdr; // At this point, m_lastCommittedDbHdr contains the header // that was read from disk, which will be the state of the // header as of the last completed checkpoint. Therefore, // we copy it into m_checkpointDbHdr. pLastCommittedDbHdr = &m_lastCommittedDbHdr; f_memcpy( &m_checkpointDbHdr, pLastCommittedDbHdr, sizeof( XFLM_DB_HDR)); // Do a physical rollback on the database to restore the last // checkpoint. if (RC_BAD( rc = pDb->physRollback( (FLMUINT)pLastCommittedDbHdr->ui32RblEOF, (FLMUINT)pLastCommittedDbHdr->ui32RblFirstCPBlkAddr, TRUE, pLastCommittedDbHdr->ui64RflLastCPTransID))) { goto Exit; } pLastCommittedDbHdr->ui32RblFirstCPBlkAddr = 0; pLastCommittedDbHdr->ui32RblEOF = (FLMUINT32)m_uiBlockSize; if (RC_BAD( rc = writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, pLastCommittedDbHdr, &m_checkpointDbHdr, TRUE))) { goto Exit; } // Set uiFirstLogCPBlkAddress to zero to indicate that no // physical blocks have been logged for the current checkpoint. // The above call to flmPhysRollback will have set the log header // to the same thing. m_uiFirstLogCPBlkAddress = 0; // Set the checkpointDbHdr to be the same as the log header f_memcpy( &m_checkpointDbHdr, pLastCommittedDbHdr, sizeof( XFLM_DB_HDR)); // Open roll forward log and redo the transactions that // occurred since the last checkpoint, if any. if( RC_BAD( rc = m_pRfl->recover( pDb, pRestoreObj, pRestoreStatus))) { goto Exit; } Exit: return( rc); } libxflaim-5.1.969/src/ffilehdr.cpp0000644000175000017500000003051010511001742020312 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines for accessing information in the database header. // // Tabs: 3 // // Copyright (c) 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: ffilehdr.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE verifyDbHdr( XFLM_DB_HDR * pDbHdr); /******************************************************************** Desc: This routine adjusts the block size that is passe in (wBlkSize) to the nearest valid block size. *********************************************************************/ FLMUINT flmAdjustBlkSize( FLMUINT uiBlkSize) { FLMUINT uiTmpBlkSize; uiTmpBlkSize = XFLM_MIN_BLOCK_SIZE; while (uiBlkSize > uiTmpBlkSize && uiTmpBlkSize < XFLM_MAX_BLOCK_SIZE) { uiTmpBlkSize <<= 1; } return( uiTmpBlkSize); } /******************************************************************** Desc: This routine initializes a XFLM_DB_HDR structure. *********************************************************************/ void flmInitDbHdr( XFLM_CREATE_OPTS * pCreateOpts, FLMBOOL bCreatingDatabase, FLMBOOL bTempDb, XFLM_DB_HDR * pDbHdr) { FLMUINT uiMinRflFileSize; FLMUINT uiMaxRflFileSize; if (bCreatingDatabase) { f_memset( pDbHdr, 0, sizeof( XFLM_DB_HDR)); } // If pCreateOpts is non-NULL, copy it into the file header. f_strcpy( (char *)pDbHdr->szSignature, XFLM_DB_SIGNATURE); pDbHdr->ui8IsLittleEndian = XFLM_NATIVE_IS_LITTLE_ENDIAN; if (pCreateOpts) { pDbHdr->ui16BlockSize = (FLMUINT16)pCreateOpts->ui32BlockSize; pDbHdr->ui8DefaultLanguage = (FLMUINT8)pCreateOpts->ui32DefaultLanguage; if (pCreateOpts->bKeepRflFiles) { pDbHdr->ui8RflKeepFiles = 1; } if (pCreateOpts->bLogAbortedTransToRfl) { pDbHdr->ui8RflKeepAbortedTrans = 1; } if( (uiMinRflFileSize = (FLMUINT)pCreateOpts->ui32MinRflFileSize) == 0) { uiMinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; } if( (uiMaxRflFileSize = (FLMUINT)pCreateOpts->ui32MaxRflFileSize) == 0) { uiMaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; } } else { // If pCreateOpts is NULL, initialize some default values. pDbHdr->ui16BlockSize = XFLM_DEFAULT_BLKSIZ; pDbHdr->ui8DefaultLanguage = XFLM_DEFAULT_LANG; uiMinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; uiMaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; } // Make sure the RFL size limits are valid. // Maximum must be enough to hold at least one packet plus // the RFL header. Minimum must not be greater than the // maximum. NOTE: Minimum and maximum are allowed to be // equal, but in all cases, maximum takes precedence over // minimum. We will first NOT exceed the maximum. Then, // if possible, we will go above the minimum. if (uiMaxRflFileSize < RFL_MAX_PACKET_SIZE + 512) { uiMaxRflFileSize = RFL_MAX_PACKET_SIZE + 512; } if (uiMaxRflFileSize > gv_XFlmSysData.uiMaxFileSize) { uiMaxRflFileSize = gv_XFlmSysData.uiMaxFileSize; } if (uiMinRflFileSize > uiMaxRflFileSize) { uiMinRflFileSize = uiMaxRflFileSize; } pDbHdr->ui32RflMinFileSize = (FLMUINT32)uiMinRflFileSize; pDbHdr->ui32RflMaxFileSize = (FLMUINT32)uiMaxRflFileSize; // Only allow database to be created with current version number pDbHdr->ui32DbVersion = XFLM_CURRENT_VERSION_NUM; pDbHdr->ui8BlkChkSummingEnabled = 1; // Round block size up to nearest legal block size. pDbHdr->ui16BlockSize = (FLMUINT16)flmAdjustBlkSize( (FLMUINT)pDbHdr->ui16BlockSize); if (!bTempDb) { pDbHdr->ui32FirstLFBlkAddr = (FLMUINT32)FSBlkAddress(1, 0); } // If creating a database, initialize some more items. if (bCreatingDatabase) { // Set the logical EOF. if (!bTempDb) { pDbHdr->ui32LogicalEOF = pDbHdr->ui32FirstLFBlkAddr + (FLMUINT32)pDbHdr->ui16BlockSize; } else { pDbHdr->ui32LogicalEOF = (FLMUINT32)FSBlkAddress(1, 0); } pDbHdr->ui64CurrTransID = (FLMUINT64)0; pDbHdr->ui32RflCurrFileNum = 1; // Putting a zero in this value tells the RFL code that the // RFL file should be created - overwriting it if it already // exists. pDbHdr->ui32RflLastCPFileNum = 1; pDbHdr->ui32RflLastCPOffset = 512; pDbHdr->ui32RblEOF = pDbHdr->ui16BlockSize; // Set the database serial number f_createSerialNumber( pDbHdr->ucDbSerialNum); // Set the "current" RFL serial number - will be stamped into the RFL // file when it is first created. f_createSerialNumber( pDbHdr->ucLastTransRflSerialNum); // Set the "next" RFL serial number f_createSerialNumber( pDbHdr->ucNextRflSerialNum); // Set the incremental backup serial number and sequence number f_createSerialNumber( pDbHdr->ucIncBackupSerialNum); pDbHdr->ui32IncBackupSeqNum = 1; // Set the file size limits pDbHdr->ui32MaxFileSize = (FLMUINT32)gv_XFlmSysData.uiMaxFileSize; // Need at least two blocks to create a database! flmAssert( (FLMUINT)pDbHdr->ui32MaxFileSize >= (FLMUINT)pDbHdr->ui16BlockSize * 2); } } /*************************************************************************** Desc: This routine changes the endian-ness of the passed in 64 bit number. If it was big-endian, it will be converted to little-endian and vice versa. *****************************************************************************/ void convert64( FLMUINT64 * pui64Num ) { FLMBYTE * pucTmp = (FLMBYTE *)pui64Num; FLMBYTE ucTmp; // Swap bytes 0 and 7 ucTmp = pucTmp [0]; pucTmp [0] = pucTmp [7]; pucTmp [7] = ucTmp; // Swap bytes 1 and 6 ucTmp = pucTmp [1]; pucTmp [1] = pucTmp [6]; pucTmp [6] = ucTmp; // Swap bytes 2 and 5 ucTmp = pucTmp [2]; pucTmp [2] = pucTmp [5]; pucTmp [5] = ucTmp; // Swap bytes 3 and 4 ucTmp = pucTmp [3]; pucTmp [3] = pucTmp [4]; pucTmp [4] = ucTmp; } /*************************************************************************** Desc: This routine changes the endian-ness of the passed in 32 bit number. If it was big-endian, it will be converted to little-endian and vice versa. *****************************************************************************/ void convert32( FLMUINT32 * pui32Num ) { FLMBYTE * pucTmp = (FLMBYTE *)pui32Num; FLMBYTE ucTmp; // Swap bytes 0 and 3 ucTmp = pucTmp [0]; pucTmp [0] = pucTmp [3]; pucTmp [3] = ucTmp; // Swap bytes 1 and 2 ucTmp = pucTmp [1]; pucTmp [1] = pucTmp [2]; pucTmp [2] = ucTmp; } /*************************************************************************** Desc: This routine changes the endian-ness of the passed in 16 bit number. If it was big-endian, it will be converted to little-endian and vice versa. *****************************************************************************/ void convert16( FLMUINT16 * pui16Num ) { FLMBYTE * pucTmp = (FLMBYTE *)pui16Num; FLMBYTE ucTmp; // Swap bytes 0 and 1 ucTmp = pucTmp [0]; pucTmp [0] = pucTmp [1]; pucTmp [1] = ucTmp; } /*************************************************************************** Desc: This routine changed a database header to native platform format. *****************************************************************************/ void convertDbHdr( XFLM_DB_HDR * pDbHdr ) { // This routine should only be called to convert a header to native // format. flmAssert( hdrIsNonNativeFormat( pDbHdr)); convert16( &pDbHdr->ui16BlockSize); convert32( &pDbHdr->ui32DbVersion); convert64( &pDbHdr->ui64LastRflCommitID); convert32( &pDbHdr->ui32RflLastFileNumDeleted); convert32( &pDbHdr->ui32RflCurrFileNum); convert32( &pDbHdr->ui32RflLastTransOffset); convert32( &pDbHdr->ui32RflLastCPFileNum); convert32( &pDbHdr->ui32RflLastCPOffset); convert64( &pDbHdr->ui64RflLastCPTransID); convert32( &pDbHdr->ui32RflMinFileSize); convert32( &pDbHdr->ui32RflMaxFileSize); convert64( &pDbHdr->ui64CurrTransID); convert64( &pDbHdr->ui64TransCommitCnt); convert32( &pDbHdr->ui32RblEOF); convert32( &pDbHdr->ui32RblFirstCPBlkAddr); convert32( &pDbHdr->ui32FirstAvailBlkAddr); convert32( &pDbHdr->ui32FirstLFBlkAddr); convert32( &pDbHdr->ui32LogicalEOF); convert32( &pDbHdr->ui32MaxFileSize); convert64( &pDbHdr->ui64LastBackupTransID); convert32( &pDbHdr->ui32IncBackupSeqNum); convert32( &pDbHdr->ui32BlksChangedSinceBackup); convert32( &pDbHdr->ui32HdrCRC); pDbHdr->ui8IsLittleEndian = XFLM_NATIVE_IS_LITTLE_ENDIAN; } /*************************************************************************** Desc: This routine verifies that the header is a real FLAIM header. It will also change the endian-ness if need be. This should always be called immediately after reading a header from disk. *****************************************************************************/ FSTATIC RCODE verifyDbHdr( XFLM_DB_HDR * pDbHdr ) { RCODE rc = NE_XFLM_OK; FLMUINT uiLen; FLMUINT32 ui32CRC; // Calculate the checksum before doing any conversions. ui32CRC = calcDbHdrCRC( pDbHdr); // Convert the header to native platform format if necessary. if (hdrIsNonNativeFormat( pDbHdr)) { convertDbHdr( pDbHdr); } // Check the signature. uiLen = f_strlen( XFLM_DB_SIGNATURE); if (f_memcmp( pDbHdr->szSignature, XFLM_DB_SIGNATURE, uiLen) != 0) { rc = RC_SET( NE_XFLM_NOT_FLAIM); goto Exit; } // See if the database version is OK. switch (pDbHdr->ui32DbVersion) { case XFLM_VER_5_12: break; default: if (pDbHdr->ui32DbVersion > XFLM_CURRENT_VERSION_NUM) { rc = RC_SET( NE_XFLM_NEWER_FLAIM); } else { rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); } goto Exit; } // Validate the checksum if (ui32CRC != pDbHdr->ui32HdrCRC) { rc = RC_SET( NE_XFLM_HDR_CRC); goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: This routine reads the header information in a FLAIM database, verifies the password, and returns the file header and log header information. *****************************************************************************/ RCODE flmGetHdrInfo( F_SuperFileHdl * pSFileHdl, XFLM_DB_HDR * pDbHdr, FLMUINT32 * pui32CalcCRC) { RCODE rc = NE_XFLM_OK; IF_FileHdl * pCFileHdl = NULL; if( RC_BAD( rc = pSFileHdl->getFileHdl( 0, FALSE, &pCFileHdl))) { goto Exit; } if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pCFileHdl, pDbHdr, pui32CalcCRC))) { goto Exit; } Exit: if( pCFileHdl) { pCFileHdl->Release(); } return( rc); } /*************************************************************************** Desc: This routine reads and verifies the information contained in the file header and log header of a FLAIM database. *****************************************************************************/ RCODE flmReadAndVerifyHdrInfo( XFLM_DB_STATS * pDbStats, IF_FileHdl * pFileHdl, XFLM_DB_HDR * pDbHdr, FLMUINT32 * pui32CalcCRC) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesRead; // Read the database header. f_memset( pDbHdr, 0, sizeof( XFLM_DB_HDR)); if (RC_BAD( rc = pFileHdl->read( (FLMUINT)0, sizeof( XFLM_DB_HDR), pDbHdr, &uiBytesRead))) { if (rc != NE_FLM_IO_END_OF_FILE) { if (pDbStats) { pDbStats->uiReadErrors++; } } else { if (pui32CalcCRC) { *pui32CalcCRC = calcDbHdrCRC( pDbHdr); } // Get what we can out of the header. if (hdrIsNonNativeFormat( pDbHdr)) { convertDbHdr( pDbHdr); } } goto Exit; } if (pui32CalcCRC) { *pui32CalcCRC = calcDbHdrCRC( pDbHdr); } if (uiBytesRead < sizeof( XFLM_DB_HDR)) { // Still get what we can out of the header. if (hdrIsNonNativeFormat( pDbHdr)) { convertDbHdr( pDbHdr); } rc = RC_SET( NE_XFLM_NOT_FLAIM); goto Exit; } else { // This routine will convert to native format if it is not // in native format. if (RC_BAD( rc = verifyDbHdr( pDbHdr))) { goto Exit; } } Exit: return( rc); } libxflaim-5.1.969/src/filesys.h0000644000175000017500000003026310511001742017657 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Various macros, prototypes, structures. // // Tabs: 3 // // Copyright (c) 1990-1993, 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: filesys.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FILESYS_H #define FILESYS_H /*************************************************************** ** ** Defined Constants that the File system cares about ** ****************************************************************/ #define MAX_DATA_BLOCK_FILE_NUMBER 0x7FF #define FIRST_LOG_BLOCK_FILE_NUMBER (MAX_DATA_BLOCK_FILE_NUMBER + 1) #define MAX_LOG_BLOCK_FILE_NUMBER 0xFFF #define FSGetFileNumber( uiBlkAddr) ((uiBlkAddr) & MAX_LOG_BLOCK_FILE_NUMBER) #define FSGetFileOffset( udBlkAddr) ((udBlkAddr) & 0xFFFFF000) #define FSBlkAddress( iFileNum, udFileOfs) ((udFileOfs) + (iFileNum)) // Max file size and log threshold. #define LOG_THRESHOLD_SIZE ((FLMUINT) 0x40000) // very large threshhold is the size we will allow the physical // log to grow to before we force a truncation. At the low end, // it is about 10 megabytes. At the high end it is about // 1 gigabyte. #define LOW_VERY_LARGE_LOG_THRESHOLD_SIZE ((FLMUINT)0xA00000) #define HIGH_VERY_LARGE_LOG_THRESHOLD_SIZE ((FLMUINT) 0x40000000) // RFL_TRUNCATE_SIZE is the size we will let an RFL file grow to // before we truncate it back. RFL files are only truncated if // we are configured to delete old RFL files. #define RFL_TRUNCATE_SIZE ((FLMUINT)1024 * (FLMUINT)1024 * (FLMUINT)10) /*============================================================================ Shared Cache Routines ============================================================================*/ RCODE flmPrepareBlockForUse( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr); RCODE flmPrepareBlockToWrite( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr); void ScaUseCache( F_CachedBlock * pSCache, FLMBOOL bMutexAlreadyLocked); void ScaReleaseCache( F_CachedBlock * pSCache, FLMBOOL bMutexAlreadyLocked); /*============================================================================ File system Btree Cache Routines ============================================================================*/ FLMUINT SENNextVal( FLMBYTE ** senPtrRV ); FLMUINT FSGetDomain( FLMBYTE ** curElmRV, FLMUINT uiElmOvhd); RCODE dbLock( F_Db * pDb, FLMUINT uiMaxLockWait ); RCODE dbUnlock( F_Db * pDb); FINLINE void FSLFileIn( FLMBYTE * pucBuf, LFILE * pLFile, F_COLLECTION * pCollection, FLMUINT uiBlkAddress, FLMUINT uiOffsetInBlk) { F_LF_HDR * pLfHdr = (F_LF_HDR *)pucBuf; pLFile->uiBlkAddress = uiBlkAddress; pLFile->uiOffsetInBlk= uiOffsetInBlk; if ((pLFile->eLfType = (eLFileType)pLfHdr->ui32LfType) != XFLM_LF_INVALID) { pLFile->uiLfNum = (FLMUINT)pLfHdr->ui32LfNumber; pLFile->uiRootBlk = (FLMUINT)pLfHdr->ui32RootBlkAddr; pLFile->uiEncId = (FLMUINT)pLfHdr->ui32EncId; if (pCollection) { flmAssert( pLFile == &pCollection->lfInfo); flmAssert( pLFile->eLfType == XFLM_LF_COLLECTION); pCollection->ui64NextNodeId = pLfHdr->ui64NextNodeId; pCollection->ui64FirstDocId = pLfHdr->ui64FirstDocId; pCollection->ui64LastDocId = pLfHdr->ui64LastDocId; pCollection->bNeedToUpdateNodes = FALSE; } else { flmAssert( pLFile->eLfType == XFLM_LF_INDEX); } } } void lgSetSyncCheckpoint( F_Database * pDatabase, FLMUINT uiCheckpoint, FLMUINT uiBlkAddress); FLMUINT32 calcBlkCRC( F_BLK_HDR * pBlkHdr, FLMUINT uiBlkEnd); FLMUINT flmAdjustBlkSize( FLMUINT uiBlkSize); void flmInitDbHdr( XFLM_CREATE_OPTS * pCreateOpts, FLMBOOL bCreatingDatabase, FLMBOOL bTempDb, XFLM_DB_HDR * pDbHdr); RCODE flmCreateLckFile( const char * pszFilePath, IF_FileHdl ** ppLockFileHdl); RCODE flmReadAndVerifyHdrInfo( XFLM_DB_STATS * pDbStats, IF_FileHdl * pFileHdl, XFLM_DB_HDR * pDbHdr, FLMUINT32 * pui32CalcCRC = NULL); void flmDoEventCallback( eEventCategory eCategory, eEventType eEvent, IF_Db * pDb, FLMUINT uiThreadId, FLMUINT64 ui64TransID, FLMUINT uiIndexOrCollection, FLMUINT64 ui64NodeId, RCODE rc); void flmLogError( RCODE rc, const char * pszDoing, const char * pszFileName = NULL, FLMINT iLineNumber = 0); RCODE flmCollation2Number( FLMUINT uiBufLen, const FLMBYTE * pucBuf, FLMUINT64 * pui64Num, FLMBOOL * pbNeg, FLMUINT * puiBytesProcessed); RCODE flmStorageNum2CollationNum( const FLMBYTE * pucStorageBuf, FLMUINT uiStorageLen, FLMBYTE * pucCollBuf, FLMUINT * puiCollLen); RCODE flmCollationNum2StorageNum( const FLMBYTE * pucCollBuf, FLMUINT uiCollLen, FLMBYTE * pucStorageBuf, FLMUINT * puiStorageLen); RCODE flmStorageNum2StorageText( const FLMBYTE * pucNum, FLMUINT uiNumLen, FLMBYTE * pucBuffer, FLMUINT * puiBufLen); RCODE flmStorageNumberToNumber( const FLMBYTE * pucNumBuf, FLMUINT uiNumBufLen, FLMUINT64 * pui64Number, FLMBOOL * pbNeg); void kyReleaseCdls( IXD * pIxd, CDL_HDR * pCdlTbl); RCODE KYCollateValue( FLMBYTE * pucDest, FLMUINT * puiDestLen, IF_PosIStream * pIStream, FLMUINT uiDataType, FLMUINT uiFlags, FLMUINT uiCompareRules, FLMUINT uiLimit, FLMUINT * puiCollationLen, FLMUINT * puiLuLen, FLMUINT uiLanguage, FLMBOOL bFirstSubstring, FLMBOOL bDataTruncated, FLMBOOL * pbDataTruncated, FLMBOOL * pbOriginalCharsLost); #define UNDF_CHR 0x0000 // Undefined char - ignore for now #define IGNR_CHR 0x0001 // Ignore this char #define SDWD_CHR 0x0002 // Space delimited word chr #define DELI_CHR 0x0040 // Delimiter #define WDJN_CHR 0x0080 // Word Joining chr ".,/-_" // Implement later #define KATA_CHR 0x0004 // Katakana word chr #define HANG_CHR 0x0008 // Hangul word chr #define CJK_CHR 0x0010 // CJK word chr RCODE KYSubstringParse( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUINT uiLimit, FLMBYTE * pucSubstrBuf, FLMUINT * puiSubstrBytes, FLMUINT * puiSubstrChars); RCODE KYEachWordParse( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUINT uiLimit, FLMBYTE * pucWordBuf, FLMUINT * puiWordLen); RCODE fdictGetState( const char * pszState, FLMUINT * puiState); RCODE fdictGetIndexState( const char * pszState, FLMUINT * puiState); #define FLM_BACKGROUND_LOCK_PRIORITY -100 void flmLogIndexingProgress( FLMUINT uiIndexNum, FLMUINT64 ui64LastDocumentId); F_BKGND_IX * flmBackgroundIndexGet( F_Database * pDatabase, FLMUINT uiValue, FLMBOOL bMutexLocked, FLMUINT * puiThreadId = NULL); RCODE flmGetHdrInfo( F_SuperFileHdl * pSFileHdl, XFLM_DB_HDR * pDbHdr, FLMUINT32 * pui32CalcCRC = NULL); void convert64( FLMUINT64 * pui64Num); void convert32( FLMUINT32 * pui32Num); void convert16( FLMUINT16 * pui16Num); void convertDbHdr( XFLM_DB_HDR * pDbHdr); void convertBlkHdr( F_BLK_HDR * pBlkHdr); void convertBlk( FLMUINT uiBlockSize, F_BLK_HDR * pBlkHdr); void convertLfHdr( F_LF_HDR * pLfHdr); /*-------------------------------------------------------- ** Inline Functions **-------------------------------------------------------*/ /************************************************************************** Desc: Returns TRUE if a block address is less than another block address. **************************************************************************/ FINLINE FLMBOOL FSAddrIsBelow( FLMUINT uiAddress1, FLMUINT uiAddress2) { if( FSGetFileNumber( uiAddress1) == FSGetFileNumber( uiAddress2)) { if( FSGetFileOffset( uiAddress1) >= FSGetFileOffset( uiAddress2)) { return FALSE; } } else if( FSGetFileNumber( uiAddress1) > FSGetFileNumber( uiAddress2)) { return FALSE; } return TRUE; } /************************************************************************** Desc: Returns TRUE if a block address is less than or equal another block address. **************************************************************************/ FINLINE FLMBOOL FSAddrIsAtOrBelow( FLMUINT uiAddress1, FLMUINT uiAddress2) { if( FSGetFileNumber( uiAddress1) == FSGetFileNumber( uiAddress2)) { if( FSGetFileOffset( uiAddress1) > FSGetFileOffset( uiAddress2)) { return FALSE; } } else if( FSGetFileNumber( uiAddress1) > FSGetFileNumber( uiAddress2)) { return FALSE; } return TRUE; } /**************************************************************************** Desc: Get the total bytes represented by a particular block address. ****************************************************************************/ FINLINE FLMUINT64 FSGetSizeInBytes( FLMUINT uiMaxFileSize, FLMUINT uiBlkAddress) { FLMUINT uiFileNum; FLMUINT uiFileOffset; FLMUINT64 ui64Size; uiFileNum = FSGetFileNumber( uiBlkAddress); uiFileOffset = FSGetFileOffset( uiBlkAddress); if( uiFileNum > 1) { ui64Size = (FLMUINT64)(((FLMUINT64)uiFileNum - (FLMUINT64)1) * (FLMUINT64)uiMaxFileSize + (FLMUINT64)uiFileOffset); } else { ui64Size = (FLMUINT64)uiFileOffset; } return( ui64Size); } /************************************************************************** Desc: Calculate the significant bits in a block size. **************************************************************************/ FINLINE FLMUINT calcSigBits( FLMUINT uiBlockSize ) { FLMUINT uiSigBitsInBlkSize = 0; while (!(uiBlockSize & 1)) { uiSigBitsInBlkSize++; uiBlockSize >>= 1; } return( uiSigBitsInBlkSize); } /************************************************************************** Desc: Outputs an update event callback. **************************************************************************/ FINLINE void flmTransEventCallback( eEventType eEventType, F_Db * pDb, RCODE rc, FLMUINT64 ui64TransId) { flmDoEventCallback( XFLM_EVENT_UPDATES, eEventType, (IF_Db *)pDb, f_threadId(), ui64TransId, 0, 0, rc); } /******************************************************************** Desc: Calculate the CRC for the database header. *********************************************************************/ FINLINE FLMUINT32 calcDbHdrCRC( XFLM_DB_HDR * pDbHdr) { FLMUINT32 ui32SaveCRC; FLMUINT32 ui32Checksum; // Checksum everything except for the ui32HdrCRC value. ui32SaveCRC = pDbHdr->ui32HdrCRC; pDbHdr->ui32HdrCRC = 0; // Calculate the checksum ui32Checksum = f_calcFastChecksum( pDbHdr, sizeof( XFLM_DB_HDR)); // Restore the checksum that was in the header pDbHdr->ui32HdrCRC = ui32SaveCRC; return( ui32Checksum); } /******************************************************************** Desc: Calculate the checksum for a block. NOTE: This is ALWAYS done on the raw image that will be written to disk. This means that if the block needs to be converted before writing it out, it should be done before calculating the checksum. *********************************************************************/ FINLINE FLMUINT32 calcBlkCRC( F_BLK_HDR * pBlkHdr, FLMUINT uiBlkEnd) { FLMUINT32 ui32SaveCRC; FLMUINT32 ui32Checksum; // Calculate CRC on everything except for the ui32BlkCRC value. // To do this, we temporarily change it to zero. The saved // value will be restored after calculating the CRC. ui32SaveCRC = pBlkHdr->ui32BlkCRC; pBlkHdr->ui32BlkCRC = 0; // Calculate the checksum ui32Checksum = f_calcFastChecksum( pBlkHdr, uiBlkEnd); // Restore the CRC that was in the block. pBlkHdr->ui32BlkCRC = ui32SaveCRC; return( ui32Checksum); } #endif // FILESYS_H libxflaim-5.1.969/src/rfl.cpp0000644000175000017500000061717310511001742017332 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This module contains routine for roll forward logging. // // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: rfl.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #define MOD_512( uiNum) (FLMUINT)((uiNum) & 511) #define ON_512_BYTE_BOUNDARY( uiNum) (!MOD_512(uiNum)) #define ROUND_DOWN_TO_NEAREST_512( uiNum) \ (FLMUINT)((uiNum) & (~((FLMUINT)511))) /******************************************************************** Desc: *********************************************************************/ FINLINE FLMBOOL F_Rfl::useDataOnlyBlocks( F_Db * pDb, FLMUINT uiDataLen) { if( uiDataLen > (pDb->m_pDatabase->m_uiBlockSize * 8) / 5) { return( TRUE); } else { return( FALSE); } } /******************************************************************** Desc: *********************************************************************/ class F_RflOStream : public IF_OStream { public: F_RflOStream( F_Rfl * pRfl, F_Db * pDb) { m_pRfl = pRfl; m_pRfl->AddRef(); m_pDb = pDb; } virtual ~F_RflOStream() { if( m_pRfl) { m_pRfl->Release(); } } RCODE FLMAPI write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten = NULL); RCODE write( IF_PosIStream * pIStream); FINLINE RCODE FLMAPI closeStream( void) { if( m_pRfl) { m_pRfl->Release(); m_pRfl = NULL; } return( NE_XFLM_OK); } private: F_Rfl * m_pRfl; F_Db * m_pDb; }; /******************************************************************** Desc: *********************************************************************/ F_Rfl::F_Rfl() { m_pDatabase = NULL; m_hBufMutex = F_MUTEX_NULL; m_pCommitBuf = NULL; m_pCurrentBuf = NULL; m_uiRflWriteBufs = DEFAULT_RFL_WRITE_BUFFERS; m_uiBufferSize = DEFAULT_RFL_BUFFER_SIZE; f_memset( &m_Buf1, 0, sizeof( m_Buf1)); f_memset( &m_Buf2, 0, sizeof( m_Buf2)); m_bKeepRflFiles = FALSE; m_uiRflMinFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; m_uiRflMaxFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; m_pFileHdl = NULL; m_uiLastRecoverFileNum = 0; f_memset( m_ucCurrSerialNum, 0, sizeof( m_ucCurrSerialNum)); m_uiTransStartFile = 0; m_uiTransStartAddr = 0; m_ui64CurrTransID = 0; m_ui64LastTransID = 0; m_ui64LastLoggedCommitTransID = 0; m_uiOperCount = 0; m_uiRflReadOffset = 0; m_uiFileEOF = 0; m_pRestore = NULL; m_pRestoreStatus = NULL; f_memset( m_szRflDir, 0, sizeof( m_szRflDir)); m_bRflDirSameAsDb = FALSE; m_bCreateRflDir = FALSE; f_memset( m_ucNextSerialNum, 0, sizeof( m_ucNextSerialNum)); m_bRflVolumeOk = TRUE; m_bRflVolumeFull = FALSE; m_uiLastLfNum = 0; m_eLastLfType = XFLM_LF_INVALID; m_pIxCompareObject = NULL; m_pCompareObject = NULL; m_uiDisableCount = 0; } /******************************************************************** Desc: Destructor *********************************************************************/ F_Rfl::~F_Rfl() { if (m_Buf1.pIOBuffer) { m_Buf1.pIOBuffer->Release(); m_Buf1.pIOBuffer = NULL; } if (m_Buf2.pIOBuffer) { m_Buf2.pIOBuffer->Release(); m_Buf2.pIOBuffer = NULL; } if( m_Buf1.pBufferMgr) { flmAssert( !m_Buf1.pBufferMgr->isIOPending()); m_Buf1.pBufferMgr->Release(); m_Buf1.pBufferMgr = NULL; } if( m_Buf2.pBufferMgr) { flmAssert( !m_Buf2.pBufferMgr->isIOPending()); m_Buf2.pBufferMgr->Release(); m_Buf2.pBufferMgr = NULL; } if (m_hBufMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hBufMutex); } if (m_pFileHdl) { m_pFileHdl->closeFile(); m_pFileHdl->Release(); m_pFileHdl = NULL; m_pDatabase = NULL; } if (m_pIxCompareObject) { m_pIxCompareObject->Release(); } } /******************************************************************** Desc: Returns a boolean indicating whether or not we are at the end of the RFL log - will only be TRUE when we are doing recovery. *********************************************************************/ FLMBOOL F_Rfl::atEndOfLog( void) { return( (!m_pRestore && m_uiFileEOF && m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >= m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) ? TRUE : FALSE); } /******************************************************************** Desc: Gets the base RFL file name - does not have directory part. This needs to be separate from the F_Rfl object so it can be called without having to instantiate and set up an F_Rfl object. *********************************************************************/ void rflGetBaseFileName( FLMUINT uiFileNum, char * pszBaseNameOut, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated) { FLMUINT uiCnt; FLMUINT uiDigit; char szTmpBuf [14]; char * pszTmp = &szTmpBuf [0]; // Output as eight digit hex number uiCnt = 0; pszTmp += 7; while (uiCnt < 8) { uiDigit = (FLMUINT)(uiFileNum & 0xF); uiFileNum >>= 4; if (uiDigit <= 9) { uiDigit += NATIVE_ZERO; } else { uiDigit += (NATIVE_LOWER_A - 10); } *pszTmp = (FLMBYTE)uiDigit; pszTmp--; uiCnt++; } // Skip to end of digits and append ".log" to name f_strcpy( pszTmp + 9, ".log"); if (*puiFileNameBufSize >= 13) { *puiFileNameBufSize = 12; f_strcpy( pszBaseNameOut, szTmpBuf); if (pbNameTruncated) { *pbNameTruncated = FALSE; } } else { flmAssert( *puiFileNameBufSize); (*puiFileNameBufSize)--; if (*puiFileNameBufSize) { f_memcpy( pszBaseNameOut, szTmpBuf, *puiFileNameBufSize); } pszBaseNameOut [*puiFileNameBufSize] = 0; if (pbNameTruncated) { *pbNameTruncated = TRUE; } } } /******************************************************************** Desc: Generates the full roll forward log file name. *********************************************************************/ void F_Rfl::getFullRflFileName( FLMUINT uiFileNum, char * pszRflFileName, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated) { FLMUINT uiBaseNameSize; FLMUINT uiLen = f_strlen( m_szRflDir); FLMBOOL bNameTruncated = FALSE; // Must at least be room for a null byte to terminate the string. flmAssert( *puiFileNameBufSize); if (uiLen > *puiFileNameBufSize - 1) { uiLen = *puiFileNameBufSize - 1; if (uiLen) { f_memcpy( pszRflFileName, m_szRflDir, uiLen); } bNameTruncated = TRUE; goto Exit; } // Get the directory name. if (uiLen) { f_memcpy( pszRflFileName, m_szRflDir, uiLen); // See if we need to append a slash. #ifdef FLM_UNIX if (m_szRflDir [uiLen - 1] != '/') #else if (m_szRflDir [uiLen - 1] != '/' && m_szRflDir [uiLen - 1] != '\\') #endif { // See if we have room for one more character, plus null if (uiLen == *puiFileNameBufSize - 1) { bNameTruncated = TRUE; goto Exit; } #ifdef FLM_UNIX pszRflFileName [uiLen] = '/'; #else pszRflFileName [uiLen] = '\\'; #endif uiLen++; } } // See if there is room for at least one more byte plus a // null byte. if (uiLen == *puiFileNameBufSize - 1) { bNameTruncated = TRUE; goto Exit; } // Get the base RFL file name. uiBaseNameSize = *puiFileNameBufSize - uiLen; rflGetBaseFileName( uiFileNum, pszRflFileName + uiLen, &uiBaseNameSize, &bNameTruncated); uiLen += uiBaseNameSize; Exit: pszRflFileName [uiLen] = 0; *puiFileNameBufSize = uiLen; if (pbNameTruncated) { *pbNameTruncated = bNameTruncated; } } /******************************************************************** Desc: Positions to the offset specified in the RFL file. *********************************************************************/ RCODE F_Rfl::positionTo( FLMUINT uiFileOffset) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesToRead; FLMUINT uiBytesRead; // Should never be attempting to position to something less // than 512 - the header is stored in the first 512 bytes. flmAssert( uiFileOffset >= 512); // If the position is within our current buffer, see if we // can adjust things without having to go back and re-read // the buffer from disk. if (m_pCurrentBuf->uiRflBufBytes && uiFileOffset >= m_pCurrentBuf->uiRflFileOffset && uiFileOffset <= m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes) { // Whatever is in the buffer beyond uiFileOffset is irrelevant // and can be discarded. m_pCurrentBuf->uiRflBufBytes = uiFileOffset - m_pCurrentBuf->uiRflFileOffset; } else { // Populate the buffer from the 512 byte boundary that is just // before the offset we are trying to position to. uiBytesToRead = MOD_512( uiFileOffset); m_pCurrentBuf->uiRflFileOffset = ROUND_DOWN_TO_NEAREST_512( uiFileOffset); m_pCurrentBuf->uiRflBufBytes = MOD_512( uiFileOffset); if (m_pCurrentBuf->uiRflBufBytes) { if (RC_BAD( rc = m_pFileHdl->read( m_pCurrentBuf->uiRflFileOffset, m_pCurrentBuf->uiRflBufBytes, m_pCurrentBuf->pIOBuffer->getBufferPtr(), &uiBytesRead))) { if (rc == NE_FLM_IO_END_OF_FILE) { rc = RC_SET( NE_XFLM_NOT_RFL); } else { m_bRflVolumeOk = FALSE; } goto Exit; } else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes) { rc = RC_SET( NE_XFLM_NOT_RFL); goto Exit; } } } Exit: return( rc); } /******************************************************************** Desc: Get the ACTUAL RFL directory, using as input parameters the database version, the name of the database, and the user specified RFL directory. Also return the database prefix. *********************************************************************/ RCODE rflGetDirAndPrefix( const char * pszDbFileName, const char * pszRflDirIn, char * pszRflDirOut) { RCODE rc = NE_XFLM_OK; char szDbPath [F_PATH_MAX_SIZE]; char szBaseName [F_FILENAME_SIZE]; char szPrefix [F_FILENAME_SIZE]; // Parse the database name into directory and base name if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce( pszDbFileName, szDbPath, szBaseName))) { goto Exit; } // Get the base path flmGetDbBasePath( szPrefix, szBaseName, NULL); // Determine the RFL directory. If one was // specified, it is whatever was specified. // Otherwise, it is relative to the database // directory. if (pszRflDirIn && *pszRflDirIn) { f_strcpy( pszRflDirOut, pszRflDirIn); } else { f_strcpy( pszRflDirOut, szDbPath); } f_strcpy( szBaseName, szPrefix); f_strcat( szBaseName, ".rfl"); gv_XFlmSysData.pFileSystem->pathAppend( pszRflDirOut, szBaseName); Exit: return( rc); } /******************************************************************** Desc: Set the RFL directory. If pszRflDir is NULL or empty string, the RFL directory is set to the same directory as the database. *********************************************************************/ RCODE F_Rfl::setRflDir( const char * pszRflDir) { // Better have set up the F_Database pointer. flmAssert( m_pDatabase != NULL); m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir)) ? TRUE : FALSE; flmAssert( m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion); m_bCreateRflDir = TRUE; return( rflGetDirAndPrefix( m_pDatabase->m_pszDbPath, pszRflDir, m_szRflDir)); } /******************************************************************** Desc: Gets an RFL file name - based on DB name and RFL directory. *********************************************************************/ RCODE rflGetFileName( const char * pszDbName, const char * pszRflDir, FLMUINT uiFileNum, char * pszRflFileName) { RCODE rc = NE_XFLM_OK; char szBaseName [F_FILENAME_SIZE]; FLMUINT uiBaseNameSize; // Get the full RFL file name. if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, pszRflFileName))) { goto Exit; } uiBaseNameSize = sizeof( szBaseName); rflGetBaseFileName( uiFileNum, szBaseName, &uiBaseNameSize, NULL); if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathAppend( pszRflFileName, szBaseName))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Gets an RFL file number from the RFL file name. *********************************************************************/ FLMBOOL rflGetFileNum( const char * pszRflFileName, FLMUINT * puiFileNum) { FLMBOOL bGotNum = FALSE; char szDir[F_PATH_MAX_SIZE]; char szBaseName[F_FILENAME_SIZE]; char * pszTmp; FLMUINT uiCharCnt; if( RC_BAD( gv_XFlmSysData.pFileSystem->pathReduce( pszRflFileName, szDir, szBaseName))) { goto Exit; } // See if it has a .log extension. pszTmp = &szBaseName [0]; while (*pszTmp && *pszTmp != '.') { pszTmp++; } // If we do not have a .log extension, it is not a legitimate // RFL file. if (f_stricmp( pszTmp, ".log") != 0) { goto Exit; } // Parse out the name according to the rules for this DB version. *pszTmp = 0; // Set period to zero pszTmp = &szBaseName [0]; *puiFileNum = 0; uiCharCnt = 0; // Name up to the period should be a hex number while (*pszTmp) { (*puiFileNum) <<= 4; if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) { *puiFileNum += (FLMUINT)(*pszTmp - NATIVE_ZERO); } else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_F) { *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_LOWER_A) + 10); } else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_F) { *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_UPPER_A) + 10); } else { goto Exit; // Not a hex number } uiCharCnt++; pszTmp++; } // Better have been exactly 8 hex digits. bGotNum = (FLMBOOL)((uiCharCnt == 8) ? TRUE : FALSE); Exit: return( bGotNum); } /******************************************************************** Desc: Sets up the RFL object - associating with a file, etc. *********************************************************************/ RCODE F_Rfl::setup( F_Database * pDatabase, const char * pszRflDir) { RCODE rc = NE_XFLM_OK; // Better not already be associated with an F_Database object flmAssert( m_pDatabase == NULL); m_pDatabase = pDatabase; // Allocate memory for the RFL buffers #ifndef FLM_UNIX if (!gv_XFlmSysData.bOkToDoAsyncWrites) #endif { m_uiRflWriteBufs = 1; m_uiBufferSize = DEFAULT_RFL_WRITE_BUFFERS * DEFAULT_RFL_BUFFER_SIZE; } if (RC_BAD( rc = f_mutexCreate( &m_hBufMutex))) { goto Exit; } if( RC_BAD( rc = FlmAllocIOBufferMgr( m_uiRflWriteBufs, m_uiRflWriteBufs * m_uiBufferSize, TRUE, &m_Buf1.pBufferMgr))) { goto Exit; } if( RC_BAD( rc = FlmAllocIOBufferMgr( m_uiRflWriteBufs, m_uiRflWriteBufs * m_uiBufferSize, TRUE, &m_Buf2.pBufferMgr))) { goto Exit; } if( RC_BAD( rc = m_Buf1.pBufferMgr->getBuffer( m_uiBufferSize, &m_Buf1.pIOBuffer))) { goto Exit; } if( RC_BAD( rc = m_Buf2.pBufferMgr->getBuffer( m_uiBufferSize, &m_Buf2.pIOBuffer))) { goto Exit; } m_pCurrentBuf = &m_Buf1; m_pCurrentBuf->uiRflBufBytes = 0; // Set the RFL directory and prefix if necessary. if (RC_BAD( rc = setRflDir( pszRflDir))) { goto Exit; } // Set up the compare object for comparing index keys. if ((m_pIxCompareObject = f_new IXKeyCompare) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Wait for the writes of a buffer to finish. This routine assumes that the m_hBufMutex is locked when coming in. It will ALWAYS unlock the mutex before exiting. *********************************************************************/ RCODE F_Rfl::waitForWrites( F_SEM hWaitSem, RFL_BUFFER * pBuffer, FLMBOOL bIsWriter) { RCODE rc = NE_XFLM_OK; RCODE TempRc; RFL_WAITER Waiter; FLMBOOL bMutexLocked = TRUE; // Put self on the wait queue for the buffer. Waiter.uiThreadId = f_threadId(); Waiter.bIsWriter = bIsWriter; Waiter.hESem = hWaitSem; // Note: rc better be changed to success or write error // by the process that signals us. rc = RC_SET( NE_XFLM_FAILURE); Waiter.pRc = &rc; Waiter.pNext = NULL; if (pBuffer->pLastWaiter) { pBuffer->pLastWaiter->pNext = &Waiter; } else { pBuffer->pFirstWaiter = &Waiter; } pBuffer->pLastWaiter = &Waiter; f_mutexUnlock( m_hBufMutex); bMutexLocked = FALSE; // Now just wait to be signaled. if (RC_BAD( TempRc = f_semWait( Waiter.hESem, F_WAITFOREVER))) { RC_UNEXPECTED_ASSERT( TempRc); rc = TempRc; } else { // Process that signaled us better set the rc to something // besides NE_XFLM_FAILURE. if (rc == NE_XFLM_FAILURE) { RC_UNEXPECTED_ASSERT( rc); } } if (bMutexLocked) { f_mutexUnlock( m_hBufMutex); } return( rc); } /******************************************************************** Desc: If a commit is in progress, wait for it to finish. *********************************************************************/ RCODE F_Rfl::waitForCommit( F_SEM hWaitSem) { RCODE rc = NE_XFLM_OK; FLMBOOL bMutexLocked = FALSE; // NOTE: If m_pCommitBuf is NULL it cannot be set to something // non-NULL except by this thread when this thread ends the // transaction. So, there is no need to lock the mutex and // re-check if it is NULL. if (m_pCommitBuf) { f_mutexLock( m_hBufMutex); bMutexLocked = TRUE; // Check m_pCommitBuf again after locking mutex - may have // finished. if (m_pCommitBuf) { bMutexLocked = FALSE; rc = waitForWrites( hWaitSem, m_pCommitBuf, FALSE); } } if (bMutexLocked) { f_mutexUnlock( m_hBufMutex); } return( rc); } /******************************************************************** Desc: Write out the header information for an RFL file. *********************************************************************/ RCODE F_Rfl::writeHeader( FLMUINT uiFileNum, FLMUINT uiEof, FLMBYTE * pucSerialNum, FLMBYTE * pucNextSerialNum, FLMBOOL bKeepSignature) { RCODE rc = NE_XFLM_OK; FLMBYTE ucBuf[ 512]; FLMUINT uiBytesWritten; flmAssert( m_pDatabase); flmAssert( m_pFileHdl); f_memset( ucBuf, 0, sizeof( ucBuf)); f_memcpy( &ucBuf [RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN); f_memcpy( &ucBuf [RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN); UD2FBA( (FLMUINT32)uiFileNum, &ucBuf [RFL_FILE_NUMBER_POS]); UD2FBA( (FLMUINT32)uiEof, &ucBuf [RFL_EOF_POS]); f_memcpy( &ucBuf [RFL_DB_SERIAL_NUM_POS], m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( &ucBuf [RFL_SERIAL_NUM_POS], pucSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( &ucBuf [RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum, XFLM_SERIAL_NUM_SIZE); f_strcpy( (char *)&ucBuf [RFL_KEEP_SIGNATURE_POS], (char *)((bKeepSignature) ? RFL_KEEP_SIGNATURE : RFL_NOKEEP_SIGNATURE)); // Write out the header if (RC_BAD( rc = m_pFileHdl->write( 0, 512, ucBuf, &uiBytesWritten))) { // Remap disk full error if (rc == NE_FLM_IO_DISK_FULL) { rc = RC_SET( NE_XFLM_RFL_DISK_FULL); m_bRflVolumeFull = TRUE; } m_bRflVolumeOk = FALSE; goto Exit; } // Flush the file handle to ensure it is forced to disk. if (RC_BAD( rc = m_pFileHdl->flush())) { // Remap disk full error if (rc == NE_FLM_IO_DISK_FULL) { rc = RC_SET( NE_XFLM_RFL_DISK_FULL); m_bRflVolumeFull = TRUE; } m_bRflVolumeOk = FALSE; goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Verifies the header of an RFL file. *********************************************************************/ RCODE F_Rfl::verifyHeader( FLMBYTE * pucHeader, FLMUINT uiFileNum, FLMBYTE * pucSerialNum) { RCODE rc = NE_XFLM_OK; flmAssert( m_pDatabase); // Check the RFL name and version number if( f_memcmp( &pucHeader [RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN) != 0) { rc = RC_SET( NE_XFLM_NOT_RFL); goto Exit; } if( f_memcmp( &pucHeader [RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN) != 0) { rc = RC_SET( NE_XFLM_NOT_RFL); goto Exit; } // Verify the database serial number if( f_memcmp( &pucHeader [RFL_DB_SERIAL_NUM_POS], m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum, XFLM_SERIAL_NUM_SIZE) != 0) { rc = RC_SET( NE_XFLM_BAD_RFL_DB_SERIAL_NUM); goto Exit; } // Verify the serial number that is expected to be on the // RFL file. If pucSerialNum is NULL, we will not verify // it. This is generally only done during recovery or restore // when we are reading through multiple RFL files and we need // to verify their serial numbers. if( pucSerialNum && f_memcmp( &pucHeader [RFL_SERIAL_NUM_POS], pucSerialNum, XFLM_SERIAL_NUM_SIZE) != 0) { rc = RC_SET( NE_XFLM_BAD_RFL_SERIAL_NUM); goto Exit; } // Verify the file number. if( uiFileNum != (FLMUINT)FB2UD( &pucHeader [RFL_FILE_NUMBER_POS])) { rc = RC_SET( NE_XFLM_BAD_RFL_FILE_NUMBER); goto Exit; } // Save serial numbers from the header. f_memcpy( m_ucCurrSerialNum, &pucHeader [RFL_SERIAL_NUM_POS], XFLM_SERIAL_NUM_SIZE); f_memcpy( m_ucNextSerialNum, &pucHeader [RFL_NEXT_FILE_SERIAL_NUM_POS], XFLM_SERIAL_NUM_SIZE); // Save some things from the header. m_uiFileEOF = (FLMUINT)FB2UD( &pucHeader [RFL_EOF_POS]); Exit: return( rc); } /******************************************************************** Desc: Opens an RFL file. Verifies the serial number for 4.3 dbs. *********************************************************************/ RCODE F_Rfl::openFile( F_SEM hWaitSem, FLMUINT uiFileNum, FLMBYTE * pucSerialNum) { RCODE rc = NE_XFLM_OK; char szRflFileName [F_PATH_MAX_SIZE]; FLMUINT uiFileNameSize; FLMBYTE ucBuf [512]; FLMUINT uiBytesRead; flmAssert( m_pDatabase); // If we have a file open and it is not the file number // passed in, close it. if (m_pFileHdl) { if (m_pCurrentBuf->uiCurrFileNum != uiFileNum) { if (RC_BAD( rc = waitForCommit( hWaitSem))) { goto Exit; } closeFile(); } else { goto Exit; // Will return NE_XFLM_OK } } else { // Should not be able to be in the middle of a commit // if we don't have a file open! flmAssert( !m_pCommitBuf); } // Generate the log file name. uiFileNameSize = sizeof( szRflFileName); getFullRflFileName( uiFileNum, szRflFileName, &uiFileNameSize, NULL); // Open the file. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( szRflFileName, gv_XFlmSysData.uiFileOpenFlags, &m_pFileHdl))) { goto Exit; } // Read the header. if (RC_BAD( rc = m_pFileHdl->read( 0, 512, ucBuf, &uiBytesRead))) { if (rc == NE_FLM_IO_END_OF_FILE) { rc = RC_SET( NE_XFLM_NOT_RFL); } else { m_bRflVolumeOk = FALSE; } goto Exit; } // If there is not enough data in the buffer, it is not an // RFL file. if (uiBytesRead < 512) { rc = RC_SET( NE_XFLM_NOT_RFL); goto Exit; } // Verify the header information if (RC_BAD( rc = verifyHeader( ucBuf, uiFileNum, pucSerialNum))) { goto Exit; } m_pCurrentBuf->uiRflBufBytes = 0; m_pCurrentBuf->uiRflFileOffset = 0; m_pCurrentBuf->uiCurrFileNum = uiFileNum; Exit: if( RC_BAD( rc)) { waitForCommit( hWaitSem); closeFile(); } return( rc); } /******************************************************************** Desc: Creates a new roll forward log file. *********************************************************************/ RCODE F_Rfl::createFile( F_Db * pDb, FLMUINT uiFileNum, FLMBYTE * pucSerialNum, FLMBYTE * pucNextSerialNum, FLMBOOL bKeepSignature) { RCODE rc = NE_XFLM_OK; char szRflFileName [F_PATH_MAX_SIZE]; FLMUINT uiFileNameSize; flmAssert( m_pDatabase); // Better not be trying to create the current file flmAssert( uiFileNum != m_pCurrentBuf->uiCurrFileNum); // If we have a file open, close it. if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) { goto Exit; } closeFile(); // Generate the log file name. uiFileNameSize = sizeof( szRflFileName); getFullRflFileName( uiFileNum, szRflFileName, &uiFileNameSize, NULL); // Delete the file if it already exists - don't care // about return code here (void)gv_XFlmSysData.pFileSystem->deleteFile( szRflFileName); // If DB is 4.3 or greater and we are in the same directory as // our database files, see if we need to create the // subdirectory. Otherwise, the RFL directory should already // have been created. If the directory already exists, it is // OK - we only try this the first time after setRflDir is // called - to either verify that the directory exists, or if // it doesn't, to create it. if (m_bCreateRflDir) { // If it already exists, don't attempt to create it. if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->doesFileExist( m_szRflDir))) { if (rc != NE_FLM_IO_PATH_NOT_FOUND && rc != NE_FLM_IO_INVALID_FILENAME) { goto Exit; } else { if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->createDir( m_szRflDir))) { goto Exit; } } } m_bCreateRflDir = FALSE; } // Create the file if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->createFile( szRflFileName, gv_XFlmSysData.uiFileCreateFlags, &m_pFileHdl))) { goto Exit; } // Initialize the header. if (RC_BAD( rc = writeHeader( uiFileNum, 0, pucSerialNum, pucNextSerialNum, bKeepSignature))) { goto Exit; } m_pCurrentBuf->uiRflBufBytes = 0; m_pCurrentBuf->uiRflFileOffset = 512; m_pCurrentBuf->uiCurrFileNum = uiFileNum; Exit: // Close the RFL log file AND delete it if we were not successful. if (RC_BAD( rc)) { closeFile(); (void)gv_XFlmSysData.pFileSystem->deleteFile( szRflFileName); } return( rc); } /******************************************************************** Desc: Copy last partial sector of last buffer written (or to be written) into a new buffer. *********************************************************************/ void F_Rfl::copyLastSector( RFL_BUFFER * pBuffer, FLMBYTE * pucOldBuffer, FLMBYTE * pucNewBuffer, FLMUINT uiCurrPacketLen, FLMBOOL bStartingNewFile) { FLMUINT uiOldBufBytes = pBuffer->uiRflBufBytes; // If we will be starting a new file, no need to keep any of // what is in the buffer. Only the current packet needs to // be copied - at the beginning of the buffer. // OTHERWISE: // If there are fewer than 512 bytes in the buffer, we simply // keep them and keep appending to the buffer the next time // we output stuff. The beginning of the buffer must ALWAYS be // a 512 byte boundary in the file, because we want to always // do our writing on 512 byte boundaries - because of direct IO. // If the number of bytes in the buffer is over 512 and it is // evenly divisible by 512, we can clear the buffer. Otherwise, // we want to move the extra bytes over the last 512 byte boundary // down to the beginning of the buffer and adjust the buffer bytes // to reflect just these left-over bytes. if (bStartingNewFile) { pBuffer->uiRflBufBytes = 0; pBuffer->uiRflFileOffset = 512; } else if (pBuffer->uiRflBufBytes >= 512) { // See if the number of bytes in the buffer is an exact // multiple of 512. if (pBuffer->uiRflBufBytes & 511) // Not exact multiple { // Round m_uiRflBufBytes down to next 512 byte boundary FLMUINT ui512Offset = ROUND_DOWN_TO_NEAREST_512( pBuffer->uiRflBufBytes); // Move all bytes above the nearest 512 byte boundary // down to the beginning of the buffer and adjust // pBuffer->uiRflBufBytes and // pBuffer->uiRflFileOffset accordingly. f_memcpy( pucNewBuffer, &pucOldBuffer[ui512Offset], pBuffer->uiRflBufBytes - ui512Offset); pBuffer->uiRflBufBytes -= ui512Offset; pBuffer->uiRflFileOffset += ui512Offset; } else { pBuffer->uiRflFileOffset += pBuffer->uiRflBufBytes; pBuffer->uiRflBufBytes = 0; } } else if (pucNewBuffer != pucOldBuffer) { f_memcpy( pucNewBuffer, pucOldBuffer, pBuffer->uiRflBufBytes); } if (uiCurrPacketLen) { flmAssert( uiOldBufBytes + uiCurrPacketLen <= m_uiBufferSize); f_memmove( &pucNewBuffer [pBuffer->uiRflBufBytes], &pucOldBuffer [uiOldBufBytes], uiCurrPacketLen); } } /******************************************************************** Desc: Flush the RFL data from the buffer to disk. *********************************************************************/ RCODE F_Rfl::flush( F_Db * pDb, RFL_BUFFER * pBuffer, FLMBOOL bFinalWrite, FLMUINT uiCurrPacketLen, FLMBOOL bStartingNewFile) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesWritten; IF_IOBuffer * pNewBuffer = NULL; IF_IOBuffer * pAsyncBuf = NULL; FLMBYTE * pucOldBuffer; FLMUINT uiFileOffset; FLMUINT uiBufBytes; if (m_pFileHdl && pBuffer->uiRflBufBytes) { // Must wait for stuff in committing buffer, if any, before // going ahead here. if (pBuffer != m_pCommitBuf) { if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) { goto Exit; } } if (m_uiRflWriteBufs > 1 && m_pFileHdl->canDoAsync()) { pAsyncBuf = pBuffer->pIOBuffer; } if ((FLMUINT)(-1) - pBuffer->uiRflFileOffset <= pBuffer->uiRflBufBytes) { rc = RC_SET( NE_XFLM_DB_FULL); goto Exit; } pucOldBuffer = pBuffer->pIOBuffer->getBufferPtr(); uiFileOffset = pBuffer->uiRflFileOffset; uiBufBytes = pBuffer->uiRflBufBytes; if (m_uiRflWriteBufs > 1) { if( RC_BAD( rc = pBuffer->pBufferMgr->getBuffer( m_uiBufferSize, &pNewBuffer))) { goto Exit; } // No need to copy data if it is the final write, // because it won't be reused anyway - the data for // the next transaction has already been copied to // another buffer. if (!bFinalWrite) { copyLastSector( pBuffer, pucOldBuffer, pNewBuffer->getBufferPtr(), uiCurrPacketLen, bStartingNewFile); } } if( pAsyncBuf) { rc = m_pFileHdl->write( uiFileOffset, uiBufBytes, pAsyncBuf); } else { rc = m_pFileHdl->write( uiFileOffset, uiBufBytes, pucOldBuffer, &uiBytesWritten); } if( m_uiRflWriteBufs == 1) { // We are counting on the fact that the write completed. // When we only have one buffer, we cannot do async // writes. flmAssert( !pAsyncBuf); if (RC_OK( rc) && !bFinalWrite) { copyLastSector( pBuffer, pucOldBuffer, pucOldBuffer, uiCurrPacketLen, bStartingNewFile); } // DO NOT call notifyComplete - that would put // pBuffer->pIOBuffer into the avail list, and we // don't want that. We simply want to keep // reusing it. } else { // No need to call copyLastSector, because it was called // above before calling write. The part of the // old buffer that needs to be transferred to the new // buffer has already been transferred. if (!pAsyncBuf) { pBuffer->pIOBuffer->notifyComplete( rc); } pBuffer->pIOBuffer = pNewBuffer; } if (RC_BAD( rc)) { // Remap disk full error if (rc == NE_FLM_IO_DISK_FULL) { rc = RC_SET( NE_XFLM_RFL_DISK_FULL); m_bRflVolumeFull = TRUE; } m_bRflVolumeOk = FALSE; goto Exit; } } Exit: return( rc); } /******************************************************************** Desc: Switch buffers. This routine assumes the m_hBufMutex is locked. *********************************************************************/ void F_Rfl::switchBuffers( void) { RFL_BUFFER * pOldBuffer = m_pCurrentBuf; if (m_pCurrentBuf == &m_Buf1) { m_pCurrentBuf = &m_Buf2; } else { m_pCurrentBuf = &m_Buf1; } m_pCurrentBuf->bTransInProgress = pOldBuffer->bTransInProgress; m_pCurrentBuf->uiCurrFileNum = pOldBuffer->uiCurrFileNum; m_pCurrentBuf->uiRflBufBytes = pOldBuffer->uiRflBufBytes; m_pCurrentBuf->uiRflFileOffset = pOldBuffer->uiRflFileOffset; if (pOldBuffer->uiRflBufBytes) { copyLastSector( m_pCurrentBuf, pOldBuffer->pIOBuffer->getBufferPtr(), m_pCurrentBuf->pIOBuffer->getBufferPtr(), 0, FALSE); } } /******************************************************************** Desc: Wait for all RFL transaction writes to be finished. The caller has the write lock on the database, which will prevent further writes to the RFL. *********************************************************************/ FLMBOOL F_Rfl::seeIfRflWritesDone( F_SEM hWaitSem, FLMBOOL bForceWait) { FLMBOOL bWritesDone; f_mutexLock( m_hBufMutex); if (!bForceWait) { bWritesDone = (FLMBOOL)((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf) ? FALSE : TRUE); if( bWritesDone) { m_pCurrentBuf->uiRflBufBytes = 0; } f_mutexUnlock( m_hBufMutex); } else { // If the current buffer has a waiter, add self to that list // to wait, because it will be notified after the commit buffer // has been notified. Otherwise, if there is a commit in // progress, add self to that list to wait. if (m_pCurrentBuf->pFirstWaiter) { // If bTransInProgress is TRUE and m_pCommitBuf is NULL // then this thread is the current transaction, and // nobody is going to wake up the first waiter until we // are done! Hence, we must wake him up. if (!m_pCommitBuf) { // If m_pCommitBuf is NULL, this could only be possible if // there is a transaction in progress. Otherwise, there // would not have been a pFirstWaiter, because when // the commit buffer finishes writing, if there is a // waiter, it will set commitbuf=currentbuf if there // is no transaction active. flmAssert( m_pCurrentBuf->bTransInProgress); m_pCommitBuf = m_pCurrentBuf; switchBuffers(); wakeUpWaiter( NE_XFLM_OK, TRUE); (void)waitForWrites( hWaitSem, m_pCommitBuf, FALSE); } else { FLMBOOL bSaveTransInProgress = m_pCurrentBuf->bTransInProgress; // Must set bTransInProgress to FALSE so that when the writer // of m_pCommitBuf finishes, it will signal the first waiter // on m_pCurrentBuf. If we don't do this, m_pCommitBuf will // simply be set to NULL, and the first waiter will never // be woke up. m_pCurrentBuf->bTransInProgress = FALSE; (void)waitForWrites( hWaitSem, m_pCurrentBuf, FALSE); // It is OK to restore the trans in progress flag to what it // was before, because whoever called this routine has a lock // on the database, and it is his trans-in-progress state // that should be preserved. No other thread will have been // able to change that state because the database is locked. f_mutexLock( m_hBufMutex); m_pCurrentBuf->bTransInProgress = bSaveTransInProgress; f_mutexUnlock( m_hBufMutex); } m_pCurrentBuf->uiRflBufBytes = 0; } else if (m_pCommitBuf) { (void)waitForWrites( hWaitSem, m_pCommitBuf, FALSE); } else { f_mutexUnlock( m_hBufMutex); } bWritesDone = TRUE; } return( bWritesDone); } /******************************************************************** Desc: Wake up the first thread that is waiting on the commit buffer. *********************************************************************/ void F_Rfl::wakeUpWaiter( RCODE rc, FLMBOOL bIsWriter) { F_SEM hESem; #ifndef FLM_DEBUG F_UNREFERENCED_PARM( bIsWriter); #else if (bIsWriter) { flmAssert( m_pCommitBuf->pFirstWaiter->bIsWriter); } else { flmAssert( !m_pCommitBuf->pFirstWaiter->bIsWriter); } #endif *(m_pCommitBuf->pFirstWaiter->pRc) = rc; hESem = m_pCommitBuf->pFirstWaiter->hESem; if ((m_pCommitBuf->pFirstWaiter = m_pCommitBuf->pFirstWaiter->pNext) == NULL) { m_pCommitBuf->pLastWaiter = NULL; } f_semSignal( hESem); } /******************************************************************** Desc: Wait for the transaction writes to be finished. *********************************************************************/ RCODE F_Rfl::completeTransWrites( F_Db * pDb, FLMBOOL bCommitting, FLMBOOL bOkToUnlock) { RCODE rc = NE_XFLM_OK; RCODE tmpRc; FLMBOOL bMutexLocked = FALSE; FLMBOOL bNotifyWaiters = FALSE; FLMBOOL bDbUnlocked = FALSE; XFLM_DB_STATS * pDbStats = NULL; F_TMSTAMP StartTime; f_mutexLock( m_hBufMutex); bMutexLocked = TRUE; m_pCurrentBuf->bTransInProgress = FALSE; flmAssert( pDb->m_uiFlags & FDB_HAS_WRITE_LOCK); // When recovering or restoring all we need to do is write out // the database header. if (pDb->m_uiFlags & FDB_REPLAYING_RFL) { if (pDb->m_bHadUpdOper && m_pCurrentBuf->bOkToWriteHdrs) { f_mutexUnlock( m_hBufMutex); bMutexLocked = FALSE; if (RC_BAD( rc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, &m_pCurrentBuf->dbHdr, &m_pCurrentBuf->cpHdr, FALSE))) { m_pDatabase->setMustCloseFlags( rc, FALSE); } } goto Exit; } // Handle empty transactions differently. // These transactions should not do any writing and do not need to // wait for all writes to complete, unless the bOkToUnlock flag // is set to FALSE. In that case they must wait for all writes // to complete before unlocking. if (!pDb->m_bHadUpdOper) { // If the current buffer has a waiter, add self to that list // to wait, because it will be notified after the commit buffer // has been notified. Otherwise, if there is a commit in // progress, add self to that list to wait. if (m_pCurrentBuf->pFirstWaiter) { // If m_pCommitBuf is NULL then nobody is going to wake up // the first waiter - we must do it. if (!m_pCommitBuf) { if (bOkToUnlock) { pDb->unlinkFromTransList( bCommitting); bDbUnlocked = TRUE; } m_pCommitBuf = m_pCurrentBuf; switchBuffers(); wakeUpWaiter( NE_XFLM_OK, TRUE); if (!bOkToUnlock) { bMutexLocked = FALSE; (void)waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE); } } else if (!bOkToUnlock) { bMutexLocked = FALSE; (void)waitForWrites( pDb->m_hWaitSem, m_pCurrentBuf, FALSE); } } else if (m_pCommitBuf) { if (!bOkToUnlock) { bMutexLocked = FALSE; rc = waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE); } } goto Exit; } // If there is a transaction committing, put self into // the wait list on the current buffer. When the committer // finishes, he will wake up the first thread in the list // and that thread will commit the buffer. if (m_pCommitBuf) { FLMBOOL bIsWriter; // Another thread has to be doing the writes to m_pCommitBuf, // which means that m_pCurrentBuf better not be equal to // m_pCommitBuf. flmAssert( m_pCommitBuf != m_pCurrentBuf); // If there are no waiters, we are the first one, so when // we get signaled, we should proceed and do the write. bIsWriter = m_pCurrentBuf->pFirstWaiter ? FALSE : TRUE; if (bOkToUnlock) { pDb->unlinkFromTransList( bCommitting); bDbUnlocked = TRUE; } bMutexLocked = FALSE; rc = waitForWrites( pDb->m_hWaitSem, m_pCurrentBuf, bIsWriter); // If we were the first one in the queue, we must now // do the write. if (!bIsWriter) { goto Exit; } // First one in the queue, fall through to do the write. // The thread that woke me up better have set m_pCommitBuf // See below. flmAssert( m_pCommitBuf); } else if (m_pCurrentBuf->pFirstWaiter) { // Another thread is ready to commit the next set of // buffers, but just needs to be woke up. if (bOkToUnlock) { pDb->unlinkFromTransList( bCommitting); bDbUnlocked = TRUE; } // Need to set things up for that first waiter and get him // going. m_pCommitBuf = m_pCurrentBuf; switchBuffers(); wakeUpWaiter( rc, TRUE); // Wait for the write to be completed. bMutexLocked = FALSE; rc = waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE); goto Exit; } else { m_pCommitBuf = m_pCurrentBuf; switchBuffers(); if (bOkToUnlock) { pDb->unlinkFromTransList( bCommitting); bDbUnlocked = TRUE; } f_mutexUnlock( m_hBufMutex); bMutexLocked = FALSE; } // NOTE: From this point on we use tmpRc because we don't want to // lose the rc that may have been set above in the call to // waitForWrites // At this point the mutex better not be locked. flmAssert( !bMutexLocked); bNotifyWaiters = TRUE; if( (pDbStats = pDb->m_pDbStats) != NULL) { f_timeGetTimeStamp( &StartTime); } // Must write out whatever we have in the commit buffer before // unlocking the database. if (RC_BAD( tmpRc = flush( pDb, m_pCommitBuf, TRUE))) { if (RC_OK( rc)) { rc = tmpRc; } goto Exit; } // Wait for any pending IO off of the log buffer if (RC_BAD( tmpRc = m_pCommitBuf->pBufferMgr->waitForAllPendingIO())) { if (RC_OK( rc)) { rc = tmpRc; } goto Exit; } // Force the RFL writes to disk if necessary. // NOTE: It is possible for m_pFileHdl to be NULL at this point if // there were no operations actually logged. This happens in // FlmDbUpgrade (see flconvrt.cpp). Even though nothing was logged // the transaction is not an empty transaction, because it still needs // to write out the log header. if (m_pFileHdl) { if (RC_BAD( tmpRc = m_pFileHdl->flush())) { // Remap disk full error if (tmpRc == NE_FLM_IO_DISK_FULL) { rc = RC_SET( NE_XFLM_RFL_DISK_FULL); m_bRflVolumeFull = TRUE; } else if (RC_OK( rc)) { rc = tmpRc; } m_bRflVolumeOk = FALSE; goto Exit; } } // Write the log header if (m_pCommitBuf->bOkToWriteHdrs) { if (RC_BAD( tmpRc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, &m_pCommitBuf->dbHdr, &m_pCommitBuf->cpHdr, FALSE))) { if (RC_OK( rc)) { rc = tmpRc; } m_pDatabase->setMustCloseFlags( tmpRc, FALSE); goto Exit; } } Exit: if (!bDbUnlocked && bOkToUnlock) { pDb->unlinkFromTransList( bCommitting); } if (bNotifyWaiters) { FLMUINT uiNumFinished = 1; // For self flmAssert( !bMutexLocked); f_mutexLock( m_hBufMutex); bMutexLocked = TRUE; // Wake up any waiters while (m_pCommitBuf->pFirstWaiter) { uiNumFinished++; wakeUpWaiter( rc, FALSE); } // If there are waiters on the current buffer, the first one // should be woke up so it can start the next set of writes. if (m_pCurrentBuf->pFirstWaiter && !m_pCurrentBuf->bTransInProgress) { flmAssert( m_pCurrentBuf != m_pCommitBuf); m_pCommitBuf = m_pCurrentBuf; switchBuffers(); wakeUpWaiter( rc, TRUE); } else { m_pCommitBuf = NULL; } if (pDbStats) { flmAddElapTime( &StartTime, &pDbStats->UpdateTransStats.GroupCompletes.ui64ElapMilli); pDbStats->UpdateTransStats.GroupCompletes.ui64Count++; pDbStats->bHaveStats = TRUE; pDbStats->UpdateTransStats.ui64GroupFinished += uiNumFinished; } } if (bMutexLocked) { f_mutexUnlock( m_hBufMutex); } return( rc); } /******************************************************************** Desc: Flush all completed packets out of the RFL buffer, and shift the new partial packet down. This guarantees that there is now room in the buffer for the maximum packet size. *********************************************************************/ RCODE F_Rfl::shiftPacketsDown( F_Db * pDb, FLMUINT uiCurrPacketLen, FLMBOOL bStartingNewFile) { RCODE rc = NE_XFLM_OK; // The call to flush will move whatever needs to be moved from // the current buffer into a new buffer if multiple buffers are // being used. If only one buffer is being used, it will move // the part of the packet that needs to be moved down to the // beginning of the buffer - AFTER writing out the buffer. if (RC_BAD( rc = flush( pDb, m_pCurrentBuf, FALSE, uiCurrPacketLen, bStartingNewFile))) { goto Exit; } // NOTE: If multiple buffers are being used, whatever was moved // to the new buffer has not yet been written out if (bStartingNewFile) { if( RC_BAD( rc = waitPendingWrites())) { goto Exit; } } Exit: return( rc); } /******************************************************************** Desc: Determine if we should start a new file. If we are over the low limit, and the bDoNewIfOverLowLimit flag is set, we will start a new log file. Or, if this packet size would put us over the upper limit, we will start a new log file. *********************************************************************/ RCODE F_Rfl::seeIfNeedNewFile( F_Db * pDb, FLMUINT uiPacketLen, FLMBOOL bDoNewIfOverLowLimit) { RCODE rc = NE_XFLM_OK; FLMBYTE ucNextSerialNum [XFLM_SERIAL_NUM_SIZE]; flmAssert( m_pDatabase); // If the keep files flag is FALSE, we won't start // a new file. NOTE: This should ALWAYS be false // for pre 4.3 databases. if( !m_bKeepRflFiles) { goto Exit; } // VERY IMPORTANT NOTE: It is preferrable that we keep transactions // entirely contained in the same RFL file if at all possible. Note // that it is NOT a hard and fast requirement. The system will work // just fine if we don't. However, it would be nice if RFL files // always ended with a commit or abort packet. This preferences is // due to what happens after a restore operation. After a restore // operation, we always need to start a new RFL file, but if possible, // we would like that new RFL file to be the next one in the sequence // after the last RFL file that was restored. We can only do this if // we were able to restore EVERY transaction that was in the last // restored RFL file - which we can only do if the last restored RFL // file ended with a commit or abort packet. // To accomplish this end, we try to roll to new files on the first // transaction begin packet that occurs after we have exceeded our // low threshold - which is why bDoNewIfOverLowLimit is only set to // TRUE on transaction begin packets. It is set to FALSE on other // packets so that we will continue logging the transaction in the // same file that we started the transaction in - if possible. The // only thing that will cause a non-transaction-begin packet to roll // to a new file is if we would exceed the high limit. if ((bDoNewIfOverLowLimit && m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >= m_uiRflMinFileSize) || (m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes + uiPacketLen >= m_uiRflMaxFileSize)) { FLMUINT uiCurrFileEOF = m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes; // Shift the current packet to the beginning of the buffer. // Any packets in the buffer before that one will be written // out to the current file. if (RC_BAD( rc = shiftPacketsDown( pDb, uiPacketLen, TRUE))) { goto Exit; } // Update the header of the current file and close it. if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiCurrFileEOF, m_ucCurrSerialNum, m_ucNextSerialNum, TRUE))) { goto Exit; } // Truncate the file. if (!ON_512_BYTE_BOUNDARY( uiCurrFileEOF)) { uiCurrFileEOF = ROUND_DOWN_TO_NEAREST_512( uiCurrFileEOF) + 512; } if (RC_BAD( rc = m_pFileHdl->truncateFile( uiCurrFileEOF))) { goto Exit; } // Close the file handle. m_pFileHdl->closeFile(); m_pFileHdl->Release(); m_pFileHdl = NULL; // Get the next serial number that will be used for the RFL // file after this one. if (RC_BAD( rc = f_createSerialNumber( ucNextSerialNum))) { goto Exit; } // Create next file in the sequence. // Use the next serial number stored in the FDB's log header // for the serial number on this RFL file. Use the serial // number we just generated as the next RFL serial number. if (RC_BAD( rc = createFile( pDb, m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum, ucNextSerialNum, TRUE))) { goto Exit; } // Move the next serial number to the current serial number // and the serial number we generated above into the next // serial number. f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( m_ucNextSerialNum, ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); } Exit: return( rc); } /******************************************************************** Desc: Finish the current RFL file - set up so that next transaction will begin a new RFL file. *********************************************************************/ RCODE F_Rfl::finishCurrFile( F_Db * pDb, FLMBOOL bNewKeepState) { RCODE rc = NE_XFLM_OK; FLMBOOL bDbLocked = FALSE; FLMUINT uiTransFileNum; FLMUINT uiTransOffset; FLMUINT uiTruncateSize; XFLM_DB_HDR * pUncommittedDbHdr; XFLM_DB_HDR checkpointDbHdr; // Make sure we don't have a transaction going if (pDb->m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // Make sure there is no active backup running m_pDatabase->lockMutex(); if (m_pDatabase->m_bBackupActive) { m_pDatabase->unlockMutex(); rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); goto Exit; } m_pDatabase->unlockMutex(); // Lock the database - need to prevent update // transactions and checkpoint thread from running. if (RC_BAD( rc = pDb->lockExclusive( FLM_NO_TIMEOUT))) { goto Exit; } bDbLocked = TRUE; // Must wait for all RFL writes before switching files. (void)seeIfRflWritesDone( pDb->m_hWaitSem, TRUE); // Better not be in the middle of a transaction. flmAssert( !m_ui64CurrTransID); pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; // Don't want to copy last committed log header into // uncommitted log header if bNewKeepState is TRUE because // the caller has already done it, and has made modifications // to the uncommitted log header that we don't want to lose. if (!bNewKeepState) { f_memcpy( pUncommittedDbHdr, &m_pDatabase->m_lastCommittedDbHdr, sizeof( XFLM_DB_HDR)); // If we are in a no-keep state, but we were not told that // we have a new keep state, we cannot roll to the next // RFL file, because a checkpoint has not been done. This is // not an error - it is just the case where FlmDbConfig was // asked to roll to the next RFL file when the keep flag was // still FALSE. if (!pUncommittedDbHdr->ui8RflKeepFiles) { goto Exit; } } // Get the last committed serial numbers from the file's log header // buffer. f_memcpy( m_ucCurrSerialNum, pUncommittedDbHdr->ucLastTransRflSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( m_ucNextSerialNum, pUncommittedDbHdr->ucNextRflSerialNum, XFLM_SERIAL_NUM_SIZE); uiTransFileNum = (FLMUINT)pUncommittedDbHdr->ui32RflCurrFileNum; uiTransOffset = (FLMUINT)pUncommittedDbHdr->ui32RflLastTransOffset; // If ui32RflLastTransOffset is zero, there is no need to go set // up to go to the next file, because we are already poised to do so at // the beginning of the next transaction. Just return if this is the // case. Same for if the file does not exist. if (!uiTransOffset) { if (!bNewKeepState) { goto Exit; } } else if (RC_BAD( rc = openFile( pDb->m_hWaitSem, uiTransFileNum, m_ucCurrSerialNum))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; if (!bNewKeepState) { goto Exit; } } else { goto Exit; } } else { // At this point, we know the file exists, so we will update // its header and then update the log header. Note that we // use the keep RFL state from the last committed log header, // not the uncommitted log header - because it will contain // the correct keep-state for the current RFL file. if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiTransOffset, m_ucCurrSerialNum, m_ucNextSerialNum, m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles ? TRUE : FALSE))) { goto Exit; } // Truncate the file down to its EOF size - the nearest 512 byte boundary. uiTruncateSize = uiTransOffset; if (!ON_512_BYTE_BOUNDARY( uiTruncateSize)) { uiTruncateSize = ROUND_DOWN_TO_NEAREST_512( uiTruncateSize) + 512; } if (RC_BAD( rc = m_pFileHdl->truncateFile( uiTruncateSize))) { goto Exit; } // Close the file handle. m_pFileHdl->closeFile(); m_pFileHdl->Release(); m_pFileHdl = NULL; // Set things up in the log header to go to the next file when // we begin the next transaction. NOTE: NO need to lock the // mutex, because nobody but an update transaction looks at // the uncommitted log header. uiTransFileNum++; pUncommittedDbHdr->ui32RflCurrFileNum = (FLMUINT32)uiTransFileNum; } // Generate a new current serial number if bNewKeepState is // TRUE. Otherwise, move the next serial number into the current // serial number. if (bNewKeepState) { if (RC_BAD( rc = f_createSerialNumber( m_ucCurrSerialNum))) { goto Exit; } } else { f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); } // Always generate a new next serial number. if (RC_BAD( rc = f_createSerialNumber( m_ucNextSerialNum))) { goto Exit; } // Set transaction offset to zero. This will force the // next RFL file to be created on the next transaction // begin. It will be created even if it is already // there. pUncommittedDbHdr->ui32RflLastTransOffset = 0; f_memcpy( pUncommittedDbHdr->ucLastTransRflSerialNum, m_ucCurrSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( pUncommittedDbHdr->ucNextRflSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); // Set the CP file number and CP offset to point into the new file. // The outer code (FlmDbConfig) has done a checkpoint and the database // is still locked. We need to set these values here, otherwise // if we crash before the next checkpoint, recovery will start in the // old RFL file, causing an NE_XFLM_BAD_RFL_SERIAL_NUM to be returned when // traversing from the old RFL file to the new RFL file. // NOTE: These changes must be made to the uncommitted log header AND // the CP log header (so that they will be written out even // though we are not forcing a checkpoint). if (bNewKeepState) { // Do a quick check to see if it looks like we are in a // checkpointed state if( !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles && m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset > 512) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } f_memcpy( &checkpointDbHdr, &m_pDatabase->m_checkpointDbHdr, sizeof( XFLM_DB_HDR)); checkpointDbHdr.ui32RflLastCPFileNum = (FLMUINT32)uiTransFileNum; pUncommittedDbHdr->ui32RflLastCPFileNum = (FLMUINT32)uiTransFileNum; checkpointDbHdr.ui32RflLastCPOffset = 512; pUncommittedDbHdr->ui32RflLastCPOffset = 512; } // Write out the db header to disk. if (RC_BAD( rc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, pUncommittedDbHdr, bNewKeepState ? &checkpointDbHdr : &m_pDatabase->m_checkpointDbHdr, FALSE))) { goto Exit; } // Copy the uncommitted log header back to the committed log header and // copy the CP log header (if changed above). m_pDatabase->lockMutex(); f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pUncommittedDbHdr, sizeof( XFLM_DB_HDR)); if( bNewKeepState) { f_memcpy( &m_pDatabase->m_checkpointDbHdr, &checkpointDbHdr, sizeof( XFLM_DB_HDR)); } m_pDatabase->unlockMutex(); Exit: if (bDbLocked) { (void)pDb->unlockExclusive(); } return( rc); } /******************************************************************** Desc: Finish packet by outputting header information for it. *********************************************************************/ RCODE F_Rfl::finishPacket( F_Db * pDb, FLMUINT uiPacketType, FLMUINT uiPacketBodyLen, FLMBOOL bDoNewIfOverLowLimit) { RCODE rc = NE_XFLM_OK; FLMUINT uiPacketLen; FLMBYTE * pucPacket; uiPacketLen = uiPacketBodyLen + RFL_PACKET_OVERHEAD; // See if this packet will cause us to overflow the limits on // the current file. If so, create a new file. if( RC_BAD( rc = seeIfNeedNewFile( pDb, uiPacketLen, bDoNewIfOverLowLimit))) { goto Exit; } // Get a pointer to packet header. pucPacket = &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[ m_pCurrentBuf->uiRflBufBytes]); // Set the packet address in the packet header. m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes; UD2FBA( (FLMUINT32)m_uiPacketAddress, &pucPacket [RFL_PACKET_ADDRESS_OFFSET]); // Set the packet type and packet body length. pucPacket [RFL_PACKET_TYPE_OFFSET] = (FLMBYTE)uiPacketType; UW2FBA( (FLMUINT16)uiPacketBodyLen, &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]); // Set the checksum for the packet. pucPacket [RFL_PACKET_CHECKSUM_OFFSET] = RflCalcChecksum( pucPacket, uiPacketBodyLen); // Increment bytes in the buffer to reflect the fact that this packet // is now complete. m_pCurrentBuf->uiRflBufBytes += uiPacketLen; Exit: return( rc); } /******************************************************************** Desc: Truncate roll-forward log file to a certain size - only do if not keeping RFL files. *********************************************************************/ RCODE F_Rfl::truncate( F_SEM hWaitSem, FLMUINT uiTruncateSize) { RCODE rc = NE_XFLM_OK; FLMUINT uiFileNum; flmAssert( uiTruncateSize >= 512); // Keeping of log files better not be enabled. flmAssert( !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles); // Better not be in the middle of a transaction. flmAssert( !m_ui64CurrTransID); // Open the current RFL file. If it does not exist, it is OK - there // is nothing to truncate. uiFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; if (RC_BAD( rc = openFile( hWaitSem, uiFileNum, m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum))) { if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME) { rc = NE_XFLM_OK; } goto Exit; } if (RC_BAD( rc = m_pFileHdl->truncateFile( uiTruncateSize))) { m_bRflVolumeOk = FALSE; goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Setup to begin a transaction *********************************************************************/ RCODE F_Rfl::setupTransaction( F_Db * pDb) { RCODE rc = NE_XFLM_OK; FLMUINT uiFileNum; FLMUINT uiLastTransOffset; FLMBOOL bCreateFile; f_mutexLock( m_hBufMutex); m_pCurrentBuf->bTransInProgress = TRUE; f_mutexUnlock( m_hBufMutex); // Get the last committed serial numbers from the file's log header // buffer. f_memcpy( m_ucCurrSerialNum, m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( m_ucNextSerialNum, m_pDatabase->m_lastCommittedDbHdr.ucNextRflSerialNum, XFLM_SERIAL_NUM_SIZE); uiFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; uiLastTransOffset = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; // If the LOG_RFL_LAST_TRANS_OFFSET is zero, we need to create the // next RFL file number no matter what. There are two cases where // this happens: 1) when the database is first created, and 2) after // a restore operation. if (!uiLastTransOffset) { bCreateFile = TRUE; // Close the current file, just in case we had opened it before. // At this point, it doesn't matter because we are going to // overwrite it. if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) { goto Exit; } closeFile(); } else if (RC_BAD( rc = openFile( pDb->m_hWaitSem, uiFileNum, m_ucCurrSerialNum))) { if (rc != NE_FLM_IO_PATH_NOT_FOUND && rc != NE_FLM_IO_INVALID_FILENAME) { goto Exit; } bCreateFile = TRUE; } else { bCreateFile = FALSE; } if (bCreateFile) { // If the log header indicates that data has already been logged // to the file, we need to return the I/O error rather than just // re-creating the file. This may mean that someone changed the // RFL directory without moving the RFL files properly. if (uiLastTransOffset > 512) { rc = RC_SET( NE_XFLM_RFL_FILE_NOT_FOUND); goto Exit; } // Create the RFL file if not found. // Use the next serial number stored in the FDB's log header // for the serial number on this RFL file. Use the serial // number we just generated as the next RFL serial number. if (RC_BAD( rc = createFile( pDb, uiFileNum, m_ucCurrSerialNum, m_ucNextSerialNum, m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles ? TRUE : FALSE))) { goto Exit; } } else { // Read in enough of the buffer from the RFL file so that // we are positioned on a 512 byte boundary. if (RC_BAD( positionTo( uiLastTransOffset))) { goto Exit; } } // These can only be changed when starting a transaction. m_bKeepRflFiles = m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles ? TRUE : FALSE; m_uiRflMaxFileSize = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflMaxFileSize; // Round maximum down to nearest 512 boundary. This is necessary // because we always write a minimum of 512 byte units in direct IO // mode. If we did not round the maximum down, our last packet could // end at an offset that is less than the maximum, but greater than // the nearest 512 byte boundary - technically within the user-specified // size limit. However, because we always write a full 512 bytes of data // to fill out the last sector when we are in direct IO mode, we would // end up with a file that was slightly larger than the user-specified // limit. The EOF in the header of the file would be below the limit, // but the actual file size would not be. Thus, the need to round down. m_uiRflMaxFileSize = ROUND_DOWN_TO_NEAREST_512( m_uiRflMaxFileSize); // The maximum cannot go below a certain threshold - must have room for // least one packet plus the header. if (m_uiRflMaxFileSize < RFL_MAX_PACKET_SIZE + 512) { m_uiRflMaxFileSize = RFL_MAX_PACKET_SIZE + 512; } else if (m_uiRflMaxFileSize > gv_XFlmSysData.uiMaxFileSize) { m_uiRflMaxFileSize = gv_XFlmSysData.uiMaxFileSize; } m_uiRflMinFileSize = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflMinFileSize; // Minimum RFL file size should not be allowed to be larger than // maximum! if (m_uiRflMinFileSize > m_uiRflMaxFileSize) { m_uiRflMinFileSize = m_uiRflMaxFileSize; } // Set the operation count to zero. m_uiOperCount = 0; m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); m_pFileHdl->setExtendSize( m_pDatabase->m_uiFileExtendSize); Exit: return( rc); } /******************************************************************** Desc: Log transaction begin. This routine will also make sure we have opened an RFL file. NOTE: The prior version of FLAIM (before 4.3) would log a time and set the RFL_TIME_LOGGED_FLAG bit in the packet type. This is no longer done. Old code should be compatible because it reads the flag. *********************************************************************/ RCODE F_Rfl::logBeginTransaction( F_Db * pDb) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } flmAssert( !(pDb->m_uiFlags & FDB_REPLAYING_RFL)); // Better not be in the middle of a transaction. flmAssert( !m_ui64CurrTransID); if( RC_BAD( rc = setupTransaction( pDb))) { goto Exit; } uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if (!haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if (RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the transaction ID. f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_TRNS_BEGIN_PACKET, uiPacketBodyLen, TRUE))) { goto Exit; } // Save the file offset for the start transaction packet. m_uiTransStartFile = m_pCurrentBuf->uiCurrFileNum; m_uiTransStartAddr = m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes - uiPacketBodyLen - RFL_PACKET_OVERHEAD; m_ui64CurrTransID = pDb->m_ui64CurrTransID; Exit: return( rc); } /******************************************************************** Desc: Do a transaction begin operation during restore or recovery. *********************************************************************/ RCODE F_Rfl::recovTransBegin( F_Db * pDb, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportBeginTrans( peAction, m_ui64CurrTransID))) { goto Exit; } if (*peAction == XFLM_RESTORE_ACTION_STOP) { // Need to set m_ui64CurrTransID to 0 since it was // set by getPacket(). We are not going to // start a transaction because of the user's request // to exit. m_ui64CurrTransID = 0; goto Exit; } } if (RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Flushes the RFL and sets some things in the log header. *********************************************************************/ void F_Rfl::finalizeTransaction( void) { FLMUINT uiRflTransEndOffset; XFLM_DB_HDR * pDbHdr = &m_pDatabase->m_uncommittedDbHdr; // Save the serial numbers and file numbers into the file's // uncommitted log header. pDbHdr->ui32RflCurrFileNum = (FLMUINT32)m_pCurrentBuf->uiCurrFileNum; uiRflTransEndOffset = getCurrWriteOffset(); pDbHdr->ui32RflLastTransOffset = (FLMUINT32)uiRflTransEndOffset; f_memcpy( pDbHdr->ucLastTransRflSerialNum, m_ucCurrSerialNum, XFLM_SERIAL_NUM_SIZE); f_memcpy( pDbHdr->ucNextRflSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); } /******************************************************************** Desc: Handles the commit and abort log operations. If aborting the transaction, or if the transaction was empty, we will simply throw away the entire transaction and not bother to log it. In that case we will reset transaction pointers, etc. back to the file and offset where the transaction began. We will also delete RFL files that were created during the transaction if necessary. NOTE: It is not essential that the RFL files be deleted. If they are not successfully deleted, they will be overwritten if need be when creating new ones. NOTE: The prior version of FLAIM (before 4.3) would log a time and set the RFL_TIME_LOGGED_FLAG bit in the packet type. This is no longer done. Old code should be compatible because it reads the flag. *********************************************************************/ RCODE F_Rfl::logEndTransaction( F_Db * pDb, FLMUINT uiPacketType, FLMBOOL bThrowLogAway, FLMBOOL * pbLoggedTransEnd) { RCODE rc = NE_XFLM_OK; RCODE rc2 = NE_XFLM_OK; FLMUINT uiLowFileNum; FLMUINT uiHighFileNum; char szRflFileName [F_PATH_MAX_SIZE]; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Initialize the "logged trans end" flag if( pbLoggedTransEnd) { *pbLoggedTransEnd = FALSE; } // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } flmAssert( m_pFileHdl); flmAssert( m_pDatabase); // If the transaction had no operations, throw it away - don't // even log the packet. An abort operation may also // elect to throw the log away even if there were // operations. That is determined by the bThrowLogAway flag. // The bThrowLogAway flag may be TRUE when doing a commit if // the caller knows that nothing happened during the transction. if (bThrowLogAway || !m_uiOperCount) { Throw_Away_Transaction: // If we have switched files, delete all but the file we // started in. if (m_pCurrentBuf->uiCurrFileNum != m_uiTransStartFile) { flmAssert( m_pCurrentBuf->uiCurrFileNum > m_uiTransStartFile); // File number in uncommitted log header better not // have been changed yet. It is only supposed to // be changed when the transaction finishes - i.e., in // this routine. Up until this point, it should only // be changed in m_pCurrentBuf->uiCurrFileNum. flmAssert( m_uiTransStartFile == (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum); uiLowFileNum = m_uiTransStartFile + 1; uiHighFileNum = m_pCurrentBuf->uiCurrFileNum; // Close the current file so it can be deleted. if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) { goto Exit; } closeFile(); // Delete as many of the files as possible. Don't worry // about errors here. while (uiLowFileNum <= uiHighFileNum) { FLMUINT uiFileNameSize = sizeof( szRflFileName); FLMBOOL bNameTruncated; getFullRflFileName( uiLowFileNum, szRflFileName, &uiFileNameSize, &bNameTruncated); if (!bNameTruncated) { (void)gv_XFlmSysData.pFileSystem->deleteFile( szRflFileName); } uiLowFileNum++; } } else { // If we are in the file the transaction started in, simply // reset to where the transaction started. if (RC_BAD( rc2 = positionTo( m_uiTransStartAddr))) { // If we got to this point because of a // "goto Throw_Away_Transaction", we don't want to // clobber the original error code. So, we use rc2 // temporarily and then determine if its value should // be set into rc. if( RC_OK( rc)) { rc = rc2; } rc2 = NE_XFLM_OK; goto Exit; } } } else { // Log a commit or abort packet. uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if (RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Throw_Away_Transaction; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the transaction ID. f_encodeSEN( m_ui64CurrTransID, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, FALSE))) { goto Throw_Away_Transaction; } finalizeTransaction(); if( pbLoggedTransEnd) { *pbLoggedTransEnd = TRUE; } } Exit: m_ui64CurrTransID = 0; return( RC_BAD( rc) ? rc : rc2); } /******************************************************************** Desc: Log a block chain free packet *********************************************************************/ RCODE F_Rfl::logBlockChainFree( F_Db * pDb, FLMUINT64 ui64MaintDocID, FLMUINT uiStartBlkAddr, FLMUINT uiEndBlkAddr, FLMUINT uiCount) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 4; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the maintenance document ID f_encodeSEN( ui64MaintDocID, &pucPacketBody); // Output the starting block address f_encodeSEN( uiStartBlkAddr, &pucPacketBody); // Output the ending block address f_encodeSEN( uiEndBlkAddr, &pucPacketBody); // Output the block count f_encodeSEN( uiCount, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_BLK_CHAIN_FREE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Free a chain of blocks *********************************************************************/ RCODE F_Rfl::recovBlockChainFree( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64MaintDocNum; FLMUINT uiStartBlkAddr; FLMUINT uiEndBlkAddr; FLMUINT uiCount; FLMUINT uiBlocksFreed; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64MaintDocNum))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiStartBlkAddr))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiEndBlkAddr))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCount))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportBlockChainFree( peAction, m_ui64CurrTransID, ui64MaintDocNum, uiStartBlkAddr, uiEndBlkAddr, uiCount))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->maintBlockChainFree( ui64MaintDocNum, uiCount, uiEndBlkAddr, &uiBlocksFreed))) { goto Exit; } if( uiCount != uiBlocksFreed) { rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Log index suspend and resume packets *********************************************************************/ RCODE F_Rfl::logIndexSuspendOrResume( F_Db * pDb, FLMUINT uiIndexNum, FLMUINT uiPacketType) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the index number. flmAssert( uiIndexNum <= FLM_MAX_UINT16); f_encodeSEN( uiIndexNum, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: Suspend or resume an index during restore or recovery. *********************************************************************/ RCODE F_Rfl::recovIndexSuspendResume( F_Db * pDb, FLMUINT uiPacketType, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiIndexNum; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiIndexNum))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( uiPacketType == RFL_INDEX_SUSPEND_PACKET) { if( RC_BAD( rc = m_pRestoreStatus->reportIndexSuspend( peAction, m_ui64CurrTransID, uiIndexNum))) { goto Exit; } } else { if( RC_BAD( rc = m_pRestoreStatus->reportIndexResume( peAction, m_ui64CurrTransID, uiIndexNum))) { goto Exit; } } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( uiPacketType == RFL_INDEX_SUSPEND_PACKET) { if( RC_BAD( rc = pDb->indexSuspend( uiIndexNum))) { goto Exit; } } else { if( RC_BAD( rc = pDb->indexResume( uiIndexNum))) { goto Exit; } } Exit: return( rc); } /******************************************************************** Desc: Log a reduce packet *********************************************************************/ RCODE F_Rfl::logReduce( F_Db * pDb, FLMUINT uiCount) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // We need to set up to log this packet as if we // were logging a transaction. The only difference // is that we don't log the begin transaction packet. if( RC_BAD( rc = setupTransaction( pDb))) { goto Exit; } uiMaxPacketBodyLen = 2 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the transaction ID. f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); // Output the count f_encodeSEN( uiCount, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_REDUCE_PACKET, uiPacketBodyLen, TRUE))) { goto Exit; } // Finalize the transaction (as if we were committing a transaction) finalizeTransaction(); Exit: return( rc); } /******************************************************************** Desc: Reduce the database during restore or recovery. *********************************************************************/ RCODE F_Rfl::recovReduce( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCount; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCount))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportReduce( peAction, m_ui64CurrTransID, uiCount))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { // Need to set m_ui64CurrTransID to 0 since it was // set by getPacket(). We are not going to // start a transaction because of the user's request // to exit. m_ui64CurrTransID = 0; goto Exit; } } if( RC_BAD( rc = pDb->reduceSize( uiCount, &uiCount))) { goto Exit; } Exit: m_ui64CurrTransID = 0; return( rc); } /******************************************************************** Desc: Log a database conversion packet Note: This routine performs most of the setup for logging a full transaction, but it does not cause begin and commit packets to be logged. It is a "standalone" transaction. *********************************************************************/ RCODE F_Rfl::logUpgrade( F_Db * pDb, FLMUINT uiOldVersion) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // We need to set up to log this packet as if we // were logging a transaction. The only difference // is that we don't log the begin transaction packet. if( RC_BAD( rc = setupTransaction( pDb))) { goto Exit; } uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if (RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the transaction ID f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); // Output the old database version f_encodeSEN( uiOldVersion, &pucPacketBody); // Output the new database version f_encodeSEN( XFLM_CURRENT_VERSION_NUM, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_UPGRADE_PACKET, uiPacketBodyLen, TRUE))) { goto Exit; } // Finalize the transaction (as if we were committing a transaction) finalizeTransaction(); Exit: m_ui64CurrTransID = 0; return( rc); } /******************************************************************** Desc: Upgrade the database during restore or recovery. *********************************************************************/ RCODE F_Rfl::recovUpgrade( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiOldDbVersion; FLMUINT uiNewDbVersion; if( uiPacketBodyLen != 8) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } uiOldDbVersion = (FLMUINT)FB2UD( pucPacketBody); pucPacketBody += 4; uiNewDbVersion = (FLMUINT)FB2UD( pucPacketBody); pucPacketBody += 4; if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportUpgrade( peAction, m_ui64CurrTransID, uiOldDbVersion, uiNewDbVersion))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { // Need to set m_ui64CurrTransID to 0 since it was // set by getPacket(). We are not going to // start a transaction because of the user's request // to exit. m_ui64CurrTransID = 0; goto Exit; } } // Attempt the conversion if the current version is less // than the target version and the target version is // less than or equal to the highest version supported // by this code. if( uiNewDbVersion > XFLM_CURRENT_VERSION_NUM) { rc = RC_SET( NE_XFLM_UNALLOWED_UPGRADE); goto Exit; } else if( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion < uiNewDbVersion) { // The logged "new" version may be a lesser version // than XFLM_CURRENT_VERSION_NUM, which is what FlmDbUpgrade // upgrades to. This is O.K. because the current version // should support all packets in the RFL for versions // that are less than it. Otherwise, the RFL chain // would have been broken by the original upgrade and it // would not have logged an upgrade packet. if( RC_BAD( rc = pDb->upgrade( NULL))) { goto Exit; } } Exit: m_ui64CurrTransID = 0; return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logEncryptionKey( F_Db * pDb, FLMUINT uiPacketType, FLMBYTE * pucKey, FLMUINT32 ui32KeyLen) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif flmAssert( uiPacketType == RFL_WRAP_KEY_PACKET || uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET); // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } if( RC_BAD( rc = setupTransaction( pDb))) { goto Exit; } uiMaxPacketBodyLen = (2 * FLM_MAX_SEN_LEN) + ui32KeyLen; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the transaction ID f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); // Store the length of the key f_encodeSEN( ui32KeyLen, &pucPacketBody); // If we were built without encryption, the key length will be zero, // so no need to store the key. if( ui32KeyLen) { f_memcpy( pucPacketBody, pucKey, ui32KeyLen); pucPacketBody += ui32KeyLen; } // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, TRUE))) { goto Exit; } finalizeTransaction(); Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovEncryptionKey( F_Db * pDb, FLMUINT uiPacketType, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiDBKeyLen; FLMBOOL bTransStarted = FALSE; XFLM_DB_HDR * pUncommittedLogHdr = &m_pDatabase->m_uncommittedDbHdr; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiDBKeyLen))) { goto Exit; } if( pucPacketBody + uiDBKeyLen != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( uiPacketType == RFL_WRAP_KEY_PACKET) { if( RC_BAD( rc = m_pRestoreStatus->reportWrapKey( peAction, m_ui64CurrTransID))) { goto Exit; } } else if( uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET) { if( RC_BAD( rc = m_pRestoreStatus->reportEnableEncryption( peAction, m_ui64CurrTransID))) { goto Exit; } } else { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { m_ui64CurrTransID = 0; goto Exit; } } if( uiDBKeyLen) { // We cannot directly set the key at this point as it may be // encrypted using a password, which we do not have here. We will // write the key out to the log header and trust the user to know whether // or not a password is needed to open the database. if( RC_BAD(rc = pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bTransStarted = TRUE; if( uiDBKeyLen > XFLM_MAX_ENC_KEY_SIZE) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_ENCKEY_SIZE); goto Exit; } f_memcpy( pUncommittedLogHdr->DbKey, pucPacketBody, uiDBKeyLen); pUncommittedLogHdr->ui32DbKeyLen = (FLMUINT32)uiDBKeyLen; pDb->m_bHadUpdOper = TRUE; if( RC_BAD( rc = pDb->commitTrans( 0, TRUE))) { goto Exit; } bTransStarted = FALSE; } Exit: if( bTransStarted) { RCODE tmpRc; if( RC_BAD( tmpRc = pDb->commitTrans( 0, TRUE))) { pDb->abortTrans(); if (RC_OK( rc)) { rc = tmpRc; } } } m_ui64CurrTransID = 0; return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logEncDefKey( F_Db * pDb, FLMUINT uiEncDefId, FLMBYTE * pucKeyValue, FLMUINT uiKeyValueLen, FLMUINT uiKeySize) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = (3 * FLM_MAX_SEN_LEN) + uiKeyValueLen; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the encryption definition ID f_encodeSEN( uiEncDefId, &pucPacketBody); // Output the key size (number of bits) f_encodeSEN( uiKeySize, &pucPacketBody); // Output the key value length f_encodeSEN( uiKeyValueLen, &pucPacketBody); // Output the key f_memcpy( pucPacketBody, pucKeyValue, uiKeyValueLen); pucPacketBody += uiKeyValueLen; // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_ENC_DEF_KEY_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovEncDefKey( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction *) // peAction { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; F_DOMNode * pAttr = NULL; FLMUINT64 ui64RootId; FLMUINT uiKeySize; FLMUINT uiKeyValueLen; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64RootId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiKeySize))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiKeyValueLen))) { goto Exit; } if( pucPacketBody + uiKeyValueLen != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Retrieve the encryption definition if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, ui64RootId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } // Set the key in the DOM node as a binary string. if( RC_BAD( rc = pNode->createAttribute( pDb, ATTR_ENCRYPTION_KEY_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->removeModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pAttr->setBinary( pDb, pucPacketBody, uiKeyValueLen))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } // Set the key size if( RC_BAD( rc = pNode->createAttribute( pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG, (IF_DOMNode **)&pAttr))) { goto Exit; } if( RC_BAD( rc = pAttr->removeModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } if( RC_BAD( rc = pAttr->setUINT( pDb, uiKeySize))) { goto Exit; } if( RC_BAD( rc = pAttr->addModeFlags( pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } if( pAttr) { pAttr->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logRollOverDbKey( F_Db * pDb) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } if( RC_BAD( rc = setupTransaction( pDb))) { goto Exit; } uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the transaction ID f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_ROLL_OVER_DB_KEY_PACKET, uiPacketBodyLen, TRUE))) { goto Exit; } finalizeTransaction(); Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovRollOverDbKey( F_Db * pDb, const FLMBYTE *, // pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; if( uiPacketBodyLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportRollOverDbKey( peAction, m_ui64CurrTransID))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { m_ui64CurrTransID = 0; goto Exit; } } if( RC_BAD( rc = pDb->rollOverDbKey())) { goto Exit; } Exit: m_ui64CurrTransID = 0; return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logDocumentDone( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64RootId) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 2 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the document ID f_encodeSEN( ui64RootId, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_DOCUMENT_DONE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovDocumentDone( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64RootId; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64RootId))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if (RC_BAD( rc = m_pRestoreStatus->reportDocumentDone( peAction, m_ui64CurrTransID, uiCollection, ui64RootId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->documentDone( uiCollection, ui64RootId))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeDelete( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 2; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_DELETE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeDelete( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64NodeId; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeDelete( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pNode->deleteNode( pDb))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logAttributeDelete( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64ElementId, FLMUINT uiAttrName) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 3; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the element ID f_encodeSEN( ui64ElementId, &pucPacketBody); // Output the attribute name f_encodeSEN( uiAttrName, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_ATTR_DELETE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovAttributeDelete( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64ElementId; FLMUINT uiAttrName; F_DOMNode * pElementNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64ElementId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiAttrName))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportAttributeDelete( peAction, m_ui64CurrTransID, uiCollection, ui64ElementId, uiAttrName))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64ElementId, XFLM_EXACT, &pElementNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pElementNode->deleteAttribute( pDb, uiAttrName))) { goto Exit; } Exit: if( pElementNode) { pElementNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeChildrenDelete( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiNameId) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the name ID f_encodeSEN( uiNameId, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_CHILDREN_DELETE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeChildrenDelete( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiNameId; FLMUINT64 ui64NodeId; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiNameId))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeChildrenDelete( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, uiNameId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pNode->deleteChildren( pDb, uiNameId))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeCreate( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64RefNodeId, eDomNodeType eNodeType, FLMUINT uiNameId, eNodeInsertLoc eLocation, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN) + 2; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the reference node ID f_encodeSEN( ui64RefNodeId, &pucPacketBody); // Output the name ID f_encodeSEN( uiNameId, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the node type *pucPacketBody = (FLMBYTE)eNodeType; pucPacketBody++; // Output the insert location *pucPacketBody = (FLMBYTE)eLocation; pucPacketBody++; // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_CREATE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeCreate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiNameId; FLMUINT64 ui64RefNodeId; FLMUINT64 ui64ExpectedNodeId; FLMUINT64 ui64ActualNodeId = 0; eDomNodeType eNodeType; eNodeInsertLoc eLocation; IF_DOMNode * pRefNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64RefNodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiNameId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64ExpectedNodeId))) { goto Exit; } if( (pucEnd - pucPacketBody) != 2) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } eNodeType = (eDomNodeType)*pucPacketBody; pucPacketBody++; eLocation = (eNodeInsertLoc)*pucPacketBody; pucPacketBody++; if (m_pRestoreStatus) { if (RC_BAD( rc = m_pRestoreStatus->reportNodeCreate( peAction, m_ui64CurrTransID, uiCollection, ui64RefNodeId, eNodeType, uiNameId, eLocation))) { goto Exit; } if (*peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( eLocation == XFLM_ROOT) { if( RC_BAD( rc = pDb->createRootNode( uiCollection, uiNameId, eNodeType, NULL, &ui64ActualNodeId))) { goto Exit; } } else { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64RefNodeId, &pRefNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pRefNode->createNode( pDb, eNodeType, uiNameId, eLocation, &pRefNode, &ui64ActualNodeId))) { goto Exit; } } if( ui64ActualNodeId != ui64ExpectedNodeId) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } Exit: if( pRefNode) { pRefNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logAttributeCreate( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64ElementNodeId, FLMUINT uiNameId, FLMUINT uiNextAttrNameId) { RCODE rc = NE_XFLM_OK; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN); // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the element node ID f_encodeSEN( ui64ElementNodeId, &pucPacketBody); // Output the name ID f_encodeSEN( uiNameId, &pucPacketBody); // Output the next attribute's name ID f_encodeSEN( uiNextAttrNameId, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_ATTR_CREATE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovAttributeCreate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiAttrNameId; FLMUINT uiNextAttrNameId; FLMUINT64 ui64ElementId; IF_DOMNode * pElementNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64ElementId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiAttrNameId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiNextAttrNameId))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if (m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeCreate( peAction, m_ui64CurrTransID, uiCollection, ui64ElementId, ATTRIBUTE_NODE, uiAttrNameId, XFLM_ATTRIBUTE))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64ElementId, &pElementNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pElementNode->createAttribute( pDb, uiAttrNameId, NULL))) { goto Exit; } Exit: if( pElementNode) { pElementNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logInsertBefore( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64Parent, FLMUINT64 ui64Child, FLMUINT64 ui64RefChild) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 4 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the parent ID f_encodeSEN( ui64Parent, &pucPacketBody); // Output the child ID f_encodeSEN( ui64Child, &pucPacketBody); // Output the ref child ID f_encodeSEN( ui64RefChild, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_INSERT_BEFORE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovInsertBefore( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64Parent; FLMUINT64 ui64NewChild; FLMUINT64 ui64RefChild; F_DOMNode * pParent = NULL; F_DOMNode * pNewChild = NULL; F_DOMNode * pRefChild = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64Parent))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NewChild))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64RefChild))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportInsertBefore( peAction, m_ui64CurrTransID, uiCollection, ui64Parent, ui64NewChild, ui64RefChild))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64Parent, XFLM_EXACT, &pParent))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NewChild, XFLM_EXACT, &pNewChild))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( ui64RefChild) { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64RefChild, XFLM_EXACT, &pRefChild))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } } if( RC_BAD( rc = pParent->insertBefore( pDb, pNewChild, pRefChild))) { goto Exit; } Exit: if( pParent) { pParent->Release(); } if( pNewChild) { pNewChild->Release(); } if( pRefChild) { pRefChild->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logEncryptedNodeUpdate( F_Db * pDb, F_CachedNode * pCachedNode) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiDataLength; IF_PosIStream * pIStream = NULL; F_RflOStream rflOStream( this, pDb); FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } if( RC_BAD( rc = pCachedNode->getRawIStream( pDb, &pIStream))) { goto Exit; } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( pCachedNode->getCollection(), &pucPacketBody); // Output the node ID f_encodeSEN( pCachedNode->getNodeId(), &pucPacketBody); // Output the stream length uiDataLength = (FLMUINT)pIStream->remainingSize(); flmAssert( uiDataLength); f_encodeSEN( uiDataLength, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_ENC_NODE_UPDATE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } // Log the data if( RC_BAD( rc = rflOStream.write( pIStream))) { goto Exit; } flmAssert( !pIStream->remainingSize()); Exit: if( pIStream) { pIStream->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovEncryptedNodeUpdate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiDataLen; FLMUINT64 ui64NodeId; FLMUINT uiPacketType; const FLMBYTE * pucDataPacketBody; FLMUINT uiDataPacketBodyLen; F_Btree * pBTree = NULL; F_COLLECTION * pCollection = NULL; FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; FLMUINT uiKeyLen; FLMBOOL bFirst; FLMBOOL bLast; FLMBOOL bUseDataOnlyBlocks; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiDataLen))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeUpdate( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } // Retrieve the node so we can clean up its keys if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pNode, IX_DEL_NODE_VALUE, TRUE))) { goto Exit; } // Clear the node cache before directly accessing the B-Tree if( RC_BAD( rc = pDb->flushDirtyNodes())) { goto Exit; } pDb->m_pDatabase->freeNodeCache(); // Open the B-Tree if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) { goto Exit; } if( RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) { goto Exit; } if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) { goto Exit; } // Build the B-Tree key uiKeyLen = sizeof( ucKey); if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen, ucKey, FALSE, TRUE))) { goto Exit; } // Determine if the item should be put into data only blocks bUseDataOnlyBlocks = useDataOnlyBlocks( pDb, uiDataLen); // Go into a loop processing packets until we have retrieved // all of the expected data. bFirst = TRUE; bLast = FALSE; while( uiDataLen) { if( RC_BAD( rc = getPacket( pDb, FALSE, &uiPacketType, &pucDataPacketBody, &uiDataPacketBodyLen))) { goto Exit; } if( uiPacketType != RFL_DATA_PACKET) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Packet body length better not be greater than the expected // data length if( uiDataPacketBodyLen > uiDataLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( uiDataLen == uiDataPacketBodyLen) { if( bFirst && bUseDataOnlyBlocks) { if( RC_BAD( rc = pBTree->btReplaceEntry( ucKey, uiKeyLen, pucDataPacketBody, uiDataPacketBodyLen, bFirst, FALSE))) { goto Exit; } uiDataPacketBodyLen = 0; } bLast = TRUE; } if( RC_BAD( rc = pBTree->btReplaceEntry( ucKey, uiKeyLen, pucDataPacketBody, uiDataPacketBodyLen, bFirst, bLast))) { goto Exit; } uiDataLen -= uiDataPacketBodyLen; bFirst = FALSE; } pNode->Release(); pNode = NULL; // Re-read the node from the btree pDb->m_pDatabase->freeNodeCache(); if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pNode, IX_ADD_NODE_VALUE, TRUE))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } if( pBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pBTree); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeSetValue( F_Db * pDb, FLMUINT uiPacketType, F_CachedNode * pCachedNode) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiDataLength; FLMUINT uiDataType; IF_PosIStream * pIStream = NULL; F_RflOStream rflOStream( this, pDb); FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; FLMBOOL bUseDataPackets = FALSE; F_NodeBufferIStream bufferIStream; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Make sure we have a valid packet type flmAssert( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET || uiPacketType == RFL_NODE_SET_BINARY_VALUE_PACKET); // If the data is encrypted we need to call a special logging routine if( pCachedNode->getEncDefId()) { rc = logEncryptedNodeUpdate( pDb, pCachedNode); goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Get the data stream and determine its length if( RC_BAD( rc = pCachedNode->getIStream( pDb, &bufferIStream, &pIStream, &uiDataType, &uiDataLength))) { goto Exit; } if( uiDataLength && uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) { FLMBYTE ucSEN; FLMUINT uiSENLen; // No reason to store the leading SEN, so skip it. if( RC_BAD( rc = pIStream->read( &ucSEN, 1, NULL))) { goto Exit; } uiSENLen = f_getSENLength( ucSEN); if( uiSENLen > 1) { if( RC_BAD( rc = pIStream->read( NULL, uiSENLen - 1, NULL))) { goto Exit; } } uiDataLength -= uiSENLen; } // Determine the maximum packet size uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN); if( uiMaxPacketBodyLen + uiDataLength > RFL_MAX_PACKET_BODY_SIZE) { bUseDataPackets = TRUE; } else { uiMaxPacketBodyLen += uiDataLength; } // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( pCachedNode->getCollection(), &pucPacketBody); // Output the node ID f_encodeSEN( pCachedNode->getNodeId(), &pucPacketBody); // Output the data length f_encodeSEN( uiDataLength, &pucPacketBody); // Output the data packet flag f_encodeSEN( bUseDataPackets ? 1 : 0, &pucPacketBody); // Output the data if we aren't using data packets if( !bUseDataPackets && uiDataLength) { if( RC_BAD( rc = pIStream->read( pucPacketBody, uiDataLength, NULL))) { goto Exit; } pucPacketBody += uiDataLength; } // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, FALSE))) { goto Exit; } // Log the data (if not done above) if( bUseDataPackets) { flmAssert( uiDataLength); if( RC_BAD( rc = rflOStream.write( pIStream))) { goto Exit; } flmAssert( !pIStream->remainingSize()); } Exit: if( pIStream) { pIStream->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeSetValue( F_Db * pDb, FLMUINT uiPacketType, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; F_DOMNode * pNode = NULL; FLMUINT uiCollection; FLMUINT uiDataLen; FLMUINT64 ui64NodeId; const FLMBYTE * pucDataPacketBody; FLMUINT uiDataPacketType; FLMUINT uiDataPacketBodyLen; FLMUINT uiHaveDataPackets; FLMBOOL bFirst; FLMBOOL bLast; FLMBOOL bUseDataOnlyBlocks; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiDataLen))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiHaveDataPackets))) { goto Exit; } if( uiHaveDataPackets) { if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } } else { if( pucPacketBody + uiDataLen != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } } if( uiPacketType != RFL_NODE_SET_TEXT_VALUE_PACKET && uiPacketType != RFL_NODE_SET_BINARY_VALUE_PACKET) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetValue( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } // Retrieve the node if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( !uiHaveDataPackets) { if( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) { if( RC_BAD( rc = pNode->setUTF8( pDb, pucPacketBody, uiDataLen, TRUE))) { goto Exit; } } else { if( RC_BAD( rc = pNode->setBinary( pDb, pucPacketBody, uiDataLen, TRUE))) { goto Exit; } } } else { // Determine if the item should be put into data only blocks bUseDataOnlyBlocks = useDataOnlyBlocks( pDb, uiDataLen); // Go into a loop processing packets until we have retrieved // all of the expected data. bFirst = TRUE; bLast = FALSE; while( uiDataLen) { if( RC_BAD( rc = getPacket( pDb, FALSE, &uiDataPacketType, &pucDataPacketBody, &uiDataPacketBodyLen))) { goto Exit; } pucEnd = pucDataPacketBody + uiDataPacketBodyLen; if( uiDataPacketType != RFL_DATA_PACKET) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Packet body length better not be greater than the expected // data length if( uiDataPacketBodyLen > uiDataLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( uiDataLen == uiDataPacketBodyLen) { if( bFirst && bUseDataOnlyBlocks) { if( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) { if( RC_BAD( rc = pNode->setUTF8( pDb, pucDataPacketBody, uiDataPacketBodyLen, FALSE))) { goto Exit; } } else { if( RC_BAD( rc = pNode->setBinary( pDb, pucDataPacketBody, uiDataPacketBodyLen, FALSE))) { goto Exit; } } uiDataPacketBodyLen = 0; } bLast = TRUE; } if( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) { if( RC_BAD( rc = pNode->setUTF8( pDb, pucDataPacketBody, uiDataPacketBodyLen, bLast))) { goto Exit; } } else { if( RC_BAD( rc = pNode->setBinary( pDb, pucDataPacketBody, uiDataPacketBodyLen, bLast))) { goto Exit; } } uiDataLen -= uiDataPacketBodyLen; bFirst = FALSE; } } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeFlagsUpdate( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId, FLMUINT uiFlags, FLMBOOL bAdd) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN) + 1; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the attribute name ID f_encodeSEN( uiAttrNameId, &pucPacketBody); // Output the flags f_encodeSEN( uiFlags, &pucPacketBody); // Output the "add" flag *pucPacketBody = bAdd ? 1 : 0; pucPacketBody++; // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_FLAGS_UPDATE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeFlagsUpdate( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiFlags; FLMUINT64 ui64NodeId; FLMUINT uiAttrNameId; FLMBOOL bAdd; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiAttrNameId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiFlags))) { goto Exit; } if( pucPacketBody + 1 != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } bAdd = *pucPacketBody ? TRUE : FALSE; pucPacketBody++; if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeFlagsUpdate( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, uiFlags, bAdd))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( uiAttrNameId) { pNode->m_uiAttrNameId = uiAttrNameId; } if( bAdd) { if( RC_BAD( pNode->addModeFlags( pDb, uiFlags))) { goto Exit; } } else { if( RC_BAD( rc = pNode->removeModeFlags( pDb, uiFlags))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeSetPrefixId( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrName, FLMUINT uiPrefixId) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 4 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the attribute name (if any) f_encodeSEN( uiAttrName, &pucPacketBody); // Output the prefix ID f_encodeSEN( uiPrefixId, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_SET_PREFIX_ID_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeSetPrefixId( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiAttrName; FLMUINT uiPrefix; FLMUINT64 ui64NodeId; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiAttrName))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiPrefix))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetPrefixId( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, uiAttrName, uiPrefix))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( uiAttrName) { if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId, uiAttrName, (IF_DOMNode **)&pNode))) { goto Exit; } } else { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } } if( RC_BAD( rc = pNode->setPrefixId( pDb, uiPrefix))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeSetMetaValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64MetaValue) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the meta value f_encodeSEN( ui64MetaValue, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_SET_META_VALUE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeSetMetaValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64NodeId; FLMUINT64 ui64MetaValue; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64MetaValue))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetMetaValue( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, ui64MetaValue))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pNode->setMetaValue( pDb, ui64MetaValue))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logSetNextNodeId( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NextNodeId) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 2; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the next node ID f_encodeSEN( ui64NextNodeId, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_SET_NEXT_NODE_ID_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovSetNextNodeId( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64NextNodeId; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NextNodeId))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportSetNextNodeId( peAction, m_ui64CurrTransID, uiCollection, ui64NextNodeId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->setNextNodeId( uiCollection, ui64NextNodeId))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeSetNumberValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64Value, FLMBOOL bNeg) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketBody; FLMBYTE * pucPacketStart; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = (3 * FLM_MAX_SEN_LEN) + 1; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the number f_encodeSEN( ui64Value, &pucPacketBody); // Output the sign flag *pucPacketBody = bNeg ? 1 : 0; pucPacketBody++; // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_SET_NUMBER_VALUE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeSetNumberValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT64 ui64NodeId; FLMUINT64 ui64Value; FLMBOOL bNegative; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64Value))) { goto Exit; } if( pucPacketBody + 1 != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } bNegative = *pucPacketBody ? TRUE : FALSE; pucPacketBody++; if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetValue( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( !bNegative) { if( RC_BAD( rc = pNode->setUINT64( pDb, ui64Value))) { goto Exit; } } else { if( RC_BAD( rc = pNode->setINT64( pDb, -((FLMINT64)ui64Value)))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logAttrSetValue( F_Db * pDb, F_CachedNode * pCachedNode, FLMUINT uiAttrName) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketBody; FLMBYTE * pucPacketStart; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; FLMUINT uiPayloadLen; F_AttrItem * pAttrItem; FLMBOOL bUseDataPackets = FALSE; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Get the attribute item if( (pAttrItem = pCachedNode->getAttribute( uiAttrName, NULL)) == NULL) { rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); goto Exit; } uiPayloadLen = pAttrItem->m_uiPayloadLen; // Determine the maximum packet size uiMaxPacketBodyLen = (7 * FLM_MAX_SEN_LEN) + pAttrItem->m_uiIVLen; if( uiMaxPacketBodyLen + uiPayloadLen > RFL_MAX_PACKET_BODY_SIZE) { bUseDataPackets = TRUE; } else { uiMaxPacketBodyLen += uiPayloadLen; } // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( pCachedNode->getCollection(), &pucPacketBody); // Output the element node ID f_encodeSEN( pCachedNode->getNodeId(), &pucPacketBody); // Output the name ID f_encodeSEN( uiAttrName, &pucPacketBody); // Output the encryption definition ID f_encodeSEN( pAttrItem->m_uiEncDefId, &pucPacketBody); // Output the initialization vector length and the // decrypted data length if( pAttrItem->m_uiEncDefId) { f_encodeSEN( pAttrItem->m_uiIVLen, &pucPacketBody); f_encodeSEN( pAttrItem->m_uiDecryptedDataLen, &pucPacketBody); } // Output the payload length f_encodeSEN( uiPayloadLen, &pucPacketBody); // Output the data packet flag f_encodeSEN( bUseDataPackets ? 1 : 0, &pucPacketBody); // Output the data if we aren't using data packets if( !bUseDataPackets && uiPayloadLen) { f_memcpy( pucPacketBody, pAttrItem->getAttrPayloadPtr(), uiPayloadLen); pucPacketBody += uiPayloadLen; } // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_ATTR_SET_VALUE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } // Log the data (if not done above) if( bUseDataPackets) { F_RflOStream rflOStream( this, pDb); flmAssert( uiPayloadLen); if( RC_BAD( rc = rflOStream.write( pAttrItem->getAttrPayloadPtr(), uiPayloadLen))) { goto Exit; } } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovAttrSetValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiAttrNameId; FLMUINT uiEncDefId; FLMUINT uiPayloadLen; FLMUINT uiDecryptedDataLen = 0; FLMUINT uiIVLen = 0; FLMUINT uiHaveDataPackets; FLMUINT uiTmp; FLMUINT64 ui64ElementId; FLMBYTE * pucData = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; FLMBYTE ucIV[ 16]; F_DOMNode * pElementNode = NULL; F_AttrElmInfo defInfo; IF_BufferIStream * pBufferIStream = NULL; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64ElementId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiAttrNameId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiEncDefId))) { goto Exit; } if( uiEncDefId) { if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiIVLen))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiDecryptedDataLen))) { goto Exit; } } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiPayloadLen))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiHaveDataPackets))) { goto Exit; } if( uiHaveDataPackets) { if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } } else { if( pucPacketBody + uiPayloadLen != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportAttributeSetValue( peAction, m_ui64CurrTransID, uiCollection, ui64ElementId, uiAttrNameId))) { goto Exit; } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( RC_BAD( rc = pDb->getNode( uiCollection, ui64ElementId, XFLM_EXACT, &pElementNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } if( RC_BAD( rc = pDb->m_pDict->getAttribute( pDb, uiAttrNameId, &defInfo))) { goto Exit; } if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferIStream))) { goto Exit; } if( !uiHaveDataPackets) { if( RC_BAD( rc = pBufferIStream->openStream( (const char *)pucPacketBody, uiPayloadLen))) { goto Exit; } } else { FLMUINT uiOffset = 0; FLMUINT uiDataPacketType; FLMUINT uiDataPacketBodyLen; const FLMBYTE * pucDataPacketBody; if( RC_BAD( rc = pBufferIStream->openStream( NULL, uiPayloadLen, (char **)&pucData))) { goto Exit; } // Go into a loop processing packets until we have retrieved // all of the expected data. while( uiOffset < uiPayloadLen) { if( RC_BAD( rc = getPacket( pDb, FALSE, &uiDataPacketType, &pucDataPacketBody, &uiDataPacketBodyLen))) { goto Exit; } if( uiDataPacketType != RFL_DATA_PACKET) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Packet body length better not be greater than the expected // data length if( uiDataPacketBodyLen + uiOffset > uiPayloadLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } f_memcpy( &pucData[ uiOffset], pucDataPacketBody, uiDataPacketBodyLen); uiOffset += uiDataPacketBodyLen; } } if( uiEncDefId) { if( RC_BAD( rc = pBufferIStream->read( ucIV, uiIVLen, NULL))) { goto Exit; } flmAssert( pucData); if( RC_BAD( rc = pDb->decryptData( uiEncDefId, ucIV, pucData, (FLMUINT)pBufferIStream->remainingSize(), pucData, (FLMUINT)pBufferIStream->remainingSize()))) { goto Exit; } pBufferIStream->truncateStream( (FLMUINT)(pBufferIStream->getCurrPosition() + uiDecryptedDataLen)); } switch( defInfo.getDataType()) { case XFLM_TEXT_TYPE: { FLMUINT uiTextLength = (FLMUINT)pBufferIStream->remainingSize(); const FLMBYTE * pucTmp; pucTmp = pBufferIStream->getBufferAtCurrentOffset(); // Skip the leading SEN uiTmp = f_getSENLength( pucTmp[ 0]); if( uiTmp > uiTextLength) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } uiTextLength -= uiTmp; pucTmp += uiTmp; if( RC_BAD( rc = pElementNode->setAttributeValueUTF8( pDb, uiAttrNameId, pucTmp, uiTextLength))) { goto Exit; } break; } case XFLM_BINARY_TYPE: { if( RC_BAD( rc = pElementNode->setAttributeValueBinary( pDb, uiAttrNameId, pBufferIStream->getBufferAtCurrentOffset(), (FLMUINT)pBufferIStream->remainingSize()))) { goto Exit; } break; } case XFLM_NUMBER_TYPE: { FLMUINT64 ui64Value; FLMBOOL bNeg; if( RC_BAD( rc = flmReadStorageAsNumber( pBufferIStream, XFLM_NUMBER_TYPE, &ui64Value, &bNeg))) { goto Exit; } if( bNeg) { if( RC_BAD( rc = pElementNode->setAttributeValueINT64( pDb, uiAttrNameId, -((FLMINT64)ui64Value)))) { goto Exit; } } else { if( RC_BAD( rc = pElementNode->setAttributeValueUINT64( pDb, uiAttrNameId, ui64Value))) { goto Exit; } } break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); break; } } Exit: if( pBufferIStream) { pBufferIStream->Release(); } if( pElementNode) { pElementNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::logNodeClearValue( F_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrName) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacketStart; FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen; FLMUINT uiMaxPacketBodyLen; flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); #ifndef FLM_DEBUG F_UNREFERENCED_PARM( pDb); #endif // Do nothing if logging is disabled. if( !isLoggingEnabled()) { goto Exit; } // Better be in the middle of a transaction. flmAssert( m_ui64CurrTransID); // Increment the operation count m_uiOperCount++; // Determine the maximum packet size uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; // Make sure we have space in the RFL buffer for a complete packet. if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } } // Get a pointer to where we will be laying down the packet body. pucPacketBody = pucPacketStart = getPacketBodyPtr(); // Output the collection number f_encodeSEN( uiCollection, &pucPacketBody); // Output the node ID f_encodeSEN( ui64NodeId, &pucPacketBody); // Output the attribute name (if any) f_encodeSEN( uiAttrName, &pucPacketBody); // Finish the packet uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_CLEAR_VALUE_PACKET, uiPacketBodyLen, FALSE))) { goto Exit; } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_Rfl::recovNodeClearValue( F_Db * pDb, const FLMBYTE * pucPacketBody, FLMUINT uiPacketBodyLen, eRestoreAction * peAction) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; FLMUINT uiAttrName; FLMUINT64 ui64NodeId; F_DOMNode * pNode = NULL; const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCollection))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) { goto Exit; } if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiAttrName))) { goto Exit; } if( pucPacketBody != pucEnd) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( m_pRestoreStatus) { if( uiAttrName) { if( RC_BAD( rc = m_pRestoreStatus->reportAttributeSetValue( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, uiAttrName))) { goto Exit; } } else { if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetValue( peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) { goto Exit; } } if( *peAction == XFLM_RESTORE_ACTION_STOP) { goto Exit; } } if( uiAttrName) { if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId, uiAttrName, (IF_DOMNode **)&pNode))) { goto Exit; } } else { if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, XFLM_EXACT, &pNode))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } } if( RC_BAD( rc = pNode->clearNodeValue( pDb))) { goto Exit; } Exit: if( pNode) { pNode->Release(); } return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE FLMAPI F_RflOStream::write( const void * pvBuffer, FLMUINT uiBytesToWrite, FLMUINT * puiBytesWritten) { RCODE rc = NE_XFLM_OK; FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; FLMUINT uiBytesAvail; FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; flmAssert( m_pRfl->isLoggingEnabled()); if( !m_pRfl->haveBuffSpace( RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = m_pRfl->flush( m_pDb, m_pRfl->m_pCurrentBuf))) { goto Exit; } } while( uiBytesToWrite) { if( RC_BAD( rc = m_pRfl->makeRoom( m_pDb, uiBytesToWrite, &uiPacketLen, RFL_DATA_PACKET, &uiBytesAvail, NULL))) { goto Exit; } f_memcpy( m_pRfl->getPacketPtr() + uiPacketLen, pucBuffer, uiBytesAvail); pucBuffer += uiBytesAvail; uiBytesToWrite -= uiBytesAvail; uiPacketLen += uiBytesAvail; if( RC_BAD( rc = m_pRfl->finishPacket( m_pDb, RFL_DATA_PACKET, uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) { goto Exit; } uiPacketLen = RFL_PACKET_OVERHEAD; } if( puiBytesWritten) { *puiBytesWritten = (FLMUINT)(pucBuffer - ((FLMBYTE *)pvBuffer)); } Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ RCODE F_RflOStream::write( IF_PosIStream * pIStream) { RCODE rc = NE_XFLM_OK; FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; FLMUINT uiBytesToWrite = (FLMUINT)pIStream->remainingSize(); FLMUINT uiBytesAvail; flmAssert( m_pRfl->isLoggingEnabled()); if( !m_pRfl->haveBuffSpace( RFL_PACKET_OVERHEAD)) { if( RC_BAD( rc = m_pRfl->flush( m_pDb, m_pRfl->m_pCurrentBuf))) { goto Exit; } } while( uiBytesToWrite) { if( RC_BAD( rc = m_pRfl->makeRoom( m_pDb, uiBytesToWrite, &uiPacketLen, RFL_DATA_PACKET, &uiBytesAvail, NULL))) { goto Exit; } if( RC_BAD( rc = pIStream->read( m_pRfl->getPacketPtr()+ uiPacketLen, uiBytesAvail))) { goto Exit; } uiBytesToWrite -= uiBytesAvail; uiPacketLen += uiBytesAvail; if( RC_BAD( rc = m_pRfl->finishPacket( m_pDb, RFL_DATA_PACKET, uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) { goto Exit; } uiPacketLen = RFL_PACKET_OVERHEAD; } Exit: return( rc); } /******************************************************************** Desc: Make room in the RFL buffer for the additional bytes. This is done by flushing the log buffer and shifting down the bytes already used in the current packet. If that doesn't make room, the current packet will be finished and a new one started. *********************************************************************/ RCODE F_Rfl::makeRoom( F_Db * pDb, FLMUINT uiAdditionalBytesNeeded, FLMUINT * puiCurrPacketLenRV, FLMUINT uiPacketType, FLMUINT * puiBytesAvailableRV, FLMUINT * puiPacketCountRV) { RCODE rc = NE_XFLM_OK; FLMUINT uiBytesNeeded; uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded; if( uiBytesNeeded <= (FLMUINT)RFL_MAX_PACKET_SIZE) { FLMUINT uiTmp = uiBytesNeeded; if( haveBuffSpace( uiTmp)) { if( puiBytesAvailableRV) { *puiBytesAvailableRV = uiAdditionalBytesNeeded; } } else { // Bytes requested will fit into a packet, but not the // buffer, so we need to shift the packets in the buffer // down. The shiftPacketsDown guarantees that there // is room in the buffer for a full size packet. if( RC_BAD( rc = shiftPacketsDown( pDb, *puiCurrPacketLenRV, FALSE))) { goto Exit; } // If a non-NULL puwBytesAvailableRV is passed in it means that we // are to return the number of bytes that we can actually output. // Since we know there is enough for the bytes needed, we simply return // the number of bytes that were requested. if( puiBytesAvailableRV) { *puiBytesAvailableRV = uiAdditionalBytesNeeded; } } } else // (uiBytesNeeded > RFL_MAX_PACKET_SIZE) { // This is the case where the bytes needed would overflow the // maximum packet size. // If puwBytesAvailableRV is NULL, it means that all of the // requested additional bytes must fit into the packet. In that // case, since the requested bytes would put us over the packet // size limit, we must finish the current packet and then // flush the packets out of the buffer so we can start a // new packet. if( !puiBytesAvailableRV) { // Finish the current packet and start a new one. if( puiPacketCountRV) { (*puiPacketCountRV)++; } if( RC_BAD( rc = finishPacket( pDb, uiPacketType, *puiCurrPacketLenRV - RFL_PACKET_OVERHEAD, FALSE))) { goto Exit; } if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) { goto Exit; } *puiCurrPacketLenRV = RFL_PACKET_OVERHEAD; } else { // When puiBytesAvailableRV is non-NULL, it means we can fill up // the rest of the packet with part of the bytes. In this case // we return the number of bytes available and then shift the // packets down in the buffer to make sure there is room for // a full-size packet. *puiBytesAvailableRV = RFL_MAX_PACKET_SIZE - *puiCurrPacketLenRV; if( RC_BAD( rc = shiftPacketsDown( pDb, *puiCurrPacketLenRV, FALSE))) { goto Exit; } } } Exit: return( rc); } /******************************************************************** Desc: Reads a full packet, based on what file offset and read offset are currently set to. *********************************************************************/ RCODE F_Rfl::readPacket( FLMUINT uiMinBytesNeeded) { RCODE rc = NE_XFLM_OK; FLMUINT uiTmpOffset; FLMUINT uiReadLen; FLMUINT uiBytesRead; // If we have enough bytes in the buffer for the minimum bytes // needed, we don't need to retrieve any more bytes. if( m_pCurrentBuf->uiRflBufBytes && m_pCurrentBuf->uiRflBufBytes >= m_uiRflReadOffset && m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset >= uiMinBytesNeeded) { goto Exit; } // If we are doing restore, we have to do only sequential // reads - cannot depend on doing reads on 512 byte boundaries. // Otherwise, we read directly from disk on 512 byte boundaries. if( m_pRestore) { FLMUINT uiCurrFilePos = m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes; if( m_uiRflReadOffset > 0 && m_pCurrentBuf->uiRflBufBytes >= m_uiRflReadOffset) { // Move the bytes left in the buffer down to the beginning // of the buffer. f_memmove( m_pCurrentBuf->pIOBuffer->getBufferPtr(), &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[ m_uiRflReadOffset]), m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset); m_pCurrentBuf->uiRflBufBytes -= m_uiRflReadOffset; m_pCurrentBuf->uiRflFileOffset += m_uiRflReadOffset; m_uiRflReadOffset = 0; } uiReadLen = m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes; // Read enough to fill the rest of the buffer, which is // guaranteed to hold at least one full packet. if( !m_uiFileEOF) { if( uiCurrFilePos > (FLMUINT)(-1) - uiReadLen) { uiReadLen = (FLMUINT)(-1) - uiCurrFilePos; } } else { if( uiCurrFilePos + uiReadLen > m_uiFileEOF) { uiReadLen = m_uiFileEOF - uiCurrFilePos; } } // If reading will not give us the minimum bytes needed, // we cannot satisfy this request from the current file. if( uiReadLen + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Read enough to get the entire packet. if( RC_BAD( rc = m_pRestore->read( uiReadLen, &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[ m_pCurrentBuf->uiRflBufBytes]), &uiBytesRead))) { if( rc == NE_FLM_IO_END_OF_FILE) { rc = NE_XFLM_OK; } else { goto Exit; } } if( m_pRestoreStatus) { eRestoreAction eAction; if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( &eAction, m_pCurrentBuf->uiCurrFileNum, uiBytesRead))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } // If we didn't read enough to satisfy the minimum bytes needed, // we cannot satisfy this request from the current file. if( uiBytesRead + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } m_pCurrentBuf->uiRflBufBytes += uiBytesRead; } else { // Set offsets so we are on a 512 byte boundary for our // next read. No need to move data, since we will be // re-reading it anyway. if( m_uiRflReadOffset > 0) { uiTmpOffset = m_uiRflReadOffset - (m_uiRflReadOffset & 511); m_pCurrentBuf->uiRflFileOffset += uiTmpOffset; m_uiRflReadOffset -= uiTmpOffset; } else if( m_pCurrentBuf->uiRflFileOffset & 511) { m_uiRflReadOffset = m_pCurrentBuf->uiRflFileOffset & 511; m_pCurrentBuf->uiRflFileOffset -= m_uiRflReadOffset; } m_pCurrentBuf->uiRflBufBytes = 0; // Read enough to fill the rest of the buffer, which is // guaranteed to hold at least one full packet. uiReadLen = m_uiBufferSize; // m_uiFileEOF better not be zero at this point - we should // always know precisely where the RFL file ends when we // are doing recovery as opposed to doing a restore. flmAssert( m_uiFileEOF >= 512); if( m_pCurrentBuf->uiRflFileOffset + uiReadLen > m_uiFileEOF) { uiReadLen = m_uiFileEOF - m_pCurrentBuf->uiRflFileOffset; } // If reading will not give us the minimum number of bytes // needed, we have a bad packet. if( uiReadLen < m_uiRflReadOffset || uiReadLen - m_uiRflReadOffset < uiMinBytesNeeded) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Read to get the entire packet. if( RC_BAD( rc = m_pFileHdl->read( m_pCurrentBuf->uiRflFileOffset, uiReadLen, m_pCurrentBuf->pIOBuffer->getBufferPtr(), &uiBytesRead))) { if( rc == NE_FLM_IO_END_OF_FILE) { rc = NE_XFLM_OK; } else { m_bRflVolumeOk = FALSE; goto Exit; } } if( uiBytesRead < uiReadLen) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } m_pCurrentBuf->uiRflBufBytes = uiReadLen; } Exit: return( rc); } /******************************************************************** Desc: Gets and verifies the next packet from the roll-forward log file. Packet checksum will be verified. *********************************************************************/ RCODE F_Rfl::getPacket( F_Db * pDb, FLMBOOL bForceNextFile, FLMUINT * puiPacketTypeRV, const FLMBYTE ** ppucPacketBodyRV, FLMUINT * puiPacketBodyLenRV) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucPacket; const FLMBYTE * pucPacketBody; const FLMBYTE * pucPacketBodyEnd; FLMUINT uiOrigPacketBodyLen; FLMUINT uiPacketBodyLen; FLMUINT uiPacketType; FLMBYTE ucHdr [512]; FLMUINT uiBytesRead; // See if we need to go to the next file. Note that we only // check for this exactly on packet boundaries. We do not expect // packets to be split across files. If we are not at the end // of processing what is in the buffer, we should be able to // read the rest of the packet from the current file. Get_Next_File: if( bForceNextFile || (m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF)) { if( m_bKeepRflFiles) { if( !m_pRestore) { // Only doing recovery after a failure, see if we are at // the last file already. if( m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) { rc = RC_SET( NE_XFLM_RFL_END); goto Exit; } else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) == m_uiLastRecoverFileNum && !m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset) { // We are going to try to open the last file. Since the log // header shows a current offset of 0, the file may have been // created but nothing was logged to it. We don't want to try // to open it here because it may not have been initialized // fully at the time of the server crash. m_pCurrentBuf->uiCurrFileNum = m_uiLastRecoverFileNum; rc = RC_SET( NE_XFLM_RFL_END); goto Exit; } // Open the next file in the sequence. if( RC_BAD( rc = openFile( pDb->m_hWaitSem, m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = RC_SET( NE_XFLM_RFL_END); } goto Exit; } // If this is the last RFL file, the EOF is contained // in the log header. Otherwise, it will be in the RFL // file's header, and openFile will already have retrieved it. if( m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) { m_uiFileEOF = m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; // Could be zero if RFL file wasn't created yet. if( !m_uiFileEOF) { m_uiFileEOF = 512; } } // By this point, the EOF better be greater than or equal to 512. flmAssert( m_uiFileEOF >= 512); } else { if( RC_BAD( rc = m_pRestore->close())) { goto Exit; } // Ask the recovery object to open the file. if( RC_BAD( rc = m_pRestore->openRflFile( m_pCurrentBuf->uiCurrFileNum + 1))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = RC_SET( NE_XFLM_RFL_END); } goto Exit; } if( m_pRestoreStatus) { eRestoreAction eAction; if( RC_BAD( rc = m_pRestoreStatus->reportOpenRflFile( &eAction, m_pCurrentBuf->uiCurrFileNum + 1))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } // Get the first 512 bytes from the file and verify the header. if( RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) { goto Exit; } if( m_pRestoreStatus) { eRestoreAction eAction; if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( &eAction, m_pCurrentBuf->uiCurrFileNum + 1, uiBytesRead))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } if( uiBytesRead < 512) { rc = RC_SET( NE_XFLM_NOT_RFL); goto Exit; } if( RC_BAD( rc = verifyHeader( ucHdr, m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum))) { goto Exit; } // We may not know the actual EOF of files during restore // operations. m_uiFileEOF could be zero here. m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr[ RFL_EOF_POS]); // File EOF may be zero or >= 512 at this point. flmAssert( !m_uiFileEOF || m_uiFileEOF >= 512); // Need to increment current file number. m_pCurrentBuf->uiCurrFileNum++; } m_pCurrentBuf->uiRflFileOffset = 512; m_uiRflReadOffset = 0; m_pCurrentBuf->uiRflBufBytes = 0; // Get the next packet from the new file. if( RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) { if( m_uiFileEOF == 512 && m_bKeepRflFiles) { // File was empty, try to go to the next file. bForceNextFile = TRUE; goto Get_Next_File; } goto Exit; } } else { // This is the case where we are not keeping the RFL files. // So, there is no next file to go to. If we get to this // point, we had better not be doing a restore. flmAssert( m_pRestore == NULL && !bForceNextFile); rc = RC_SET( NE_XFLM_RFL_END); goto Exit; } } // Make sure we at least have a packet header in the buffer. if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) { goto Exit; } // Verify the packet address. m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_uiRflReadOffset; pucPacket = &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[m_uiRflReadOffset]); if ((FLMUINT)FB2UD( &pucPacket [RFL_PACKET_ADDRESS_OFFSET]) != m_uiPacketAddress) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } // Get packet type, time flag, and packet body length *puiPacketTypeRV = uiPacketType = RFL_GET_PACKET_TYPE( pucPacket [RFL_PACKET_TYPE_OFFSET]); uiPacketBodyLen = uiOrigPacketBodyLen = (FLMUINT)FB2UW( &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]); // Make sure we have the entire packet in the buffer. if (RC_BAD( rc = readPacket( uiPacketBodyLen + RFL_PACKET_OVERHEAD))) { goto Exit; } pucPacket = &(m_pCurrentBuf->pIOBuffer->getBufferPtr()[m_uiRflReadOffset]); // At this point, we are guaranteed to have the entire packet // in the buffer. pucPacketBody = &pucPacket [RFL_PACKET_OVERHEAD]; pucPacketBodyEnd = pucPacketBody + uiPacketBodyLen; // Validate the packet checksum if (RflCalcChecksum( pucPacket, uiPacketBodyLen) != pucPacket [RFL_PACKET_CHECKSUM_OFFSET]) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if (uiPacketType == RFL_TRNS_BEGIN_PACKET || uiPacketType == RFL_UPGRADE_PACKET || uiPacketType == RFL_REDUCE_PACKET || uiPacketType == RFL_WRAP_KEY_PACKET || uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET || uiPacketType == RFL_ROLL_OVER_DB_KEY_PACKET) { // Current transaction ID better be zero, otherwise, we // have two or more begin packets in a row. if( m_ui64CurrTransID) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucPacketBodyEnd, &m_ui64CurrTransID))) { goto Exit; } uiPacketBodyLen = (FLMUINT)(pucPacketBodyEnd - pucPacketBody); // Make sure the transaction numbers are ascending if( m_ui64CurrTransID <= m_ui64LastTransID) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } } else { // If transaction ID is not zero, we are not inside a // transaction, and it is likely that we have a corrupt // packet. if( !m_ui64CurrTransID) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( uiPacketType == RFL_TRNS_COMMIT_PACKET || uiPacketType == RFL_TRNS_ABORT_PACKET) { FLMUINT64 ui64Tmp; if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucPacketBodyEnd, &ui64Tmp))) { goto Exit; } if( ui64Tmp != m_ui64CurrTransID) { rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); goto Exit; } uiPacketBodyLen = (FLMUINT)(pucPacketBodyEnd - pucPacketBody); } } // Set read offset to beginning of next packet. m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiOrigPacketBodyLen); *puiPacketBodyLenRV = uiPacketBodyLen; *ppucPacketBodyRV = pucPacketBody; Exit: return( rc); } /******************************************************************** Desc: Restore transactions from the roll-forward log to the database. *********************************************************************/ RCODE F_Rfl::recover( F_Db * pDb, IF_RestoreClient * pRestore, IF_RestoreStatus * pRestoreStatus) { RCODE rc = NE_XFLM_OK; FLMUINT uiStartFileNum; FLMUINT uiStartOffset; FLMUINT uiOffset; FLMUINT uiReadLen; FLMUINT uiBytesRead; FLMBYTE ucHdr[ 512]; FLMUINT uiPacketType; const FLMBYTE * pucPacketBody; FLMUINT uiPacketBodyLen = 0; eRestoreAction eAction; FLMBOOL bTransActive = FALSE; FLMBOOL bHadOperations = FALSE; FLMBOOL bLastTransEndedAtFileEOF = FALSE; FLMBOOL bForceNextFile; FLMUINT uiRflToken = 0; flmAssert( m_pDatabase); m_pCurrentBuf = &m_Buf1; m_ui64LastLoggedCommitTransID = 0; // We need to allow all updates logged in the RFL // (including dictionary updates). pDb->m_bItemStateUpdOk = TRUE; // Turn off logging. disableLogging( &uiRflToken); // Set the replay flag on the database. pDb->m_uiFlags |= FDB_REPLAYING_RFL; // Set the flag as to whether or not we are using multiple RFL files. m_bKeepRflFiles = m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles ? TRUE : FALSE; // If pRestore is NULL, we are doing a database recovery after // open. In that case, we start from the last checkpoint offset // and only run until the last transaction offset. m_pRestoreStatus = pRestoreStatus; if( (m_pRestore = pRestore) == NULL) { FLMBYTE * pucCheckSerialNum; FLMUINT uiEndOffset; // Clear the restore status pointer. Assert that the // pointer is NULL so we can catch the improper use // of this object. if( m_pRestoreStatus) { flmAssert( 0); m_pRestoreStatus = NULL; } uiStartFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPFileNum; m_uiLastRecoverFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; uiStartOffset = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPOffset; uiEndOffset = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; // Could be zero if the file was created, but no transactions were // ever committed to it. if( !uiEndOffset) { uiEndOffset = 512; } // Start offset better not be less than 512. flmAssert( uiStartOffset >= 512); flmAssert( uiEndOffset >= 512); // If start and end are at the same place, there is nothing // to recover. if( uiStartFileNum == m_uiLastRecoverFileNum && uiStartOffset == uiEndOffset) { goto Finish_Recovery; } // We have not recorded the serial number of the last checkpoint file // number, so we pass in NULL, unless it happens to be the same as the // last transaction file number, in which case we can pass in the // serial number we have stored in the log header. pucCheckSerialNum = (uiStartFileNum == m_uiLastRecoverFileNum) ? &m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum [0] : NULL; if( RC_BAD( rc = openFile( pDb->m_hWaitSem, uiStartFileNum, pucCheckSerialNum))) { goto Exit; } // If this is the last RFL file, the EOF is contained // in the log header. Otherwise, it will be in the RFL // file's header, and openFile will already have retrieved it. if( uiStartFileNum == m_uiLastRecoverFileNum) { m_uiFileEOF = uiEndOffset; } // At this point, file EOF better be greater than or equal to 512. flmAssert( m_uiFileEOF >= 512); } else if( !m_bKeepRflFiles) { // FlmDbRestore should be checking the "keep" flag and not // attempting to do a restore of the RFL. rc = RC_SET_AND_ASSERT( NE_XFLM_CANNOT_RESTORE_RFL_FILES); goto Exit; } else { uiStartFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; uiStartOffset = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; // Could be zero if the RFL file had never been created. if( !uiStartOffset) { uiStartOffset = 512; } // Ask the recovery object to open the file. Retry_Open: flmAssert( uiStartFileNum); if( RC_BAD( rc = m_pRestore->openRflFile( uiStartFileNum))) { if( rc == NE_FLM_IO_PATH_NOT_FOUND) { // Need to set m_pCurrentBuf->uiCurrFileNum in case the first // call to openRflFile fails. This will cause the code at the // Finish_Recovery label to correctly set up the log // header. if( !uiStartOffset) { m_pCurrentBuf->uiCurrFileNum = uiStartFileNum - 1; } else { m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; } rc = NE_XFLM_OK; goto Finish_Recovery; } else { goto Exit; } } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportOpenRflFile( &eAction, uiStartFileNum))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } // Get the first 512 bytes from the file and verify the header. if( RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) { goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( &eAction, uiStartFileNum, uiBytesRead))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } if( uiBytesRead < 512) { rc = RC_SET( NE_XFLM_NOT_RFL); goto Exit; } if( RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum, m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum))) { RCODE tmpRc; if( m_pRestoreStatus) { if( RC_BAD( tmpRc = m_pRestoreStatus->reportError( &eAction, rc))) { rc = tmpRc; goto Exit; } if( eAction == XFLM_RESTORE_ACTION_RETRY) { if( RC_BAD( rc = m_pRestore->close())) { goto Exit; } goto Retry_Open; } } goto Exit; } // We may not know the actual EOF of files during restore operations. if( (m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr [RFL_EOF_POS])) == 0) { bLastTransEndedAtFileEOF = TRUE; } else { bLastTransEndedAtFileEOF = (m_uiFileEOF == uiStartOffset) ? TRUE : FALSE; } // Position to the start offset. Unfortunately, this means reading // through the data and discarding it. uiOffset = 512; while( uiOffset < uiStartOffset) { uiReadLen = (uiStartOffset - uiOffset); if( uiReadLen > m_uiBufferSize) { uiReadLen = m_uiBufferSize; } if( RC_BAD( rc = m_pRestore->read( uiReadLen, m_pCurrentBuf->pIOBuffer->getBufferPtr(), &uiBytesRead))) { goto Exit; } if( m_pRestoreStatus) { if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( &eAction, uiStartFileNum, uiBytesRead))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } // RFL file is incomplete if we could not read up to the last // committed transaction. if( uiBytesRead < uiReadLen) { rc = RC_SET( NE_XFLM_RFL_INCOMPLETE); goto Exit; } uiOffset += uiBytesRead; } // Need to set current file number m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; // Better not be any transactions to recover - last database // state needs to be a completed checkpoint. flmAssert( m_pDatabase->m_lastCommittedDbHdr.ui64RflLastCPTransID == m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID); // Use uiStartOffset here instead of ui32RflLastTransOffset, // because ui32RflLastTransOffset may be zero, but we in that // case we should be comparing to 512, and uiStartOffset will have // been adjusted to 512 if that is the case. flmAssert( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPOffset == uiStartOffset); flmAssert( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPFileNum == uiStartFileNum); } // Set last transaction ID to the last transaction // that was checkpointed - transaction numbers should ascend // from here. m_ui64LastTransID = m_pDatabase->m_lastCommittedDbHdr.ui64RflLastCPTransID; // Set the last committed trans ID m_ui64LastLoggedCommitTransID = m_pDatabase->m_lastCommittedDbHdr.ui64LastRflCommitID; m_pCurrentBuf->uiRflFileOffset = uiStartOffset; m_uiRflReadOffset = 0; m_pCurrentBuf->uiRflBufBytes = 0; // Now, read until we are done. bForceNextFile = FALSE; for (;;) { flmAssert( pDb->m_uiFlags & FDB_REPLAYING_RFL); // Get the next operation from the file. rc = getPacket( pDb, bForceNextFile, &uiPacketType, &pucPacketBody, &uiPacketBodyLen); bForceNextFile = FALSE; if( RC_BAD( rc)) { if( rc == NE_XFLM_RFL_END) { if( !m_pRestore) { // If we didn't end exactly where we should have, we have // an incomplete log. The same is true if we are in the // middle of a transaction. if( m_pCurrentBuf->uiCurrFileNum != m_uiLastRecoverFileNum || bTransActive) { rc = RC_SET( NE_XFLM_RFL_INCOMPLETE); } else { rc = NE_XFLM_OK; goto Finish_Recovery; } } else { // If we are doing a restore, and we get to the end of the // log, it is OK - even if we are in the middle of a // transaction - the transaction will simply be aborted. rc = NE_XFLM_OK; goto Finish_Recovery; } } else if( rc == NE_XFLM_BAD_RFL_PACKET) { // If we don't know the current file size, and we // are doing a restore, it is OK to end on a bad // packet - we will simply abort the current // transaction, if any. Then, try to go to the // next file, because we really don't know where // this file ends. if( m_pRestore && !m_uiFileEOF) { if( bTransActive) { pDb->transAbort(); bTransActive = FALSE; } // Set current transaction ID to zero - as if we had encountered // an abort packet. m_ui64CurrTransID = 0; bLastTransEndedAtFileEOF = TRUE; // Force to go to the next file bForceNextFile = TRUE; rc = NE_XFLM_OK; continue; } } goto Exit; } // At this point, we know we have a good packet, see what it // is and handle it. bHadOperations = TRUE; eAction = XFLM_RESTORE_ACTION_CONTINUE; switch (uiPacketType) { case RFL_TRNS_BEGIN_PACKET: { // If we already have a transaction active, we have // a problem. flmAssert( !bTransActive); if( uiPacketBodyLen) { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); goto Exit; } if( RC_BAD( rc = recovTransBegin( pDb, &eAction))) { goto Exit; } if (eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } bTransActive = TRUE; break; } case RFL_TRNS_COMMIT_PACKET: { // Commit the current transaction. if (m_pRestoreStatus) { if (RC_BAD( rc = m_pRestoreStatus->reportCommitTrans( &eAction, m_ui64CurrTransID))) { goto Exit; } if (eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } } flmAssert( bTransActive); pDb->m_uiFlags |= FDB_REPLAYING_COMMIT; rc = pDb->transCommit(); pDb->m_uiFlags &= ~FDB_REPLAYING_COMMIT; bTransActive = FALSE; if (RC_BAD( rc)) { goto Exit; } m_ui64LastLoggedCommitTransID = m_ui64CurrTransID; Finish_Transaction: if (!m_uiFileEOF) { bLastTransEndedAtFileEOF = TRUE; } else { bLastTransEndedAtFileEOF = (m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF) ? TRUE : FALSE; } m_ui64CurrTransID = 0; break; } case RFL_TRNS_ABORT_PACKET: { // Abort the current transaction. if (m_pRestoreStatus) { if (RC_BAD( rc = m_pRestoreStatus->reportAbortTrans( &eAction, m_ui64CurrTransID))) { goto Exit; } if (eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } } flmAssert( bTransActive); m_uiLastLfNum = 0; m_eLastLfType = XFLM_LF_INVALID; rc = pDb->transAbort(); bTransActive = FALSE; if (RC_BAD( rc)) { goto Exit; } goto Finish_Transaction; } case RFL_REDUCE_PACKET: { if( RC_BAD( rc = recovReduce( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } goto Finish_Transaction; } case RFL_UPGRADE_PACKET: { if( RC_BAD( rc = recovUpgrade( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } goto Finish_Transaction; } case RFL_INDEX_SUSPEND_PACKET: case RFL_INDEX_RESUME_PACKET: { if( RC_BAD( rc = recovIndexSuspendResume( pDb, uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_BLK_CHAIN_FREE_PACKET: { if( RC_BAD( rc = recovBlockChainFree( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_ENABLE_ENCRYPTION_PACKET: case RFL_WRAP_KEY_PACKET: { if( RC_BAD( rc = recovEncryptionKey( pDb, uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } goto Finish_Transaction; } case RFL_ENC_DEF_KEY_PACKET: { if( RC_BAD( rc = recovEncDefKey( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_ROLL_OVER_DB_KEY_PACKET: { if( RC_BAD( rc = recovRollOverDbKey( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } goto Finish_Transaction; } case RFL_NODE_DELETE_PACKET: { if( RC_BAD( rc = recovNodeDelete( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_CHILDREN_DELETE_PACKET: { if( RC_BAD( rc = recovNodeChildrenDelete( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_CREATE_PACKET: { if( RC_BAD( rc = recovNodeCreate( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_ATTR_CREATE_PACKET: { if( RC_BAD( rc = recovAttributeCreate( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_ATTR_DELETE_PACKET: { if( RC_BAD( rc = recovAttributeDelete( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_ENC_NODE_UPDATE_PACKET: { if( RC_BAD( rc = recovEncryptedNodeUpdate( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_SET_TEXT_VALUE_PACKET: case RFL_NODE_SET_BINARY_VALUE_PACKET: { if( RC_BAD( rc = recovNodeSetValue( pDb, uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_SET_NUMBER_VALUE_PACKET: { if( RC_BAD( rc = recovNodeSetNumberValue( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_ATTR_SET_VALUE_PACKET: { if( RC_BAD( rc = recovAttrSetValue( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_CLEAR_VALUE_PACKET: { if( RC_BAD( rc = recovNodeClearValue( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_DOCUMENT_DONE_PACKET: { if( RC_BAD( rc = recovDocumentDone( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_INSERT_BEFORE_PACKET: { if( RC_BAD( rc = recovInsertBefore( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_FLAGS_UPDATE_PACKET: { if( RC_BAD( rc = recovNodeFlagsUpdate( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_SET_PREFIX_ID_PACKET: { if( RC_BAD( rc = recovNodeSetPrefixId( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_SET_NEXT_NODE_ID_PACKET: { if( RC_BAD( rc = recovSetNextNodeId( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } case RFL_NODE_SET_META_VALUE_PACKET: { if( RC_BAD( rc = recovNodeSetMetaValue( pDb, pucPacketBody, uiPacketBodyLen, &eAction))) { goto Exit; } if( eAction == XFLM_RESTORE_ACTION_STOP) { bLastTransEndedAtFileEOF = FALSE; goto Finish_Recovery; } break; } default: { // Should not be getting other packet types at this // point. // If we don't know the current file size, and we // are doing a restore, it is OK to end on a bad // packet - we will simply abort the current // transaction, if any. if( m_pRestore && !m_uiFileEOF) { rc = NE_XFLM_OK; goto Finish_Recovery; } else { rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); } goto Exit; } } } Finish_Recovery: if( bTransActive) { pDb->transAbort(); bTransActive = FALSE; } if( m_pRestore) { FLMUINT uiNextRflFileNum = m_pCurrentBuf->uiCurrFileNum + 1; // At the end of the restore operation, we need to set things // up so that the next transaction will begin a new RFL file. // If we ended the restore in the middle of an RFL file, we // need to set it up so that the new RFL file will have a new // serial number. If we ended at the end of an RFL file, we // can set it up so that the new RFL file will have the next // serial number. // Set up the next RFL file number and offset. m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum = (FLMINT32)uiNextRflFileNum; // Set a zero into the offset, this is a special case which tells // us that we should create the file no matter what - even if // it already exists - it should be overwritten. m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset = 0; if( bLastTransEndedAtFileEOF) { // Move the next serial number of the last RFL file processed into // into the current RFL serial number so that the log header // will be correct f_memcpy( m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); } else { // Must create a new serial number so that when the new RFL // file is created, it will have that next serial number. if( RC_BAD( rc = f_createSerialNumber( m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum))) { goto Exit; } } // Save the last logged commit transaction ID. if( m_ui64LastLoggedCommitTransID) { m_pDatabase->m_lastCommittedDbHdr.ui64LastRflCommitID = m_ui64LastLoggedCommitTransID; } // No matter what, we must generate a new next serial number. This // is what will be written to the new RFL file's header when it is // created. if( RC_BAD( rc = f_createSerialNumber( m_pDatabase->m_lastCommittedDbHdr.ucNextRflSerialNum))) { goto Exit; } } if( !bHadOperations) { // No transactions were recovered, but still need to // setup a few things. m_pDatabase->m_uiFirstLogCPBlkAddress = 0; m_pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); // Save the state of the log header into the checkpointDbHdr buffer. f_memcpy( &m_pDatabase->m_checkpointDbHdr, &m_pDatabase->m_lastCommittedDbHdr, sizeof( XFLM_DB_HDR)); } // Force a checkpoint to force the log files to be truncated and // everything to be reset. This is done because during recovery // the checkpoints that are executed do NOT truncate the RFL file - // because it is using the log file to recover! closeFile(); m_pRestore = NULL; m_pRestoreStatus = NULL; enableLogging( &uiRflToken); pDb->m_uiFlags &= ~FDB_REPLAYING_RFL; if( RC_BAD( rc = pDb->doCheckpoint( 0))) { goto Exit; } Exit: if( bTransActive) { pDb->transAbort(); } pDb->m_bItemStateUpdOk = FALSE; pDb->m_uiFlags &= ~FDB_REPLAYING_RFL; m_uiLastLfNum = 0; return( rc); } /**************************************************************************** Desc: Returns the name of an RFL file given its number ****************************************************************************/ void FLMAPI F_Db::getRflFileName( FLMUINT uiFileNum, FLMBOOL bBaseOnly, char * pszFileName, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated) { if (bBaseOnly) { rflGetBaseFileName( uiFileNum, pszFileName, puiFileNameBufSize, pbNameTruncated); } else { m_pDatabase->m_pRfl->getFullRflFileName( uiFileNum, pszFileName, puiFileNameBufSize, pbNameTruncated); } } /******************************************************************** Desc: Calculate the checksum for a packet. *********************************************************************/ FLMBYTE RflCalcChecksum( FLMBYTE * pucPacket, FLMUINT uiPacketBodyLen) { FLMUINT uiBytesToChecksum; FLMBYTE * pucStart; // Checksum is calculated for every byte in the packet that // comes after the checksum byte. uiBytesToChecksum = (FLMUINT)(uiPacketBodyLen + RFL_PACKET_OVERHEAD - (RFL_PACKET_CHECKSUM_OFFSET + 1)); pucStart = &pucPacket [RFL_PACKET_CHECKSUM_OFFSET + 1]; return( f_calcPacketChecksum( pucStart, uiBytesToChecksum)); } libxflaim-5.1.969/src/kyeword.cpp0000644000175000017500000002332310511001742020217 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the code to parse out individual words and // substrings in a text string. // // Tabs: 3 // // Copyright (c) 1990-2000, 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: kyeword.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE flmGetCharacter( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUINT16 * pui16WPValue, FLMUNICODE * puUniValue); FSTATIC RCODE flmTextGetCharType( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUNICODE * puUniValue, FLMUINT * puiType); /***************************************************************************** Desc: *****************************************************************************/ FINLINE FLMUINT flmCharTypeAnsi7( FLMUINT16 ui16Char) { if( (ui16Char >= ASCII_LOWER_A && ui16Char <= ASCII_LOWER_Z) || (ui16Char >= ASCII_UPPER_A && ui16Char <= ASCII_UPPER_Z) || (ui16Char >= ASCII_ZERO && ui16Char <= ASCII_NINE)) { return SDWD_CHR; } if( ui16Char == 0x27) { return WDJN_CHR; } if( ui16Char <= 0x2B) { return DELI_CHR; } if( ui16Char == ASCII_COMMA || ui16Char == ASCII_DASH || ui16Char == ASCII_DOT || ui16Char == ASCII_SLASH || ui16Char == ASCII_COLON || ui16Char == ASCII_AT || ui16Char == ASCII_BACKSLASH || ui16Char == ASCII_UNDERSCORE) { return WDJN_CHR; } return DELI_CHR; } /***************************************************************************** Desc: Return the next WP or unicode character value. Return: Number of bytes formatted to return the character value. *****************************************************************************/ FSTATIC RCODE flmGetCharacter( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUINT16 * pui16WPValue, FLMUNICODE * puUniValue) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar = 0; FLMUINT64 ui64AfterLastSpacePos = 0; FLMBOOL bLastCharWasSpace = FALSE; FLMUINT uiCompareRules = *puiCompareRules; for( ;;) { if (RC_BAD( rc = f_readUTF8CharAsUnicode( pIStream, &uChar))) { if (rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; if (bLastCharWasSpace && !(uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE)) { // bLastCharWasSpace flag can only be TRUE if either // XFLM_COMP_IGNORE_TRAILING_SPACE is set or // XFLM_COMP_COMPRESS_WHITESPACE is set. flmAssert( uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE); uChar = ASCII_SPACE; } else { uChar = 0; } break; } if ((uChar = f_convertChar( uChar, uiCompareRules)) == 0) { continue; } if (uChar == ASCII_SPACE) { if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) { bLastCharWasSpace = TRUE; ui64AfterLastSpacePos = pIStream->getCurrPosition(); } else if (uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE) { // If the ignore trailing space flag is set, but the compress // space flag is not set, remember the position of the // first space character. If we hit a non-space character, // we will reposition to after this space character. if (!bLastCharWasSpace) { bLastCharWasSpace = TRUE; ui64AfterLastSpacePos = pIStream->getCurrPosition(); } } else { break; } } else { // Disable the ignore leading space flag, because we are now // past all leading space, and we don't want spaces ignored // now on account of that flag. uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); if (bLastCharWasSpace) { // Position to after the last space if (RC_BAD( rc = pIStream->positionTo( ui64AfterLastSpacePos))) { goto Exit; } uChar = ASCII_SPACE; bLastCharWasSpace = FALSE; } break; } } if (pui16WPValue) { if (!f_unicodeToWP( uChar, pui16WPValue)) { *pui16WPValue = 0; } } if (puUniValue) { *puUniValue = uChar; } Exit: *puiCompareRules = uiCompareRules; return( rc); } /**************************************************************************** Desc: Substring-ize the string in a node. Normalize spaces and hyphens if told to. Example: ABC DEF ABC DEF BC DEF C DEF DEF ****************************************************************************/ RCODE KYSubstringParse( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, // [in/out] comparison rules FLMUINT uiLimitParm, // [in] Max characters FLMBYTE * pucSubstrBuf, // [out] buffer to fill FLMUINT * puiSubstrBytes, // [out] returns length FLMUINT * puiSubstrChars) { RCODE rc = NE_XFLM_OK; FLMUINT uiDestOffset = 0; FLMUINT uiDestSize = *puiSubstrBytes; FLMUINT uiLimit = uiLimitParm ? uiLimitParm : ICD_DEFAULT_SUBSTRING_LIMIT; FLMUINT uiCharCnt = 0; FLMUINT uiSize; FLMBOOL bFirstCharacter = TRUE; FLMUINT64 ui64SavePosition = pIStream->getCurrPosition(); // The limit must return one more than requested in order // for the text to collation routine to set the truncated flag. uiLimit++; while (uiLimit--) { FLMUNICODE uChar; if( RC_BAD( rc = flmGetCharacter( pIStream, puiCompareRules, NULL, &uChar))) { goto Exit; } if (!uChar) { break; } uiCharCnt++; uiSize = uiDestSize - uiDestOffset; if (RC_BAD( rc = f_uni2UTF8( uChar, &pucSubstrBuf[ uiDestOffset], &uiSize))) { goto Exit; } uiDestOffset += uiSize; // If on the first word, position to start on next character // for the next call. if (bFirstCharacter) { bFirstCharacter = FALSE; // First character - save position so we can restore it // upon leaving the routine. ui64SavePosition = pIStream->getCurrPosition(); } } if (uiDestOffset) { pucSubstrBuf[ uiDestOffset++] = 0; } *puiSubstrBytes = (FLMUINT)uiDestOffset; *puiSubstrChars = uiCharCnt; // Restore position of stream to first character after the first // character we found - to ready for next call. if (RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE KYEachWordParse( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUINT uiLimit, // [in] Max characters FLMBYTE * pucWordBuf, // [out] Buffer of at least XFLM_MAX_KEY_SIZE FLMUINT * puiWordLen) { RCODE rc = NE_XFLM_OK; FLMBOOL bSkippingDelim = TRUE; FLMUINT uiWordLen = 0; FLMUINT uiWordBufSize = *puiWordLen; FLMUNICODE uChar; FLMUINT uiType = 0; FLMUINT uiSize; if (!uiLimit) { uiLimit = ICD_DEFAULT_SUBSTRING_LIMIT; } while (uiLimit) { if (RC_BAD( rc = flmTextGetCharType( pIStream, puiCompareRules, &uChar, &uiType))) { goto Exit; } if (!uChar) { break; } // Determine how to handle what we got. if (bSkippingDelim) { // If we were skipping delimiters, and we run into a non-delimiter // character, set the bSkippingDelim flag to FALSE to indicate the // beginning of a word. if (uiType & SDWD_CHR) { bSkippingDelim = FALSE; uiLimit--; uiSize = uiWordBufSize - uiWordLen; if (RC_BAD( rc = f_uni2UTF8( uChar, &pucWordBuf [uiWordLen], &uiSize))) { goto Exit; } uiWordLen += uiSize; } } else { // If we were NOT skipping delimiters, and we run into a delimiter // output the word. if (uiType & (DELI_CHR | WDJN_CHR)) { break; } uiSize = uiWordBufSize - uiWordLen; if (RC_BAD( rc = f_uni2UTF8( uChar, &pucWordBuf [uiWordLen], &uiSize))) { goto Exit; } uiWordLen += uiSize; } } // Return the word, if any if (uiWordLen) { pucWordBuf [uiWordLen++] = 0; } *puiWordLen = uiWordLen; Exit: return( rc); } /***************************************************************************** Desc: Return the next WP or unicode character value and parsing type. *****************************************************************************/ FSTATIC RCODE flmTextGetCharType( IF_PosIStream * pIStream, FLMUINT * puiCompareRules, FLMUNICODE * puUniValue, // [out] Unicode value FLMUINT * puiType // Char attribute type. ) { RCODE rc = NE_XFLM_OK; FLMUINT16 ui16WPValue; FLMUINT uiCharSet; // We add on compress white space flag because we really want to ignore // spaces anyway - we are trying to get the "words" from this stream. if( RC_BAD( rc = flmGetCharacter( pIStream, puiCompareRules, &ui16WPValue, puUniValue))) { goto Exit; } if (ui16WPValue) { if (ui16WPValue < 0x080) { *puiType = flmCharTypeAnsi7( ui16WPValue); goto Exit; } uiCharSet = (FLMUINT)(ui16WPValue >> 8); if (uiCharSet == 1 || uiCharSet == 2 || (uiCharSet >= 8 && uiCharSet <= 11)) { *puiType = SDWD_CHR; goto Exit; } *puiType = DELI_CHR; } else { // For now all unmapped unicode characters are treated // as delimeters *puiType = DELI_CHR; } Exit: return( rc); } libxflaim-5.1.969/src/flkeyret.cpp0000644000175000017500000001661210511001742020363 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the F_Db::keyRetrieve method. // // Tabs: 3 // // Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flkeyret.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /*API~*********************************************************************** Area : RETRIEVAL Desc: Retrieves a key from an index based on a passed-in GEDCOM tree and DRN. *END************************************************************************/ RCODE F_Db::keyRetrieve( FLMUINT uiIndex, // [IN] Index number. IF_DataVector * ifpSearchKey, // [IN] Pointer to the search key. FLMUINT uiFlags, // [IN] Flag (XFLM_FIRST, XFLM_LAST, XFLM_EXCL, XFLM_INCL, // XFLM_EXACT, XFLM_KEY_EXACT IF_DataVector * ifpFoundKey // [OUT] Returns key that was found - may also have data components. ) { RCODE rc = NE_XFLM_OK; IXD * pIxd = NULL; LFILE * pLFile; FLMBYTE * pucSearchKey = NULL; FLMBYTE * pucFoundKey = NULL; void * pvMark = m_tempPool.poolMark(); FLMUINT uiSearchKeyLen = 0; FLMUINT uiFoundKeyLen; FLMUINT uiOriginalFlags; F_Btree * pbtree = NULL; FLMUINT uiDataLen; FLMBOOL bStartedTrans = FALSE; FLMUINT uiIdMatchFlags = uiFlags & (XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID); IXKeyCompare compareObject; FLMBOOL bCompareDocId = FALSE; FLMBOOL bCompareNodeIds = FALSE; F_DataVector * pSearchKey = (F_DataVector *)ifpSearchKey; F_DataVector * pFoundKey = (F_DataVector *)ifpFoundKey; // See if the database is being forced to close if (RC_BAD( rc = checkState( __FILE__, __LINE__))) { goto Exit; } // If we are not in a transaction, we cannot read. if (m_eTransType == XFLM_NO_TRANS) { if( RC_BAD( rc = transBegin( XFLM_READ_TRANS))) { goto Exit; } bStartedTrans = TRUE; } // See if we have a transaction going which should be aborted. if( RC_BAD( m_AbortRc)) { rc = RC_SET( NE_XFLM_ABORT_TRANS); goto Exit; } // Allocate key buffers. if (pSearchKey) { if (RC_BAD( rc = m_tempPool.poolAlloc( XFLM_MAX_KEY_SIZE, (void **)&pucSearchKey))) { goto Exit; } } if (RC_BAD( rc = m_tempPool.poolAlloc( XFLM_MAX_KEY_SIZE, (void **)&pucFoundKey))) { goto Exit; } // Make sure it is a valid index definition if (RC_BAD( rc = m_pDict->getIndex( uiIndex, &pLFile, &pIxd))) { goto Exit; } if (RC_BAD( rc = flushKeys())) { goto Exit; } if (uiFlags & XFLM_FIRST || (!pSearchKey && !(uiFlags & XFLM_FIRST) && !(uiFlags & XFLM_LAST))) { uiOriginalFlags = uiFlags = XFLM_FIRST; } else if (uiFlags & XFLM_LAST) { uiOriginalFlags = uiFlags = XFLM_LAST; } else { uiOriginalFlags = uiFlags; if( !(uiIdMatchFlags & XFLM_MATCH_IDS)) { flmAssert( !(uiFlags & XFLM_KEY_EXACT)); if (uiFlags & XFLM_EXCL) { uiFlags = XFLM_EXCL; } else if (uiFlags & XFLM_EXACT) { uiOriginalFlags = XFLM_EXACT | XFLM_KEY_EXACT; uiFlags = XFLM_INCL; } else { uiFlags = XFLM_INCL; } } else { if (uiFlags & XFLM_EXACT) { flmAssert( !(uiFlags & XFLM_KEY_EXACT)); uiFlags = XFLM_EXACT; } else if (uiFlags & XFLM_EXCL) { uiFlags = XFLM_EXCL; } else { uiFlags = XFLM_INCL; } } if (RC_BAD( rc = pSearchKey->outputKey( pIxd, uiIdMatchFlags, pucSearchKey, XFLM_MAX_KEY_SIZE, &uiSearchKeyLen, SEARCH_KEY_FLAG))) { goto Exit; } // If we are not matching on the IDs and this is an XFLM_EXCL // search, tack on a 0xFF for the IDs, which should get us past // all keys that match. We need to turn on the match IDs flags // in this case so that the comparison routine will match on the // 0xFF. if (!uiIdMatchFlags && (uiFlags & XFLM_EXCL)) { pucSearchKey [uiSearchKeyLen++] = 0xFF; bCompareDocId = TRUE; bCompareNodeIds = TRUE; } else { if (uiIdMatchFlags & XFLM_MATCH_IDS) { bCompareNodeIds = TRUE; bCompareDocId = TRUE; } else if (uiIdMatchFlags & XFLM_MATCH_DOC_ID) { bCompareDocId = TRUE; } } } compareObject.setIxInfo( this, pIxd); compareObject.setCompareNodeIds( bCompareNodeIds); compareObject.setCompareDocId( bCompareDocId); compareObject.setSearchKey( pSearchKey); // Get a btree if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) { goto Exit; } if( RC_BAD( rc = pbtree->btOpen( this, pLFile, (pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE, (pIxd->pFirstData) ? TRUE : FALSE, &compareObject))) { goto Exit; } // Search the for the key if (uiSearchKeyLen) { f_memcpy( pucFoundKey, pucSearchKey, uiSearchKeyLen); } uiFoundKeyLen = uiSearchKeyLen; if( RC_BAD( rc = pbtree->btLocateEntry( pucFoundKey, XFLM_MAX_KEY_SIZE, &uiFoundKeyLen, uiFlags, NULL, &uiDataLen))) { if (rc == NE_XFLM_EOF_HIT && uiOriginalFlags & XFLM_EXACT) { rc = RC_SET( NE_XFLM_NOT_FOUND); } goto Exit; } // See if we are in the same key if (uiOriginalFlags & XFLM_KEY_EXACT) { FLMINT iTmpCmp; if (RC_BAD( rc = ixKeyCompare( this, pIxd, (F_DataVector *)pSearchKey, NULL, NULL, (uiIdMatchFlags == XFLM_MATCH_DOC_ID) ? TRUE : FALSE, FALSE, pucFoundKey, uiFoundKeyLen, pucSearchKey, uiSearchKeyLen, &iTmpCmp))) { goto Exit; } if (iTmpCmp != 0) { rc = (uiOriginalFlags & (XFLM_INCL | XFLM_EXCL)) ? RC_SET( NE_XFLM_EOF_HIT) : RC_SET( NE_XFLM_NOT_FOUND); goto Exit; } } // Parse the found key into its individual components if (pFoundKey) { pFoundKey->reset(); if (RC_BAD( rc = pFoundKey->inputKey( pIxd, pucFoundKey, uiFoundKeyLen))) { goto Exit; } // See if there is a data part if (pIxd->pFirstData) { FLMUINT uiDataBufSize; FLMBYTE * pucData; // If the data will fit in the search key buffer, just // reuse it since we are not going to do anything with // it after this. Otherwise, allocate a new buffer. if (uiDataLen <= XFLM_MAX_KEY_SIZE && pucSearchKey) { uiDataBufSize = XFLM_MAX_KEY_SIZE; pucData = pucSearchKey; } else { uiDataBufSize = uiDataLen; if (RC_BAD( rc = m_tempPool.poolAlloc( uiDataBufSize, (void **)&pucData))) { goto Exit; } } // Retrieve the data if (RC_BAD( rc = pbtree->btGetEntry( pucFoundKey, XFLM_MAX_KEY_SIZE, uiFoundKeyLen, pucData, uiDataBufSize, &uiDataLen))) { goto Exit; } // Parse the data if (RC_BAD( rc = pFoundKey->inputData( pIxd, pucData, uiDataLen))) { goto Exit; } } } Exit: m_tempPool.poolReset( pvMark); if (pbtree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); } if( bStartedTrans) { transAbort(); } return( rc); } libxflaim-5.1.969/src/flcreate.cpp0000644000175000017500000003077710511001742020333 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Create a database. // // Tabs: 3 // // Copyright (c) 1990-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flcreate.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /*API~*********************************************************************** Desc: Creates a new FLAIM database. *END************************************************************************/ RCODE FLMAPI F_DbSystem::dbCreate( const char * pszFilePath, // [IN] Full path file name of the database which is to be created. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory. NULL indicates that the RFL files should // be put in the same directory as the database. const char * pszDictFileName, // [IN] Full path of a file containing dictionary definitions to be // imported into the dictionary collection during database // creation. This is only used if pszDictBuf is NULL. If // both pszDictFileName and pszDictBuf are NULL, the database // will be created with an empty dictionary. const char * pszDictBuf, // [IN] Buffer containing dictionary definitions in external XML // format. If the value of this parameter is NULL, // pszDictFileName will be used. XFLM_CREATE_OPTS * pCreateOpts, // [IN] Create options for the database. All members of the // structure should be initialized through a call to f_memset: // // f_memset( pCreateOpts, 0, sizeof( XFLM_CREATE_OPTS)); // // Once initialized, the values of specific members can be set to // reflect the options desired when the database is created (such // as block size and various roll-forward logging options). If // NULL is passed as the value of this parameter, default options // will be used. FLMBOOL bTempDb, // [IN] Flag indicating whether this is a temporary database. // Should try to minimize writing to disk if this is the case. IF_Db ** ppDb // [OUT] Pointer to a database object. If the creation is // successful, the database object will be initialized. ) { RCODE rc = NE_XFLM_OK; F_Db * pDb = NULL; F_Database * pDatabase = NULL; FLMBOOL bDatabaseCreated = FALSE; FLMBOOL bNewDatabase = FALSE; FLMBOOL bMutexLocked = FALSE; FLMUINT uiRflToken = 0; // Make sure the path looks valid if (!pszFilePath || !pszFilePath [0]) { rc = RC_SET( NE_FLM_IO_INVALID_FILENAME); goto Exit; } // Allocate and initialize an F_Db structure. if (RC_BAD( rc = allocDb( &pDb, FALSE))) { goto Exit; } f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; for( ;;) { // See if we already have the file open. // May unlock and re-lock the global mutex. if (RC_BAD( rc = findDatabase( pszFilePath, pszDataDir, &pDatabase))) { goto Exit; } // Didn't find the database if (!pDatabase) { break; } // See if file is open or being opened. if (pDatabase->m_uiOpenIFDbCount || (pDatabase->m_uiFlags & DBF_BEING_OPENED)) { rc = RC_SET( NE_FLM_IO_ACCESS_DENIED); goto Exit; } // Free the F_Database object. May temporarily unlock the global mutex. // For this reason, we must call findDatabase again (see above) after // freeing the object. pDatabase->freeDatabase(); pDatabase = NULL; } // Allocate a new F_Database object if (RC_BAD( rc = allocDatabase( pszFilePath, pszDataDir, bTempDb, &pDatabase))) { goto Exit; } bNewDatabase = TRUE; pDatabase->m_uiMaxFileSize = gv_XFlmSysData.uiMaxFileSize; // Link the F_Db object to the F_Database object. rc = pDb->linkToDatabase( pDatabase); f_mutexUnlock( gv_XFlmSysData.hShareMutex); bMutexLocked = FALSE; if (RC_BAD( rc)) { goto Exit; } // If the database has not already been created, do so now. if (RC_OK( gv_XFlmSysData.pFileSystem->doesFileExist( pszFilePath))) { rc = RC_SET( NE_XFLM_FILE_EXISTS); goto Exit; } // Create the .db file. pDb->m_pSFileHdl->setMaxAutoExtendSize( gv_XFlmSysData.uiMaxFileSize); pDb->m_pSFileHdl->setExtendSize( pDb->m_pDatabase->m_uiFileExtendSize); if (RC_BAD( rc = pDb->m_pSFileHdl->createFile( 0))) { goto Exit; } bDatabaseCreated = TRUE; (void)flmStatGetDb( &pDb->m_Stats, pDatabase, 0, &pDb->m_pDbStats, NULL, NULL); // We must have exclusive access. Create a lock file for that // purpose, if there is not already a lock file. // NOTE: No need for a lock file if this is a temporary database. if (!bTempDb) { if (RC_BAD( rc = pDatabase->getExclAccess( pszFilePath))) { goto Exit; } } if (RC_BAD( rc = pDb->initDbFiles( pszRflDir, pszDictFileName, pszDictBuf, pCreateOpts))) { goto Exit; } // Disable RFL logging (m_pRfl was initialized in initDbFiles) if( pDatabase->m_pRfl) { pDatabase->m_pRfl->disableLogging( &uiRflToken); } // Set FFILE stuff to same state as a completed checkpoint. pDatabase->m_uiFirstLogCPBlkAddress = 0; pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); // Create a checkpoint thread - no need if this is a temporary // database. if (!bTempDb) { if (RC_BAD( rc = pDatabase->startCPThread())) { goto Exit; } if( RC_BAD( rc = pDatabase->startMaintThread())) { goto Exit; } } Exit: if (bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } if (pDb) { pDb->completeOpenOrCreate( rc, bNewDatabase); // completeOpenOrCreate will delete pDb if RC_BAD( rc) if (RC_BAD( rc)) { pDb = NULL; } else { *ppDb = (IF_Db *)pDb; pDb = NULL; // This isn't strictly necessary, but it makes it // obvious that we are no longer using the object. } } if (RC_BAD( rc)) { if (bDatabaseCreated) { gv_pXFlmDbSystem->dbRemove( pszFilePath, pszDataDir, pszRflDir, TRUE); } } else if( uiRflToken) { pDatabase->m_pRfl->enableLogging( &uiRflToken); } return( rc); } /**************************************************************************** Desc: Create a database - initialize all physical areas & data dictionary. ****************************************************************************/ RCODE F_Db::initDbFiles( const char * pszRflDir, const char * pszDictFileName, // Name of dictionary file. This // is only used if pszDictBuf is // NULL. If both pszDictFileName // and pszDictBuf are NULL, the // file will be craeted with an empty // dictionary. const char * pszDictBuf, // Buffer containing dictionary in // XML format. If NULL, // pszDictFileName will be used. XFLM_CREATE_OPTS * pCreateOpts) // Create options for the database. { RCODE rc = NE_XFLM_OK; FLMUINT bTransStarted = FALSE; FLMBYTE * pucBuf = NULL; FLMUINT uiBlkSize; FLMUINT uiWriteBytes; FLMUINT uiRflToken = 0; XFLM_DB_HDR * pDbHdr; F_BLK_HDR * pBlkHdr; F_CachedBlock * pSCache = NULL; FLMBYTE * pucWrappingKey = NULL; FLMUINT32 ui32KeyLen = 0; // Determine what size of buffer to allocate. uiBlkSize = (FLMUINT)(pCreateOpts ? flmAdjustBlkSize( (FLMUINT)pCreateOpts->ui32BlockSize) : (FLMUINT)XFLM_DEFAULT_BLKSIZ); // Allocate a buffer for writing. if (RC_BAD( rc = f_alloc( (FLMUINT)uiBlkSize, &pucBuf))) { goto Exit; } // Initialize the database header structure. pDbHdr = (XFLM_DB_HDR *)pucBuf; flmInitDbHdr( pCreateOpts, TRUE, m_pDatabase->m_bTempDb, pDbHdr); m_pDatabase->m_uiBlockSize = (FLMUINT)pDbHdr->ui16BlockSize; m_pDatabase->m_uiDefaultLanguage = (FLMUINT)pDbHdr->ui8DefaultLanguage; m_pDatabase->m_uiMaxFileSize = (FLMUINT)pDbHdr->ui32MaxFileSize; m_pDatabase->m_uiSigBitsInBlkSize = calcSigBits( uiBlkSize); f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pDbHdr, sizeof( XFLM_DB_HDR)); // Create the first block file. if (!m_pDatabase->m_bTempDb) { if (RC_BAD( rc = m_pSFileHdl->createFile( 1))) { goto Exit; } } if( RC_OK( rc = createDbKey())) { if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( &pucWrappingKey, &ui32KeyLen, m_pDatabase->m_pszDbPasswd, NULL))) { goto Exit; } f_memcpy( m_pDatabase->m_lastCommittedDbHdr.DbKey, pucWrappingKey, ui32KeyLen); m_pDatabase->m_lastCommittedDbHdr.ui32DbKeyLen = ui32KeyLen; m_pDatabase->m_rcLimitedCode = NE_XFLM_OK; m_pDatabase->m_bInLimitedMode = FALSE; m_pDatabase->m_bHaveEncKey = TRUE; } else if( rc == NE_XFLM_ENCRYPTION_UNAVAILABLE) { rc = NE_XFLM_OK; m_pDatabase->m_rcLimitedCode = NE_XFLM_ENCRYPTION_UNAVAILABLE; m_pDatabase->m_bInLimitedMode = TRUE; m_pDatabase->m_bHaveEncKey = FALSE; } else { goto Exit; } // Write out the log header if (RC_BAD( rc = m_pDatabase->writeDbHdr( m_pDbStats, m_pSFileHdl, &m_pDatabase->m_lastCommittedDbHdr, NULL, TRUE))) { goto Exit; } // Initialize and output the first LFH block if (m_pDatabase->m_bTempDb) { getDbHdrInfo( &m_pDatabase->m_lastCommittedDbHdr); if (RC_BAD( rc = m_pDatabase->createBlock( this, &pSCache))) { goto Exit; } pBlkHdr = (F_BLK_HDR *)pSCache->m_pBlkHdr; m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr = pBlkHdr->ui32BlkAddr; } else { // Copy the Db header to the checkpointDbHdr buffer. // This is now the first official checkpoint version of the log // header. It must be copied to the checkpointDbHdr buffer so that // it will not be lost in subsequent calls to flmWriteDbHdr. f_memcpy( &m_pDatabase->m_checkpointDbHdr, &m_pDatabase->m_lastCommittedDbHdr, sizeof( XFLM_DB_HDR)); f_memset( pucBuf, 0, uiBlkSize); pBlkHdr = (F_BLK_HDR *)pucBuf; pBlkHdr->ui32BlkAddr = m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr; pBlkHdr->ui64TransID = 0; } pBlkHdr->ui8BlkType = BT_LFH_BLK; pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(uiBlkSize - SIZEOF_STD_BLK_HDR); blkSetNativeFormat( pBlkHdr); if (!m_pDatabase->m_bTempDb) { pBlkHdr->ui32BlkCRC = calcBlkCRC( pBlkHdr, SIZEOF_STD_BLK_HDR); if (RC_BAD( rc = m_pSFileHdl->writeBlock( pBlkHdr->ui32BlkAddr, uiBlkSize, pucBuf, &uiWriteBytes))) { goto Exit; } // Force things to disk. if (RC_BAD( rc = m_pSFileHdl->flush())) { goto Exit; } } // Allocate the pRfl object. Could not do this until this point // because we need to have the version number, block size, etc. // setup in the database header flmAssert( !m_pDatabase->m_pRfl); if (!m_pDatabase->m_bTempDb) { if ((m_pDatabase->m_pRfl = f_new F_Rfl) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = m_pDatabase->m_pRfl->setup( m_pDatabase, pszRflDir))) { goto Exit; } // Disable RFL logging m_pDatabase->m_pRfl->disableLogging( &uiRflToken); // Start an update transaction and populate the dictionary. // This also creates the default collections and indexes. if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bTransStarted = TRUE; if (RC_BAD( rc = dictCreate( pszDictFileName, pszDictBuf))) { goto Exit; } // Because the checkpoint thread has not yet been created, // flmCommitDbTrans will force a checkpoint when it completes, // ensuring a consistent database state. bTransStarted = FALSE; if (RC_BAD( rc = commitTrans( 0, TRUE))) { goto Exit; } } else { // The uncommitted header must have all of the stuff from the committed header // in order for this to be able to work as if an update transaction was in // progress. f_memcpy( &m_pDatabase->m_uncommittedDbHdr, &m_pDatabase->m_lastCommittedDbHdr, sizeof( XFLM_DB_HDR)); } Exit: // Free the temporary buffer, if it was allocated. f_free( &pucBuf); if (pucWrappingKey) { f_free( &pucWrappingKey); } if (bTransStarted) { abortTrans(); } if( uiRflToken) { m_pDatabase->m_pRfl->enableLogging( &uiRflToken); } if (pSCache) { ScaReleaseCache( pSCache, FALSE); } return( rc); } libxflaim-5.1.969/src/xflaim.h0000644000175000017500000061720610511001742017471 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: XFLAIM public definitions and interfaces // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: xflaim.h 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #ifndef XFLAIM_H #define XFLAIM_H #include "xflaimtk.h" /**************************************************************************** Forward References ****************************************************************************/ flminterface IF_Backup; flminterface IF_DataVector; flminterface IF_Db; flminterface IF_DbInfo; flminterface IF_DirHdl; flminterface IF_DOMNode; flminterface IF_FileHdl; flminterface IF_FileSystem; flminterface IF_IStream; flminterface IF_PosIStream; flminterface IF_ResultSet; flminterface IF_Query; flminterface IF_ThreadInfo; flminterface IF_NodeInfo; flminterface IF_BTreeInfo; // These are interfaces that need to be implemented by // applications. XFlaim uses them to report status or to do // callbacks of various kinds. flminterface IF_OStream; flminterface IF_BackupClient; flminterface IF_BackupStatus; flminterface IF_CommitClient; flminterface IF_DbCheckStatus; flminterface IF_DbCopyStatus; flminterface IF_DbRebuildStatus; flminterface IF_DbRenameStatus; flminterface IF_DeleteStatus; flminterface IF_EventClient; flminterface IF_IxClient; flminterface IF_IxStatus; flminterface IF_LoggerClient; flminterface IF_LogMessageClient; flminterface IF_OperandComparer; flminterface IF_RestoreClient; flminterface IF_RestoreStatus; flminterface IF_ResultSetSortStatus; flminterface IF_ResultSetCompare; flminterface IF_QueryStatus; flminterface IF_QueryValidator; flminterface IF_QueryValFunc; flminterface IF_QueryNodeSource; flminterface IF_UpgradeClient; flminterface IF_BTreeInfoStatus; /**************************************************************************** Desc: This structure is used as a parameter to dbCreate to specify the create options for a database. It is also optionally returned when calling dbOpen. IMPORTANT NOTE: This needs to be kept in sync with the same structure that is defined in java and csharp modules. ****************************************************************************/ typedef struct { FLMUINT32 ui32BlockSize; #define XFLM_DEFAULT_BLKSIZ 4096 FLMUINT32 ui32VersionNum; #define XFLM_VER_5_12 512 #define XFLM_CURRENT_VERSION_NUM XFLM_VER_5_12 #define XFLM_CURRENT_VER_STR "5.12" FLMUINT32 ui32MinRflFileSize; #define XFLM_DEFAULT_MIN_RFL_FILE_SIZE ((FLMUINT)100 * (FLMUINT)1024 * (FLMUINT)1024) FLMUINT32 ui32MaxRflFileSize; #define XFLM_DEFAULT_MAX_RFL_FILE_SIZE FLM_MAXIMUM_FILE_SIZE FLMBOOL bKeepRflFiles; #define XFLM_DEFAULT_KEEP_RFL_FILES_FLAG FALSE FLMBOOL bLogAbortedTransToRfl; #define XFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG FALSE FLMUINT32 ui32DefaultLanguage; #define XFLM_DEFAULT_LANG (FLM_US_LANG) } XFLM_CREATE_OPTS, F_CREATE_OPTS; typedef enum { XML_NO_ERROR = 0, XML_ERR_BAD_ELEMENT_NAME, // 1 Invalid element name - does not start with a valid character for element names XML_ERR_XMLNS_IN_ELEMENT_NAME, // 2 Element names cannot be "xmlns" or have "xmlns:" as a prefix XML_ERR_ELEMENT_NAME_MISMATCH, // 3 The element name inside the "' XML_ERR_EXPECTING_ELEMENT_LT, // 6 Expecting a '<' to begin an element name XML_ERR_EXPECTING_EQ, // 7 Expecting a '=' after the attribute name XML_ERR_MULTIPLE_XMLNS_DECLS, // 8 Multiple "xmlns" default namespace declarations in an element XML_ERR_MULTIPLE_PREFIX_DECLS, // 9 Multiple definitions for the same prefix ("xmlns:prefix=...") in an element XML_ERR_EXPECTING_QUEST_GT, // 10 Expecting "?>" to terminate " FDB.ui64CurrTransID // ui32LogicalEOF --> FDB.uiLogicalEOF // ui32FirstAvailBlkAddr --> FDB.uiFirstAvailBlkAddr; // IMPORTANT NOTE: The following items cannot currently be changed // during an update transaction: // ui32DbVersion // ui16BlockSize // ui8DefaultLanguage // ui32MaxFileSize // ui32FirstLFBlkAddr // This is because they are always accessed from pFile->lastCommittedDbHdr // regardless of transaction type. If we ever want to change them in an // update transaction, we will need to modify flmGetDbHdrInfo to copy them // into the FDB, and then only access them from within the FDB. FLMUINT32 ui32DbVersion; // LOG_FLAIM_VERSION FLMUINT8 ui8BlkChkSummingEnabled; // Is block checksumming enabled? // Not currently used. FLMUINT8 ui8RflKeepFiles; // LOG_KEEP_RFL_FILES FLMUINT8 ui8RflAutoTurnOffKeep; // LOG_AUTO_TURN_OFF_KEEP_RFL FLMUINT8 ui8RflKeepAbortedTrans; // LOG_KEEP_ABORTED_TRANS_IN_RFL FLMUINT32 ui32RflCurrFileNum; // LOG_RFL_FILE_NUM FLMUINT64 ui64LastRflCommitID; // LOG_LAST_RFL_COMMIT_ID FLMUINT32 ui32RflLastFileNumDeleted; // LOG_LAST_RFL_FILE_DELETED FLMUINT32 ui32RflLastTransOffset; // LOG_RFL_LAST_TRANS_OFFSET FLMUINT32 ui32RflLastCPFileNum; // LOG_RFL_LAST_CP_FILE_NUM FLMUINT32 ui32RflLastCPOffset; // LOG_RFL_LAST_CP_OFFSET FLMUINT64 ui64RflLastCPTransID; // LOG_LAST_CP_TRANS_ID FLMUINT32 ui32RflMinFileSize; // LOG_RFL_MIN_FILE_SIZE FLMUINT32 ui32RflMaxFileSize; // LOG_RFL_MAX_FILE_SIZE FLMUINT64 ui64CurrTransID; // LOG_CURR_TRANS_ID FLMUINT64 ui64TransCommitCnt; // LOG_COMMIT_COUNT FLMUINT32 ui32RblEOF; // LOG_ROLLBACK_EOF FLMUINT32 ui32RblFirstCPBlkAddr; // LOG_PL_FIRST_CP_BLOCK_ADDR FLMUINT32 ui32FirstAvailBlkAddr; // LOG_PF_AVAIL_BLKS FLMUINT32 ui32FirstLFBlkAddr; // First logical file block. FLMUINT32 ui32LogicalEOF; // LOG_LOGICAL_EOF FLMUINT32 ui32MaxFileSize; // LOG_MAX_FILE_SIZE FLMUINT64 ui64LastBackupTransID; // LOG_LAST_BACKUP_TRANS_ID FLMUINT32 ui32IncBackupSeqNum; // LOG_INC_BACKUP_SEQ_NUM FLMUINT32 ui32BlksChangedSinceBackup;// LOG_BLK_CHG_SINCE_BACKUP #define XFLM_SERIAL_NUM_SIZE 16 FLMBYTE ucDbSerialNum[ XFLM_SERIAL_NUM_SIZE]; // LOG_DB_SERIAL_NUM FLMBYTE ucLastTransRflSerialNum[ XFLM_SERIAL_NUM_SIZE]; // LOG_LAST_TRANS_RFL_SERIAL_NUM FLMBYTE ucNextRflSerialNum[ XFLM_SERIAL_NUM_SIZE]; // LOG_RFL_NEXT_SERIAL_NUM FLMBYTE ucIncBackupSerialNum[ XFLM_SERIAL_NUM_SIZE]; // LOG_INC_BACKUP_SERIAL_NUM FLMUINT32 ui32DbKeyLen; // LOG_DATABASE_KEY_LEN // IMPORTANT NOTE: If anything is changed in here, need to make // corresponding changes to convertDbHdr routine and // flmVerifyDiskStructOffsets routine. FLMBYTE ucReserved[ 64]; // Reserved for future // Always initialized to zero // Checksum should ALWAYS be last FLMUINT32 ui32HdrCRC; // LOG_HDR_CHECKSUM // Encryption Key stuff #define XFLM_MAX_ENC_KEY_SIZE 256 FLMBYTE DbKey[ XFLM_MAX_ENC_KEY_SIZE]; // LOG_DATABASE_KEY // Offsets of variables in the structure #define XFLM_DB_HDR_szSignature_OFFSET 0 #define XFLM_DB_HDR_ui8IsLittleEndian_OFFSET 8 #define XFLM_DB_HDR_ui8DefaultLanguage_OFFSET 9 #define XFLM_DB_HDR_ui16BlockSize_OFFSET 10 #define XFLM_DB_HDR_ui32DbVersion_OFFSET 12 #define XFLM_DB_HDR_ui8BlkChkSummingEnabled_OFFSET 16 #define XFLM_DB_HDR_ui8RflKeepFiles_OFFSET 17 #define XFLM_DB_HDR_ui8RflAutoTurnOffKeep_OFFSET 18 #define XFLM_DB_HDR_ui8RflKeepAbortedTrans_OFFSET 19 #define XFLM_DB_HDR_ui32RflCurrFileNum_OFFSET 20 #define XFLM_DB_HDR_ui64LastRflCommitID_OFFSET 24 #define XFLM_DB_HDR_ui32RflLastFileNumDeleted_OFFSET 32 #define XFLM_DB_HDR_ui32RflLastTransOffset_OFFSET 36 #define XFLM_DB_HDR_ui32RflLastCPFileNum_OFFSET 40 #define XFLM_DB_HDR_ui32RflLastCPOffset_OFFSET 44 #define XFLM_DB_HDR_ui64RflLastCPTransID_OFFSET 48 #define XFLM_DB_HDR_ui32RflMinFileSize_OFFSET 56 #define XFLM_DB_HDR_ui32RflMaxFileSize_OFFSET 60 #define XFLM_DB_HDR_ui64CurrTransID_OFFSET 64 #define XFLM_DB_HDR_ui64TransCommitCnt_OFFSET 72 #define XFLM_DB_HDR_ui32RblEOF_OFFSET 80 #define XFLM_DB_HDR_ui32RblFirstCPBlkAddr_OFFSET 84 #define XFLM_DB_HDR_ui32FirstAvailBlkAddr_OFFSET 88 #define XFLM_DB_HDR_ui32FirstLFBlkAddr_OFFSET 92 #define XFLM_DB_HDR_ui32LogicalEOF_OFFSET 96 #define XFLM_DB_HDR_ui32MaxFileSize_OFFSET 100 #define XFLM_DB_HDR_ui64LastBackupTransID_OFFSET 104 #define XFLM_DB_HDR_ui32IncBackupSeqNum_OFFSET 112 #define XFLM_DB_HDR_ui32BlksChangedSinceBackup_OFFSET 116 #define XFLM_DB_HDR_ucDbSerialNum_OFFSET 120 #define XFLM_DB_HDR_ucLastTransRflSerialNum_OFFSET 136 #define XFLM_DB_HDR_ucNextRflSerialNum_OFFSET 152 #define XFLM_DB_HDR_ucIncBackupSerialNum_OFFSET 168 #define XFLM_DB_HDR_ui32DbKeyLen 184 #define XFLM_DB_HDR_ucReserved_OFFSET 188 #define XFLM_DB_HDR_ui32HdrCRC_OFFSET 252 #define XFLM_DB_HDR_DbKey 256 } XFLM_DB_HDR; // uiFlags values for keyRetrieve() method // IMPORTANT NOTE: If these change, please be sure to make // corresponding changes in java and C# code. #define XFLM_INCL 0x0010 #define XFLM_EXCL 0x0020 #define XFLM_EXACT 0x0040 #define XFLM_KEY_EXACT 0x0080 #define XFLM_FIRST 0x0100 #define XFLM_LAST 0x0200 #define XFLM_MATCH_IDS 0x0400 #define XFLM_MATCH_DOC_ID 0x0800 // Flags used by openDb method #define XFLM_ONLINE 0x0020 #define XFLM_DONT_REDO_LOG 0x0040 #define XFLM_DONT_RESUME_THREADS 0x0080 #define XFLM_DO_LOGICAL_CHECK 0x0100 #define XFLM_SKIP_DOM_LINK_CHECK 0x0200 // Used only in dbCheck. #define XFLM_ALLOW_LIMITED_MODE 0x0400 // Maximum key size #define XFLM_MAX_KEY_SIZE 1024 // "Wait forever" when starting a transaction #define FLM_NO_TIMEOUT 0xFF // Node types. The order of these enums // must be preserved as the code sometimes // depends on the order to test ranges of // node types. // IMPORTANT NOTE: If any of these change or there // are additions, be sure to also modify the // corresponding java and C# definitions. typedef enum { INVALID_NODE = 0x00, DOCUMENT_NODE = 0x01, ELEMENT_NODE = 0x02, DATA_NODE = 0x03, COMMENT_NODE = 0x04, CDATA_SECTION_NODE = 0x05, ANNOTATION_NODE = 0x06, PROCESSING_INSTRUCTION_NODE = 0x07, ATTRIBUTE_NODE = 0x08, ANY_NODE_TYPE = 0xFFFF } eDomNodeType; // NOTE: The eNodeInsertLoc enum values cannot // be changed. The RFL uses these values // when logging packets. // IMPORTANT NOTE: If any of these change or there // are additions, be sure to also modify the // corresponding java and C# definitions. typedef enum { XFLM_ROOT = 0, XFLM_FIRST_CHILD, XFLM_LAST_CHILD, XFLM_PREV_SIB, XFLM_NEXT_SIB, XFLM_ATTRIBUTE } eNodeInsertLoc; // NOTE: The order of the eDbTransType enum values // cannot be changed. XFLAIM tests ranges of these // values. // IMPORTANT NOTE: If these are changed, be sure to make the // corresponding changes in java and C# code. typedef enum { XFLM_NO_TRANS = 0, XFLM_READ_TRANS, XFLM_UPDATE_TRANS } eDbTransType; // Transaction flags // IMPORTANT NOTE: If any of these are changed, the corresponding // java and C# code should also be updated. #define XFLM_DONT_KILL_TRANS 0x1 #define XFLM_DONT_POISON_CACHE 0x2 // Backup types typedef enum { // These values are stored in the header of the // backup, so do not change their values. XFLM_FULL_BACKUP = 0, XFLM_INCREMENTAL_BACKUP } eDbBackupType; // Encryption #define FLM_NICI_AES 0 #define FLM_NICI_DES3 1 #define FLM_NICI_UNDEFINED 0xFF #define XFLM_NICI_AES128 128 #define XFLM_NICI_AES192 192 #define XFLM_NICI_AES256 256 #define XFLM_NICI_DES3X 168 // Data types. #define XFLM_NODATA_TYPE 0 #define XFLM_TEXT_TYPE 1 #define XFLM_NUMBER_TYPE 2 #define XFLM_BINARY_TYPE 3 #define XFLM_NUM_OF_TYPES 4 #define XFLM_UNKNOWN_TYPE 0xF #define XFLM_STRING_OPTION_STR "string" #define XFLM_INTEGER_OPTION_STR "integer" #define XFLM_BINARY_OPTION_STR "binary" #define XFLM_NODATA_OPTION_STR "nodata" #define XFLM_CHECKING_OPTION_STR "checking" #define XFLM_PURGE_OPTION_STR "purge" #define XFLM_ACTIVE_OPTION_STR "active" #define XFLM_INDEX_SUSPENDED_STR "suspended" #define XFLM_INDEX_OFFLINE_STR "offline" #define XFLM_INDEX_ONLINE_STR "online" #define XFLM_CASE_INSENSITIVE_OPTION_STR "caseinsensitive" #define XFLM_DESCENDING_OPTION_STR "sortdescending" #define XFLM_MISSING_HIGH_OPTION_STR "sortmissinghigh" #define XFLM_MINSPACES_OPTION_STR "minspaces" #define XFLM_WHITESPACE_AS_SPACE_STR "whitespaceasspace" #define XFLM_IGNORE_LEADINGSPACES_OPTION_STR "ignoreleadingspaces" #define XFLM_IGNORE_TRAILINGSPACES_OPTION_STR "ignoretrailingspaces" #define XFLM_NOUNDERSCORE_OPTION_STR "nounderscores" #define XFLM_NOSPACE_OPTION_STR "nospaces" #define XFLM_NODASH_OPTION_STR "nodashes" #define XFLM_VALUE_OPTION_STR "value" #define XFLM_PRESENCE_OPTION_STR "presence" #define XFLM_SUBSTRING_OPTION_STR "substring" #define XFLM_EACHWORD_OPTION_STR "eachword" #define XFLM_ABS_POS_OPTION_STR "abspos" #define XFLM_METAPHONE_OPTION_STR "metaphone" // Encryption Schemes #define XFLM_ENC_AES_OPTION_STR "aes" #define XFLM_ENC_DES3_OPTION_STR "des3" // Recovery actions typedef enum { XFLM_RESTORE_ACTION_CONTINUE = 0, // Continue recovery XFLM_RESTORE_ACTION_STOP, // Stop recovery XFLM_RESTORE_ACTION_SKIP, // Skip operation (future) XFLM_RESTORE_ACTION_RETRY // Retry the operation } eRestoreAction; // Events typedef enum { XFLM_EVENT_LOCKS, XFLM_EVENT_UPDATES, XFLM_MAX_EVENT_CATEGORIES // Should always be last. } eEventCategory; typedef enum { XFLM_EVENT_LOCK_WAITING, XFLM_EVENT_LOCK_GRANTED, XFLM_EVENT_LOCK_SUSPENDED, XFLM_EVENT_LOCK_RESUMED, XFLM_EVENT_LOCK_RELEASED, XFLM_EVENT_LOCK_TIMEOUT, XFLM_EVENT_BEGIN_TRANS, XFLM_EVENT_COMMIT_TRANS, XFLM_EVENT_ABORT_TRANS, XFLM_EVENT_CREATE_NODE, XFLM_EVENT_MODIFY_NODE, XFLM_EVENT_DELETE_NODE, XFLM_EVENT_LINK_NODE, XFLM_EVENT_UNLINK_NODE, XFLM_EVENT_INDEXING_PROGRESS, XFLM_MAX_EVENT_TYPES // Should always be last. } eEventType; // Logical files typedef enum { XFLM_LF_INVALID = 0, XFLM_LF_COLLECTION, XFLM_LF_INDEX } eLFileType; // Message logging typedef enum { XFLM_QUERY_MESSAGE, XFLM_TRANSACTION_MESSAGE, XFLM_GENERAL_MESSAGE, XFLM_NUM_MESSAGE_TYPES } eLogMessageType; typedef struct { FLMBOOL bRunning; FLMUINT32 ui32RunningTime; FLMBOOL bForcingCheckpoint; FLMUINT32 ui32ForceCheckpointRunningTime; FLMUINT32 ui32ForceCheckpointReason; #define XFLM_CP_TIME_INTERVAL_REASON 1 #define XFLM_CP_SHUTTING_DOWN_REASON 2 #define XFLM_CP_RFL_VOLUME_PROBLEM 3 FLMBOOL bWritingDataBlocks; FLMUINT32 ui32LogBlocksWritten; FLMUINT32 ui32DataBlocksWritten; FLMUINT32 ui32DirtyCacheBytes; FLMUINT32 ui32BlockSize; FLMUINT32 ui32WaitTruncateTime; } XFLM_CHECKPOINT_INFO; typedef struct { FLMUINT uiByteCount; FLMUINT uiCount; FLMUINT uiOldVerCount; FLMUINT uiOldVerBytes; FLMUINT uiCacheHits; FLMUINT uiCacheHitLooks; FLMUINT uiCacheFaults; FLMUINT uiCacheFaultLooks; FLM_SLAB_USAGE slabUsage; } XFLM_CACHE_USAGE; typedef struct { FLMUINT uiMaxBytes; FLMUINT uiTotalBytesAllocated; FLMBOOL bDynamicCacheAdjust; FLMUINT uiCacheAdjustPercent; FLMUINT uiCacheAdjustMin; FLMUINT uiCacheAdjustMax; FLMUINT uiCacheAdjustMinToLeave; FLMUINT uiDirtyCount; FLMUINT uiDirtyBytes; FLMUINT uiNewCount; FLMUINT uiNewBytes; FLMUINT uiLogCount; FLMUINT uiLogBytes; FLMUINT uiFreeCount; FLMUINT uiFreeBytes; FLMUINT uiReplaceableCount; FLMUINT uiReplaceableBytes; XFLM_CACHE_USAGE BlockCache; XFLM_CACHE_USAGE NodeCache; FLMBOOL bPreallocatedCache; } XFLM_CACHE_INFO; // IMPORTANT NOTE: This structure needs to be kept in sync with the // corresponding structures and classes in java and C#. // This structure should be like the XFLM_CACHE_USAGE structure, // except that it always keeps 64 bit values for some of the // members so that we can hold the value if we are on a 64 // bit platforms where we can address more than 4 GB of memory. // We didn't want to modify the XFLM_CACHE_USAGE structure because // we didn't want the overhead of 64 bit arithmetic when keeping // track of cache statistics. Hence, we made this "C#" structure // instead to copy data into when passing the information out // to C#. typedef struct { FLMUINT64 ui64ByteCount; FLMUINT64 ui64Count; FLMUINT64 ui64OldVerCount; FLMUINT64 ui64OldVerBytes; FLMUINT32 ui32CacheHits; FLMUINT32 ui32CacheHitLooks; FLMUINT32 ui32CacheFaults; FLMUINT32 ui32CacheFaultLooks; FLM_SLAB_USAGE slabUsage; } CS_XFLM_CACHE_USAGE; // IMPORTANT NOTE: This structure needs to be kept in sync with the // corresponding structures and classes in java and C#. // This structure should be like the XFLM_CACHE_INFO structure, // except that it always keeps 64 bit values for some of the // members so that we can hold the value if we are on a 64 // bit platforms where we can address more than 4 GB of memory. // We didn't want to modify the XFLM_CACHE_INFO structure because // we didn't want the overhead of 64 bit arithmetic when keeping // track of cache statistics. Hence, we made this "C#" structure // instead to copy data into when passing the information out // to C#. typedef struct { FLMUINT64 ui64MaxBytes; FLMUINT64 ui64TotalBytesAllocated; FLMBOOL bDynamicCacheAdjust; FLMUINT32 ui32CacheAdjustPercent; FLMUINT64 ui64CacheAdjustMin; FLMUINT64 ui64CacheAdjustMax; FLMUINT64 ui64CacheAdjustMinToLeave; FLMUINT64 ui64DirtyCount; FLMUINT64 ui64DirtyBytes; FLMUINT64 ui64NewCount; FLMUINT64 ui64NewBytes; FLMUINT64 ui64LogCount; FLMUINT64 ui64LogBytes; FLMUINT64 ui64FreeCount; FLMUINT64 ui64FreeBytes; FLMUINT64 ui64ReplaceableCount; FLMUINT64 ui64ReplaceableBytes; CS_XFLM_CACHE_USAGE BlockCache; CS_XFLM_CACHE_USAGE NodeCache; FLMBOOL bPreallocatedCache; } CS_XFLM_CACHE_INFO; // IMPORTANT NOTE: This structure needs to stay in sync with // corresponding structures in java and C# code. typedef struct { FLMUINT64 ui64Count; // Number of times read or // write operation was // performed. FLMUINT64 ui64TotalBytes; // Total number of bytes // involved in the read or // write operations. FLMUINT64 ui64ElapMilli; // Total elapsed time for the // read or write operations. } XFLM_DISKIO_STAT; // IMPORTANT NOTE: This structure needs to stay in sync with // corresponding structures in java and C# code. typedef struct { F_COUNT_TIME_STAT CommittedTrans; // Transactions committed F_COUNT_TIME_STAT AbortedTrans; // Transactions aborted } XFLM_RTRANS_STATS; // IMPORTANT NOTE: This structure needs to stay in sync with // corresponding structures in java and C# code. typedef struct { F_COUNT_TIME_STAT CommittedTrans; // Transactions committed F_COUNT_TIME_STAT GroupCompletes; // Group completes. FLMUINT64 ui64GroupFinished; // Transactions finished in group F_COUNT_TIME_STAT AbortedTrans; // Transactions aborted } XFLM_UTRANS_STATS; // IMPORTANT NOTE: This structure needs to stay in sync with // corresponding structures in java and C# code. typedef struct { XFLM_DISKIO_STAT BlockReads; // Statistics on block reads. XFLM_DISKIO_STAT BlockWrites; // Statistics on Block writes. XFLM_DISKIO_STAT OldViewBlockReads; // Statistics on old view // block reads. FLMUINT32 ui32BlockChkErrs; // Number of times we had // check errors reading // blocks. FLMUINT32 ui32OldViewBlockChkErrs;// Number of times we had // check errors reading an // old view of a block. FLMUINT32 ui32OldViewErrors; // Number of times we had an // old view error when // reading. } XFLM_BLOCKIO_STATS; typedef struct { FLMBOOL bHaveStats; // Flag indicating whether or // not there are statistics // for this LFILE. XFLM_BLOCKIO_STATS RootBlockStats; // Block I/O statistics for root // blocks. XFLM_BLOCKIO_STATS MiddleBlockStats; // Block I/O statistics for // blocks that are not root // blocks or leaf blocks. XFLM_BLOCKIO_STATS LeafBlockStats; // Block I/O statistics for leaf // blocks. FLMUINT64 ui64BlockSplits; // Number of block splits that // have occurred in this logical // file. FLMUINT64 ui64BlockCombines; // Number of block combines that // have occurred in this LFile FLMUINT uiLFileNum; // Logical file number. eLFileType eLfType; // Logical file type. } XFLM_LFILE_STATS; typedef struct { char * pszDbName; // Database name - from pFile. FLMBOOL bHaveStats; // Flag indicating whether or // not there are statistics // for this database. XFLM_RTRANS_STATS ReadTransStats; // Read Transaction // Statistics. XFLM_UTRANS_STATS UpdateTransStats; // Update Transaction // Statistics. FLMUINT uiLFileAllocSeq; // Allocation sequence number // for LFILE array. XFLM_LFILE_STATS * pLFileStats; // Pointer to LFILE statistics // array. FLMUINT uiLFileStatArraySize; // Size of LFILE statistics // array. FLMUINT uiNumLFileStats; // Number of elements in LFILE // array currently in use. XFLM_BLOCKIO_STATS LFHBlockStats; // Block I/O statistics for // LFH blocks. XFLM_BLOCKIO_STATS AvailBlockStats; // Block I/O statistics for // AVAIL blocks. // Write statistics XFLM_DISKIO_STAT DbHdrWrites; // Statistics on DB header // writes. XFLM_DISKIO_STAT LogBlockWrites; // Statistics on log block // writes XFLM_DISKIO_STAT LogBlockRestores; // Statistics on log block // restores // Read statistics. XFLM_DISKIO_STAT LogBlockReads; // Statistics on log block // reads. FLMUINT uiLogBlockChkErrs; // Number of times we had // checksum errors reading // blocks from the rollback // log. FLMUINT uiReadErrors; // Number of times we got read // errors. FLMUINT uiWriteErrors; // Number of times we got write // errors. // Lock statistics F_LOCK_STATS LockStats; } XFLM_DB_STATS; typedef struct { XFLM_DB_STATS * pDbStats; // Pointer to array of database // statistics. FLMUINT uiDBAllocSeq; // Allocation sequence number // for database statistics. FLMUINT uiDbStatArraySize; // Size of the database statistics // array. FLMUINT uiNumDbStats; // Number of elements in the // database statistics array that // are currently in use. FLMBOOL bCollectingStats; // Flag indicating whether or // not we are currently // collecting statistics. FLMUINT uiStartTime; // Time we started collecting // statistics. FLMUINT uiStopTime; // Time we stopped collecting // statistics. } XFLM_STATS; // IMPORTANT NOTE: If these change, please be sure to make // corresponding changes in java and C# code. typedef enum { XFLM_INDEX_ONLINE = 0, XFLM_INDEX_BRINGING_ONLINE, XFLM_INDEX_SUSPENDED } eXFlmIndexState; // IMPORTANT NOTE: If these change, please be sure to make // corresponding changes in java and C# code. typedef struct { FLMUINT64 ui64LastDocumentIndexed;// If ~0 then index is online, // otherwise this is the value of the last // document ID that was indexed. FLMUINT64 ui64KeysProcessed; // Keys processed for offline thread. FLMUINT64 ui64DocumentsProcessed; // Documents processed for offline thread. FLMUINT64 ui64Transactions; // Number of transactions started by the // indexing thread FLMUINT32 ui32IndexNum; // Index number FLMUINT32 ui32StartTime; // Start time of the offline process or zero. eXFlmIndexState eState; } XFLM_INDEX_STATUS; /**************************************************************************** Desc: The following structures are used to pass data to the client via the IF_DbRebuildStatus interface IMPORTANT NOTE: If this structure changes, corresponding changes need to be made to the same structure in java and/or C# code. ****************************************************************************/ typedef struct { FLMINT32 i32DoingFlag; #define REBUILD_GET_BLK_SIZ 1 #define REBUILD_RECOVER_DICT 2 #define REBUILD_RECOVER_DATA 3 FLMBOOL bStartFlag; FLMUINT64 ui64FileSize; FLMUINT64 ui64BytesExamined; FLMUINT64 ui64TotNodes; FLMUINT64 ui64NodesRecov; FLMUINT64 ui64DiscardedDocs; } XFLM_REBUILD_INFO; // IMPORTANT NOTE: If this structure changes, corresponding changes need to be made // to the same structure in java and/or C# code typedef struct { FLMINT32 i32ErrCode; // Zero means no error is being reported FLMUINT32 ui32ErrLocale; #define XFLM_LOCALE_NONE 0 #define XFLM_LOCALE_LFH_LIST 1 #define XFLM_LOCALE_AVAIL_LIST 2 #define XFLM_LOCALE_B_TREE 3 #define XFLM_LOCALE_INDEX 4 FLMUINT32 ui32ErrLfNumber; FLMUINT32 ui32ErrLfType; FLMUINT32 ui32ErrBTreeLevel; FLMUINT32 ui32ErrBlkAddress; FLMUINT32 ui32ErrParentBlkAddress; FLMUINT32 ui32ErrElmOffset; FLMUINT64 ui64ErrNodeId; IF_DataVector * ifpErrIxKey; } XFLM_CORRUPT_INFO; // IMPORTANT NOTE: If this structure changes, corresponding changes need to be made // to the same structure in java and/or C# code typedef struct { FLMINT32 i32CheckPhase; #define XFLM_CHECK_LFH_BLOCKS 1 #define XFLM_CHECK_B_TREE 2 #define XFLM_CHECK_AVAIL_BLOCKS 3 #define XFLM_CHECK_RS_SORT 4 #define XFLM_CHECK_DOM_LINKS 5 FLMBOOL bStartFlag; FLMUINT64 ui64FileSize; FLMUINT32 ui32NumLFs; FLMUINT32 ui32CurrLF; FLMUINT32 ui32LfNumber; /* Logical File Pass */ FLMUINT32 ui32LfType; FLMUINT64 ui64BytesExamined; FLMUINT32 ui32NumProblemsFixed; /* Number of corruptions repaired */ FLMUINT64 ui64NumDomNodes; /* in the current Lf */ FLMUINT64 ui64NumDomLinksVerified; /* in the current Lf */ FLMUINT64 ui64NumBrokenDomLinks; /* in the current Lf */ /* Index check progress */ FLMUINT64 ui64NumKeys; /* Number of keys in the result set */ FLMUINT64 ui64NumDuplicateKeys; /* Number of duplicate keys generated */ FLMUINT64 ui64NumKeysExamined; /* Number of keys checked */ FLMUINT64 ui64NumKeysNotFound; /* Keys in index but missing from document */ FLMUINT64 ui64NumDocKeysNotFound; /* Keys in documents but missing from indexes */ FLMUINT64 ui64NumConflicts; /* # of non-corruption conflicts */ } XFLM_PROGRESS_CHECK_INFO; /************************************************************************** Desc: STUFF FOR IF_Query interface IMPORTANT NOTE: If any of these change or new ones are added, corresponding changes should be made in the java and C# code. **************************************************************************/ typedef enum { XFLM_UNKNOWN_OP = 0, // NOTE: These operators MUST stay in this order - this order is assumed // by the precedence table - see fquery.cpp XFLM_AND_OP = 1, XFLM_OR_OP = 2, XFLM_NOT_OP = 3, XFLM_EQ_OP = 4, XFLM_NE_OP = 5, XFLM_APPROX_EQ_OP = 6, XFLM_LT_OP = 7, XFLM_LE_OP = 8, XFLM_GT_OP = 9, XFLM_GE_OP = 10, XFLM_BITAND_OP = 11, XFLM_BITOR_OP = 12, XFLM_BITXOR_OP = 13, XFLM_MULT_OP = 14, XFLM_DIV_OP = 15, XFLM_MOD_OP = 16, XFLM_PLUS_OP = 17, XFLM_MINUS_OP = 18, XFLM_NEG_OP = 19, XFLM_LPAREN_OP = 20, XFLM_RPAREN_OP = 21, XFLM_COMMA_OP = 22, XFLM_LBRACKET_OP = 23, XFLM_RBRACKET_OP = 24, // IMPORTANT NOTE: If operators are added after this point, // modify the isLegalOperator method below. // The following operators are only used internally. They // may NOT be passed into the addOperator method. XFLM_EXISTS_OP = 25, XFLM_RANGE_OP = 26, XFLM_MATCH_OP = 27 } eQueryOperators; #define XFLM_FIRST_ARITH_OP XFLM_BITAND_OP #define XFLM_LAST_ARITH_OP XFLM_NEG_OP // Comparison rules for strings #define XFLM_COMP_CASE_INSENSITIVE FLM_COMP_CASE_INSENSITIVE #define XFLM_COMP_COMPRESS_WHITESPACE FLM_COMP_COMPRESS_WHITESPACE // Compress consecutive spaces to single space #define XFLM_COMP_NO_WHITESPACE FLM_COMP_NO_WHITESPACE // Ignore all whitespace. This and // COMP_COMPRESS_WHITESPACE cannot be used // together. #define XFLM_COMP_NO_UNDERSCORES FLM_COMP_NO_UNDERSCORES // Convert underscores to whitespace. NOTE: This // should be applied before COMP_COMPRESS_WHITESPACE // or COMP_NO_WHITESPACE #define XFLM_COMP_NO_DASHES FLM_COMP_NO_DASHES // Remove all dashes #define XFLM_COMP_WHITESPACE_AS_SPACE FLM_COMP_WHITESPACE_AS_SPACE // Convert tab, NL, and CR characters // to space #define XFLM_COMP_IGNORE_LEADING_SPACE FLM_COMP_IGNORE_LEADING_SPACE // Ignore leading space characters #define XFLM_COMP_IGNORE_TRAILING_SPACE FLM_COMP_IGNORE_TRAILING_SPACE // Ignore trailing space characters typedef enum { XFLM_QUERY_NOT_POSITIONED, XFLM_QUERY_AT_BOF, XFLM_QUERY_AT_FIRST, XFLM_QUERY_AT_FIRST_AND_LAST, XFLM_QUERY_POSITIONED, XFLM_QUERY_AT_LAST, XFLM_QUERY_AT_EOF } eQueryStates; typedef enum { XFLM_FUNC_xxx = 0 } eQueryFunctions; // IMPORTANT NOTE: If any of these change, corresponding changes need to // be made in java and C# code. typedef enum { ROOT_AXIS = 0, CHILD_AXIS, PARENT_AXIS, ANCESTOR_AXIS, DESCENDANT_AXIS, FOLLOWING_SIBLING_AXIS, PRECEDING_SIBLING_AXIS, FOLLOWING_AXIS, PRECEDING_AXIS, ATTRIBUTE_AXIS, NAMESPACE_AXIS, SELF_AXIS, DESCENDANT_OR_SELF_AXIS, ANCESTOR_OR_SELF_AXIS, META_AXIS } eXPathAxisTypes; typedef enum { XFLM_FALSE = 0, XFLM_TRUE, XFLM_UNKNOWN } XFlmBoolType; typedef enum QueryValueTypes { XFLM_MISSING_VAL = 0, // WARNING: Don't renumber below _VAL enums without // re-doing gv_DoValAndDictTypesMatch table XFLM_BOOL_VAL = 1, // 1 // XFlmBoolType XFLM_UINT_VAL, // 2 // FLMUINT XFLM_UINT64_VAL, // 3 // FLMUINT64 XFLM_INT_VAL, // 4 // FLMINT XFLM_INT64_VAL, // 5 // FLMINT64 XFLM_BINARY_VAL, // 6 // FLMBYTE * XFLM_UTF8_VAL, // 7 // FLMBYTE * // XFLM_PASSING_VAL passes all criteria. XFLM_PASSING_VAL = 0xFFFF } eValTypes; typedef enum { GET_FIRST_VAL = 0, GET_LAST_VAL, GET_NEXT_VAL, GET_PREV_VAL } ValIterator; // IMPORTANT NOTE: Changes to these should be synchronized to the // corresponding enums in java and C# code. typedef enum { XFLM_QOPT_NONE = 0, XFLM_QOPT_USING_INDEX, XFLM_QOPT_FULL_COLLECTION_SCAN, XFLM_QOPT_SINGLE_NODE_ID, XFLM_QOPT_NODE_ID_RANGE } eQOptTypes; typedef struct { eQOptTypes eOptType; // Type of optimization done FLMUINT uiCost; // Cost calculated for predicate FLMUINT64 ui64NodeId; // Only valid if eOptType is // XFLM_QOPT_SINGLE_NODE_ID or // XFLM_QOPT_NODE_ID_RANGE FLMUINT64 ui64EndNodeId; // Only valid if eOptType is // XFLM_QOPT_NODE_ID_RANGE FLMUINT uiIxNum; // Index used to execute query if // eOptType == QOPT_USING_INDEX FLMBYTE szIxName[ 80]; FLMBOOL bMustVerifyPath; // Must verify node path. FLMBOOL bDoNodeMatch; // Node must be retrieved to exe // query. Only valid if eOptType // is XFLM_QOPT_USING_INDEX. FLMUINT bCanCompareOnKey; // Can we compare on index keys? Only // valid if eOptType == XFLM_QOPT_USING_INDEX. FLMUINT64 ui64KeysRead; FLMUINT64 ui64KeyHadDupDoc; FLMUINT64 ui64KeysPassed; FLMUINT64 ui64NodesRead; FLMUINT64 ui64NodesTested; FLMUINT64 ui64NodesPassed; FLMUINT64 ui64DocsRead; FLMUINT64 ui64DupDocsEliminated; FLMUINT64 ui64NodesFailedValidation; FLMUINT64 ui64DocsFailedValidation; FLMUINT64 ui64DocsPassed; } XFLM_OPT_INFO; /************************************************************************** * XFLAIM Dictionary Tag Numbers * * NOTES: * 1) These numbers cannot be changed for backward compatibility reasons. * 2) IF ANY NEW TAGS ARE INSERTED - Then you MUST change the database * version number, because old databases will become invalid..... * ***************************************************************************/ // Special purpose collections // NOTE: Do not change the order of these definitions. The // getNextCollection routine assumes they are in this order. // We have reserved from 65501 to 65535 for internal collections // These should be allocated starting from 65535 and going down. #define XFLM_MAX_COLLECTION_NUM 65500 #define XFLM_MAINT_COLLECTION 65533 #define XFLM_DATA_COLLECTION 65534 #define XFLM_DICT_COLLECTION 65535 FINLINE FLMBOOL isDictCollection( FLMUINT uiCollectionNum) { return( (uiCollectionNum == XFLM_DICT_COLLECTION) ? TRUE : FALSE); } // Special purpose indexes // NOTE: Do not change the order of these definitions. The // getNextIndex routine assumes they are in this order. // We have reserved from 65501 to 65535 for internal indexes // These should be allocated starting from 65535 and going down. #define XFLM_MAX_INDEX_NUM 65500 #define XFLM_DICT_NUMBER_INDEX 65534 #define XFLM_DICT_NAME_INDEX 65535 // This is the reserved dictionary document that maintains // information about dictionary IDs #define XFLM_DICTINFO_DOC_ID ((FLMUINT64)1) // Prefixes #define XFLM_MAX_PREFIX_NUM 65500 // Encryption Defs #define XFLM_MAX_ENCDEF_NUM 65500 /**************************************************************************** Dictionary Identifiers ****************************************************************************/ #define XFLM_FIRST_RESERVED_ELEMENT_TAG 0xFFFFFE00 // Special definitions - cannot actually be used for an element name ID, but // in indexing specifies the root tag #define ELM_ROOT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG - 1) #define ELM_ELEMENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 0) #define ELM_ELEMENT_TAG_NAME "element" #define ELM_ATTRIBUTE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 1) #define ELM_ATTRIBUTE_TAG_NAME "attribute" #define ELM_INDEX_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 2) #define ELM_INDEX_TAG_NAME "Index" #define ELM_ELEMENT_COMPONENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 4) #define ELM_ELEMENT_COMPONENT_TAG_NAME "ElementComponent" #define ELM_ATTRIBUTE_COMPONENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 5) #define ELM_ATTRIBUTE_COMPONENT_TAG_NAME "AttributeComponent" #define ELM_COLLECTION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 6) #define ELM_COLLECTION_TAG_NAME "Collection" #define ELM_PREFIX_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 7) #define ELM_PREFIX_TAG_NAME "Prefix" #define ELM_NEXT_DICT_NUMS_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 8) #define ELM_NEXT_DICT_NUMS_TAG_NAME "NextDictNums" #define ELM_DOCUMENT_TITLE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 9) #define ELM_DOCUMENT_TITLE_TAG_NAME "DocumentTitle" #define ELM_INVALID_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 10) #define ELM_INVALID_TAG_NAME "Invalid" #define ELM_QUARANTINED_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 11) #define ELM_QUARANTINED_TAG_NAME "Quarantined" #define ELM_ALL_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 12) #define ELM_ALL_TAG_NAME "All" #define ELM_ANNOTATION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 13) #define ELM_ANNOTATION_TAG_NAME "Annotation" #define ELM_ANY_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 14) #define ELM_ANY_TAG_NAME "Any" #define ELM_ATTRIBUTE_GROUP_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 15) #define ELM_ATTRIBUTE_GROUP_TAG_NAME "AttributeGroup" #define ELM_CHOICE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 16) #define ELM_CHOICE_TAG_NAME "Choice" #define ELM_COMPLEX_CONTENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 17) #define ELM_COMPLEX_CONTENT_TAG_NAME "ComplexContent" #define ELM_COMPLEX_TYPE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 18) #define ELM_COMPLEX_TYPE_TAG_NAME "ComplexType" #define ELM_DOCUMENTATION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 19) #define ELM_DOCUMENTATION_TAG_NAME "Documentation" #define ELM_ENUMERATION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 20) #define ELM_ENUMERATION_TAG_NAME "enumeration" #define ELM_EXTENSION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 21) #define ELM_EXTENSION_TAG_NAME "extension" #define ELM_DELETE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 22) #define ELM_DELETE_TAG_NAME "Delete" #define ELM_BLOCK_CHAIN_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 23) #define ELM_BLOCK_CHAIN_TAG_NAME "BlockChain" #define ELM_ENCDEF_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 24) #define ELM_ENCDEF_TAG_NAME "EncDef" #define ELM_SWEEP_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 25) #define ELM_SWEEP_TAG_NAME "Sweep" // IMPORTANT NOTE: Change this value whenever adding new reserved element tags! #define XFLM_LAST_RESERVED_ELEMENT_TAG ELM_SWEEP_TAG #define XFLM_FIRST_RESERVED_ATTRIBUTE_TAG 0xFFFFFE00 #define ATTR_DICT_NUMBER_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 0) #define ATTR_DICT_NUMBER_TAG_NAME "DictNumber" #define ATTR_COLLECTION_NUMBER_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1) #define ATTR_COLLECTION_NUMBER_TAG_NAME "CollectionNumber" #define ATTR_COLLECTION_NAME_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 2) #define ATTR_COLLECTION_NAME_TAG_NAME "CollectionName" #define ATTR_NAME_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 3) #define ATTR_NAME_TAG_NAME "name" #define ATTR_TARGET_NAMESPACE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 4) #define ATTR_TARGET_NAMESPACE_TAG_NAME "targetNameSpace" #define ATTR_TYPE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 5) #define ATTR_TYPE_TAG_NAME "type" #define ATTR_STATE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 6) #define ATTR_STATE_TAG_NAME "State" #define ATTR_LANGUAGE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 7) #define ATTR_LANGUAGE_TAG_NAME "Language" #define ATTR_INDEX_OPTIONS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 8) #define ATTR_INDEX_OPTIONS_TAG_NAME "IndexOptions" #define ATTR_INDEX_ON_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 9) #define ATTR_INDEX_ON_TAG_NAME "IndexOn" #define ATTR_REQUIRED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 10) #define ATTR_REQUIRED_TAG_NAME "Required" #define ATTR_LIMIT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 11) #define ATTR_LIMIT_TAG_NAME "Limit" #define ATTR_COMPARE_RULES_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 12) #define ATTR_COMPARE_RULES_TAG_NAME "CompareRules" #define ATTR_KEY_COMPONENT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 13) #define ATTR_KEY_COMPONENT_TAG_NAME "KeyComponent" #define ATTR_DATA_COMPONENT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 14) #define ATTR_DATA_COMPONENT_TAG_NAME "DataComponent" #define ATTR_LAST_DOC_INDEXED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 15) #define ATTR_LAST_DOC_INDEXED_TAG_NAME "LastDocumentIndexed" #define ATTR_NEXT_ELEMENT_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 16) #define ATTR_NEXT_ELEMENT_NUM_TAG_NAME "NextElementNum" #define ATTR_NEXT_ATTRIBUTE_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 17) #define ATTR_NEXT_ATTRIBUTE_NUM_TAG_NAME "NextAttributeNum" #define ATTR_NEXT_INDEX_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 18) #define ATTR_NEXT_INDEX_NUM_TAG_NAME "NextIndexNum" #define ATTR_NEXT_COLLECTION_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 19) #define ATTR_NEXT_COLLECTION_NUM_TAG_NAME "NextCollectionNum" #define ATTR_NEXT_PREFIX_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 20) #define ATTR_NEXT_PREFIX_NUM_TAG_NAME "NextPrefixNum" #define ATTR_SOURCE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 21) #define ATTR_SOURCE_TAG_NAME "Source" #define ATTR_STATE_CHANGE_COUNT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 22) #define ATTR_STATE_CHANGE_COUNT_TAG_NAME "StateChangeCount" #define ATTR_XMLNS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 23) #define ATTR_XMLNS_TAG_NAME "xmlns" #define ATTR_ABSTRACT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 24) #define ATTR_ABSTRACT_TAG_NAME "abstract" #define ATTR_BASE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 25) #define ATTR_BASE_TAG_NAME "base" #define ATTR_BLOCK_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 26) #define ATTR_BLOCK_TAG_NAME "block" #define ATTR_DEFAULT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 27) #define ATTR_DEFAULT_TAG_NAME "default" #define ATTR_FINAL_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 28) #define ATTR_FINAL_TAG_NAME "final" #define ATTR_FIXED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 29) #define ATTR_FIXED_TAG_NAME "fixed" #define ATTR_ITEM_TYPE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 30) #define ATTR_ITEM_TYPE_TAG_NAME "itemtype" #define ATTR_MEMBER_TYPES_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 31) #define ATTR_MEMBER_TYPES_TAG_NAME "membertypes" #define ATTR_MIXED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 32) #define ATTR_MIXED_TAG_NAME "mixed" #define ATTR_NILLABLE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 33) #define ATTR_NILLABLE_TAG_NAME "nillable" #define ATTR_REF_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 34) #define ATTR_REF_TAG_NAME "ref" #define ATTR_USE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 35) #define ATTR_USE_TAG_NAME "use" #define ATTR_VALUE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 36) #define ATTR_VALUE_TAG_NAME "value" #define ATTR_ADDRESS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 37) #define ATTR_ADDRESS_TAG_NAME "address" #define ATTR_XMLNS_XFLAIM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 38) #define ATTR_XMLNS_XFLAIM_TAG_NAME "xmlns:xflaim" #define ATTR_ENCRYPTION_KEY_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 39) #define ATTR_ENCRYPTION_KEY_TAG_NAME "Key" #define ATTR_TRANSACTION_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 40) #define ATTR_TRANSACTION_TAG_NAME "Transaction" #define ATTR_NEXT_ENCDEF_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 41) #define ATTR_NEXT_ENCDEF_NUM_TAG_NAME "NextEncDefNum" #define ATTR_ENCRYPTION_ID_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 42) #define ATTR_ENCRYPTION_ID_TAG_NAME "encId" #define ATTR_ENCRYPTION_KEY_SIZE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 43) #define ATTR_ENCRYPTION_KEY_SIZE_TAG_NAME "keySize" #define ATTR_UNIQUE_SUB_ELEMENTS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 44) #define ATTR_UNIQUE_SUB_ELEMENTS_TAG_NAME "UniqueSubElements" // IMPORTANT NOTE: Change this value whenever adding new reserved attribute tags! #define XFLM_LAST_RESERVED_ATTRIBUTE_TAG ATTR_UNIQUE_SUB_ELEMENTS_TAG // max element number is first reserved -2 instead of -1 because we don't want // anyone using ELM_ROOT_TAG either. #define XFLM_MAX_ELEMENT_NUM (XFLM_FIRST_RESERVED_ELEMENT_TAG - 2) #define XFLM_MAX_ATTRIBUTE_NUM (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG - 1) // Types of metadata available on DOM nodes - can index on these. // Also, can search on these via the META_AXIS #define XFLM_META_NODE_ID 1 #define XFLM_META_DOCUMENT_ID 2 #define XFLM_META_PARENT_ID 3 #define XFLM_META_FIRST_CHILD_ID 4 #define XFLM_META_LAST_CHILD_ID 5 #define XFLM_META_NEXT_SIBLING_ID 6 #define XFLM_META_PREV_SIBLING_ID 7 #define XFLM_META_VALUE 8 #define XFLM_INI_CACHE "cache" #define XFLM_INI_CACHE_ADJUST_INTERVAL "cacheadjustinterval" #define XFLM_INI_CACHE_CLEANUP_INTERVAL "cachecleanupinterval" #define XFLM_INI_MAX_DIRTY_CACHE "maxdirtycache" #define XFLM_INI_LOW_DIRTY_CACHE "lowdirtycache" // Defaults for certain other settable items in the IF_DbSystem #define XFLM_DEFAULT_MAX_CP_INTERVAL 180 #define XFLM_DEFAULT_CACHE_ADJUST_PERCENT 70 #define XFLM_DEFAULT_CACHE_ADJUST_MIN (16 * 1024 * 1024) #define XFLM_DEFAULT_CACHE_ADJUST_MAX 0xE0000000 #define XFLM_DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE 0 #define XFLM_DEFAULT_CACHE_ADJUST_INTERVAL 15 #define XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL 15 #define XFLM_DEFAULT_UNUSED_CLEANUP_INTERVAL 2 #define XFLM_DEFAULT_MAX_UNUSED_TIME 120 #define XFLM_DEFAULT_FILE_EXTEND_SIZE (8192 * 1024) #define XFLM_MIN_BLOCK_SIZE 4096 #define XFLM_MAX_BLOCK_SIZE 8192 #define XFLM_DEFAULT_OPEN_THRESHOLD 100 // 100 file handles to cache #define XFLM_DEFAULT_MAX_AVAIL_TIME 900 // 15 minutes #define XFLM_DEFAULT_REHASH_BACKOFF_INTERVAL 60 // 1 minute /** * @brief The IF_DbSystem is actually an interface definition that provides public access * to the XFlaim database environment. * * This class represents the XFlaim database system. It extends XFLMIUnknown. * The IF_DbSystem interface is used to represent the actual database system object. * The database system object incorporates functionality to startup, shutdown, create, * open and copy an XFlaim database. Note that the database system when XFlaim is * running is more than the files that store the data. The database system also * includes all of the in-memory structures and objects that are used, either * intermittently or throughout the life of the database system. The class id for * this interface is CLSID_F_DbSystemFactory and the interface id is IID_IF_DbSystem. */ flminterface IF_DbSystem : public IF_Object { virtual RCODE FLMAPI updateIniFile( const char * pszParamName, const char * pszValue) = 0; /** * @brief Return an IF_FileSystem object for performing file system operations. * * The getFileSystem method returns an IF_FileSystem object that can be used to * perform various operations on files. * * @param ppFileSystem A pointer to a file system object that can * be used to perform various operations on files. */ virtual void FLMAPI getFileSystem( IF_FileSystem ** ppFileSystem) = 0; /** * @brief Creates a new database. * * A pointer to a database object (IF_Db) is returned in the ppDb parameter. The database system * engine must first be started using the init method. After creating a new XFlaim database, * the database is open and ready to use. * * @param pszDbFileName Name of the control file for the database. See the XFlaim Concepts/Database * Files for a discussion on the different database files, including the control file. * @param pszDataDir The directory where the data files are to be created. If a NULL is passed in, * data files will be created in the same directory as the control file (as specified by the * pszDbFileName parameter). See the XFlaim Concepts/Database Files for a discussion on the different * database files. * @param pszRflDir The directory where the RFL (roll forward log) files are to be located. If a * NULL is passed, roll forward log files will reside in the same directory as the control file * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a * discussion on the different database files, including the rfl (Roll-forward log) file. * @param pszDictFileName Name of file containing dictionary definitions to be imported into the * dictionary collection during database creation. Note that this parameter is ignored if pzDictBuf * is non-NULL. See the XFlaim Concepts section for more information about the XFlaim Dictionary. * @param pszDictBuf Null-terminated string buffer containing dictionary definitions in external * XML format. If the value of this parameter is NULL, pszDictFileName will be used. If both * pszDictFileName and pszDictBuf are NULL, the database will be created with an empty dictionary. * See the XFlaim Concepts section for more information about the XFlaim Dictionary. * @param pCreateOpts Create options for the database. All members of the structure should be * initialized to specify options desired when the database is created. If NULL is passed as * the value of this parameter, default options will be used. See the glossary for a description * of the XFLM_CREATE_OPTS structure and the default value for each field. * @param ppDb A pointer to a database object that references the newly created database. * @return RCODE */ virtual RCODE FLMAPI dbCreate( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pDictFileName, const char * pszDictBuf, XFLM_CREATE_OPTS * pCreateOpts, IF_Db ** ppDb) = 0; /** * @brief Opens an existing database. * * The dbOpen method opens an existing database. A pointer to a database object (IF_Db) is returned * in the ppDb parameter. The database system engine must first be started using the init method. * After opening an XFlaim database, the database is ready to use. * * @param pszDbFileName Name of the control file for the database that is to be opened. See the * XFlaim Concepts/Database Files for a discussion on the different database files, including the control file. * @param pszDataDir The directory where the data files are located. If a NULL is passed in, * it is assumed that the data files are located in the same directory as the control file * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a * discussion on the different database files. * @param pszRflDir The directory where the RFL (roll forward log) files are located. If a NULL * is passed, roll forward log files are assumed to reside in the same directory as the control file * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a * discussion on the different database files, including the rfl (Roll-forward log) file. * @param ppDb A pointer to a database object that references the newly created database. * @return RCODE */ virtual RCODE FLMAPI dbOpen( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMBOOL bAllowLimited, IF_Db ** ppDb) = 0; /** * @brief Rebuilds a database. * * The dbRebuild method will attempt to reconstruct a database, recovering everything that it can * from the blocks of the database. * * @param pszSourceDbFileName Name of the source control file for the database that is to be rebuilt. * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the control file. * @param pszSourceDataDir The directory where the data files are located. If a NULL is passed in, * it is assumed that the data files are located in the same directory as the control file * (as specified by the pszSourceDbFileName parameter). See the XFlaim Concepts/Database Files * for a discussion on the different database files. * @param pszDestDbFileName Name of the destination control file for the recovered database. * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the control file. * @param pszDestDataDir The directory where the data files are to be located. If a NULL is * passed in, it is assumed that the data files are to be located in the same directory as * the control file (as specified by the pszDestDbFileName parameter). See the XFlaim * Concepts/Database Files for a discussion on the different database files. * @param pszDestRflDir The directory where the RFL (roll forward log) files of the * destination database are to be created. If a NULL is passed, roll forward log files are * assumed to reside in the same directory as the control file (as specified by the * pszDestDbFileName parameter). See the XFlaim Concepts/Database Files for a discussion * on the different database files, including the rfl (Roll-forward log) file. * @param pszDictFileName The name of a file containing the dictionary definitions that * will be used when rebuilding the database. A NULL may be passed in this parameter. * In addition to using the definitions specified in this file, dbRebuild will attempt * to recover any additional dictionary entries from the dictionary collection. * See XFlaim Concepts / Dictionary for a discussion on the dictionary in XFlaim. * @param pCreateOpts Create options for the database. All members of the structure should * be initialized to specify options desired when the database is created. If NULL is passed * as the value of this parameter, default options will be used. See the glossary for a description * of the XFLM_CREATE_OPTS structure and the default value for each field. * @param pDbRebuild A pointer to an application defined status reporting object. Methods * on this object are used by dbRebuild to report progress during the rebuild. A NULL may be * passed in this parameter. This object is NOT implemented by XFlaim, but is implemented by * the application. The application must create a class that inherits from the IF_DbRebuildStatus * interface and implements the pure virtual methods of that interface. * @param pui64TotalNodes The total number of DOM nodes. * @param pui64NodesRecovered The total number of DOM nodes recovered. * @param pui64NodesDiscardedDocs The total number of documents that couldn't be recovered. * @return RCODE */ virtual RCODE FLMAPI dbRebuild( const char * pszSourceDbPath, const char * pszSourceDataDir, const char * pszDestDbPath, const char * pszDestDataDir, const char * pszDestRflDir, const char * pszDictPath, const char * pszPassword, XFLM_CREATE_OPTS * pCreateOpts, FLMUINT64 * pui64TotNodes, FLMUINT64 * pui64NodesRecov, FLMUINT64 * pui64DiscardedDocs, IF_DbRebuildStatus * pDbRebuild) = 0; /** * @brief Checks a database for corruptions. * * The dbCheck method will check the database for corruptions ans in certain cases it can repair them. * * @param pszDbFileName Name of the control file for the database that is to be checked. * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the control file. * @param pszDataDir The directory where the data files are located. If a NULL is passed in, * it is assumed that the data files are located in the same directory as the control file * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a * discussion on the different database files. * @param pszRflDir The directory where the RFL (roll forward log) files are located. If a NULL is * passed, roll forward log files are assumed to reside in the same directory as the control file * (as specified by the pszDbFileName parameters). See the XFlaim Concepts/Database Files for a * discussion on the different database files, including the rfl (Roll-forward log) file. * @param pDbCheckStatus A pointer to an application defined status reporting object. Methods * on this object are used by dbCheck to report progress and corruptions to the calling application. * A NULL may be passed in this parameter. This object is NOT implemented by XFlaim, but is * implemented by the application. The application must create a class that inherits from the * IF_DbCheckStatus interface and implements the pure virtual methods of that interface. * @param ppDbInfo If a non-NULL ppDbInfo pointer is passed, an IF_DbInfo object will be returned * that contains detailed statistical information about the various B-trees in the database. * The information includes things like percent utilization of various blocks at each level in * a B-tree, number of keys, etc. Methods of the IF_DbInfo object provide for retrieval of this information. * @return RCODE */ virtual RCODE FLMAPI dbCheck( const char * pszDbFileName, const char * pszDataDir, const char * pszRflDir, const char * pszPassword, FLMUINT uiFlags, IF_DbInfo ** ppDbInfo, IF_DbCheckStatus * pDbCheckStatus) = 0; /** * @brief Copies a database. * * Copies a database to a new database. The destination database will be created if it * does not exist and overwritten if it does exist. * * @param pszSrcDbName Name of the control file for the database that is to be copied. * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the control file. * @param pszSrcDataDir The directory where the source database's data files are located. * If a NULL is passed in, it is assumed that the source database's data files are located in * the same directory as the source database's control file (as specified by the pszSrcDbName * parameter). See the XFlaim Concepts/Database Files for a discussion on the different database files. * @param pszSrcRflDir The directory where the RFL (roll forward log) files of the source database * are located. If a NULL is passed, the roll forward log files of the source database are assumed * to reside in the same directory as the source database's control file (as specified by the pszSrcDbName * parameter). See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the rfl (Roll-forward log) file. * @param pszDestDbName Name of the control file for the destination database. If the destination * database already exists, it will be overwritten. See the XFlaim Concepts/Database Files for a * discussion on the different database files, including the control file. * @param pszDestDataDir The directory where the destination database's data files are to be stored. * If a NULL is passed in, the destination database's data files will be put in the same directory * as the destination database's control file (as specified by the pszDestDbName parameter). * See the XFlaim Concepts/Database Files for a discussion on the different database files. * @param pszDestRflDir The directory where the RFL (roll forward log) files of the destination database * are to be put. If a NULL is passed, the roll forward log files of the destination database will be * put in the same directory as the destination database's control file (as specified by the * pszDestDbName parameter). See the XFlaim Concepts/Database Files for a discussion on the different * database files, including the rfl (Roll-forward log) file. * @param ifpStatus A pointer to an application defined status reporting object. Methods on this object * are used by dbCopy to report progress during the copy. A NULL may be passed in this parameter. * This object is NOT implemented by XFlaim, but is implemented by the application. The application * must create a class that inherits from the IF_DbCopyStatus interface and implements the pure virtual * methods of that interface. Those methods may be called by dbCopy to report copy progress. * @return RCODE */ virtual RCODE FLMAPI dbCopy( const char * pszSrcDbName, const char * pszSrcDataDir, const char * pszSrcRflDir, const char * pszDestDbName, const char * pszDestDataDir, const char * pszDestRflDir, IF_DbCopyStatus * ifpStatus) = 0; /** * @brief Renames a database. * * This method will rename an existing database to a specified new name. * * @param pszDbName Name of the control file for the database that is to be renamed. * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the control file. * @param pszDataDir The directory where the database's data files are located. If a NULL * is passed in, it is assumed that the data files are located in the same directory as the * control file (as specified by the pszDbName parameter). See the XFlaim Concepts/Database * Files for a discussion on the different database files. * @param pszRflDir The directory where the RFL (roll forward log) files of the database * are located. If a NULL is passed, the roll forward log files of the database are assumed * to reside in the same directory as the control file (as specified by the pszDbName parameter). * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the RFL (Roll-forward log) file. * @param pszNewDbName Name the control file is to be renamed to. This name also determines * what data files and RFL files will be renamed to. See the XFlaim Concepts/Database Files * for a discussion on the different database files, including the control file. Note that the * directory of the new database name must be the same as the directory specified in pszDbName. * @param bOverwriteDestOk If pszNewDbName specifies the name of a file that already exists, * this flag indicates whether that file should be deleted so that the rename can proceed. * If FALSE, the rename will fail. * @param ifpStatus A pointer to an application defined status reporting object. Methods on * this object are used by dbRename to report progress during the rename. A NULL may be passed * in this parameter. This object is NOT implemented by XFlaim, but is implemented by the * application. The application must create a class that inherits from the IF_DbRenameStatus * interface and implements the pure virtual methods of that interface. * @return RCODE */ virtual RCODE FLMAPI dbRename( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, const char * pszNewDbName, FLMBOOL bOverwriteDestOk, IF_DbRenameStatus * ifpStatus) = 0; /** * @brief Removes a database. * * This method will remove (delete) an existing database. * * @param pszDbName Name of the control file for the database that is to be removed. * See the XFlaim Concepts/Database Files for a discussion on the different database * files, including the control file. * @param pszDataDir The directory where the database's data files are located. If a * NULL is passed in, it is assumed that the data files are located in the same directory * as the control file (as specified by the pszDbName parameter). See the XFlaim * Concepts/Database Files for a discussion on the different database files. * @param pszRflDir The directory where the RFL (roll forward log) files of the database are * located. If a NULL is passed, the roll forward log files of the database are assumed to * reside in the same directory as the control file (as specified by the pszDbName parameter). * See the XFlaim Concepts/Database Files for a discussion on the different database files, * including the RFL (Roll-forward log) file. * @param bRemoveRflFiles A flag that indicate whether or not the RFL file(s) should be removed as well. * @return RCODE */ virtual RCODE FLMAPI dbRemove( const char * pszDbName, const char * pszDataDir, const char * pszRflDir, FLMBOOL bRemoveRflFiles) = 0; /** * @brief Restores a database from a backup set. * * This method will restore a database from a backup set. The client is responsible for * providing an implementation of the IF_RestoreClient and IF_RestoreStatus interfaces. * * @param pszDbPath Name of the control file (including path) for the database that is to * be restored. See the XFlaim Concepts/Database Files for a discussion on the different * database files, including the control file. * @param pszDataDir The directory where the database's data files are located. If a * NULL is passed in, it is assumed that the data files are located in the same directory * as the control file (as specified by the pszDbName parameter). See the XFlaim * Concepts/Database Files for a discussion on the different database files. * @param pszBackupPath The path to the backup file set. * @param pszRflDir The directory where the RFL (roll forward log) files of the database * are located. If a NULL is passed, the roll forward log files of the database are * assumed to reside in the same directory as the control file (as specified by the * pszDbName parameter). See the XFlaim Concepts/Database Files for a discussion on * the different database files, including the RFL (Roll-forward log) file. * @param pRestoreObj A pointer to an application defined restore client object. Methods * on this object are used by dbRestore to read the backed-up data from the location it is * stored in. The application is responsible for implementing this object. In this way, * an application can have a database restored from virtually any media. * If a NULL is passed in this parameter, pszBackupPath is used to restore from. * Visit: Need to say something about the default backup file set. * @param pRestoreStatus A pointer to an application defined restore status object. * Methods on this object are used by dbRestore to report progress during the restore. * The application is responsible for implementing this object. * @return RCODE */ virtual RCODE FLMAPI dbRestore( const char * pszDbPath, const char * pszDataDir, const char * pszRflDir, const char * pszBackupPath, const char * pszPassword, IF_RestoreClient * pRestoreObj, IF_RestoreStatus * pRestoreStatus) = 0; /** * @brief Duplicates the IF_Db interface object. * * @par * This method will duplicate the IF_Db interface object. It is similar to the dbOpen method, * except that it doesn't take a database file name as input, but takes an already open IF_Db * object as input. The returned IF_Db object is the same as if it had been created by the dbOpen * method. However, calling dbDup is slightly more efficient than calling dbOpen. * @par * Since IF_Db objects cannot be shared between threads (they are not thread-safe), an application * would normally have each of its threads call dbOpen to obtain an IF_Db object. The dbDup * method provides an alternative way to create IF_Db objects for multiple threads. * For example, one thread could call dbOpen to obtain the first IF_Db object. Thereafter, * it could call dbDup and pass the created IF_Db objects to different threads. * @par * Although the most common use of dbDup is to create IF_Db objects and pass them to different * threads, it is conceivable that one thread might want multiple IF_Db objects on the same * database. The uses for this are probably somewhat exotic. For example, a single thread * using multiple IF_Db objects could have multiple different transactions open simultaneously. * * @param pDb The database object to be duplicated. * @param ppDb A new database object. * @return RCODE */ virtual RCODE FLMAPI dbDup( IF_Db * pDb, IF_Db ** ppDb) = 0; /** * @brief Converts a corruption code to a string equivalent. * * This method converts a corruption code to a string equivalent that can then be printed in * a diagnostic log or some other human readable output. * * @param iErrCode The error code to be translated. * @return const char * */ virtual const char * FLMAPI checkErrorToStr( FLMINT iCheckErrorCode) = 0; /** * @brief Opens a buffered input stream. * * This method opens a buffered input stream. The pucBuffer buffer holds that data that is to be * streamed through the IF_PosIStream object. NOTE: The returned IF_PosIStream object's Release() * method should be called when the application is done using the object. * * @param pucBuffer The buffer that will be associated with the input stream. * @param uiLength The size of the buffer (bytes). * @param ppIStream The input stream object used to read the data in. * @return RCODE */ virtual RCODE FLMAPI openBufferIStream( const char * pucBuffer, FLMUINT uiLength, IF_PosIStream ** ppIStream) = 0; /** * @brief Opens a file input stream. * * This method opens a file input stream. The pszPath parameter points to a file that holds the data * that is to be streamed through the IF_PosIStream object. NOTE: The returned IF_PosIStream object's * Release() method should be called when the application is done using the object. * * @param pszPath The name of file whose data is to be read via the IF_PosIStream object. * @param ppIStream The input stream object used to read the data in. * @return RCODE */ virtual RCODE FLMAPI openFileIStream( const char * pszPath, IF_PosIStream ** ppIStream) = 0; /** * @brief Open a multi-file input stream. * * (*ppIStream)->read() will read data from the files in the directory (pszDirectory) * that match the base name given (pszBaseName). EOF is returned when there are no * more files to read from. File names start with pszBaseName, then * pszBaseName.00000001, pszBaseName.00000002, etc. The extension is a hex number. */ virtual RCODE FLMAPI openMultiFileIStream( const char * pszDirectory, const char * pszBaseName, IF_IStream ** ppIStream) = 0; /** * @brief Convert an input stream (pIStream) into a buffered input stream (*ppIStream) * * When (*ppIStream)->read() is called, it will fill an internal buffer of * uiBufferSize by reading from pIStream. Data is returned from the buffer until * the buffer is emptied, at which time another read will be done from pIStream, * until pIStream has no more data to return. This method allows any input stream * to be turned into a buffered stream. */ virtual RCODE FLMAPI openBufferedIStream( IF_IStream * pIStream, FLMUINT uiBufferSize, IF_IStream ** ppIStream) = 0; /** * @brief Read uncompressed data from a compressed stream (pIStream) * * When (*ppIStream)->read() is called, it will read and uncompress data from * pIStream. */ virtual RCODE FLMAPI openUncompressingIStream( IF_IStream * pIStream, IF_IStream ** ppIStream) = 0; // METHODS FOR GETTING OUTPUT STREAM OBJECTS /** * @brief Create a file output stream. * * Data is written out to the specified file. The file may be created, overwritten, or * appended to, depending on iAccessFlags. */ virtual RCODE FLMAPI openFileOStream( const char * pszFileName, FLMBOOL bTruncateIfExists, IF_OStream ** ppOStream) = 0; /** * @brief Create a multi-file output stream. * * Data is written to the specified directory, creating files using the given * base file name. When a file reaches the specified size, another file will * be created by appending a suffix with an incrementing HEX number. The * bOverwrite flag indicates whether to overwrite files that already exist. */ virtual RCODE FLMAPI openMultiFileOStream( const char * pszDirectory, const char * pszBaseName, FLMUINT uiMaxFileSize, FLMBOOL bOkToOverwrite, IF_OStream ** ppStream) = 0; /** * @brief Remove a multi-file stream */ virtual RCODE FLMAPI removeMultiFileStream( const char * pszDirectory, const char * pszBaseName) = 0; /** * @brief Convert an output stream (pOStream) into a buffered output stream (*ppOStream) * * As data is written to *ppOStream, it is buffered before ultimately being * written to pOStream. */ virtual RCODE FLMAPI openBufferedOStream( IF_OStream * pOStream, FLMUINT uiBufferSize, IF_OStream ** ppOStream) = 0; /** * @brief Convert an output stream (pOStream) into a compressed output stream (*ppOStream) * * As data is written to *ppOStream, it is compressed before ultimately being * written to pOStream. */ virtual RCODE FLMAPI openCompressingOStream( IF_OStream * pOStream, IF_OStream ** ppOStream) = 0; /** * @brief All data is read from the input stream (pIStream) and written * to the output stream (pOStream). This goes until pIStream returns EOF. */ virtual RCODE FLMAPI writeToOStream( IF_IStream * pIStream, IF_OStream * pOStream) = 0; /** * @brief Opens a base64 encoder stream * * This method opens a stream for encoding a user-supplied input stream to * base64 (ASCII). * * @param pInputStream The stream to be encoded * @param bInsertLineBreaks A line break will be inserted every 72 * characters * @param ppEncodedStream The stream object used to read the encoded data. * @return RCODE */ virtual RCODE FLMAPI openBase64Encoder( IF_IStream * pInputStream, FLMBOOL bInsertLineBreaks, IF_IStream ** ppEncodedStream) = 0; /** * @brief Opens a base64 decoder stream * * This method opens a stream for decoding a user-supplied input stream from * base64 (ASCII) to binary. * * @param pInputStream The stream to be decoded * @param ppDecodedStream The stream object used to read the decoded data. * @return RCODE */ virtual RCODE FLMAPI openBase64Decoder( IF_IStream * pInputStream, IF_IStream ** ppDecodedStream) = 0; /** * @brief Creates an IF_DataVector interface object. * * This method creates an IF_DataVector interface object. The IF_DataVector * is used in index key searches. * * @param ifppDV The IF_DataVector object. * @return RCODE */ virtual RCODE FLMAPI createIFDataVector( IF_DataVector ** ifppDV) = 0; /** * @brief Creates an IF_ResultSet interface object. * * This method creates an IF_ResultSet interface object. * * @param ifppResultSet The IF_ResultSet object. * @return RCODE */ virtual RCODE FLMAPI createIFResultSet( IF_ResultSet ** ifppResultSet) = 0; /** * @brief Creates an IF_Query interface object. * * This method creates an IF_Query interface object. * * @param ifppQuery The IF_Query object. * @return RCODE */ virtual RCODE FLMAPI createIFQuery( IF_Query ** ifppQuery) = 0; /** * @brief Frees memory for allocations that are returned from various XFlaim methods. * * This method frees memory that has been allocated by various methods in XFlaim. If * a method allocates memory that needs to be freed by this method, it will be documented * in that method. * * @param ppMem Pointer to the pointer of the memory to be freed. When the memory is * successfully freed, the pointer will be set to NULL */ virtual void FLMAPI freeMem( void ** ppMem) = 0; // Various configuration routines /** * @brief Set a dynamic memory limit in the XFlaim database system. * * This method sets the dynamic memory parameters in the XFlaim database system. When this * method is called, XFlaim is put into a mode where it periodically (every 15 seconds unless * otherwise specified - see setCacheAdjustInterval) adjusts its cache limit. The parameters * passed into this method are used to calculate the new limit. The new limit remains in * effect until the next adjustment is made. * * @param uiCacheAdjustPercent Percentage of available physical memory to set or adjust to. * @param uiCacheAdjustMin Minimum bytes to adjust down to. * @param uiCacheAdjustMax Maximum bytes to adjust up to. NOTE: If this parameter is non-zero, * the uiCacheAdjustMinToLeave parameter is ignored. * @param uiCacheAdjustMinToLeave Minimum bytes to leave available after making adjustment. * This is an alternative way to specify a maximum to adjust to. Using this value, XFlaim will * calculate the maximum by subtracting this number from the total bytes it thinks is available. * That calculated number becomes the effective maximum to adjust to. * @return RCODE */ virtual RCODE FLMAPI setDynamicMemoryLimit( FLMUINT uiCacheAdjustPercent, FLMUINT uiCacheAdjustMin, FLMUINT uiCacheAdjustMax, FLMUINT uiCacheAdjustMinToLeave) = 0; /** * @brief Set a hard limit on the amount of memory that the database system can access. * * This method sets a hard limit on the amount of memory that the database system can access. * The important difference between this routine and the setDynamicMemoryLimit routine is that * the limit remains in force until a subsequent call to setHardMemoryLimit is made, or until * a call to setDynamicMemoryLimit is made. The setDynamicMemoryLimit routine, on the other * hand, puts XFlaim into a mode where a new limit is automatically calculated on a preset * interval (see setCacheAdjustInterval). Thus, the purpose of the setHardMemoryLimit routine * is to let the application control the limit instead of having it automatically adjusted periodically. * * @param uiPercent If this parameter is zero, the uiMax parameter determines the hard cache limit. * Otherwise, this parameter (which must contain a number between 1 and 100) is used to determine * a hard cache limit. The hard limit will be calculated as a percentage of available physical * memory on the system. * @param bPercentOfAvail A flag to indicate that the percentage (uiPercent) is to be interpreted * as a percentage of available memory as opposed to a percentage of all of physical memory. * This parameter is only used if uiPercent is non-zero. * @param uiMin Minimum bytes to set the hard cache limit to. Note that this parameter is only used * if uiPercent is non-zero and we are calculating a hard limit to set. * @param uiMax Maximum bytes to set the hard limit to. Note that if uiPercent is zero, * this number contains the hard limit. * @param uiMinToLeave This parameter is only used if uiPercent is non-zero and we are calculating * a hard limit to set. Instead of uiMax determining the maximum cache limit we could set, this * value will determine the maximum. This number will be subtracted from the total memory on the * system or the total memory currently available (if bPercentOfAvail is TRUE) to establish a maximum. * @param bPreallocate Boolean flag. Used to indicate that the cache should be preallocated when XFlaim * starts up, rather than allow it to grow as needed. The default value to FALSE. * @return RCODE */ virtual RCODE FLMAPI setHardMemoryLimit( FLMUINT uiPercent, FLMBOOL bPercentOfAvail, FLMUINT uiMin, FLMUINT uiMax, FLMUINT uiMinToLeave, FLMBOOL bPreallocate = FALSE) = 0; /** * @brief Get a flag which indicates whether or not dynamic cache adjusting * is allowed. * * This method returns a boolean TRUE or FALSE. * * @return FLMBOOL TRUE=supported or FALSE=not supported. */ virtual FLMBOOL FLMAPI getDynamicCacheSupported( void) = 0; /** * @brief Query the database system for information regarding the current cache usage. * * This method is used to query the database system for information regarding the current cache usage. * * @param pCacheInfo The cache info structure. */ virtual void FLMAPI getCacheInfo( XFLM_CACHE_INFO * pCacheInfo) = 0; /** * @brief A method to either enable or disable cache debug mode. * * This is a method to either enable or disable cache debug mode. If bDebug is TRUE (1), * then cache debug will be enabled. If bDebug is FALSE (0), cache debug will be disabled. * * @param bDebug A boolean to indicate whether to enable or disable cache debug mode. */ virtual void FLMAPI enableCacheDebug( FLMBOOL bDebug) = 0; /** * @brief A method to find out if cache debug mode is either enabled or disabled. * * This is a method to find out if cache debug mode is either enabled or disabled. * * @return FLMBOOL True or False */ virtual FLMBOOL FLMAPI cacheDebugEnabled( void) = 0; /** * @brief Close all file handles (descriptors) that have not been used for a specified * amount of time. * * This is a method to close all file handles (descriptors) that have not been used or * accessed for a specified number of seconds. * * @param uiSeconds The number of seconds. File handles (descriptors) that have been * unused for a period of time greater than or equal to this number of seconds will be * closed. A value of zero will have the effect of closing all unused file handles * (descriptors), regardless of how long they have been unused. * @return RCODE */ virtual RCODE FLMAPI closeUnusedFiles( FLMUINT uiSeconds) = 0; /** * @brief Start the collection of statistics on the database system. * * This method starts the collection of statistics on the database system. */ virtual void FLMAPI startStats( void) = 0; /** * @brief Stop the collection of statistics on the database system. * * This method stops the collection of statistics on the database system. */ virtual void FLMAPI stopStats( void) = 0; /** * @brief Reset the statistics counters on the database system. * * This method resets the statistics counters on the database system. */ virtual void FLMAPI resetStats( void) = 0; /** * @brief Retrieve the statistics from the database system. * * This method returns the current set of statistics from the database system. * * @param pFlmStats The structure where statistics are returned. * @return RCODE */ virtual RCODE FLMAPI getStats( XFLM_STATS * pFlmStats) = 0; /** * @brief Free the statistic object in the database system. * * This method frees any memory allocations that are associated with the FLM_STATS * structure. The FLM_STATS structure will have been populated by a call to the * getStats method. * * @param pFlmStats The statistics structure whose memory allocations are to be freed. */ virtual void FLMAPI freeStats( XFLM_STATS * pFlmStats) = 0; /** * @brief Set the directory where temporary files are created. * * This method sets the directory where temporary files are to be created. * * @param pszPath The temporary directory path. * @return RCODE */ virtual RCODE FLMAPI setTempDir( const char * pszPath) = 0; /** * @brief Get the directory where temporary files are created. * * This method returns the directory name where temporary files are created. * If no temporary directory is set, this function returns NE_FLM_IO_PATH_NOT_FOUND. * * @param pszPath The temporary directory path is returned here. * @return RCODE */ virtual RCODE FLMAPI getTempDir( char * pszPath) = 0; /** * @brief Set the time between checkpoints in the database. * * * This method sets the maximum time between completed checkpoints in the database. * This is NOT the same thing as how often a checkpoint will be performed. Checkpoints * are actually performed and completed much more frequently, depending on how much * update transaction activity there is. A background thread (often referred to as the * checkpoint thread) is responsible for performing database checkpoints. The checkpoint * thread wakes up about once a second to see if there are dirty blocks in the cache * that need to be written to disk. If there are, and there is no currently active * update transaction, it will begin performing a checkpoint. If no update * transactions occur while it is performing the checkpoint, it will be able to complete * the checkpoint. If the application attempts to start an update transaction while * the checkpoint is being done, the checkpoint thread has the option of yielding * to the update transaction and not completing the checkpoint. The checkpoint * thread may also choose to not yield and complete the checkpoint. One of the * conditions under which it will not yield is if the elapsed time since the last * completed checkpoint is greater than the time specified by this method. In that * case, the update transaction is held off until the checkpoint completes. * Note that it is possible for the time between completed checkpoints to be longer than * the interval specified in this routine. The checkpoint thread cannot interrupt an * active transaction when it wakes up. If it wakes up and finds that a transaction * is active, it must wait for the transaction to complete before it can start a checkpoint. * Thus, if that transaction runs a long time, the time between completed checkpoints * could exceed the time specified in this method. * * @param uiSeconds The maximum number of seconds allowed to elapse between completed * checkpoints. Default is 180 seconds. NOTE: It is possible that the time * between completed checkpoints can be greater than this value. This is because the * checkpoint thread cannot run if there is an update transaction that is currently * active. If an update transaction is active and runs for a long time, the time * between completed checkpoints could exceed the time specified in this method. */ virtual void FLMAPI setCheckpointInterval( FLMUINT uiSeconds) = 0; /** * @brief Get the current checkpoint interval. * * This method returns the current checkpoint interval. * @return FLMUINT The current checkpoint interval (seconds). */ virtual FLMUINT FLMAPI getCheckpointInterval( void) = 0; /** * @brief Set the time interval for dynamically adjusting the cache limit. * * This method sets the time interval for dynamically adjusting the cache limit. * * @param uiSeconds The time interval for dynamically adjusting the cache limit. */ virtual void FLMAPI setCacheAdjustInterval( FLMUINT uiSeconds) = 0; /** * @brief Get the time interval for dynamically adjusting the cache limit. * * This method returns the time interval (in seconds) for dynamically adjusting the cache limit. * * @return FLMUINT The curernt cache adjust interval (seconds). */ virtual FLMUINT FLMAPI getCacheAdjustInterval( void) = 0; /** * @brief Set the time interval for dynamically cleaning out old cache blocks from block cache. * * This method sets the time interval for cleaning out old cache blocks from block cache. * * @param uiSeconds The time interval for dynamically cleaning out old cache blocks. */ virtual void FLMAPI setCacheCleanupInterval( FLMUINT uiSeconds) = 0; /** * @brief Get the time interval for dynamically cleaning out old cache blocks from block cache. * * This method returns the time interval (in seconds) for cleaning out old cache blocks from block cache. * * @return FLMUINT The current cache cleanup inerval (seconds). */ virtual FLMUINT FLMAPI getCacheCleanupInterval( void) = 0; /** * @brief Set time interval for cleaning up unused resources (such as file handles). * * This method sets the time interval for cleaning up unused resources (such as file handles). * * @param uiSeconds The time interval for cleaning up unused resources (such as file handles). */ virtual void FLMAPI setUnusedCleanupInterval( FLMUINT uiSeconds) = 0; /** * @brief Get time interval for cleaning up unused resources (such as file handles). * * This method returns the time interval (in seconds) for cleaning up unused resources (such as file handles). * * @return FLMUINT The current unused cleanup interval (seconds). */ virtual FLMUINT FLMAPI getUnusedCleanupInterval( void) = 0; /** * @brief Set maximum time for a resource (such as a file handle) to be unused before it is cleaned up. * * This method sets the maximum time for a resource (such as a file handle) to be unused before it * is cleaned up. * * @param uiSeconds The maximum time for a resource (such as a file handle) to be unused before it is * cleaned up. */ virtual void FLMAPI setMaxUnusedTime( FLMUINT uiSeconds) = 0; /** * @brief Get maximum time for a resource (such as a file handle) to be unused before it is cleaned up. * * This method returns the maximum time for a resource (such as a file handle) to be unused * before it is cleaned up. * * @return FLMUINT The current maximum unused time (seconds). */ virtual FLMUINT FLMAPI getMaxUnusedTime( void) = 0; /** * @brief Set the logger client. * * @param pLogger Pointer to the logger client object. */ virtual void FLMAPI setLogger( IF_LoggerClient * pLogger) = 0; /** * @brief Enable or disable extended server memory (ESM). * * @param bEnable A boolean flag. When TRUE, Extended Server Memory is enabled. * When FALSE, Extended Server Memory is disabled. */ virtual void FLMAPI enableExtendedServerMemory( FLMBOOL bEnable) = 0; /** * @brief Determine if extended server memory (ESM) is enabled or disabled. * * @return FLMBOOL True if enabled, otherwise False. */ virtual FLMBOOL FLMAPI extendedServerMemoryEnabled( void) = 0; /** * @brief Deactivate open database objects, forcing the database(s) to eventually be closed. * * This method deactivates all open database objects (IF_Db objects) for a particular * database, forcing the database to eventually be closed. Passing NULL in the pszDbFileName * parameter will deactivate all active database objects for all open databases. * * @param pszDatabasePath Name of the control file ( including path ) for the database that * is to be deactivated. See the XFlaim Concepts/Database Files for a discussion on the * different database files, including the control file. NOTE: Passing a NULL in this * parameter will cause all open databases to be deactivated. * @param pszDataFilePath The directory where the data files are located. If a NULL is passed * in, it is assumed that the data files are located in the same directory as the control file * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for * a discussion on the different database files. */ virtual void FLMAPI deactivateOpenDb( const char * pszDatabasePath, const char * pszDataFilePath) = 0; /** * @brief Set the maximum number of queries to save when statistics gathering is enabled. * * This method sets the maximum number of queries to save when statistics gathering is enabled. * * @param uiMaxToSave The maximum number of queries to save. */ virtual void FLMAPI setQuerySaveMax( FLMUINT uiMaxToSave) = 0; /** * @brief Get the maximum number of queries to save when statistics gathering is enabled. * * This method returns the maximum number of queries to save when statistics gathering is enabled. * * @return FLMUINT The maximum number of queries to save. */ virtual FLMUINT FLMAPI getQuerySaveMax( void) = 0; /** * @brief Set the minimum and maximum dirty cache limits. * * This method sets dirty cache limits - a "maximum" and a "low." The maximum specifies the maximum * dirty cache to be allowed. When a database exceeds this amount, the checkpoint thread will kick * in and write out dirty blocks until the dirty cache comes back down below the amount specified by * the "low" value. In this way, the application can control how much dirty cache builds up between * forced checkpoints (see setCheckpointInterval). The more dirty cache there is when the checkpoint * forces a checkpoint, the longer it will take to complete the checkpoint. It should be noted that * the overall time that will be needed to write out dirty blocks is still the same. It's just that * the writing gets spread out more over time. Instead of doing it all in one big chunk, it gets done * in lots of little chunks. This has both pros and cons, so this method should be used with extreme caution! * In a bulk load situation, where there is one thread doing the bulk loading, lowering the maximum * dirty cache could actually lengthen out the overall time it takes to complete the bulk load. This * is because the bulk load will be interrupted more often by the checkpoint thread to do smaller units * of writing than it otherwise would. There will also be less piggy-backing of writes. Piggy-backing * occurs when multiple transactions write to the same data block before the block is flushed out of * cache. If the database system is flushing dirty blocks out of cache more often, it could end up * writing the same block multiple times, whereas it might not not have had to if it had waited longer between flushes. * The advantage to spreading out the flushing of dirty cache blocks occurs when there are multiple threads * trying to do update transactions. When the checkpoint thread forces a checkpoint, it holds back all * pending update transactions. If it has to do more writing when it forces a checkpoint, it may cause * many threads to wait for a longer period of time than it would if there were less writing to do. * This is less efficient to the overall throughput of the system, because it is likely that each of the * threads could be doing other useful work instead of waiting. * * @param uiMaxDirty The maximum amount (in bytes) of dirty cache allowed. * @param uiLowDirty The low threshold (in bytes) for dirty cache. */ virtual void FLMAPI setDirtyCacheLimits( FLMUINT uiMaxDirty, FLMUINT uiLowDirty) = 0; /** * @brief Get the minimum and maximum dirty cache limits. * * This method returns the minimum and maximum dirty cache limits. * * @param puiMaxDirty The maximum number of dirty blocks allowed in the cache. * @param puiLowDirty The low threshold for the number of dirty blocks in cache. */ virtual void FLMAPI getDirtyCacheLimits( FLMUINT * puiMaxDirty, FLMUINT * puiLowDirty) = 0; /** * @brief Get an information object that can be used to get information about threads owned by the database system. * * This method returns a thread information object which has methods for retrieving various pieces of information * about threads owned by the database system. NOTE: When the application is done using the returned IF_ThreadInfo * object, it should call its Release method. * * @param ifppThreadInfo A pointer to the allocated thread info object is returned here. * * @return RCODE */ virtual RCODE FLMAPI getThreadInfo( IF_ThreadInfo ** ifppThreadInfo) = 0; /** * @brief Register a catcher object to catch database events in a particular category. * * This method registers an object to catch events in a particular category of database events. * * @param eCategory The category of events the application is registering for. * @param ifpEventClient The client object whose methods are to be invoked when the event occurs. * * @return RCODE */ virtual RCODE FLMAPI registerForEvent( eEventCategory eCategory, IF_EventClient * ifpEventClient) = 0; /** * @brief Deregister a catcher object from catching database events in a particular category. * * This method deregisters an object from catching events in a particular * category of database events. * * @param eCategory The type of event. * @param ifpEventClient The client object that was passed into the registerForEvent * method. This is necessary so that if there are multiple objects that have * registered for an event, XFlaim can know exactly which object to * deregister. */ virtual void FLMAPI deregisterForEvent( eEventCategory eCategory, IF_EventClient * ifpEventClient) = 0; /** * @brief Returns the metaphone codes for the next word in an input stream. * * This parses the next word from the input stream (ifpIStream) and returns the metaphone codes for it. This * method returns NE_XFLM_EOF_HIT when it hits the end of the input stream. Visit: This should probably be a * method on the IF_IStream interface, not the IF_DbSystem interface. * * @param ifpIStream Input stream object. * @param puiMetaphone Primary metaphone returned for the next word in the input stream. * @param puiAltMetaphone Alternate metaphone returned for the next word in the input stream. * * @return RCODE */ virtual RCODE FLMAPI getNextMetaphone( IF_IStream * ifpIStream, FLMUINT * puiMetaphone, FLMUINT * puiAltMetaphone = NULL) = 0; /** * @brief Compares two UTF-8 strings */ virtual RCODE FLMAPI compareUTF8Strings( const FLMBYTE * pucLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMBYTE * pucRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult) = 0; /** * @brief Compares two Unicode strings */ virtual RCODE FLMAPI compareUnicodeStrings( const FLMUNICODE * puzLString, FLMUINT uiLStrBytes, FLMBOOL bLeftWild, const FLMUNICODE * puzRString, FLMUINT uiRStrBytes, FLMBOOL bRightWild, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMINT * piResult) = 0; virtual RCODE FLMAPI utf8IsSubStr( const FLMBYTE * pszString, const FLMBYTE * pszSubString, FLMUINT uiCompareRules, FLMUINT uiLanguage, FLMBOOL * pbExists) = 0; virtual FLMBOOL FLMAPI uniIsUpper( FLMUNICODE uzChar) = 0; virtual FLMBOOL FLMAPI uniIsLower( FLMUNICODE uzChar) = 0; virtual FLMBOOL FLMAPI uniIsAlpha( FLMUNICODE uzChar) = 0; virtual FLMBOOL FLMAPI uniIsDecimalDigit( FLMUNICODE uzChar) = 0; virtual FLMUNICODE FLMAPI uniToLower( FLMUNICODE uzChar) = 0; // When the nextUCS2Char method is called, the UCS-2 version of the character // pointed to by *ppszUTF8 is stored in *puzChar and *ppszUTF8 is updated to // point to the next the next character. If *ppszUTF8 >= pszEndOfUTF8String // or if **ppszUTF8 == 0, *puzChar returns 0, but no error is returned. // Note: Remember to keep a copy of the pointer to the start of the // string, because whatever is passed in as ppszUTF8 will be modified. virtual RCODE FLMAPI nextUCS2Char( const FLMBYTE ** ppszUTF8, const FLMBYTE * pszEndOfUTF8String, FLMUNICODE * puzChar) = 0; virtual RCODE FLMAPI numUCS2Chars( const FLMBYTE * pszUTF8, FLMUINT * puiNumChars) = 0; /** * @brief Waits for a specific database to close * * @param pszDbName Name of the control file for the database that we * want to have closed. * * @return RCODE */ virtual RCODE FLMAPI waitToClose( const char * pszDbFileName) = 0; /** * @brief Creates an IF_NodeInfo interface object. * * This method creates an IF_NodeInfo interface object. * * @param ifppNodeInfo The IF_NodeInfo object. * @return RCODE */ virtual RCODE FLMAPI createIFNodeInfo( IF_NodeInfo ** ifppNodeInfo) = 0; /** * @brief Creates an IF_BTreeInfo interface object. * * This method creates an IF_BTreeInfo interface object. * * @param ifppBTreeInfo The IF_BTreeInfo object. * @return RCODE */ virtual RCODE FLMAPI createIFBTreeInfo( IF_BTreeInfo ** ifppBTreeInfo) = 0; /** * @brief A method to attempt to remove everything from cache * * This method will attempt to remove all blocks and nodes from * cache. The pDb parameter is optional. If provided (and an update * transaction is active), any dirty cache items associated with the * database will be flushed. * * @return RCODE */ virtual RCODE FLMAPI clearCache( IF_Db * pDb) = 0; }; // IMPORTANT NOTE: Changes to these enums need to be synced with the // corresponding definitions in java and C# code. typedef enum { XFLM_EXPORT_NO_FORMAT = 0x00, // No Formatting XFLM_EXPORT_NEW_LINE = 0x01, // New Line For Each Element XFLM_EXPORT_INDENT = 0x02, // Indent Elements XFLM_EXPORT_INDENT_DATA = 0x03 // Indent Data - this changes the data } eExportFormatType; FLMEXP RCODE FLMAPI FlmAllocDbSystem( IF_DbSystem ** ppDbSystem); /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_Db : public F_Object { virtual RCODE FLMAPI transBegin( eDbTransType eTransType, FLMUINT uiMaxLockWait = FLM_NO_TIMEOUT, FLMUINT uiFlags = 0, XFLM_DB_HDR * pDbHeader = NULL) = 0; virtual RCODE FLMAPI transBegin( IF_Db * pDb) = 0; virtual RCODE FLMAPI transCommit( FLMBOOL * pbEmpty = NULL) = 0; virtual RCODE FLMAPI transAbort( void) = 0; virtual eDbTransType FLMAPI getTransType( void) = 0; virtual RCODE FLMAPI doCheckpoint( FLMUINT uiTimeout) = 0; virtual RCODE FLMAPI dbLock( eLockType lockType, FLMINT iPriority, FLMUINT uiTimeout) = 0; virtual RCODE FLMAPI dbUnlock( void) = 0; virtual RCODE FLMAPI getLockType( eLockType * pLockType, FLMBOOL * pbImplicit) = 0; virtual RCODE FLMAPI getLockInfo( FLMINT iPriority, eLockType * pCurrLockType, FLMUINT * puiThreadId, FLMUINT * puiNumExclQueued, FLMUINT * puiNumSharedQueued, FLMUINT * puiPriorityCount) = 0; virtual RCODE FLMAPI indexStatus( FLMUINT uiIndexNum, XFLM_INDEX_STATUS * pIndexStatus) = 0; virtual RCODE FLMAPI indexGetNext( FLMUINT * puiIndexNum) = 0; virtual RCODE FLMAPI indexSuspend( FLMUINT uiIndexNum) = 0; virtual RCODE FLMAPI indexResume( FLMUINT uiIndexNum) = 0; virtual RCODE FLMAPI keyRetrieve( FLMUINT uiIndex, IF_DataVector * pSearchKey, FLMUINT uiFlags, IF_DataVector * pFoundKey) = 0; virtual RCODE FLMAPI enableEncryption( void) = 0; virtual RCODE FLMAPI wrapKey( const char * pszPassword = NULL) = 0; virtual RCODE FLMAPI rollOverDbKey( void) = 0; virtual RCODE FLMAPI changeItemState( FLMUINT uiDictType, FLMUINT uiDictNum, const char * pszState) = 0; virtual RCODE FLMAPI reduceSize( FLMUINT uiCount, FLMUINT * puiCount) = 0; virtual RCODE FLMAPI upgrade( IF_UpgradeClient * pUpgradeClient) = 0; virtual RCODE FLMAPI createRootElement( FLMUINT uiCollection, FLMUINT uiNameId, IF_DOMNode ** ppElementNode, FLMUINT64 * pui64NodeId = NULL) = 0; virtual RCODE FLMAPI createDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode, FLMUINT64 * pui64NodeId = NULL) = 0; virtual RCODE FLMAPI getFirstDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode) = 0; virtual RCODE FLMAPI getLastDocument( FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode) = 0; virtual RCODE FLMAPI getDocument( FLMUINT uiCollection, FLMUINT uiFlags, FLMUINT64 ui64DocumentId, IF_DOMNode ** ppDocumentNode) = 0; virtual RCODE FLMAPI documentDone( FLMUINT uiCollection, FLMUINT64 ui64RootId) = 0; virtual RCODE FLMAPI documentDone( IF_DOMNode * pDocNode) = 0; virtual RCODE FLMAPI createElementDef( const char * pszNamespaceURI, const char * pszElementName, FLMUINT uiDataType, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) = 0; virtual RCODE FLMAPI createElementDef( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT uiDataType, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) = 0; virtual RCODE FLMAPI createUniqueElmDef( const char * pszNamespaceURI, const char * pszElementName, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) = 0; virtual RCODE FLMAPI createUniqueElmDef( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT * puiElementNameId = NULL, IF_DOMNode ** ppDocumentNode = NULL) = 0; virtual RCODE FLMAPI getElementNameId( const char * pszNamespaceURI, const char * pszElementName, FLMUINT * puiElementNameId) = 0; virtual RCODE FLMAPI getElementNameId( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzElementName, FLMUINT * puiElementNameId) = 0; virtual RCODE FLMAPI createAttributeDef( const char * pszNamespaceURI, const char * pszAttributeName, FLMUINT uiDataType, FLMUINT * puiAttributeNameId, IF_DOMNode ** ppDocumentNode = NULL) = 0; virtual RCODE FLMAPI createAttributeDef( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzAttributeName, FLMUINT uiDataType, FLMUINT * puiAttributeNameId, IF_DOMNode ** ppDocumentNode = NULL) = 0; virtual RCODE FLMAPI getAttributeNameId( const char * pszNamespaceURI, const char * pszAttributeName, FLMUINT * puiAttributeNameId) = 0; virtual RCODE FLMAPI getAttributeNameId( const FLMUNICODE * puzNamespaceURI, const FLMUNICODE * puzAttributeName, FLMUINT * puiAttributeNameId) = 0; virtual RCODE FLMAPI createPrefixDef( const char * pszPrefixName, FLMUINT * puiPrefixNumber) = 0; virtual RCODE FLMAPI createPrefixDef( const FLMUNICODE * puzPrefixName, FLMUINT * puiPrefixNumber) = 0; virtual RCODE FLMAPI getPrefixId( const char * pszPrefixName, FLMUINT * puiPrefixNumber) = 0; virtual RCODE FLMAPI getPrefixId( const FLMUNICODE * puzPrefixName, FLMUINT * puiPrefixNumber) = 0; virtual RCODE FLMAPI createEncDef( const char * pszEncType, const char * pszEncName, FLMUINT uiKeySize, FLMUINT * puiEncDefNumber) = 0; virtual RCODE FLMAPI createEncDef( const FLMUNICODE * puzEncType, const FLMUNICODE * puzEncName, FLMUINT uiKeySize, FLMUINT * puiEncDefNumber) = 0; virtual RCODE FLMAPI getEncDefId( const char * pszEncDefName, FLMUINT * puiPrefixNumber) = 0; virtual RCODE FLMAPI getEncDefId( const FLMUNICODE * puzEncDefName, FLMUINT * puiEncDefNumber) = 0; virtual RCODE FLMAPI createCollectionDef( const char * pszCollectionName, FLMUINT * puiCollectionNumber, FLMUINT uiEncNumber = 0) = 0; virtual RCODE FLMAPI createCollectionDef( const FLMUNICODE * puzCollectionName, FLMUINT * puiCollectionNumber, FLMUINT uiEncNumber = 0) = 0; virtual RCODE FLMAPI getCollectionNumber( const char * pszCollectionName, FLMUINT * puiCollectionNumber) = 0; virtual RCODE FLMAPI getCollectionNumber( const FLMUNICODE * puzCollectionName, FLMUINT * puiCollectionNumber) = 0; virtual RCODE FLMAPI getIndexNumber( const char * pszIndexName, FLMUINT * puiIndexNumber) = 0; virtual RCODE FLMAPI getIndexNumber( const FLMUNICODE * puzIndexName, FLMUINT * puiIndexNumber) = 0; virtual RCODE FLMAPI getDictionaryDef( FLMUINT uiDictType, FLMUINT uiDictNumber, IF_DOMNode ** ppDocumentNode) = 0; virtual RCODE FLMAPI getDictionaryName( FLMUINT uiDictType, FLMUINT uiDictNumber, char * pszName, FLMUINT * puiNameBufSize, char * pszNamespace = NULL, FLMUINT * puiNamespaceBufSize = NULL) = 0; virtual RCODE FLMAPI getDictionaryName( FLMUINT uiDictType, FLMUINT uiDictNumber, FLMUNICODE * puzName, FLMUINT * puiNameBufSize, FLMUNICODE * puzNamespace = NULL, FLMUINT * puiNamespaceBufSize = NULL) = 0; virtual RCODE FLMAPI getNode( FLMUINT uiCollection, FLMUINT64 ui64NodeId, IF_DOMNode ** ppNode) = 0; virtual RCODE FLMAPI getAttribute( FLMUINT uiCollection, FLMUINT64 ui64ElementNodeId, FLMUINT uiAttrNameId, IF_DOMNode ** ppNode) = 0; virtual RCODE FLMAPI getDataType( FLMUINT uiDictType, FLMUINT uiNameId, FLMUINT * puiDataType) = 0; virtual RCODE FLMAPI backupBegin( eDbBackupType eBackupType, eDbTransType eTransType, FLMUINT uiMaxLockWait, IF_Backup ** ppBackup) = 0; virtual void FLMAPI getRflFileName( FLMUINT uiFileNum, FLMBOOL bBaseOnly, char * pszFileName, FLMUINT * puiFileNameBufSize, FLMBOOL * pbNameTruncated = NULL) = 0; virtual RCODE FLMAPI import( IF_IStream * pIStream, FLMUINT uiCollection, IF_DOMNode * pNodeToLinkTo = NULL, eNodeInsertLoc eInsertLoc = XFLM_LAST_CHILD, XFLM_IMPORT_STATS * pImportStats = NULL) = 0; virtual RCODE FLMAPI importDocument( IF_IStream * ifpStream, FLMUINT uiCollection, IF_DOMNode ** ppDocumentNode = NULL, XFLM_IMPORT_STATS * pImportStats = NULL) = 0; virtual RCODE FLMAPI exportXML( IF_DOMNode * pStartNode, IF_OStream * pOStream, eExportFormatType eFormat = XFLM_EXPORT_INDENT) = 0; virtual RCODE FLMAPI setNextNodeId( FLMUINT uiCollection, FLMUINT64 ui64NextNodeId) = 0; virtual RCODE FLMAPI setNextDictNum( FLMUINT uiDictType, FLMUINT uiDictNumber) = 0; // Configuration "set" and "get" methods virtual RCODE FLMAPI setRflKeepFilesFlag( FLMBOOL bKeep) = 0; virtual RCODE FLMAPI getRflKeepFlag( FLMBOOL * pbKeep) = 0; virtual RCODE FLMAPI setRflDir( const char * pszNewRflDir) = 0; virtual void FLMAPI getRflDir( char * pszRflDir) = 0; virtual RCODE FLMAPI getRflFileNum( FLMUINT * puiRflFileNum) = 0; virtual RCODE FLMAPI getHighestNotUsedRflFileNum( FLMUINT * puiHighestNotUsedRflFileNum) = 0; virtual RCODE FLMAPI setRflFileSizeLimits( FLMUINT uiMinRflSize, FLMUINT uiMaxRflSize) = 0; virtual RCODE FLMAPI getRflFileSizeLimits( FLMUINT * puiRflMinFileSize, FLMUINT * puiRflMaxFileSize) = 0; virtual RCODE FLMAPI rflRollToNextFile( void) = 0; virtual RCODE FLMAPI setKeepAbortedTransInRflFlag( FLMBOOL bKeep) = 0; virtual RCODE FLMAPI getKeepAbortedTransInRflFlag( FLMBOOL * pbKeep) = 0; virtual RCODE FLMAPI setAutoTurnOffKeepRflFlag( FLMBOOL bAutoTurnOff) = 0; virtual RCODE FLMAPI getAutoTurnOffKeepRflFlag( FLMBOOL * pbAutoTurnOff) = 0; virtual void FLMAPI setFileExtendSize( FLMUINT uiFileExtendSize) = 0; virtual FLMUINT FLMAPI getFileExtendSize( void) = 0; virtual void FLMAPI setAppData( void * pvAppData) = 0; virtual void * FLMAPI getAppData( void) = 0; virtual void FLMAPI setDeleteStatusObject( IF_DeleteStatus * pDeleteStatus) = 0; virtual void FLMAPI setCommitClientObject( IF_CommitClient * pCommitClient) = 0; virtual void FLMAPI setIndexingClientObject( IF_IxClient * pIxClient) = 0; virtual void FLMAPI setIndexingStatusObject( IF_IxStatus * pIxStatus) = 0; // Configuration information getting methods virtual FLMUINT FLMAPI getDbVersion( void) = 0; virtual FLMUINT FLMAPI getBlockSize( void) = 0; virtual FLMUINT FLMAPI getDefaultLanguage( void) = 0; virtual FLMUINT64 FLMAPI getTransID( void) = 0; virtual void FLMAPI getCheckpointInfo( XFLM_CHECKPOINT_INFO * pCheckpointInfo) = 0; virtual RCODE FLMAPI getDbControlFileName( char * pszControlFileName, FLMUINT uiControlFileBufSize) = 0; virtual RCODE FLMAPI getLockWaiters( IF_LockInfoClient * pLockInfo) = 0; virtual RCODE FLMAPI getLastBackupTransID( FLMUINT64 * pui64LastBackupTransID) = 0; virtual RCODE FLMAPI getBlocksChangedSinceBackup( FLMUINT * puiBlocksChangedSinceBackup) = 0; virtual RCODE FLMAPI getNextIncBackupSequenceNum( FLMUINT * puiNextIncBackupSequenceNum) = 0; virtual void FLMAPI getSerialNumber( char * pucSerialNumber) = 0; virtual RCODE FLMAPI getDiskSpaceUsage( FLMUINT64 * pui64DataSize, FLMUINT64 * pui64RollbackSize, FLMUINT64 * pui64RflSize) = 0; virtual RCODE FLMAPI getMustCloseRC( void) = 0; virtual RCODE FLMAPI getAbortRC( void) = 0; virtual void FLMAPI setMustAbortTrans( RCODE rc) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DOMNode : public F_Object { virtual RCODE FLMAPI createNode( IF_Db * pDb, eDomNodeType eNodeType, FLMUINT uiNameId, eNodeInsertLoc eLocation, IF_DOMNode ** ppNewNode, FLMUINT64 * pui64NodeId = NULL) = 0; virtual RCODE FLMAPI createChildElement( IF_Db * pDb, FLMUINT uiChildElementNameId, eNodeInsertLoc eLocation, IF_DOMNode ** ppNewChildElementNode, FLMUINT64 * pui64NodeId = NULL) = 0; virtual RCODE FLMAPI deleteNode( IF_Db * pDb) = 0; virtual RCODE FLMAPI deleteChildren( IF_Db * pDb, FLMUINT uiNameId = 0) = 0; virtual RCODE FLMAPI createAttribute( IF_Db * pDb, FLMUINT uiAttrNameId, IF_DOMNode ** ppAttrNode) = 0; virtual RCODE FLMAPI getFirstAttribute( IF_Db * pDb, IF_DOMNode ** ppAttrNode) = 0; virtual RCODE FLMAPI getLastAttribute( IF_Db * pDb, IF_DOMNode ** ppAttrNode) = 0; virtual RCODE FLMAPI getAttribute( IF_Db * pDb, FLMUINT uiAttrNameId, IF_DOMNode ** ppAttrNode) = 0; virtual RCODE FLMAPI deleteAttribute( IF_Db * pDb, FLMUINT uiAttrNameId) = 0; virtual RCODE FLMAPI hasAttribute( IF_Db * pDb, FLMUINT uiAttrNameId, IF_DOMNode ** ppAttrNode = NULL) = 0; virtual RCODE FLMAPI hasAttributes( IF_Db * pDb, FLMBOOL * pbHasAttrs) = 0; virtual RCODE FLMAPI hasNextSibling( IF_Db * pDb, FLMBOOL * pbHasNextSibling) = 0; virtual RCODE FLMAPI hasPreviousSibling( IF_Db * pDb, FLMBOOL * pbHasPreviousSibling) = 0; virtual RCODE FLMAPI hasChildren( IF_Db * pDb, FLMBOOL * pbHasChildren) = 0; virtual RCODE FLMAPI isNamespaceDecl( IF_Db * pDb, FLMBOOL * pbIsNamespaceDecl) = 0; virtual eDomNodeType FLMAPI getNodeType( void) = 0; virtual RCODE FLMAPI getNodeId( IF_Db * pDb, FLMUINT64 * pui64NodeId) = 0; virtual RCODE FLMAPI getParentId( IF_Db * pDb, FLMUINT64 * pui64ParentId) = 0; virtual RCODE FLMAPI getDocumentId( IF_Db * pDb, FLMUINT64 * pui64DocumentId) = 0; virtual RCODE FLMAPI getPrevSibId( IF_Db * pDb, FLMUINT64 * pui64PrevSibId) = 0; virtual RCODE FLMAPI getNextSibId( IF_Db * pDb, FLMUINT64 * pui64NextSibId) = 0; virtual RCODE FLMAPI getFirstChildId( IF_Db * pDb, FLMUINT64 * pui64FirstChildId) = 0; virtual RCODE FLMAPI getLastChildId( IF_Db * pDb, FLMUINT64 * pui64LastChildId) = 0; virtual RCODE FLMAPI getNameId( IF_Db * pDb, FLMUINT * puiNameId) = 0; virtual RCODE FLMAPI getEncDefId( IF_Db * pDb, FLMUINT * puiEncDefId) = 0; virtual RCODE FLMAPI getDataType( IF_Db * pDb, FLMUINT * puiDataType) = 0; virtual RCODE FLMAPI getDataLength( IF_Db * pDb, FLMUINT * puiLength) = 0; virtual RCODE FLMAPI getUINT32( IF_Db * pDb, FLMUINT32 * pui32Value) = 0; virtual RCODE FLMAPI getUINT( IF_Db * pDb, FLMUINT * puiValue) = 0; virtual RCODE FLMAPI getUINT64( IF_Db * pDb, FLMUINT64 * pui64Value) = 0; virtual RCODE FLMAPI getINT32( IF_Db * pDb, FLMINT32 * pi32Value) = 0; virtual RCODE FLMAPI getINT( IF_Db * pDb, FLMINT * piValue) = 0; virtual RCODE FLMAPI getINT64( IF_Db * pDb, FLMINT64 * pi64Value) = 0; virtual RCODE FLMAPI getMetaValue( IF_Db * pDb, FLMUINT64 * pui64Value) = 0; virtual RCODE FLMAPI getUnicodeChars( IF_Db * pDb, FLMUINT * puiNumChars) = 0; virtual RCODE FLMAPI getUnicode( IF_Db * pDb, FLMUNICODE * puzValueBuffer, FLMUINT uiBufferSize, FLMUINT uiCharOffset, FLMUINT uiMaxCharsRequested, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL) = 0; virtual RCODE FLMAPI getUnicode( IF_Db * pDb, FLMUNICODE ** ppuzUnicodeValue) = 0; virtual RCODE FLMAPI getUnicode( IF_Db * pDb, F_DynaBuf * pDynaBuf) = 0; virtual RCODE FLMAPI getUTF8( IF_Db * pDb, FLMBYTE * pucValueBuffer, FLMUINT uiBufferSize, FLMUINT uiCharOffset, FLMUINT uiMaxCharsRequested, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL) = 0; virtual RCODE FLMAPI getUTF8( IF_Db * pDb, FLMBYTE ** ppszUTF8Value) = 0; virtual RCODE FLMAPI getUTF8( IF_Db * pDb, F_DynaBuf * pDynaBuf) = 0; virtual RCODE FLMAPI getBinary( IF_Db * pDb, void * pvValue, FLMUINT uiByteOffset, FLMUINT uiBytesRequested, FLMUINT * puiBytesReturned) = 0; virtual RCODE FLMAPI getBinary( IF_Db * pDb, F_DynaBuf * pBuffer) = 0; virtual RCODE FLMAPI getAttributeValueUINT32( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT32 * pui32Num) = 0; virtual RCODE FLMAPI getAttributeValueUINT32( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT32 * pui32Num, FLMUINT32 ui32NotFoundDefault) = 0; virtual RCODE FLMAPI getAttributeValueUINT( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT * puiNum) = 0; virtual RCODE FLMAPI getAttributeValueUINT( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT * puiNum, FLMUINT uiNotFoundDefault) = 0; virtual RCODE FLMAPI getAttributeValueUINT64( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 * pui64Num) = 0; virtual RCODE FLMAPI getAttributeValueUINT64( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 * pui64Num, FLMUINT64 ui64NotFoundDefault) = 0; virtual RCODE FLMAPI getAttributeValueINT( IF_Db * pDb, FLMUINT uiAttrNameId, FLMINT * piNum) = 0; virtual RCODE FLMAPI getAttributeValueINT( IF_Db * pDb, FLMUINT uiAttrNameId, FLMINT * piNum, FLMINT iNotFoundDefault) = 0; virtual RCODE FLMAPI getAttributeValueINT64( IF_Db * pDb, FLMUINT uiAttrNameId, FLMINT64 * pi64Num) = 0; virtual RCODE FLMAPI getAttributeValueINT64( IF_Db * pDb, FLMUINT uiAttrNameId, FLMINT64 * pi64Num, FLMINT64 i64NotFoundDefault) = 0; virtual RCODE FLMAPI getAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUNICODE * puzValueBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL) = 0; virtual RCODE FLMAPI getAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUNICODE ** ppuzValueBuffer) = 0; virtual RCODE FLMAPI getAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrNameId, F_DynaBuf * pDynaBuf) = 0; virtual RCODE FLMAPI getAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrNameId, FLMBYTE * pucValueBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL, FLMUINT * puiBufferBytesUsed = NULL) = 0; virtual RCODE FLMAPI getAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrNameId, FLMBYTE ** ppszValueBuffer) = 0; virtual RCODE FLMAPI getAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrNameId, F_DynaBuf * pDynaBuf) = 0; virtual RCODE FLMAPI getAttributeValueBinary( IF_Db * pDb, FLMUINT uiAttrNameId, void * pvValueBuffer, FLMUINT uiBufferSize, FLMUINT * puiValueLength) = 0; virtual RCODE FLMAPI getAttributeValueBinary( IF_Db * pDb, FLMUINT uiAttrNameId, F_DynaBuf * pDynaBuf) = 0; virtual RCODE FLMAPI setUINT( IF_Db * pDb, FLMUINT uiValue, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setUINT64( IF_Db * pDb, FLMUINT64 ui64Value, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setINT( IF_Db * pDb, FLMINT iValue, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setINT64( IF_Db * pDb, FLMINT64 i64Value, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setMetaValue( IF_Db * pDb, FLMUINT64 ui64Value) = 0; virtual RCODE FLMAPI setUnicode( IF_Db * pDb, const FLMUNICODE * puzValue, FLMUINT uiValueLength = 0, FLMBOOL bLast = TRUE, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setUTF8( IF_Db * pDb, const FLMBYTE * pszValue, FLMUINT uiValueLength = 0, FLMBOOL bLast = TRUE, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setBinary( IF_Db * pDb, const void * pvValue, FLMUINT uiValueLength, FLMBOOL bLast = TRUE, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueUINT( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT uiValue, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueUINT64( IF_Db * pDb, FLMUINT uiAttrNameId, FLMUINT64 ui64Value, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueINT( IF_Db * pDb, FLMUINT uiAttrNameId, FLMINT iValue, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueINT64( IF_Db * pDb, FLMUINT uiAttrNameId, FLMINT64 i64Value, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueUnicode( IF_Db * pDb, FLMUINT uiAttrNameId, const FLMUNICODE * puzValue, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueUTF8( IF_Db * pDb, FLMUINT uiAttrNameId, const FLMBYTE * pucValue, FLMUINT uiLength = 0, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI setAttributeValueBinary( IF_Db * pDb, FLMUINT uiAttrNameId, const void * pvValue, FLMUINT uiLength, FLMUINT uiEncDefId = 0) = 0; virtual RCODE FLMAPI getDocumentNode( IF_Db * pDb, IF_DOMNode ** ppDocument) = 0; virtual RCODE FLMAPI getNextDocument( IF_Db * pDb, IF_DOMNode ** ppNextDocument) = 0; virtual RCODE FLMAPI getPreviousDocument( IF_Db * pDb, IF_DOMNode ** ppPrevDocument) = 0; virtual RCODE FLMAPI getParentNode( IF_Db * pDb, IF_DOMNode ** ppParent) = 0; virtual RCODE FLMAPI getFirstChild( IF_Db * pDb, IF_DOMNode ** ppFirstChild) = 0; virtual RCODE FLMAPI getLastChild( IF_Db * pDb, IF_DOMNode ** ppLastChild) = 0; virtual RCODE FLMAPI getNextSibling( IF_Db * pDb, IF_DOMNode ** ppNextSibling) = 0; virtual RCODE FLMAPI getPreviousSibling( IF_Db * pDb, IF_DOMNode ** ppPrevSibling) = 0; virtual RCODE FLMAPI getChild( IF_Db * pDb, eDomNodeType eNodeType, IF_DOMNode ** ppChild) = 0; virtual RCODE FLMAPI getChildElement( IF_Db * pDb, FLMUINT uiElementNameId, IF_DOMNode ** ppChild, FLMUINT uiFlags = 0) = 0; virtual RCODE FLMAPI getSiblingElement( IF_Db * pDb, FLMUINT uiElementNameId, FLMBOOL bNext, IF_DOMNode ** ppSibling) = 0; virtual RCODE FLMAPI getAncestorElement( IF_Db * pDb, FLMUINT uiElementNameId, IF_DOMNode ** ppAncestor) = 0; virtual RCODE FLMAPI getDescendantElement( IF_Db * pDb, FLMUINT uiElementNameId, IF_DOMNode ** ppDescendant) = 0; virtual RCODE FLMAPI insertBefore( IF_Db * pDb, IF_DOMNode * pNewChild, IF_DOMNode * pRefChild) = 0; virtual RCODE FLMAPI getPrefix( IF_Db * pDb, FLMUNICODE * puzPrefixBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getPrefix( IF_Db * pDb, char * pszPrefixBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getPrefixId( IF_Db * pDb, FLMUINT * puiPrefixId) = 0; virtual RCODE FLMAPI setPrefix( IF_Db * pDb, const FLMUNICODE * puzPrefix) = 0; virtual RCODE FLMAPI setPrefix( IF_Db * pDb, const char * pszPrefix) = 0; virtual RCODE FLMAPI setPrefixId( IF_Db * pDb, FLMUINT uiPrefixId) = 0; virtual RCODE FLMAPI getNamespaceURI( IF_Db * pDb, FLMUNICODE * puzNamespaceURIBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getNamespaceURI( IF_Db * pDb, char * pszNamespaceURIBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getLocalName( IF_Db * pDb, FLMUNICODE * puzLocalNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getLocalName( IF_Db * pDb, char * pszLocalNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getQualifiedName( IF_Db * pDb, FLMUNICODE * puzQualifiedNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getQualifiedName( IF_Db * pDb, char * pszQualifiedNameBuffer, FLMUINT uiBufferSize, FLMUINT * puiCharsReturned = NULL) = 0; virtual RCODE FLMAPI getCollection( IF_Db * pDb, FLMUINT * puiCollection) = 0; virtual RCODE FLMAPI createAnnotation( IF_Db * pDb, IF_DOMNode ** ppAnnotation, FLMUINT64 * pui64NodeId = NULL) = 0; virtual RCODE FLMAPI getAnnotation( IF_Db * pDb, IF_DOMNode ** ppAnnotation) = 0; virtual RCODE FLMAPI getAnnotationId( IF_Db * pDb, FLMUINT64 * pui64AnnotationId) = 0; virtual RCODE FLMAPI hasAnnotation( IF_Db * pDb, FLMBOOL * pbHasAnnotation) = 0; virtual RCODE FLMAPI getIStream( IF_Db * pDb, IF_PosIStream ** ppIStream, FLMUINT * puiDataType = NULL, FLMUINT * puiDataLength = NULL) = 0; virtual RCODE FLMAPI getTextIStream( IF_Db * pDb, IF_PosIStream ** ppIStream, FLMUINT * puiNumChars = NULL) = 0; virtual FLMUINT FLMAPI compareNode( IF_DOMNode * pNode, IF_Db * pDb1, IF_Db * pDb2, char * pszErrBuff, FLMUINT uiErrBuffLen) = 0; virtual RCODE FLMAPI isDataLocalToNode( IF_Db * pDb, FLMBOOL * pbDataIsLocal) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DataVector : public F_Object { virtual void FLMAPI setDocumentID( FLMUINT64 ui64DocumentID) = 0; virtual RCODE FLMAPI setID( FLMUINT uiElementNumber, FLMUINT64 ui64ID) = 0; virtual RCODE FLMAPI setNameId( FLMUINT uiElementNumber, FLMUINT uiNameId, FLMBOOL bIsAttr, FLMBOOL bIsData) = 0; virtual RCODE FLMAPI setINT( FLMUINT uiElementNumber, FLMINT iNum) = 0; virtual RCODE FLMAPI setINT64( FLMUINT uiElementNumber, FLMINT64 i64Num) = 0; virtual RCODE FLMAPI setUINT( FLMUINT uiElementNumber, FLMUINT uiNum) = 0; virtual RCODE FLMAPI setUINT64( FLMUINT uiElementNumber, FLMUINT64 ui64Num) = 0; virtual RCODE FLMAPI setUnicode( FLMUINT uiElementNumber, const FLMUNICODE * puzUnicode) = 0; virtual RCODE FLMAPI setUTF8( FLMUINT uiElementNumber, const FLMBYTE * pszUtf8, FLMUINT uiBytesInBuffer = 0) = 0; virtual RCODE FLMAPI setBinary( FLMUINT uiElementNumber, const void * pvBinary, FLMUINT uiBinaryLen) = 0; virtual void FLMAPI setRightTruncated( FLMUINT uiElementNumber) = 0; virtual void FLMAPI setLeftTruncated( FLMUINT uiElementNumber) = 0; virtual void FLMAPI clearRightTruncated( FLMUINT uiElementNumber) = 0; virtual void FLMAPI clearLeftTruncated( FLMUINT uiElementNumber) = 0; virtual FLMBOOL FLMAPI isRightTruncated( FLMUINT uiElementNumber) = 0; virtual FLMBOOL FLMAPI isLeftTruncated( FLMUINT uiElementNumber) = 0; virtual FLMUINT64 FLMAPI getDocumentID( void) = 0; virtual FLMUINT64 FLMAPI getID( FLMUINT uiElementNumber) = 0; virtual FLMUINT FLMAPI getNameId( FLMUINT uiElementNumber) = 0; virtual FLMBOOL FLMAPI isAttr( FLMUINT uiElementNumber) = 0; virtual FLMBOOL FLMAPI isDataComponent( FLMUINT uiElementNumber) = 0; virtual FLMBOOL FLMAPI isKeyComponent( FLMUINT uiElementNumber) = 0; virtual FLMUINT FLMAPI getDataLength( FLMUINT uiElementNumber) = 0; virtual FLMUINT FLMAPI getDataType( FLMUINT uiElementNumber) = 0; virtual RCODE FLMAPI getUTF8Ptr( FLMUINT uiElementNumber, const FLMBYTE ** ppszUTF8, FLMUINT * puiBufLen) = 0; virtual RCODE FLMAPI getINT( FLMUINT uiElementNumber, FLMINT * piNum) = 0; virtual RCODE FLMAPI getINT64( FLMUINT uiElementNumber, FLMINT64 * pi64Num) = 0; virtual RCODE FLMAPI getUINT( FLMUINT uiElementNumber, FLMUINT * puiNum) = 0; virtual RCODE FLMAPI getUINT64( FLMUINT uiElementNumber, FLMUINT64 * pui64Num) = 0; virtual RCODE FLMAPI getUnicode( FLMUINT uiElementNumber, FLMUNICODE ** ppuzUnicode) = 0; virtual RCODE FLMAPI getUnicode( FLMUINT uiElementNumber, FLMUNICODE * puzUnicode, FLMUINT * puiBufLen) = 0; virtual RCODE FLMAPI getUnicode( FLMUINT uiElementNumber, F_DynaBuf * pBuffer) = 0; virtual RCODE FLMAPI getUTF8( FLMUINT uiElementNumber, FLMBYTE * pszUTF8, FLMUINT * puiBufLen) = 0; virtual RCODE FLMAPI getBinary( FLMUINT uiElementNumber, void * pvBuffer, FLMUINT * puiBufferLen) = 0; virtual RCODE FLMAPI outputKey( IF_Db * pDb, FLMUINT uiIndexNum, FLMUINT uiMatchFlags, FLMBYTE * pucKeyBuf, FLMUINT uiKeyBufSize, FLMUINT * puiKeyLen) = 0; virtual RCODE FLMAPI outputData( IF_Db * pDb, FLMUINT uiIndexNum, FLMBYTE * pucDataBuf, FLMUINT uiDataBufSize, FLMUINT * puiDataLen) = 0; virtual RCODE FLMAPI inputKey( IF_Db * pDb, FLMUINT uiIndexNum, const FLMBYTE * pucKey, FLMUINT uiKeyLen) = 0; virtual RCODE FLMAPI inputData( IF_Db * pDb, FLMUINT uiIndexNum, const FLMBYTE * pucData, FLMUINT uiDataLen) = 0; // Miscellaneous methods virtual void FLMAPI reset( void) = 0; virtual const void * FLMAPI getDataPtr( FLMUINT uiElementNumber) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_Backup : public F_Object { virtual FLMUINT64 FLMAPI getBackupTransId( void) = 0; virtual FLMUINT64 FLMAPI getLastBackupTransId( void) = 0; virtual RCODE FLMAPI backup( const char * pszBackupPath, const char * pszPassword, IF_BackupClient * ifpClient, IF_BackupStatus * ifpStatus, FLMUINT * puiIncSeqNum) = 0; virtual RCODE FLMAPI endBackup( void) = 0; }; // Note: Any interfaces ending in Client or Status are interfaces // that XFlaim does not provide implementations of. They exist to // allow XFlaim to pass data back to the client. Interfaces ending in // Status are, generally, informational only, while interfaces ending // in Client exist to allow the client to modify the data or take // other action. /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_BackupClient : public F_Object { virtual RCODE FLMAPI WriteData( const void * pvBuffer, FLMUINT uiBytesToWrite) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_BackupStatus : public F_Object { virtual RCODE FLMAPI backupStatus( FLMUINT64 ui64BytesToDo, FLMUINT64 ui64BytesDone) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_CommitClient : public F_Object { virtual void FLMAPI commit( IF_Db * pDb) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_EventClient : public F_Object { virtual void FLMAPI catchEvent( eEventType eEvent, IF_Db * pDb, FLMUINT uiThreadId, FLMUINT64 ui64TransID, FLMUINT uiIndexOrCollection, FLMUINT64 ui64NodeId, RCODE rc) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_IxClient : public F_Object { virtual RCODE FLMAPI doIndexing( IF_Db * pDb, FLMUINT uiIndexNum, FLMUINT uiCollectionNum, IF_DOMNode * pDocNode) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_RestoreStatus : public F_Object { virtual RCODE FLMAPI reportProgress( eRestoreAction * peAction, FLMUINT64 ui64BytesToDo, FLMUINT64 ui64BytesDone) = 0; virtual RCODE FLMAPI reportError( eRestoreAction * peAction, RCODE rcErr) = 0; virtual RCODE FLMAPI reportOpenRflFile( eRestoreAction * peAction, FLMUINT uiFileNum) = 0; virtual RCODE FLMAPI reportRflRead( eRestoreAction * peAction, FLMUINT uiFileNum, FLMUINT uiBytesRead) = 0; virtual RCODE FLMAPI reportBeginTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId) = 0; virtual RCODE FLMAPI reportCommitTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId) = 0; virtual RCODE FLMAPI reportAbortTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId) = 0; virtual RCODE FLMAPI reportBlockChainFree( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT64 ui64MaintDocNum, FLMUINT uiStartBlkAddr, FLMUINT uiEndBlkAddr, FLMUINT uiCount) = 0; virtual RCODE FLMAPI reportIndexSuspend( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiIndexNum) = 0; virtual RCODE FLMAPI reportIndexResume( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiIndexNum) = 0; virtual RCODE FLMAPI reportReduce( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCount) = 0; virtual RCODE FLMAPI reportUpgrade( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiOldDbVersion, FLMUINT uiNewDbVersion) = 0; virtual RCODE FLMAPI reportEnableEncryption( eRestoreAction * peAction, FLMUINT64 ui64TransId) = 0; virtual RCODE FLMAPI reportWrapKey( eRestoreAction * peAction, FLMUINT64 ui64TransId) = 0; virtual RCODE FLMAPI reportRollOverDbKey( eRestoreAction * peAction, FLMUINT64 ui64TransId) = 0; virtual RCODE FLMAPI reportDocumentDone( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64DocumentId) = 0; virtual RCODE FLMAPI reportNodeDelete( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId) = 0; virtual RCODE FLMAPI reportAttributeDelete( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64ElementId, FLMUINT uiAttrNameId) = 0; virtual RCODE FLMAPI reportNodeChildrenDelete( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64ParentNodeId, FLMUINT uiNameId) = 0; virtual RCODE FLMAPI reportNodeCreate( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64RefNodeId, eDomNodeType eNodeType, FLMUINT uiNameId, eNodeInsertLoc eLocation) = 0; virtual RCODE FLMAPI reportInsertBefore( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64ParentNodeId, FLMUINT64 ui64NewChildNodeId, FLMUINT64 ui64RefChildNodeId) = 0; virtual RCODE FLMAPI reportNodeUpdate( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId) = 0; virtual RCODE FLMAPI reportNodeSetValue( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId) = 0; virtual RCODE FLMAPI reportAttributeSetValue( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64ElementNodeId, FLMUINT uiAttrNameId) = 0; virtual RCODE FLMAPI reportNodeFlagsUpdate( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiFlags, FLMBOOL bAdd) = 0; virtual RCODE FLMAPI reportNodeSetPrefixId( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId, FLMUINT uiPrefixId) = 0; virtual RCODE FLMAPI reportNodeSetMetaValue( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT64 ui64MetaValue) = 0; virtual RCODE FLMAPI reportSetNextNodeId( eRestoreAction * peAction, FLMUINT64 ui64TransId, FLMUINT uiCollection, FLMUINT64 ui64NextNodeId) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_RestoreClient : public F_Object { virtual RCODE FLMAPI openBackupSet( void) = 0; virtual RCODE FLMAPI openRflFile( // Open an RFL file FLMUINT uiFileNum) = 0; virtual RCODE FLMAPI openIncFile( // Open an incremental backup file FLMUINT uiFileNum) = 0; virtual RCODE FLMAPI read( FLMUINT uiLength, // Number of bytes to read void * pvBuffer, // Buffer to place read bytes into FLMUINT * puiBytesRead) = 0; // [out] Number of bytes read virtual RCODE FLMAPI close( void) = 0; // Close the current file virtual RCODE FLMAPI abortFile( void) = 0; // Abort processing the file // and close file handles, etc. }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_UpgradeClient : public F_Object { }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DbCopyStatus : public F_Object { virtual RCODE FLMAPI dbCopyStatus( FLMUINT64 ui64BytesToCopy, FLMUINT64 ui64BytesCopied, FLMBOOL bNewSrcFile, const char * pszSrcFileName, const char * pszDestFileName) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DbRebuildStatus : public F_Object { virtual RCODE FLMAPI reportRebuild( XFLM_REBUILD_INFO * pRebuild) = 0; virtual RCODE FLMAPI reportRebuildErr( XFLM_CORRUPT_INFO * pCorruptInfo) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DbCheckStatus : public F_Object { virtual RCODE FLMAPI reportProgress( XFLM_PROGRESS_CHECK_INFO * pProgCheck) = 0; virtual RCODE FLMAPI reportCheckErr( XFLM_CORRUPT_INFO * pCorruptInfo, FLMBOOL * pbFix) = 0; // [OUT] - If the client sets this to true, then XFlaim will // attempt to fix the problem. NOTE: It is allowable for // XFlaim to pass in NULL here!! (This means that the client // doesn't have a choice regarding XFlaim's actions.) The // client must check for NULL before attempting to assing a // value to this parameter!! }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DbRenameStatus : public F_Object { virtual RCODE FLMAPI dbRenameStatus( const char * pszSrcFileName, const char * pszDstFileName) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_IxStatus : public F_Object { virtual RCODE FLMAPI reportIndex( FLMUINT64 ui64LastDocumentId) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_DbInfo : public F_Object { virtual FLMUINT FLMAPI getNumCollections( void) = 0; virtual FLMUINT FLMAPI getNumIndexes( void) = 0; virtual FLMUINT FLMAPI getNumLogicalFiles( void) = 0; virtual FLMUINT64 FLMAPI getFileSize( void) = 0; virtual const XFLM_DB_HDR * FLMAPI getDbHdr( void) = 0; virtual void FLMAPI getAvailBlockStats( FLMUINT64 * pui64BytesUsed, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors) = 0; virtual void FLMAPI getLFHBlockStats( FLMUINT64 * pui64BytesUsed, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors) = 0; virtual void FLMAPI getBTreeInfo( FLMUINT uiNthLogicalFile, FLMUINT * puiLfNum, eLFileType * peLfType, FLMUINT * puiRootBlkAddress, FLMUINT * puiNumLevels) = 0; virtual void FLMAPI getBTreeBlockStats( FLMUINT uiNthLogicalFile, FLMUINT uiLevel, FLMUINT64 * pui64KeyCount, FLMUINT64 * pui64BytesUsed, FLMUINT64 * pui64ElementCount, FLMUINT64 * pui64ContElementCount, FLMUINT64 * pui64ContElmBytes, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_QueryStatus : public F_Object { virtual RCODE FLMAPI queryStatus( XFLM_OPT_INFO * pOptInfo) = 0; virtual RCODE FLMAPI newSource( XFLM_OPT_INFO * pOptInfo) = 0; virtual RCODE FLMAPI resultSetStatus( FLMUINT64 ui64TotalDocsRead, FLMUINT64 ui64TotalDocsPassed, FLMBOOL bCanRetrieveDocs) = 0; virtual RCODE FLMAPI resultSetComplete( FLMUINT64 ui64TotalDocsRead, FLMUINT64 ui64TotalDocsPassed) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_QueryValidator : public F_Object { virtual RCODE FLMAPI validateNode( IF_Db * pDb, IF_DOMNode * pNode, FLMBOOL * pbPassed) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_QueryValFunc : public F_Object { // NOTE: pDynaBuf should only be used when returning XFLM_UTF8_VAL or // XFLM_BINARY_VAL. pvVal should be used for all other types. // If there are no more values, return NE_XFLM_EOF_HIT or // NE_XFLM_BOF_HIT, depending on eValueToGet. virtual RCODE FLMAPI getValue( IF_Db * pDb, IF_DOMNode * pContextNode, ValIterator eValueToGet, eValTypes * peValType, FLMBOOL * pbLastValue, void * pvVal, F_DynaBuf * pDynaBuf = NULL) = 0; virtual RCODE FLMAPI cloneSelf( IF_QueryValFunc ** ppNewObj) = 0; }; /*============================================================================ Desc: Abstract base class which provides the interface that XFLAIM uses to allow an application to embed a node source inside an XPATH component. ============================================================================*/ flminterface IF_QueryNodeSource : public F_Object { public: // Method that returns the search cost of this object in providing // nodes for a query. virtual RCODE FLMAPI searchCost( IF_Db * pDb, FLMBOOL bNotted, FLMUINT * puiCost, FLMBOOL * pbMustScan) = 0; // Position to and return the first node that satisfies the predicate. virtual RCODE FLMAPI getFirst( IF_Db * pDb, IF_DOMNode * pContextNode, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, // milliseconds IF_QueryStatus * pQueryStatus) = 0; // Position to and return the last node that satisfies the predicate. virtual RCODE FLMAPI getLast( IF_Db * pDb, IF_DOMNode * pContextNode, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, // milliseconds IF_QueryStatus * pQueryStatus) = 0; // Position to and return the next node that satisfies the predicate. // If no prior positioning has been done, // position to and return the first node. virtual RCODE FLMAPI getNext( IF_Db * pDb, IF_DOMNode * pContextNode, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, // milliseconds IF_QueryStatus * pQueryStatus) = 0; // Position to and return the previous node that satisfies the predicate. // If no prior positioning has been done, // position to and return the last node. virtual RCODE FLMAPI getPrev( IF_Db * pDb, IF_DOMNode * pContextNode, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, // milliseconds IF_QueryStatus * pQueryStatus) = 0; // Return index being used, 0 if none. virtual RCODE FLMAPI getIndex( IF_Db * pDb, FLMUINT * puiIndex, FLMBOOL * pbHaveMultiple) = 0; virtual RCODE FLMAPI getOptInfoCount( IF_Db * pDb, FLMUINT * puiOptInfoCount) = 0; virtual RCODE FLMAPI getOptInfo( IF_Db * pDb, XFLM_OPT_INFO * pOptInfoArray, FLMUINT uiNumOptInfoStructsToGet) = 0; // Return a copy of the object. Result set should be // emptied, score should be unset - only the predicate // should be preserved. // Returns NULL if the copy fails. virtual RCODE copy( IF_QueryNodeSource ** ppNodeSourceCopy) = 0; virtual void releaseResources( void) = 0; }; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_OperandComparer : public F_Object { virtual RCODE FLMAPI compare( IF_PosIStream * pLeftOperandStream, IF_PosIStream * pRightOperandStream, FLMINT * piCompare) = 0; }; #define XFLM_MAX_SORT_KEYS 32 /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_Query : public F_Object { virtual RCODE FLMAPI setLanguage( FLMUINT uiLanguage) = 0; virtual RCODE FLMAPI setCollection( FLMUINT uiCollection) = 0; virtual RCODE FLMAPI setupQueryExpr( IF_Db * pDb, const FLMUNICODE * puzQuery) = 0; virtual RCODE FLMAPI setupQueryExpr( IF_Db * pDb, const char * pszQueryExpr) = 0; virtual RCODE FLMAPI copyCriteria( IF_Query * pSrcQuery) = 0; virtual RCODE FLMAPI addXPathComponent( eXPathAxisTypes eXPathAxis, eDomNodeType eNodeType, FLMUINT uiNameId, IF_QueryNodeSource * pNodeSource = NULL) = 0; virtual RCODE FLMAPI addOperator( eQueryOperators eOperator, FLMUINT uiCompareRules = 0, IF_OperandComparer * pOpComparer = NULL) = 0; virtual RCODE FLMAPI addUnicodeValue( const FLMUNICODE * puzVal) = 0; virtual RCODE FLMAPI addUTF8Value( const char * pszVal, FLMUINT uiUTF8Len = 0) = 0; virtual RCODE FLMAPI addBinaryValue( const void * pvVal, FLMUINT uiValLen) = 0; virtual RCODE FLMAPI addUINTValue( FLMUINT uiVal) = 0; virtual RCODE FLMAPI addINTValue( FLMINT iVal) = 0; virtual RCODE FLMAPI addUINT64Value( FLMUINT64 ui64Val) = 0; virtual RCODE FLMAPI addINT64Value( FLMINT64 i64Val) = 0; virtual RCODE FLMAPI addBoolean( FLMBOOL bVal, FLMBOOL bUnknown = FALSE) = 0; virtual RCODE FLMAPI addFunction( eQueryFunctions eFunction) = 0; virtual RCODE FLMAPI addFunction( IF_QueryValFunc * pFuncObj, FLMBOOL bHasXPathExpr) = 0; virtual RCODE FLMAPI getFirst( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0) = 0; // milliseconds virtual RCODE FLMAPI getLast( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0) = 0; // milliseconds virtual RCODE FLMAPI getNext( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0, // milliseconds FLMUINT uiNumToSkip = 0, FLMUINT * puiNumSkipped = NULL) = 0; virtual RCODE FLMAPI getPrev( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit = 0, // milliseconds FLMUINT uiNumToSkip = 0, FLMUINT * puiNumSkipped = NULL) = 0; virtual RCODE FLMAPI getCurrent( IF_Db * pDb, IF_DOMNode ** ppNode) = 0; virtual void FLMAPI resetQuery( void) = 0; virtual RCODE FLMAPI getStatsAndOptInfo( FLMUINT * puiNumOptInfos, XFLM_OPT_INFO ** ppOptInfo) = 0; virtual void FLMAPI freeStatsAndOptInfo( XFLM_OPT_INFO ** ppOptInfo) = 0; virtual void FLMAPI setDupHandling( FLMBOOL bRemoveDups) = 0; virtual RCODE FLMAPI setIndex( FLMUINT uiIndex) = 0; virtual RCODE FLMAPI getIndex( IF_Db * pDb, FLMUINT * puiIndex, FLMBOOL * pbHaveMultiple) = 0; virtual RCODE FLMAPI addSortKey( void * pvSortKeyContext, FLMBOOL bChildToContext, FLMBOOL bElement, FLMUINT uiNameId, FLMUINT uiCompareRules, FLMUINT uiLimit, FLMUINT uiKeyComponent, FLMBOOL bSortDescending, FLMBOOL bSortMissingHigh, void ** ppvContext) = 0; virtual RCODE FLMAPI enablePositioning( void) = 0; virtual RCODE FLMAPI positionTo( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, FLMUINT uiPosition) = 0; virtual RCODE FLMAPI positionTo( IF_Db * pDb, IF_DOMNode ** ppNode, FLMUINT uiTimeLimit, IF_DataVector * pSearchKey, FLMUINT uiFlags) = 0; virtual RCODE FLMAPI getPosition( IF_Db * pDb, FLMUINT * puiPosition) = 0; virtual RCODE FLMAPI buildResultSet( IF_Db * pDb, FLMUINT uiTimeLimit) = 0; virtual void FLMAPI stopBuildingResultSet( void) = 0; virtual RCODE FLMAPI getCounts( IF_Db * pDb, FLMUINT uiTimeLimit, FLMBOOL bPartialCountOk, FLMUINT * puiReadCount, FLMUINT * puiPassedCount, FLMUINT * puiPositionableToCount, FLMBOOL * pbDoneBuildingResultSet = NULL) = 0; virtual void FLMAPI enableResultSetEncryption( void) = 0; virtual void FLMAPI setQueryStatusObject( IF_QueryStatus * pQueryStatus) = 0; virtual void FLMAPI setQueryValidatorObject( IF_QueryValidator * pQueryValidator) = 0; }; typedef struct XFLM_NODE_INFO_ITEM { FLMUINT64 ui64Count; FLMUINT64 ui64Bytes; } XFLM_NODE_INFO_ITEM; typedef struct XFLM_NODE_INFO { XFLM_NODE_INFO_ITEM headerSize; XFLM_NODE_INFO_ITEM nodeAndDataType; XFLM_NODE_INFO_ITEM flags; XFLM_NODE_INFO_ITEM nameId; XFLM_NODE_INFO_ITEM prefixId; XFLM_NODE_INFO_ITEM baseId; XFLM_NODE_INFO_ITEM documentId; XFLM_NODE_INFO_ITEM parentId; XFLM_NODE_INFO_ITEM prevSibId; XFLM_NODE_INFO_ITEM nextSibId; XFLM_NODE_INFO_ITEM firstChildId; XFLM_NODE_INFO_ITEM lastChildId; XFLM_NODE_INFO_ITEM childElmCount; XFLM_NODE_INFO_ITEM dataChildCount; XFLM_NODE_INFO_ITEM attrCount; XFLM_NODE_INFO_ITEM attrBaseId; XFLM_NODE_INFO_ITEM attrFlags; XFLM_NODE_INFO_ITEM attrPayloadLen; XFLM_NODE_INFO_ITEM annotationId; XFLM_NODE_INFO_ITEM metaValue; XFLM_NODE_INFO_ITEM encDefId; XFLM_NODE_INFO_ITEM unencDataLen; XFLM_NODE_INFO_ITEM childElmNameId; XFLM_NODE_INFO_ITEM childElmNodeId; XFLM_NODE_INFO_ITEM encIV; XFLM_NODE_INFO_ITEM encPadding; // Total overhead - sum of all of the above types of overhead. XFLM_NODE_INFO_ITEM totalOverhead; // Data totals XFLM_NODE_INFO_ITEM dataNodata; XFLM_NODE_INFO_ITEM dataString; XFLM_NODE_INFO_ITEM dataNumeric; XFLM_NODE_INFO_ITEM dataBinary; // Summary - contains both overhead and data for each type of node XFLM_NODE_INFO_ITEM attributeNode; XFLM_NODE_INFO_ITEM elementNode; XFLM_NODE_INFO_ITEM dataNode; XFLM_NODE_INFO_ITEM commentNode; XFLM_NODE_INFO_ITEM otherNode; } XFLM_NODE_INFO; /**************************************************************************** Desc: Node Info. Gatherer ****************************************************************************/ flminterface IF_NodeInfo : public F_Object { virtual void FLMAPI clearNodeInfo( void) = 0; virtual RCODE FLMAPI addNodeInfo( IF_Db * pDb, IF_DOMNode * pNode, FLMBOOL bDoSubTree, FLMBOOL bDoSelf = TRUE) = 0; virtual FLMUINT64 FLMAPI getTotalNodeCount( void) = 0; virtual void FLMAPI getNodeInfo( XFLM_NODE_INFO * pNodeInfo) = 0; }; /**************************************************************************** Desc: Types of information that can be gathered about a B-Tree. ****************************************************************************/ typedef struct XFLM_BTREE_LEVEL_INFO { FLMUINT64 ui64BlockCount; FLMUINT64 ui64BlockLength; FLMUINT64 ui64BlockFreeSpace; FLMUINT64 ui64ElmOffsetOverhead; FLMUINT64 ui64ElmCount; FLMUINT64 ui64ContElmCount; FLMUINT64 ui64ElmFlagOvhd; FLMUINT64 ui64ElmKeyLengthOvhd; FLMUINT64 ui64ElmCountsOvhd; FLMUINT64 ui64ElmChildAddrsOvhd; FLMUINT64 ui64ElmDataLenOvhd; FLMUINT64 ui64ElmOADataLenOvhd; FLMUINT64 ui64ElmKeyLength; FLMUINT64 ui64ElmDataLength; // The following three are how ui64ElmKeyLength is subdivided. // They are only applicable to index keys. FLMUINT64 ui64KeyDataSize; FLMUINT64 ui64KeyIdSize; FLMUINT64 ui64KeyComponentLengthsSize; // Data only blocks FLMUINT64 ui64DataOnlyBlockCount; FLMUINT64 ui64DataOnlyBlockLength; FLMUINT64 ui64DataOnlyBlockFreeSpace; } XFLM_BTREE_LEVEL_INFO; /**************************************************************************** Desc: ****************************************************************************/ flminterface IF_BTreeInfoStatus : public F_Object { virtual RCODE FLMAPI infoStatus( FLMUINT uiCurrLfNum, FLMBOOL bIsCollection, char * pszCurrLfName, FLMUINT uiCurrLevel, FLMUINT64 ui64CurrLfBlockCount, FLMUINT64 ui64CurrLevelBlockCount, FLMUINT64 ui64TotalBlockCount) = 0; }; /**************************************************************************** Desc: BTree Info. Gatherer ****************************************************************************/ flminterface IF_BTreeInfo : public F_Object { virtual void FLMAPI clearBTreeInfo( void) = 0; virtual RCODE FLMAPI collectIndexInfo( IF_Db * pDb, FLMUINT uiIndexNum, IF_BTreeInfoStatus * pInfoStatus) = 0; virtual RCODE FLMAPI collectCollectionInfo( IF_Db * pDb, FLMUINT uiCollectionNum, IF_BTreeInfoStatus * pInfoStatus) = 0; virtual FLMUINT FLMAPI getNumIndexes( void) = 0; virtual FLMUINT FLMAPI getNumCollections( void) = 0; virtual FLMBOOL FLMAPI getIndexInfo( FLMUINT uiNthIndex, FLMUINT * puiIndexNum, char ** ppszIndexName, FLMUINT * puiNumLevels) = 0; virtual FLMBOOL FLMAPI getCollectionInfo( FLMUINT uiNthCollection, FLMUINT * puiCollectionNum, char ** ppszCollectionName, FLMUINT * puiNumLevels) = 0; virtual FLMBOOL FLMAPI getIndexLevelInfo( FLMUINT uiNthIndex, FLMUINT uiBTreeLevel, XFLM_BTREE_LEVEL_INFO * pLevelInfo) = 0; virtual FLMBOOL FLMAPI getCollectionLevelInfo( FLMUINT uiNthCollection, FLMUINT uiBTreeLevel, XFLM_BTREE_LEVEL_INFO * pLevelInfo) = 0; }; /**************************************************************************** Desc: Status and return codes ****************************************************************************/ // IMPORTANT NOTE: If changes are made to the RCODEs, please be sure to make // corresponding changes in csharp/xflaim/RCODE.cs and java/xflaim/RCODE.java. #define NE_XFLM_NOT_IMPLEMENTED NE_FLM_NOT_IMPLEMENTED #define NE_XFLM_MEM NE_FLM_MEM #define NE_XFLM_INVALID_PARM NE_FLM_INVALID_PARM #define NE_XFLM_NOT_FOUND NE_FLM_NOT_FOUND #define NE_XFLM_EXISTS NE_FLM_EXISTS #define NE_XFLM_FAILURE NE_FLM_FAILURE #define NE_XFLM_BOF_HIT NE_FLM_BOF_HIT #define NE_XFLM_EOF_HIT NE_FLM_EOF_HIT #define NE_XFLM_CONV_DEST_OVERFLOW NE_FLM_CONV_DEST_OVERFLOW #define NE_XFLM_CONV_ILLEGAL NE_FLM_CONV_ILLEGAL #define NE_XFLM_CONV_NUM_OVERFLOW NE_FLM_CONV_NUM_OVERFLOW #define NE_XFLM_SYNTAX NE_FLM_SYNTAX #define NE_XFLM_ILLEGAL_OP NE_FLM_ILLEGAL_OP #define NE_XFLM_BAD_SEN NE_FLM_BAD_SEN #define NE_XFLM_COULD_NOT_START_THREAD NE_FLM_COULD_NOT_START_THREAD #define NE_XFLM_BAD_BASE64_ENCODING NE_FLM_BAD_BASE64_ENCODING #define NE_XFLM_STREAM_EXISTS NE_FLM_STREAM_EXISTS #define NE_XFLM_MULTIPLE_MATCHES NE_FLM_MULTIPLE_MATCHES #define NE_XFLM_NOT_UNIQUE NE_FLM_NOT_UNIQUE #define NE_XFLM_BTREE_ERROR NE_FLM_BTREE_ERROR #define NE_XFLM_BTREE_KEY_SIZE NE_FLM_BTREE_KEY_SIZE #define NE_XFLM_BTREE_FULL NE_FLM_BTREE_FULL #define NE_XFLM_BTREE_BAD_STATE NE_FLM_BTREE_BAD_STATE #define NE_XFLM_COULD_NOT_CREATE_MUTEX NE_FLM_COULD_NOT_CREATE_MUTEX #define NE_XFLM_DATA_ERROR NE_FLM_DATA_ERROR #define NE_XFLM_IO_PATH_NOT_FOUND NE_FLM_IO_PATH_NOT_FOUND #define NE_XFLM_IO_END_OF_FILE NE_FLM_IO_END_OF_FILE #define NE_XFLM_IO_NO_MORE_FILES NE_FLM_IO_NO_MORE_FILES #define NE_XFLM_COULD_NOT_CREATE_SEMAPHORE NE_FLM_COULD_NOT_CREATE_SEMAPHORE #define NE_XFLM_BAD_UTF8 NE_FLM_BAD_UTF8 #define NE_XFLM_ERROR_WAITING_ON_SEMPAHORE NE_FLM_ERROR_WAITING_ON_SEMPAHORE #define NE_XFLM_BAD_PLATFORM_FORMAT NE_FLM_BAD_PLATFORM_FORMAT /**************************************************************************** Desc: General XFLAIM errors ****************************************************************************/ // IMPORTANT NOTE: If changes are made to the RCODEs, please be sure to make // corresponding changes in csharp/xflaim/RCODE.cs and java/xflaim/RCODE.java. #define NE_XFLM_OK NE_FLM_OK #define NE_XFLM_USER_ABORT 0xD100 // User or application aborted (canceled) operation. #define NE_XFLM_BAD_PREFIX 0xD101 // Invalid XLM namespace prefix specified. Either a prefix name or number that was specified was not defined. #define NE_XFLM_ATTRIBUTE_PURGED 0xD102 // XML attribute cannot be used - it is being deleted from the database. #define NE_XFLM_BAD_COLLECTION 0xD103 // Invalid collection number specified. Collection is not defined. #define NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT 0xD104 // Request to lock the database timed out. #define NE_XFLM_ILLEGAL_DATA_COMPONENT 0xD105 // Cannot use ELM_ROOT_TAG as a data component in an index. #define NE_XFLM_MUST_INDEX_ON_PRESENCE 0xD106 // When using ELM_ROOT_TAG in an index component, must specify PRESENCE indexing only. #define NE_XFLM_BAD_IX 0xD107 // Invalid index number specified. Index is not defined. #define NE_XFLM_BACKUP_ACTIVE 0xD108 // Operation could not be performed because a backup is currently in progress. #define NE_XFLM_SERIAL_NUM_MISMATCH 0xD109 // Serial number on backup file does not match the serial number that is expected. #define NE_XFLM_BAD_RFL_DB_SERIAL_NUM 0xD10A // Bad database serial number in roll-forward log file header. #define NE_XFLM_BAD_RFL_FILE_NUMBER 0xD10B // Bad roll-forward log file number in roll-forward log file header. #define NE_XFLM_CANNOT_DEL_ELEMENT 0xD10C // Cannot delete an XML element definition in the dictionary because it is in use. #define NE_XFLM_CANNOT_MOD_DATA_TYPE 0xD10D // Cannot modify the data type for an XML element or attribute definition in the dictionary. #define NE_XFLM_CANNOT_INDEX_DATA_TYPE 0xD10E // Data type of XML element or attribute is not one that can be indexed. #define NE_XFLM_BAD_ELEMENT_NUM 0xD10F // Bad element number specified - element not defined in dictionary. #define NE_XFLM_BAD_ATTRIBUTE_NUM 0xD110 // Bad attribute number specified - attribute not defined in dictionary. #define NE_XFLM_BAD_ENCDEF_NUM 0xD111 // Bad encryption number specified - encryption definition not defined in dictionary. #define NE_XFLM_INVALID_FILE_SEQUENCE 0xD112 // Incremental backup file number provided during a restore is invalid. #define NE_XFLM_DUPLICATE_ELEMENT_NUM 0xD113 // Element number specified in element definition is already in use. #define NE_XFLM_ILLEGAL_TRANS_TYPE 0xD114 // Illegal transaction type specified for transaction begin operation. #define NE_XFLM_UNSUPPORTED_VERSION 0xD115 // Version of database found in database header is not supported. #define NE_XFLM_ILLEGAL_TRANS_OP 0xD116 // Illegal operation for transaction type. #define NE_XFLM_INCOMPLETE_LOG 0xD117 // Incomplete rollback log. #define NE_XFLM_ILLEGAL_INDEX_DEF 0xD118 // Index definition document is illegal - does not conform to the expected form of an index definition document. #define NE_XFLM_ILLEGAL_INDEX_ON 0xD119 // The "IndexOn" attribute of an index definition has an illegal value. #define NE_XFLM_ILLEGAL_STATE_CHANGE 0xD11A // Attempted an illegal state change on an element or attribute definition. #define NE_XFLM_BAD_RFL_SERIAL_NUM 0xD11B // Serial number in roll-forward log file header does not match expected serial number. #define NE_XFLM_NEWER_FLAIM 0xD11C // Running old code on a newer version of database. Newer code must be used. #define NE_XFLM_CANNOT_MOD_ELEMENT_STATE 0xD11D // Attempted to change state of a predefined element definition. #define NE_XFLM_CANNOT_MOD_ATTRIBUTE_STATE 0xD11E // Attempted to change state of a predefined attribute definition. #define NE_XFLM_NO_MORE_ELEMENT_NUMS 0xD11F // The highest element number has already been used, cannot create more element definitions. #define NE_XFLM_NO_TRANS_ACTIVE 0xD120 // Operation must be performed inside a database transaction. #define NE_XFLM_NOT_FLAIM 0xD121 // The file specified is not a FLAIM database. #define NE_XFLM_OLD_VIEW 0xD122 // Unable to maintain read transaction's view of the database. #define NE_XFLM_SHARED_LOCK 0xD123 // Attempted to perform an operation on the database that requires exclusive access, but cannot because there is a shared lock. #define NE_XFLM_TRANS_ACTIVE 0xD124 // Operation cannot be performed while a transaction is active. #define NE_XFLM_RFL_TRANS_GAP 0xD125 // A gap was found in the transaction sequence in the roll-forward log. #define NE_XFLM_BAD_COLLATED_KEY 0xD126 // Something in collated key is bad. #define NE_XFLM_MUST_DELETE_INDEXES 0xD127 // Attempting to delete a collection that has indexes defined for it. Associated indexes must be deleted before the collection can be deleted. #define NE_XFLM_RFL_INCOMPLETE 0xD128 // Roll-forward log file is incomplete. #define NE_XFLM_CANNOT_RESTORE_RFL_FILES 0xD129 // Cannot restore roll-forward log files - not using multiple roll-forward log files. #define NE_XFLM_INCONSISTENT_BACKUP 0xD12A // A problem (corruption), etc. was detected in a backup set. #define NE_XFLM_BLOCK_CRC 0xD12B // CRC for database block was invalid. May indicate problems in reading from or writing to disk. #define NE_XFLM_ABORT_TRANS 0xD12C // Attempted operation after a critical error - transaction should be aborted. #define NE_XFLM_NOT_RFL 0xD12D // File was not a roll-forward log file as expected. #define NE_XFLM_BAD_RFL_PACKET 0xD12E // Roll-forward log file packet was bad. #define NE_XFLM_DATA_PATH_MISMATCH 0xD12F // Bad data path specified to open database. Does not match data path specified for prior opens of the database. #define NE_XFLM_MUST_CLOSE_DATABASE 0xD130 // Database must be closed due to a critical error. #define NE_XFLM_INVALID_ENCKEY_CRC 0xD131 // Encryption key CRC could not be verified. #define NE_XFLM_HDR_CRC 0xD132 // Database header has a bad CRC. #define NE_XFLM_NO_NAME_TABLE 0xD133 // No name table was set up for the database. #define NE_XFLM_UNALLOWED_UPGRADE 0xD134 // Cannot upgrade database from one version to another. #define NE_XFLM_DUPLICATE_ATTRIBUTE_NUM 0xD135 // Attribute number specified in attribute definition is already in use. #define NE_XFLM_DUPLICATE_INDEX_NUM 0xD136 // Index number specified in index definition is already in use. #define NE_XFLM_DUPLICATE_COLLECTION_NUM 0xD137 // Collection number specified in collection definition is already in use. #define NE_XFLM_DUPLICATE_ELEMENT_NAME 0xD138 // Element name+namespace specified in element definition is already in use. #define NE_XFLM_DUPLICATE_ATTRIBUTE_NAME 0xD139 // Attribute name+namespace specified in attribute definition is already in use. #define NE_XFLM_DUPLICATE_INDEX_NAME 0xD13A // Index name specified in index definition is already in use. #define NE_XFLM_DUPLICATE_COLLECTION_NAME 0xD13B // Collection name specified in collection definition is already in use. #define NE_XFLM_ELEMENT_PURGED 0xD13C // XML element cannot be used - it is deleted from the database. #define NE_XFLM_TOO_MANY_OPEN_DATABASES 0xD13D // Too many open databases, cannot open another one. #define NE_XFLM_DATABASE_OPEN 0xD13E // Operation cannot be performed because the database is currently open. #define NE_XFLM_CACHE_ERROR 0xD13F // Cached database block has been compromised while in cache. #define NE_XFLM_DB_FULL 0xD140 // Database is full, cannot create more blocks. #define NE_XFLM_QUERY_SYNTAX 0xD141 // Query expression had improper syntax. #define NE_XFLM_INDEX_OFFLINE 0xD142 // Index is offline, cannot be used in a query. #define NE_XFLM_RFL_DISK_FULL 0xD143 // Disk which contains roll-forward log is full. #define NE_XFLM_MUST_WAIT_CHECKPOINT 0xD144 // Must wait for a checkpoint before starting transaction - due to disk problems - usually in disk containing roll-forward log files. #define NE_XFLM_MISSING_ENC_ALGORITHM 0xD145 // Encryption definition is missing an encryption algorithm. #define NE_XFLM_INVALID_ENC_ALGORITHM 0xD146 // Invalid encryption algorithm specified in encryption definition. #define NE_XFLM_INVALID_ENC_KEY_SIZE 0xD147 // Invalid key size specified in encryption definition. #define NE_XFLM_ILLEGAL_DATA_TYPE 0xD148 // Data type specified for XML element or attribute definition is illegal. #define NE_XFLM_ILLEGAL_STATE 0xD149 // State specified for index definition or XML element or attribute definition is illegal. #define NE_XFLM_ILLEGAL_ELEMENT_NAME 0xD14A // XML element name specified in element definition is illegal. #define NE_XFLM_ILLEGAL_ATTRIBUTE_NAME 0xD14B // XML attribute name specified in attribute definition is illegal. #define NE_XFLM_ILLEGAL_COLLECTION_NAME 0xD14C // Collection name specified in collection definition is illegal. #define NE_XFLM_ILLEGAL_INDEX_NAME 0xD14D // Index name specified is illegal #define NE_XFLM_ILLEGAL_ELEMENT_NUMBER 0xD14E // Element number specified in element definition or index definition is illegal. #define NE_XFLM_ILLEGAL_ATTRIBUTE_NUMBER 0xD14F // Attribute number specified in attribute definition or index definition is illegal. #define NE_XFLM_ILLEGAL_COLLECTION_NUMBER 0xD150 // Collection number specified in collection definition or index definition is illegal. #define NE_XFLM_ILLEGAL_INDEX_NUMBER 0xD151 // Index number specified in index definition is illegal. #define NE_XFLM_ILLEGAL_ENCDEF_NUMBER 0xD152 // Encryption definition number specified in encryption definition is illegal. #define NE_XFLM_COLLECTION_NAME_MISMATCH 0xD153 // Collection name and number specified in index definition do not correspond to each other. #define NE_XFLM_ELEMENT_NAME_MISMATCH 0xD154 // Element name+namespace and number specified in index definition do not correspond to each other. #define NE_XFLM_ATTRIBUTE_NAME_MISMATCH 0xD155 // Attribute name+namespace and number specified in index definition do not correspond to each other. #define NE_XFLM_INVALID_COMPARE_RULE 0xD156 // Invalid comparison rule specified in index definition. #define NE_XFLM_DUPLICATE_KEY_COMPONENT 0xD157 // Duplicate key component number specified in index definition. #define NE_XFLM_DUPLICATE_DATA_COMPONENT 0xD158 // Duplicate data component number specified in index definition. #define NE_XFLM_MISSING_KEY_COMPONENT 0xD159 // Index definition is missing a key component. #define NE_XFLM_MISSING_DATA_COMPONENT 0xD15A // Index definition is missing a data component. #define NE_XFLM_INVALID_INDEX_OPTION 0xD15B // Invalid index option specified on index definition. #define NE_XFLM_NO_MORE_ATTRIBUTE_NUMS 0xD15C // The highest attribute number has already been used, cannot create more. #define NE_XFLM_MISSING_ELEMENT_NAME 0xD15D // Missing element name in XML element definition. #define NE_XFLM_MISSING_ATTRIBUTE_NAME 0xD15E // Missing attribute name in XML attribute definition. #define NE_XFLM_MISSING_ELEMENT_NUMBER 0xD15F // Missing element number in XML element definition. #define NE_XFLM_MISSING_ATTRIBUTE_NUMBER 0xD160 // Missing attribute number from XML attribute definition. #define NE_XFLM_MISSING_INDEX_NAME 0xD161 // Missing index name in index definition. #define NE_XFLM_MISSING_INDEX_NUMBER 0xD162 // Missing index number in index definition. #define NE_XFLM_MISSING_COLLECTION_NAME 0xD163 // Missing collection name in collection definition. #define NE_XFLM_MISSING_COLLECTION_NUMBER 0xD164 // Missing collection number in collection definition. #define NE_XFLM_MISSING_ENCDEF_NAME 0xD165 // Missing encryption definition name in encryption definition. #define NE_XFLM_MISSING_ENCDEF_NUMBER 0xD166 // Missing encryption definition number in encryption definition. #define NE_XFLM_NO_MORE_INDEX_NUMS 0xD167 // The highest index number has already been used, cannot create more. #define NE_XFLM_NO_MORE_COLLECTION_NUMS 0xD168 // The highest collection number has already been used, cannot create more. #define NE_XFLM_CANNOT_DEL_ATTRIBUTE 0xD169 // Cannot delete an XML attribute definition because it is in use. #define NE_XFLM_TOO_MANY_PENDING_NODES 0xD16A // Too many documents in the pending document list. #define NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG 0xD16B // ELM_ROOT_TAG, if used, must be the sole root component of an index definition. #define NE_XFLM_DUP_SIBLING_IX_COMPONENTS 0xD16C // Sibling components in an index definition cannot have the same XML element or attribute number. #define NE_XFLM_RFL_FILE_NOT_FOUND 0xD16D // Could not open a roll-forward log file - was not found in the roll-forward log directory. #define NE_XFLM_ILLEGAL_KEY_COMPONENT_NUM 0xD16E // Key component of zero in index definition is not allowed. #define NE_XFLM_ILLEGAL_DATA_COMPONENT_NUM 0xD16F // Data component of zero in index definition is not allowed. #define NE_XFLM_ILLEGAL_PREFIX_NUMBER 0xD170 // Prefix number specified in prefix definition is illegal. #define NE_XFLM_MISSING_PREFIX_NAME 0xD171 // Missing prefix name in prefix definition. #define NE_XFLM_MISSING_PREFIX_NUMBER 0xD172 // Missing prefix number in prefix definition. #define NE_XFLM_UNDEFINED_ELEMENT_NAME 0xD173 // XML element name+namespace that was specified in index definition or XML document is not defined in dictionary. #define NE_XFLM_UNDEFINED_ATTRIBUTE_NAME 0xD174 // XML attribute name+namespace that was specified in index definition or XML document is not defined in dictionary. #define NE_XFLM_DUPLICATE_PREFIX_NAME 0xD175 // Prefix name specified in prefix definition is already in use. #define NE_XFLM_NAMESPACE_NOT_ALLOWED 0xD176 // Cannot define a namespace for XML attributes whose name begins with "xmlns:" or that is equal to "xmlns" #define NE_XFLM_INVALID_NAMESPACE_DECL 0xD177 // Name for namespace declaration attribute must be "xmlns" or begin with "xmlns:" #define NE_XFLM_ILLEGAL_NAMESPACE_DECL_DATATYPE 0xD178 // Data type for XML attributes that are namespace declarations must be text. #define NE_XFLM_NO_MORE_PREFIX_NUMS 0xD179 // The highest prefix number has already been used, cannot create more. #define NE_XFLM_NO_MORE_ENCDEF_NUMS 0xD17A // The highest encryption definition number has already been used, cannot create more. #define NE_XFLM_COLLECTION_OFFLINE 0xD17B // Collection is encrypted, cannot be accessed while in operating in limited mode. #define NE_XFLM_DELETE_NOT_ALLOWED 0xD17C // Item cannot be deleted. #define NE_XFLM_RESET_NEEDED 0xD17D // Used during check operations to indicate we need to reset the view. NOTE: This is an internal error code and should not be documented. #define NE_XFLM_ILLEGAL_REQUIRED_VALUE 0xD17E // An illegal value was specified for the "Required" attribute in an index definition. #define NE_XFLM_ILLEGAL_INDEX_COMPONENT 0xD17F // A leaf index component in an index definition was not marked as a data component or key component. #define NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE 0xD180 // Illegal value for the "UniqueSubElements" attribute in an element definition. #define NE_XFLM_DATA_TYPE_MUST_BE_NO_DATA 0xD181 // Data type for an element definition with UniqueSubElements="yes" must be nodata. #define NE_XFLM_CANNOT_SET_REQUIRED 0xD182 // Cannot set the "Required" attribute on a non-key index component in index definition. #define NE_XFLM_CANNOT_SET_LIMIT 0xD183 // Cannot set the "Limit" attribute on a non-key index component in index definition. #define NE_XFLM_CANNOT_SET_INDEX_ON 0xD184 // Cannot set the "IndexOn" attribute on a non-key index component in index definition. #define NE_XFLM_CANNOT_SET_COMPARE_RULES 0xD185 // Cannot set the "CompareRules" on a non-key index component in index definition. #define NE_XFLM_INPUT_PENDING 0xD186 // Attempt to set a value while an input stream is still open. #define NE_XFLM_INVALID_NODE_TYPE 0xD187 // Bad node type #define NE_XFLM_INVALID_CHILD_ELM_NODE_ID 0xD188 // Attempt to insert a unique child element that has a lower node ID than the parent element #define NE_XFLM_RFL_END 0xD189 // Hit the end of the RFL #define NE_XFLM_ILLEGAL_FLAG 0xD18A // Illegal flag passed to getChildElement method. Must be zero for elements that can have non-unique child elements. #define NE_XFLM_TIMEOUT 0xD18B // Operation timed out. #define NE_XFLM_CONV_BAD_DIGIT 0xD18C // Non-numeric digit found in text to numeric conversion. #define NE_XFLM_CONV_NULL_SRC 0xD18D // Data source cannot be NULL when doing data conversion. #define NE_XFLM_CONV_NUM_UNDERFLOW 0xD18E // Numeric underflow (< lower bound) converting to numeric type. #define NE_XFLM_UNSUPPORTED_FEATURE 0xD18F // Attempting to use a feature for which full support has been disabled. #define NE_XFLM_FILE_EXISTS 0xD190 // Attempt to create a database, but the file already exists. #define NE_XFLM_BUFFER_OVERFLOW 0xD191 // Buffer overflow. #define NE_XFLM_INVALID_XML 0xD192 // Invalid XML encountered while parsing document. #define NE_XFLM_BAD_DATA_TYPE 0xD193 // Attempt to set/get data on an XML element or attribute using a data type that is incompatible with the data type specified in the dictionary. #define NE_XFLM_READ_ONLY 0xD194 // Item is read-only and cannot be updated. #define NE_XFLM_KEY_OVERFLOW 0xD195 // Generated index key too large. #define NE_XFLM_UNEXPECTED_END_OF_INPUT 0xD196 // Encountered unexpected end of input when parsing XPATH expression. /**************************************************************************** Desc: DOM Errors ****************************************************************************/ // IMPORTANT NOTE: If changes are made to the RCODEs, please be sure to make // corresponding changes in csharp/xflaim/RCODE.cs and java/xflaim/RCODE.java. #define NE_XFLM_DOM_HIERARCHY_REQUEST_ERR 0xD201 // Attempt to insert a DOM node somewhere it doesn't belong. #define NE_XFLM_DOM_WRONG_DOCUMENT_ERR 0xD202 // A DOM node is being used in a different document than the one that created it. #define NE_XFLM_DOM_DATA_ERROR 0xD203 // Links between DOM nodes in a document are corrupt. #define NE_XFLM_DOM_NODE_NOT_FOUND 0xD204 // The requested DOM node does not exist. #define NE_XFLM_DOM_INVALID_CHILD_TYPE 0xD205 // Attempting to insert a child DOM node whose type cannot be inserted as a child node. #define NE_XFLM_DOM_NODE_DELETED 0xD206 // DOM node being accessed has been deleted. #define NE_XFLM_DOM_DUPLICATE_ELEMENT 0xD207 // Node already has a child element with the given name id - this node's child nodes must all be unique. /**************************************************************************** Desc: Query Errors ****************************************************************************/ // IMPORTANT NOTE: If changes are made to the RCODEs, please be sure to make // corresponding changes in csharp/xflaim/RCODE.cs and java/xflaim/RCODE.java. #define NE_XFLM_Q_UNMATCHED_RPAREN 0xD301 // Query setup error: Unmatched right paren. #define NE_XFLM_Q_UNEXPECTED_LPAREN 0xD302 // Query setup error: Unexpected left paren. #define NE_XFLM_Q_UNEXPECTED_RPAREN 0xD303 // Query setup error: Unexpected right paren. #define NE_XFLM_Q_EXPECTING_OPERAND 0xD304 // Query setup error: Expecting an operand. #define NE_XFLM_Q_EXPECTING_OPERATOR 0xD305 // Query setup error: Expecting an operator. #define NE_XFLM_Q_UNEXPECTED_COMMA 0xD306 // Query setup error: Unexpected comma. #define NE_XFLM_Q_EXPECTING_LPAREN 0xD307 // Query setup error: Expecting a left paren. #define NE_XFLM_Q_UNEXPECTED_VALUE 0xD308 // Query setup error: Unexpected value. #define NE_XFLM_Q_INVALID_NUM_FUNC_ARGS 0xD309 // Query setup error: Invalid number of arguments for a function. #define NE_XFLM_Q_UNEXPECTED_XPATH_COMPONENT 0xD30A // Query setup error: Unexpected XPATH componenent. #define NE_XFLM_Q_ILLEGAL_LBRACKET 0xD30B // Query setup error: Illegal left bracket ([). #define NE_XFLM_Q_ILLEGAL_RBRACKET 0xD30C // Query setup error: Illegal right bracket (]). #define NE_XFLM_Q_ILLEGAL_OPERAND 0xD30D // Query setup error: Operand for some operator is not valid for that operator type. #define NE_XFLM_Q_ALREADY_OPTIMIZED 0xD30E // Operation is illegal, cannot change certain things after query has been optimized. #define NE_XFLM_Q_MISMATCHED_DB 0xD30F // Database handle passed in does not match database associated with query. #define NE_XFLM_Q_ILLEGAL_OPERATOR 0xD310 // Illegal operator - cannot pass this operator into the addOperator method. #define NE_XFLM_Q_ILLEGAL_COMPARE_RULES 0xD311 // Illegal combination of comparison rules passed to addOperator method. #define NE_XFLM_Q_INCOMPLETE_QUERY_EXPR 0xD312 // Query setup error: Query expression is incomplete. #define NE_XFLM_Q_NOT_POSITIONED 0xD313 // Query not positioned due to previous error, cannot call getNext, getPrev, or getCurrent #define NE_XFLM_Q_INVALID_NODE_ID_VALUE 0xD314 // Query setup error: Invalid type of value constant used for node id value comparison. #define NE_XFLM_Q_INVALID_META_DATA_TYPE 0xD315 // Query setup error: Invalid meta data type specified. #define NE_XFLM_Q_NEW_EXPR_NOT_ALLOWED 0xD316 // Query setup error: Cannot add an expression to an XPATH component after having added an expression that tests context position. #define NE_XFLM_Q_INVALID_CONTEXT_POS 0xD317 // Invalid context position value encountered - must be a positive number. #define NE_XFLM_Q_INVALID_FUNC_ARG 0xD318 // Query setup error: Parameter to user-defined functions must be a single XPATH only. #define NE_XFLM_Q_EXPECTING_RPAREN 0xD319 // Query setup error: Expecting right paren. #define NE_XFLM_Q_TOO_LATE_TO_ADD_SORT_KEYS 0xD31A // Query setup error: Cannot add sort keys after having called getFirst, getLast, getNext, or getPrev. #define NE_XFLM_Q_INVALID_SORT_KEY_COMPONENT 0xD31B // Query setup error: Invalid sort key component number specified in query. #define NE_XFLM_Q_DUPLICATE_SORT_KEY_COMPONENT 0xD31C // Query setup error: Duplicate sort key component number specified in query. #define NE_XFLM_Q_MISSING_SORT_KEY_COMPONENT 0xD31D // Query setup error: Missing sort key component number in sort keys that were specified for query. #define NE_XFLM_Q_NO_SORT_KEY_COMPONENTS_SPECIFIED 0xD31E // Query setup error: addSortKeys was called, but no sort key components were specified. #define NE_XFLM_Q_SORT_KEY_CONTEXT_MUST_BE_ELEMENT 0xD31F // Query setup error: A sort key context cannot be an XML attribute. #define NE_XFLM_Q_INVALID_ELEMENT_NUM_IN_SORT_KEYS 0xD320 // Query setup error: The XML element number specified for a sort key in a query is invalid - no element definition in the dictionary. #define NE_XFLM_Q_INVALID_ATTR_NUM_IN_SORT_KEYS 0xD321 // Query setup error: The XML attribute number specified for a sort key in a query is invalid - no attribute definition in the dictionary. #define NE_XFLM_Q_NON_POSITIONABLE_QUERY 0xD322 // Attempt is being made to position in a query that is not positionable. #define NE_XFLM_Q_INVALID_POSITION 0xD323 // Attempt is being made to position to an invalid position in the result set. /**************************************************************************** Desc: NICI / Encryption Errors ****************************************************************************/ // IMPORTANT NOTE: If changes are made to the RCODEs, please be sure to make // corresponding changes in csharp/xflaim/RCODE.cs and java/xflaim/RCODE.java. #define NE_XFLM_NICI_CONTEXT 0xD401 // Error occurred while creating NICI context for encryption/decryption. #define NE_XFLM_NICI_ATTRIBUTE_VALUE 0xD402 // Error occurred while accessing an attribute on a NICI encryption key. #define NE_XFLM_NICI_BAD_ATTRIBUTE 0xD403 // Value retrieved from an attribute on a NICI encryption key was bad. #define NE_XFLM_NICI_WRAPKEY_FAILED 0xD404 // Error occurred while wrapping a NICI encryption key in another NICI encryption key. #define NE_XFLM_NICI_UNWRAPKEY_FAILED 0xD405 // Error occurred while unwrapping a NICI encryption key that is wrapped in another NICI encryption key. #define NE_XFLM_NICI_INVALID_ALGORITHM 0xD406 // Attempt to use invalid NICI encryption algorithm. #define NE_XFLM_NICI_GENKEY_FAILED 0xD407 // Error occurred while attempting to generate a NICI encryption key. #define NE_XFLM_NICI_BAD_RANDOM 0xD408 // Error occurred while generating random data using NICI. #define NE_XFLM_PBE_ENCRYPT_FAILED 0xD409 // Error occurred while attempting to wrap a NICI encryption key in a password. #define NE_XFLM_PBE_DECRYPT_FAILED 0xD40A // Error occurred while attempting to unwrap a NICI encryption key that was previously wrapped in a password. #define NE_XFLM_DIGEST_INIT_FAILED 0xD40B // Error occurred while attempting to initialize the NICI digest functionality. #define NE_XFLM_DIGEST_FAILED 0xD40C // Error occurred while attempting to create a NICI digest. #define NE_XFLM_INJECT_KEY_FAILED 0xD40D // Error occurred while attempting to inject an encryption key into NICI. #define NE_XFLM_NICI_FIND_INIT 0xD40E // Error occurred while attempting to initialize NICI to find information on a NICI encryption key. #define NE_XFLM_NICI_FIND_OBJECT 0xD40F // Error occurred while attempting to find information on a NICI encryption key. #define NE_XFLM_NICI_KEY_NOT_FOUND 0xD410 // Could not find the NICI encryption key or information on the NICI encryption key. #define NE_XFLM_NICI_ENC_INIT_FAILED 0xD411 // Error occurred while initializing NICI to encrypt data. #define NE_XFLM_NICI_ENCRYPT_FAILED 0xD412 // Error occurred while encrypting data. #define NE_XFLM_NICI_DECRYPT_INIT_FAILED 0xD413 // Error occurred while initializing NICI to decrypt data. #define NE_XFLM_NICI_DECRYPT_FAILED 0xD414 // Error occurred while decrypting data. #define NE_XFLM_NICI_WRAPKEY_NOT_FOUND 0xD415 // Could not find the NICI encryption key used to wrap another NICI encryption key. #define NE_XFLM_NOT_EXPECTING_PASSWORD 0xD416 // Password supplied when none was expected. #define NE_XFLM_EXPECTING_PASSWORD 0xD417 // No password supplied when one was required. #define NE_XFLM_EXTRACT_KEY_FAILED 0xD418 // Error occurred while attempting to extract a NICI encryption key. #define NE_XFLM_NICI_INIT_FAILED 0xD419 // Error occurred while initializing NICI. #define NE_XFLM_BAD_ENCKEY_SIZE 0xD41A // Bad encryption key size found in roll-forward log packet. #define NE_XFLM_ENCRYPTION_UNAVAILABLE 0xD41B // Attempt was made to encrypt data when NICI is unavailable. /**************************************************************************** Dictionary Document Definitions - below are comments that document valid dictionary objects and their structure. ****************************************************************************/ /* Element Definition Desc: The XML syntax given below is used to define an element in the dictionary collection. Notes: 1) If the xflaim:type attribute is missing, any type of data may be stored on the attribute. 2) If the xflaim:State attribute is missing, the attribute's state is, by default, active. */ /* Attribute Definition Desc: The XML syntax given below is used to define an attribute in the dictionary collection. Notes: 1) If the xflaim:type attribute is missing, any type of data may be stored on the attribute. 2) If the xflaim:State attribute is missing, the attribute's state is, by default, active. */ /* Collection Definition Desc: The XML syntax given below is used to define a collection in the dictionary collection. */ /* Prefix Definition Desc: The XML syntax given below is used to define a namespace prefix in the dictionary collection. */ /* Index Definition Desc: The XML syntax given below is used to define an index in the dictionary collection. ... NOTE: The IndexOn, Required, Limit, and CompareRules attributes can only be set for key components - i.e., when the KeyComponent attribute is also specified. xflaim:ElementComponent elements may have one or more xflaim:ElementComponent or xflaim:AttributeComponent sub-elements. ... NOTE: The IndexOn, Required, Limit, and CompareRules attributes can only be set for key components - i.e., when the KeyComponent attribute is also specified. xflaim:AttributeComponent elements must be subordinate to a xflaim:ElementComponent element. They may not have any elements subordinate to them. Notes: 1) It is not valid to specify both an xflaim:CollectionName and an xflaim:CollectionNumber unless they refer to the same collection. Only one of the two is needed. If both are omitted, the collection that will be indexed is the default data collection. 2) Valid values for the IndexOptions attribute are any combination of the following: a) abspos - enables storing of absolute positioning information 3) For the xflaim:ElementComponent and xflaim:AttributeComponent, it is not valid to have both an xflaim:DictNumber and an xflaim:name attribute, unless they both specify the same element or attribute. One or the other must be specified. 4) If the xflaim:type attribute is specified on the xflaim:ElementComponent or xflaim:AttributeComponent element, it indicates what type the component is to be coerced to for indexing purposes. This is only relevant if the xflaim:IndexOn attribute is set to "value". For example, if a type of integer is specified, then the value will be coerced to an integer before putting it into the index. If the value were a string of "123" it would be coerced to an integer value of 123 for indexing purposes. If the xflaim:type attribute is omitted, then the element or attribute specified by the component must have a type specified in the element or attribute definition, and that is the type that will be used. 5) If the xflaim:Required attribute is missing from the xflaim:ElementComponent or xflaim:AttributeComponent element, or does not have a value of "yes", "on", "1", or "true", the index component is assumed to be optional. 6) The xflaim:CompareRules attribute specifies special comparison rules for the index component if the element or attribute is of type xflaim:string. Rules may be any combination of the following key words: a) caseinsensitive - don't compare case b) whitespaceasspace - treat whitespace as space (must be applied before minspaces, ignoreleadingspaces, ignoretrailingspaces, or nospaces) c) minspaces - compress out extra spaces d) ignoreleadingspaces - remove leading spaces e) ignoretrailingspaces - remove trailing spaces f) nospaces - remove all whitespace g) nounderscore - change all underscores to spaces (must be applied before minspaces, ignoreleadingspaces, ignoretrailingspaces, or nospaces) h) nodashes - remove all dashes. i) sortdescending - sort in descending order. j) sortmissinghigh - sort missing values high. Note that if the xflaim:CompareRules attribute is omitted, the default comparison rule for xflaim:string values is case sensitive. Note also that the xflaim:CompareRules attribute is ignored if the xflaim:IndexOn attribute is set to "presence". In that case, comparison rules are irrelevant. */ /* EncDef Definition Desc: The XML syntax given below is used to define an encryption definition in the dictionary collection. Notes: 1) Only AES and DES3 (Triple DES) encryption algorithms are supported 2) AES supports three key lengths: 256, 192 and 128 bits in length. 3) DES3 keys are 168 bits in length. 4. The xflaim:keySize attribute is optional. If it is not specified, the maximum key size allowed for the chosen algorithm will be selected. For AES, the key size chosen will depend on what is supported by the NICI installation. */ #endif // XFLAIM_H libxflaim-5.1.969/src/f_nici.cpp0000644000175000017500000015703310511001742017770 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the functions needed for the NICI interface // functions. Adapted from ss_crypto.c written by Cameron Mashayekhi. // // Tabs: 3 // // Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: f_nici.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #ifdef FLM_USE_NICI #ifdef FLM_NLM #define N_PLAT_NLM #endif #include "nwccs.h" #ifndef IDV_NOV_AES128CBCPad #define IDV_NOV_AES128CBCPad NICI_AlgorithmPrefix(1), 97 #endif /**************************************************************************** Desc: ****************************************************************************/ #define IV_SZ 16 #define IV_SZ8 8 #define SALT_SZ 8 #define SALT_COUNT 895 /**************************************************************************** Desc: ****************************************************************************/ typedef struct { FLMUINT uiKeyType; FLMUINT uiFormatLen; FLMUINT uiKeyLen; FLMUINT uiKeySize; } EXTRACTED_KEY; /**************************************************************************** Desc: ****************************************************************************/ class F_NICICCS : public IF_CCS { public: F_NICICCS() { m_bInitCalled = FALSE; m_bKeyVerified = FALSE; f_memset( m_ucIV, 0, IV_SZ); m_keyHandle = 0; m_hContext = 0; m_uiEncKeySize = 0; m_hMutex = F_MUTEX_NULL; } ~F_NICICCS(); RCODE init( FLMBOOL bKeyIsWrappingKey, FLMUINT uiAlgType); RCODE generateEncryptionKey( FLMUINT uiEncKeySize); RCODE generateWrappingKey( FLMUINT uiEncKeySize); RCODE encryptToStore( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV = NULL); RCODE decryptFromStore( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV = NULL); RCODE getKeyToStore( FLMBYTE ** ppucKeyInfo, FLMUINT32 * pui32BufLen, FLMBYTE * pzEncKeyPasswd, IF_CCS * pWrappingCcs); RCODE setKeyFromStore( FLMBYTE * pucKeyInfo, FLMBYTE * pszEncKeyPasswd = NULL, IF_CCS * pWrappingCcs = NULL); FINLINE FLMBOOL keyVerified( void) { return( m_bKeyVerified); } FINLINE FLMUINT getEncType( void) { return( m_uiAlgType); } FLMUINT getIVLen( void); RCODE generateIV( FLMUINT uiIVLen, FLMBYTE * pucIV); RCODE getWrappingKey( NICI_OBJECT_HANDLE * pWrappingKeyHandle); RCODE wrapKey( FLMBYTE ** ppucWrappedKey, FLMUINT32 * pui32Length, NICI_OBJECT_HANDLE masterWrappingKey = 0 ); RCODE unwrapKey( FLMBYTE * pucWrappedKey, FLMUINT32 ui32WrappedKeyLength, NICI_OBJECT_HANDLE masterWrappingKey = 0); RCODE extractKey( FLMBYTE ** ppucShroudedKey, FLMUINT32 * pui32Length, FLMUNICODE * puzEncKeyPasswd ); RCODE injectKey( FLMBYTE * pucBuffer, FLMUINT32 ui32Length, FLMUNICODE * puzEncKeyPasswd ); RCODE encryptToStoreAES( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV); RCODE encryptToStoreDES3( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV); RCODE encryptToStoreDES( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV); RCODE decryptFromStoreAES( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV); RCODE decryptFromStoreDES3( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV); RCODE decryptFromStoreDES( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV); RCODE generateEncryptionKeyAES( FLMUINT uiEncKeySize); RCODE generateEncryptionKeyDES3( FLMUINT uiEncKeySize); RCODE generateEncryptionKeyDES( FLMUINT uiEncKeySize); RCODE generateWrappingKeyAES( FLMUINT uiEncKeySize); RCODE generateWrappingKeyDES3( FLMUINT uiEncKeySize); RCODE generateWrappingKeyDES( FLMUINT uiEncKeySize); FLMUINT m_uiAlgType; FLMBOOL m_bInitCalled; FLMBOOL m_bKeyIsWrappingKey; FLMBOOL m_bKeyVerified; NICI_OBJECT_HANDLE m_keyHandle; // Handle to the clear key - we don't ever get the actual key. FLMBYTE m_ucIV[ IV_SZ]; // Used when the algorithm type is DES, 3DES or AES FLMBYTE m_ucRndIV[ IV_SZ]; // Used when the IV is stored with the data. FLMUINT m_uiIVFactor; NICI_CC_HANDLE m_hContext; FLMUINT m_uiEncKeySize; F_MUTEX m_hMutex; }; /**************************************************************************** Desc: ****************************************************************************/ F_NICICCS::~F_NICICCS() { if( m_keyHandle) { if( !m_hContext) { if( RC_BAD( CCS_CreateContext(0, &m_hContext))) { flmAssert( 0); } } // Get rid of the key handle. if( m_hContext) { CCS_DestroyObject( m_hContext, m_keyHandle); CCS_DestroyContext( m_hContext); } } if (m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: Save the wrapped key in m_pKey. Note: Make sure there is a buffer allocated for the wrapped key. ****************************************************************************/ RCODE F_NICICCS::wrapKey( FLMBYTE ** ppucWrappedKey, FLMUINT32 * pui32Length, NICI_OBJECT_HANDLE masterWrappingKey) { RCODE rc = NE_XFLM_OK; NICI_ATTRIBUTE wKey[2]; NICI_ALGORITHM algorithm; NICI_PARAMETER_INFO parm[1]; FLMBYTE oid_aes128[] = {IDV_NOV_AES128CBCPad}; FLMBYTE oid_aes192[] = {IDV_NOV_AES192CBCPad}; FLMBYTE oid_aes256[] = {IDV_NOV_AES256CBCPad}; FLMBYTE oid_3des[] = {IDV_DES_EDE3_CBCPadIV8}; NICI_OBJECT_HANDLE wrappingKeyHandle; FLMBOOL bLocked = FALSE; if (masterWrappingKey) { wrappingKeyHandle = masterWrappingKey; } else { if (RC_BAD( rc = getWrappingKey( &wrappingKeyHandle))) { goto Exit; } } f_mutexLock( m_hMutex); bLocked = TRUE; // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } f_memset( &wKey, 0, sizeof(NICI_ATTRIBUTE) * 2); wKey[0].type = NICI_A_KEY_TYPE; wKey[1].type = NICI_A_KEY_SIZE; if( RC_BAD( rc = CCS_GetAttributeValue( m_hContext, wrappingKeyHandle, &wKey[0], 2))) { rc = RC_SET( NE_XFLM_NICI_ATTRIBUTE_VALUE); m_hContext = 0; goto Exit; } if( !wKey[0].u.f.hasValue || !wKey[1].u.f.hasValue) { rc = RC_SET( NE_XFLM_NICI_BAD_ATTRIBUTE); goto Exit; } switch( wKey[0].u.f.value) { case NICI_K_AES: { switch( wKey[1].u.f.value) { case XFLM_NICI_AES128: { algorithm.algorithm = (nuint8 *)oid_aes128; break; } case XFLM_NICI_AES192: { algorithm.algorithm = (nuint8 *)oid_aes192; break; } case XFLM_NICI_AES256: { algorithm.algorithm = (nuint8 *)oid_aes256; break; } default: { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } } algorithm.parameter = parm; algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ sizeof(algorithm.parameter->count); algorithm.parameter->count = 1; algorithm.parameter->parms[0].parmType = NICI_P_IV; algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; break; } case NICI_K_DES3X: { algorithm.algorithm = (nuint8 *)oid_3des; algorithm.parameter = parm; algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ sizeof(algorithm.parameter->count); algorithm.parameter->count = 1; algorithm.parameter->parms[0].parmType = NICI_P_IV; algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; break; } default: { rc = RC_SET( NE_XFLM_NICI_WRAPKEY_FAILED); goto Exit; } } // We should be able to call this with NULL for the // wrapped key, to get the length. if( RC_BAD( rc = CCS_WrapKey( m_hContext, &algorithm, NICI_KM_UNSPECIFIED, 0, wrappingKeyHandle, m_keyHandle, (nuint8 *)NULL, (pnuint32)pui32Length))) { rc = RC_SET( NE_XFLM_NICI_WRAPKEY_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = f_calloc( *pui32Length, ppucWrappedKey))) { goto Exit; } if( RC_BAD( rc = CCS_WrapKey( m_hContext, &algorithm, NICI_KM_UNSPECIFIED, 0, wrappingKeyHandle, m_keyHandle, (nuint8 *)*ppucWrappedKey, (pnuint32)pui32Length))) { rc = RC_SET( NE_XFLM_NICI_WRAPKEY_FAILED); m_hContext = 0; goto Exit; } Exit: if (bLocked) { f_mutexUnlock( m_hMutex); } return(rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::unwrapKey( FLMBYTE * pucWrappedKey, FLMUINT32 ui32WrappedKeyLength, NICI_OBJECT_HANDLE masterWrappingKey) { RCODE rc = NE_XFLM_OK; NICI_ATTRIBUTE wKey; NICI_OBJECT_HANDLE wrappingKeyHandle; FLMBOOL bLocked = FALSE; if (masterWrappingKey) { wrappingKeyHandle = masterWrappingKey; } else { if (RC_BAD( rc = getWrappingKey( &wrappingKeyHandle))) { goto Exit; } } f_mutexLock( m_hMutex); bLocked = TRUE; // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } if( RC_BAD( rc = CCS_UnwrapKey( m_hContext, wrappingKeyHandle, (nuint8 *)pucWrappedKey, ui32WrappedKeyLength, &m_keyHandle))) { rc = RC_SET( NE_XFLM_NICI_UNWRAPKEY_FAILED); m_hContext = 0; goto Exit; } // We need to get the key size... f_memset( &wKey, 0, sizeof( NICI_ATTRIBUTE)); wKey.type = NICI_A_KEY_SIZE; if( RC_BAD( rc = CCS_GetAttributeValue( m_hContext, m_keyHandle, &wKey, 1))) { rc = RC_SET( NE_XFLM_NICI_ATTRIBUTE_VALUE); m_hContext = 0; goto Exit; } if( !wKey.u.f.hasValue) { rc = RC_SET( NE_XFLM_NICI_BAD_ATTRIBUTE); goto Exit; } m_uiEncKeySize = wKey.u.f.value; Exit: if( bLocked) { f_mutexUnlock( m_hMutex); } return(rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::generateEncryptionKey( FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; switch( m_uiAlgType) { case FLM_NICI_AES: { rc = generateEncryptionKeyAES( uiEncKeySize); break; } case FLM_NICI_DES3: { rc = generateEncryptionKeyDES3( uiEncKeySize); break; } default: { flmAssert( 0); rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::generateEncryptionKeyAES( FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_ATTRIBUTE keyAttr[3]; nbool8 keySizeChanged; FLMBYTE oid_aes128[] = {IDV_AES128CBC}; FLMBYTE oid_aes192[] = {IDV_AES192CBC}; FLMBYTE oid_aes256[] = {IDV_AES256CBC}; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // Set up AES Algorithm switch( uiEncKeySize) { case XFLM_NICI_AES128: { algorithm.algorithm = (nuint8 *)oid_aes128; break; } case XFLM_NICI_AES192: { algorithm.algorithm = (nuint8 *)oid_aes192; break; } case XFLM_NICI_AES256: { algorithm.algorithm = (nuint8 *)oid_aes256; break; } default: { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } } algorithm.parameterLen = 0; algorithm.parameter = NULL; // Set up key attributes keyAttr[0].type = NICI_A_KEY_USAGE; keyAttr[0].u.f.hasValue = 1; keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; keyAttr[0].u.f.valueInfo = 0; keyAttr[1].type = NICI_A_KEY_SIZE; keyAttr[1].u.f.hasValue = 1; keyAttr[1].u.f.value = uiEncKeySize; keyAttr[1].u.f.valueInfo = 0; keyAttr[2].type = NICI_A_GLOBAL; keyAttr[2].u.f.hasValue = 1; keyAttr[2].u.f.value = N_TRUE; keyAttr[2].u.f.valueInfo = 0; // Generate a AES key if( RC_BAD( rc = CCS_GenerateKey( m_hContext, &algorithm, keyAttr, 3, &keySizeChanged, &m_keyHandle, NICI_H_INVALID))) { rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); m_hContext = 0; goto Exit; } // Generate some IV to use with this key. if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)m_ucIV, IV_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } m_uiEncKeySize = uiEncKeySize; Exit: f_mutexUnlock( m_hMutex); return(rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::generateEncryptionKeyDES3( FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_ATTRIBUTE keyAttr[3]; nbool8 keySizeChanged; FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; f_mutexLock( m_hMutex); // Only one DES3 key size supported. if( uiEncKeySize != XFLM_NICI_DES3X) { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // Set up AES Algorithm algorithm.algorithm = (nuint8 *)oid_des3; algorithm.parameterLen = 0; algorithm.parameter = NULL; // Set up key attributes keyAttr[0].type = NICI_A_KEY_USAGE; keyAttr[0].u.f.hasValue = 1; keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; keyAttr[0].u.f.valueInfo = 0; keyAttr[1].type = NICI_A_KEY_SIZE; keyAttr[1].u.f.hasValue = 1; keyAttr[1].u.f.value = uiEncKeySize; keyAttr[1].u.f.valueInfo = 0; keyAttr[2].type = NICI_A_GLOBAL; keyAttr[2].u.f.hasValue = 1; keyAttr[2].u.f.value = N_TRUE; keyAttr[2].u.f.valueInfo = 0; // Generate a AES key if( RC_BAD( rc = CCS_GenerateKey( m_hContext, &algorithm, keyAttr, 3, &keySizeChanged, &m_keyHandle, NICI_H_INVALID))) { rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); m_hContext = 0; goto Exit; } // Generate some IV to use with this key. if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)m_ucIV, IV_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } m_uiEncKeySize = uiEncKeySize; Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::generateWrappingKey( FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; switch( m_uiAlgType) { case FLM_NICI_AES: { rc = generateWrappingKeyAES( uiEncKeySize); break; } case FLM_NICI_DES3: { rc = generateWrappingKeyDES3( uiEncKeySize); break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_INVALID_ALGORITHM); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::generateWrappingKeyAES( FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_ATTRIBUTE keyAttr[6]; nbool8 keySizeChanged; FLMBYTE oid_aes128[] = {IDV_AES128CBC}; FLMBYTE oid_aes192[] = {IDV_AES192CBC}; FLMBYTE oid_aes256[] = {IDV_AES256CBC}; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // Set up AES Algorithm switch( uiEncKeySize) { case XFLM_NICI_AES128: { algorithm.algorithm = (nuint8 *)oid_aes128; keyAttr[1].u.v.valuePtr = oid_aes128; keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_aes128); break; } case XFLM_NICI_AES192: { algorithm.algorithm = (nuint8 *)oid_aes192; keyAttr[1].u.v.valuePtr = oid_aes192; keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_aes192); break; } case XFLM_NICI_AES256: { algorithm.algorithm = (nuint8 *)oid_aes256; keyAttr[1].u.v.valuePtr = oid_aes256; keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_aes256); break; } default: { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } } algorithm.parameterLen = 0; algorithm.parameter = NULL; // Set up key attributes keyAttr[0].type = NICI_A_KEY_TYPE; keyAttr[0].u.f.hasValue = 1; keyAttr[0].u.f.value = NICI_K_AES; keyAttr[0].u.f.valueInfo = 0; keyAttr[1].type = NICI_A_KEY_FORMAT; keyAttr[1].u.v.valueInfo = 0; keyAttr[2].type = NICI_A_KEY_USAGE; keyAttr[2].u.f.hasValue = 1; keyAttr[2].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; keyAttr[2].u.f.valueInfo = 0; keyAttr[3].type = NICI_A_KEY_SIZE; keyAttr[3].u.f.hasValue = 1; keyAttr[3].u.f.value = uiEncKeySize; keyAttr[3].u.f.valueInfo = 0; keyAttr[4].type = NICI_A_GLOBAL; keyAttr[4].u.f.hasValue = 1; keyAttr[4].u.f.value = N_TRUE; keyAttr[4].u.f.valueInfo = 0; keyAttr[5].type = NICI_A_CLASS; keyAttr[5].u.f.hasValue = 1; keyAttr[5].u.f.value = NICI_O_SECRET_KEY; keyAttr[5].u.f.valueInfo = 0; // Generate an AES wrapping key if( RC_BAD( rc = CCS_GenerateKey( m_hContext, &algorithm, keyAttr, 6, &keySizeChanged, &m_keyHandle, NICI_H_INVALID))) { rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); m_hContext = 0; goto Exit; } // Generate some IV to use with this key. if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)m_ucIV, IV_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } // If we generated a wrapping key, then this object's key handle is // actually a wrapping key. This means that we will use it to wrap the // other keys in the system. m_bKeyIsWrappingKey = TRUE; m_uiEncKeySize = uiEncKeySize; Exit: f_mutexUnlock( m_hMutex); return(rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::generateWrappingKeyDES3( FLMUINT uiEncKeySize) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_ATTRIBUTE keyAttr[ 6]; nbool8 keySizeChanged; FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; f_mutexLock( m_hMutex); if( uiEncKeySize != XFLM_NICI_DES3X) { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // Set up AES Algorithm algorithm.algorithm = (nuint8 *)oid_des3; algorithm.parameterLen = 0; algorithm.parameter = NULL; // Set up key attributes keyAttr[0].type = NICI_A_KEY_TYPE; keyAttr[0].u.f.hasValue = 1; keyAttr[0].u.f.value = NICI_K_DES3X; keyAttr[0].u.f.valueInfo = 0; keyAttr[1].type = NICI_A_KEY_FORMAT; keyAttr[1].u.v.valuePtr = oid_des3; keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_des3); keyAttr[1].u.v.valueInfo = 0; keyAttr[2].type = NICI_A_KEY_USAGE; keyAttr[2].u.f.hasValue = 1; keyAttr[2].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; keyAttr[2].u.f.valueInfo = 0; keyAttr[3].type = NICI_A_KEY_SIZE; keyAttr[3].u.f.hasValue = 1; keyAttr[3].u.f.value = uiEncKeySize; keyAttr[3].u.f.valueInfo = 0; keyAttr[4].type = NICI_A_GLOBAL; keyAttr[4].u.f.hasValue = 1; keyAttr[4].u.f.value = N_TRUE; keyAttr[4].u.f.valueInfo = 0; keyAttr[5].type = NICI_A_CLASS; keyAttr[5].u.f.hasValue = 1; keyAttr[5].u.f.value = NICI_O_SECRET_KEY; keyAttr[5].u.f.valueInfo = 0; // Generate an AES wrapping key if( RC_BAD( rc = CCS_GenerateKey( m_hContext, &algorithm, keyAttr, 6, &keySizeChanged, &m_keyHandle, NICI_H_INVALID))) { rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); m_hContext = 0; goto Exit; } // Generate some IV to use with this key. if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)m_ucIV, IV_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } // If we generated a wrapping key, then this object's key handle is // actually a wrapping key. This means that we will use it to wrap the // other keys in the system. m_bKeyIsWrappingKey = TRUE; m_uiEncKeySize = uiEncKeySize; Exit: f_mutexUnlock( m_hMutex); return(rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::encryptToStore( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; switch( m_uiAlgType) { case FLM_NICI_AES: { rc = encryptToStoreAES( pucIn, uiInLen, pucOut, puiOutLen, pucIV); break; } case FLM_NICI_DES3: { rc = encryptToStoreDES3( pucIn, uiInLen, pucOut, puiOutLen, pucIV); break; } default: { flmAssert( 0); rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); goto Exit; } } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::decryptFromStore( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; switch( m_uiAlgType) { case FLM_NICI_AES: { rc = decryptFromStoreAES( pucIn, uiInLen, pucOut, puiOutLen, pucIV); break; } case FLM_NICI_DES3: { rc = decryptFromStoreDES3( pucIn, uiInLen, pucOut, puiOutLen, pucIV); break; } default: { rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_INVALID_ALGORITHM); goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::encryptToStoreAES( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_PARAMETER_INFO parm[1]; FLMBYTE oid_aes128[] = {IDV_AES128CBC}; FLMBYTE oid_aes192[] = {IDV_AES192CBC}; FLMBYTE oid_aes256[] = {IDV_AES256CBC}; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } switch( m_uiEncKeySize) { case XFLM_NICI_AES128: { algorithm.algorithm = (nuint8 *)oid_aes128; break; } case XFLM_NICI_AES192: { algorithm.algorithm = (nuint8 *)oid_aes192; break; } case XFLM_NICI_AES256: { algorithm.algorithm = (nuint8 *)oid_aes256; break; } default: { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } } algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ sizeof(algorithm.parameter->count); algorithm.parameter = parm; algorithm.parameter->count = 1; algorithm.parameter->parms[0].parmType = NICI_P_IV; if( pucIV) { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; } else { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; } algorithm.parameter->parms[0].u.b.len = IV_SZ; // Init encryption if( RC_BAD( rc = CCS_DataEncryptInit( m_hContext, &algorithm, m_keyHandle))) { rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_ENC_INIT_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = CCS_Encrypt( m_hContext, (nuint8 *)pucIn, uiInLen, (nuint8 *)pucOut, puiOutLen))) { rc = RC_SET( NE_XFLM_NICI_ENCRYPT_FAILED); m_hContext = 0; goto Exit; } Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::decryptFromStoreAES( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_PARAMETER_INFO parm[1]; FLMBYTE oid_aes128[] = {IDV_AES128CBC}; FLMBYTE oid_aes192[] = {IDV_AES192CBC}; FLMBYTE oid_aes256[] = {IDV_AES256CBC}; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } switch( m_uiEncKeySize) { case XFLM_NICI_AES128: { algorithm.algorithm = (nuint8 *)oid_aes128; break; } case XFLM_NICI_AES192: { algorithm.algorithm = (nuint8 *)oid_aes192; break; } case XFLM_NICI_AES256: { algorithm.algorithm = (nuint8 *)oid_aes256; break; } default: { rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); goto Exit; } } algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ sizeof(algorithm.parameter->count); algorithm.parameter = parm; algorithm.parameter->count = 1; algorithm.parameter->parms[0].parmType = NICI_P_IV; if( pucIV) { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; } else { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; } algorithm.parameter->parms[0].u.b.len = IV_SZ; // Init encryption if( RC_BAD( rc = CCS_DataDecryptInit( m_hContext, &algorithm, m_keyHandle))) { rc = RC_SET( NE_XFLM_NICI_DECRYPT_INIT_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = CCS_Decrypt( m_hContext, (nuint8 *)pucIn, uiInLen, (nuint8 *)pucOut, puiOutLen))) { rc = RC_SET( NE_XFLM_NICI_DECRYPT_FAILED); m_hContext = 0; goto Exit; } Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::encryptToStoreDES3( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_PARAMETER_INFO parm[1]; FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } algorithm.algorithm = (nuint8 *)oid_des3; algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ sizeof(algorithm.parameter->count); algorithm.parameter = parm; algorithm.parameter->count = 1; algorithm.parameter->parms[0].parmType = NICI_P_IV; if( pucIV) { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; } else { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; } algorithm.parameter->parms[0].u.b.len = IV_SZ8; // Init encryption if( RC_BAD( rc = CCS_DataEncryptInit( m_hContext, &algorithm, m_keyHandle))) { rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_ENC_INIT_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = CCS_Encrypt( m_hContext, (nuint8 *)pucIn, uiInLen, (nuint8 *)pucOut, puiOutLen))) { rc = RC_SET( NE_XFLM_NICI_ENCRYPT_FAILED); m_hContext = 0; goto Exit; } Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::decryptFromStoreDES3( FLMBYTE * pucIn, FLMUINT uiInLen, FLMBYTE * pucOut, FLMUINT * puiOutLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_PARAMETER_INFO parm[1]; FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // Set up alogrithm now to do triple des decryption algorithm.algorithm = (nuint8 *)oid_des3; algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ sizeof(algorithm.parameter->count); algorithm.parameter = parm; algorithm.parameter->count = 1; algorithm.parameter->parms[0].parmType = NICI_P_IV; if( pucIV) { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; } else { algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; } algorithm.parameter->parms[0].u.b.len = IV_SZ8; // Init encryption if( RC_BAD( rc = CCS_DataDecryptInit( m_hContext, &algorithm, m_keyHandle))) { rc = RC_SET( NE_XFLM_NICI_DECRYPT_INIT_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = CCS_Decrypt( m_hContext, (nuint8 *)pucIn, uiInLen, (nuint8 *)pucOut, puiOutLen))) { rc = RC_SET( NE_XFLM_NICI_DECRYPT_FAILED); m_hContext = 0; goto Exit; } Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::init( FLMBOOL bKeyIsWrappingKey, FLMUINT uiAlgType) { RCODE rc = NE_XFLM_OK; FLMBOOL bLocked = FALSE; if (m_bInitCalled) { rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); goto Exit; } m_bKeyIsWrappingKey = bKeyIsWrappingKey; if( uiAlgType != FLM_NICI_AES && uiAlgType != FLM_NICI_DES3) { flmAssert( 0); rc = RC_SET( NE_XFLM_INVALID_ENC_ALGORITHM); goto Exit; } m_uiAlgType = uiAlgType; // Create a mutex to control access to the nici operations. if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } f_mutexLock( m_hMutex); bLocked = TRUE; // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } else { flmAssert( 0); } // Generate the Random IV if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)&m_ucRndIV, IV_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } // Generate an adjustment factor for the IV if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)&m_uiIVFactor, sizeof( FLMUINT)))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } m_bInitCalled = TRUE; Exit: if (bLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::getWrappingKey( NICI_OBJECT_HANDLE * pWrappingKeyHandle) { RCODE rc = NE_XFLM_OK; NICI_ATTRIBUTE find[2]; FLMUINT uiCount; f_mutexLock( m_hMutex); // Create NICI context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } find[0].type = NICI_A_GLOBAL; find[0].u.f.hasValue = 1; find[0].u.f.value = 1; find[0].u.f.valueInfo = 0; find[1].type = NICI_A_FEATURE; find[1].u.f.hasValue = 1; find[1].u.f.value = NICI_AV_STORAGE; find[1].u.f.valueInfo = 0; if( RC_BAD( rc = CCS_FindObjectsInit( m_hContext, find, 2))) { rc = RC_SET( NE_XFLM_NICI_FIND_INIT); m_hContext = 0; goto Exit; } uiCount = 1; if( RC_BAD( rc = CCS_FindObjects( m_hContext, pWrappingKeyHandle, &uiCount))) { rc = RC_SET( NE_XFLM_NICI_FIND_OBJECT); m_hContext = 0; goto Exit; } if( uiCount < 1) { rc = RC_SET( NE_XFLM_NICI_WRAPKEY_NOT_FOUND); goto Exit; } Exit: f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::getKeyToStore( FLMBYTE ** ppucKeyInfo, FLMUINT32 * pui32BufLen, FLMBYTE * pszEncKeyPasswd, IF_CCS * pWrappingCcs) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucTmp = NULL; FLMBYTE * pucPtr = NULL; FLMUINT32 ui32PaddedLength; FLMBYTE * pucWrappedKey = NULL; FLMUINT32 ui32WrappedKeyLen = 0; FLMBYTE * pszFormattedEncKeyPasswd = NULL; NICI_OBJECT_HANDLE wrappingKeyHandle = 0; *ppucKeyInfo = NULL; *pui32BufLen = 0; if( pWrappingCcs) { flmAssert(m_bKeyIsWrappingKey == FALSE); wrappingKeyHandle = pWrappingCcs->m_keyHandle; } else if( !pszEncKeyPasswd) { flmAssert( m_bKeyIsWrappingKey); } // Either extract the key or wrap the key. if( pszEncKeyPasswd && pszEncKeyPasswd[ 0]) { // The password that is passed in to CCS_pbeEncrypt is NOT actually // unicode. It must be treated as a sequence of bytes that that is // terminated with 2 nulls and has an even length. If we treat it // as unicode, then we'll have endian issues if we move the database // to machines with different byte ordering. if( RC_BAD( rc = f_calloc( f_strlen(pszEncKeyPasswd) + (f_strlen(pszEncKeyPasswd) % 2) + 2, &pszFormattedEncKeyPasswd))) { goto Exit; } f_strcpy( pszFormattedEncKeyPasswd, pszEncKeyPasswd); if( RC_BAD( rc = extractKey( &pucWrappedKey, &ui32WrappedKeyLen, (FLMUNICODE *)pszFormattedEncKeyPasswd))) { goto Exit; } } else { if( RC_BAD( rc = wrapKey( &pucWrappedKey, &ui32WrappedKeyLen, wrappingKeyHandle))) { goto Exit; } } // The shrouded or wrapped key will be stored in m_pKey ui32PaddedLength = (ui32WrappedKeyLen + sizeof( FLMBOOL) + sizeof (FLMUINT32) + IV_SZ ); // Make sure our buffer size is padded to a 16 byte boundary. if( (ui32PaddedLength % 16) != 0) { ui32PaddedLength += (16 - (ui32PaddedLength % 16)); } // Add one extra byte for a NULL terminator if( RC_BAD(rc = f_alloc( ui32PaddedLength + 1, &pucTmp))) { goto Exit; } if( !m_hContext) { if( CCS_CreateContext( 0, &m_hContext)) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } pucPtr = pucTmp; // Save a flag indicating whether the key is wrapped or encoded in // a password. UD2FBA( (pszEncKeyPasswd && pszEncKeyPasswd[0]) ? (FLMUINT)TRUE : (FLMUINT)FALSE, pucPtr); pucPtr += sizeof(FLMBOOL); // Copy the key length. UD2FBA(ui32WrappedKeyLen, pucPtr); pucPtr += sizeof(FLMUINT32); // Copy the IV too. f_memcpy( pucPtr, m_ucIV, IV_SZ); pucPtr += IV_SZ; // Copy the wrapped key value f_memcpy( pucPtr, pucWrappedKey, ui32WrappedKeyLen); pucPtr += ui32WrappedKeyLen; // Fill the remainder of the buffer with random data. if( CCS_GetRandom( m_hContext, (nuint8 *)pucPtr, ((FLMUINT)pucTmp + ui32PaddedLength) - (FLMUINT)pucPtr)) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } pucTmp[ ui32PaddedLength] = '\0'; *ppucKeyInfo = pucTmp; *pui32BufLen = ui32PaddedLength; pucTmp = NULL; Exit: if( pucTmp) { f_free(&pucTmp); } if( pucWrappedKey) { f_free( &pucWrappedKey); } if( pszFormattedEncKeyPasswd) { f_free( &pszFormattedEncKeyPasswd); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::setKeyFromStore( FLMBYTE * pucKeyInfo, FLMBYTE * pszEncKeyPasswd, IF_CCS * pWrappingCcs) { RCODE rc = NE_XFLM_OK; FLMBYTE * pucTmp = pucKeyInfo; FLMBYTE * pucBuffer = NULL; FLMBOOL bShrouded = FALSE; FLMUINT32 ui32Length; FLMBYTE * pucKeyBuf = NULL; FLMBYTE * pszFormattedEncKeyPasswd = NULL; NICI_OBJECT_HANDLE wrappingKeyHandle = 0; if( pWrappingCcs) { flmAssert(m_bKeyIsWrappingKey == FALSE); wrappingKeyHandle = pWrappingCcs->m_keyHandle; } // Extract the fields from the buffer bShrouded = FB2UD( pucTmp); pucTmp += sizeof(FLMUINT); // Actual length - note that the passed buffer is padded to 16 byte boundary. ui32Length = FB2UD( pucTmp); pucTmp += sizeof(FLMUINT32); // Get the IV f_memcpy( m_ucIV, pucTmp, IV_SZ); pucTmp += IV_SZ; // Need another temporary buffer to hold the encrypted / shrouded key. if( RC_BAD( rc = f_alloc( ui32Length, &pucBuffer))) { goto Exit; } f_memcpy( pucBuffer, pucTmp, ui32Length); if( bShrouded) { if( pszEncKeyPasswd == NULL || pszEncKeyPasswd[0] == '\0') { rc = RC_SET( NE_XFLM_EXPECTING_PASSWORD); goto Exit; } // The password that is passed in to CCS_pbeDecrypt is NOT actually // unicode. It must be treated as a sequence of bytes that that is // terminated with 2 nulls and has an even length. If we treat it // as unicode, then we'll have endian issues if we move the database // to machines with different byte ordering. if( RC_BAD( rc = f_calloc( f_strlen(pszEncKeyPasswd) + (f_strlen(pszEncKeyPasswd) % 2) + 2, &pszFormattedEncKeyPasswd))) { goto Exit; } f_strcpy( pszFormattedEncKeyPasswd, pszEncKeyPasswd); // Unshroud the key using the password. // Key handle is always kept in m_keyHandle. if( RC_BAD( rc = injectKey( pucBuffer, ui32Length, (FLMUNICODE *)pszFormattedEncKeyPasswd))) { goto Exit; } } else { if( pszEncKeyPasswd) { if( pszEncKeyPasswd[0] != '\0') { rc = RC_SET( NE_XFLM_NOT_EXPECTING_PASSWORD); goto Exit; } } // Unwrap the key. The Key handle is always store in m_keyHandle. if( RC_BAD( rc = unwrapKey( pucBuffer, ui32Length, wrappingKeyHandle))) { goto Exit; } } m_bKeyVerified = TRUE; Exit: if( pucBuffer) { f_free( &pucBuffer); } if( pucKeyBuf) { f_free( &pucKeyBuf); } if( pszFormattedEncKeyPasswd) { f_free( &pszFormattedEncKeyPasswd); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::extractKey( FLMBYTE ** ppucExtractedKey, FLMUINT32 * pui32Length, FLMUNICODE * puzEncKeyPasswd) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_ATTRIBUTE keyAttr[2]; NICI_ATTRIBUTE attr[2]; FLMBYTE oid_sha1[] = {IDV_SHA1}; FLMBYTE oid_pbe[] = {IDV_pbeWithSHA1And3Key3xDES_CBC}; FLMBYTE ucDigest[ 20]; FLMUINT uiDigestLen = sizeof(ucDigest); FLMUINT uiBufferSize; FLMBYTE * pucKey = NULL; FLMBYTE * pucFormat = NULL; EXTRACTED_KEY * pExtractedKey = NULL; FLMUINT uiEncLen; FLMBYTE * pTemp = NULL; NICI_PARAMETER_INFO * pParmInfo; FLMBYTE * pucSalt; FLMUINT uiAllocSize; FLMUINT uiIndx; FLMBYTE * pucTempPtr; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } f_memset( &attr[0], 0, sizeof(NICI_ATTRIBUTE) * 2); attr[0].type = NICI_A_KEY_TYPE; attr[1].type = NICI_A_KEY_FORMAT; if( RC_BAD( rc = CCS_GetAttributeValue( m_hContext, m_keyHandle, &attr[0], 2))) { rc = RC_SET( NE_XFLM_NICI_ATTRIBUTE_VALUE); m_hContext = 0; goto Exit; } if( !attr[0].u.f.hasValue) { rc = RC_SET( NE_XFLM_NICI_BAD_ATTRIBUTE); goto Exit; } f_memset( &keyAttr[0], 0, sizeof(NICI_ATTRIBUTE) * 2); switch( attr[0].u.f.value) { case NICI_K_AES: { uiIndx = 0; keyAttr[uiIndx].type = NICI_A_KEY_VALUE; switch( m_uiEncKeySize) { case XFLM_NICI_AES128: { keyAttr[uiIndx].u.v.valueLen = 16; break; } case XFLM_NICI_AES192: { keyAttr[uiIndx].u.v.valueLen = 24; break; } case XFLM_NICI_AES256: { keyAttr[uiIndx].u.v.valueLen = 32; break; } } uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; break; } case NICI_K_DES3X: { uiIndx = 0; keyAttr[uiIndx].type = NICI_A_KEY_VALUE; keyAttr[uiIndx].u.v.valueLen = 24; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; break; } case NICI_K_DES: { uiIndx = 0; keyAttr[uiIndx].type = NICI_A_KEY_VALUE; keyAttr[uiIndx].u.v.valueLen = 8; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; break; } default: { flmAssert( 0); rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); goto Exit; } } // Make one allocation that we can then use to hold several different things. uiBufferSize = sizeof( EXTRACTED_KEY) + attr[1].u.v.valueLen + keyAttr[0].u.v.valueLen + sizeof (ucDigest); uiAllocSize = uiBufferSize + SALT_SZ + (sizeof( NICI_PARAMETER_DATA) * 2) + sizeof( FLMUINT32); // Make sure the allocation size is on a 8 byte boundary if( (uiAllocSize % 8) != 0) { uiAllocSize += (8 - (uiAllocSize % 8)); } if( RC_BAD( rc = f_calloc( uiAllocSize, &pExtractedKey))) { goto Exit; } keyAttr[1].u.v.valuePtr = &pExtractedKey[1]; pucFormat = (FLMBYTE *)keyAttr[1].u.v.valuePtr; keyAttr[0].u.v.valuePtr = pucFormat + attr[1].u.v.valueLen; pucKey = (FLMBYTE *)keyAttr[0].u.v.valuePtr; pucSalt = (FLMBYTE *)pExtractedKey + uiBufferSize; pParmInfo = (NICI_PARAMETER_INFO *)(pucSalt + SALT_SZ); // Make sure that pParmInfo is 8 byte alligned. if( (FLMUINT)pParmInfo % 8) { FLMBYTE * pucTemp; pucTemp = (FLMBYTE *)pParmInfo + (8 - ((FLMUINT)pParmInfo % 8)); pParmInfo = (NICI_PARAMETER_INFO *)pucTemp; } // Extracted the key value now if( RC_BAD( rc = CCS_ExtractKey( m_hContext, m_keyHandle, &keyAttr[0], 2))) { rc = RC_SET( NE_XFLM_EXTRACT_KEY_FAILED); m_hContext = 0; goto Exit; } // Calculate a SHA1 checksum. algorithm.algorithm = (nuint8 *)oid_sha1; algorithm.parameter = NULL; algorithm.parameterLen = 0; if( RC_BAD( rc = CCS_DigestInit( m_hContext, &algorithm))) { rc = RC_SET( NE_XFLM_DIGEST_INIT_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = CCS_Digest( m_hContext, (nuint8 *)pucFormat, keyAttr[0].u.v.valueLen + attr[1].u.v.valueLen, (nuint8 *)ucDigest, &uiDigestLen))) { rc = RC_SET( NE_XFLM_DIGEST_FAILED); m_hContext = 0; goto Exit; } flmAssert( uiDigestLen == sizeof( ucDigest)); pucTempPtr = (FLMBYTE *)pExtractedKey; UD2FBA( attr[0].u.f.value, pucTempPtr); pucTempPtr += 4; UD2FBA( attr[1].u.v.valueLen, pucTempPtr); pucTempPtr += 4; UD2FBA( keyAttr[0].u.v.valueLen, pucTempPtr); pucTempPtr += 4; UD2FBA( m_uiEncKeySize, pucTempPtr); // Point to the Digest... pTemp = (FLMBYTE *)&pExtractedKey[1] + attr[1].u.v.valueLen + keyAttr[0].u.v.valueLen; f_memcpy( pTemp, ucDigest, uiDigestLen); // Generate some salt if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)pucSalt, SALT_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; pTemp = NULL; goto Exit; } // This buffer needs to be a separate allocation because it is returned to // the caller. We will be returning the value of the SALT with the // encrypted key. The call to CCS_pbeEncrypt may return an extra 8 bytes. if( RC_BAD( rc = f_alloc( uiBufferSize + SALT_SZ + 8, &pTemp))) { goto Exit; } // Now to encrypt the buffer. algorithm.algorithm = (nuint8 *)oid_pbe; pParmInfo->count = 2; pParmInfo->parms[0].parmType = NICI_P_SALT; pParmInfo->parms[0].u.b.len = SALT_SZ; pParmInfo->parms[0].u.b.ptr = (nuint8 *)pucSalt; pParmInfo->parms[1].parmType = NICI_P_COUNT; pParmInfo->parms[1].u.value = SALT_COUNT; algorithm.parameter = pParmInfo; algorithm.parameterLen = sizeof( NICI_PARAMETER_DATA) * 2 + sizeof( FLMUINT32); uiEncLen = uiBufferSize + 8; if( RC_BAD( rc = CCS_pbeEncrypt( m_hContext, &algorithm, puzEncKeyPasswd, (nuint8 *)pExtractedKey, uiBufferSize, (nuint8 *)pTemp, &uiEncLen))) { rc = RC_SET( NE_XFLM_PBE_ENCRYPT_FAILED); m_hContext = 0; goto Exit; } *ppucExtractedKey = pTemp; // Now add the salt to the end of the buffer. pTemp += uiEncLen; f_memcpy( pTemp, pucSalt, SALT_SZ); pTemp = NULL; *pui32Length = uiEncLen + SALT_SZ; Exit: if( pTemp) { f_free( &pTemp); } if( pucKey) { f_free( &pExtractedKey); } f_mutexUnlock( m_hMutex); return(rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_NICICCS::injectKey( FLMBYTE * pszExtractedKey, FLMUINT32 ui32Length, FLMUNICODE * puzEncKeyPasswd) { RCODE rc = NE_XFLM_OK; NICI_ALGORITHM algorithm; NICI_ATTRIBUTE keyAttr[7]; FLMBYTE oid_sha1[] = {IDV_SHA1}; FLMBYTE oid_pbe[] = {IDV_pbeWithSHA1And3Key3xDES_CBC}; FLMUINT uiIndx; FLMBYTE ucDigest[ 20]; FLMUINT uiDigestLen = sizeof(ucDigest); FLMBYTE * pKey; FLMBYTE * pucFormat; EXTRACTED_KEY * pExtractedKey; FLMUINT uiEncLen; FLMBYTE * pTemp; FLMBYTE * pucBuffer = NULL; FLMBYTE * pucSalt; FLMUINT uiAllocSize; NICI_PARAMETER_INFO * pParmInfo = NULL; FLMBYTE * pucTempPtr; f_mutexLock( m_hMutex); // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // Extract the SALT from the key buffer. pucSalt = pszExtractedKey + (ui32Length - SALT_SZ); ui32Length -= SALT_SZ; // Make one allocation and point into it for the different buffers we need. uiAllocSize = ui32Length + sizeof( NICI_PARAMETER_DATA) * 2 + sizeof( FLMUINT32); if( RC_BAD( rc = f_calloc( uiAllocSize, &pucBuffer))) { goto Exit; } pParmInfo = (NICI_PARAMETER_INFO *)(pucBuffer + ui32Length); // Now to decrypt the buffer. algorithm.algorithm = (nuint8 *)oid_pbe; pParmInfo->count = 2; pParmInfo->parms[0].parmType = NICI_P_SALT; pParmInfo->parms[0].u.b.len = SALT_SZ; pParmInfo->parms[0].u.b.ptr = (nuint8 *)pucSalt; pParmInfo->parms[1].parmType = NICI_P_COUNT; pParmInfo->parms[1].u.value = SALT_COUNT; algorithm.parameter = pParmInfo; algorithm.parameterLen = sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); uiEncLen = ui32Length; if( RC_BAD( rc = CCS_pbeDecrypt( m_hContext, &algorithm, puzEncKeyPasswd, (nuint8 *)pszExtractedKey, ui32Length, (nuint8 *)pucBuffer, &uiEncLen))) { rc = RC_SET( NE_XFLM_PBE_DECRYPT_FAILED); m_hContext = 0; goto Exit; } // For cross platform compatibility, we need to first extract the KeyType, // FormatLen and KeyLen values then we will set them back again. They are // stored in a specific byte order, which may not match the native order for // referencing integers on the local platform. pExtractedKey = (EXTRACTED_KEY *)pucBuffer; pucTempPtr = pucBuffer; pExtractedKey->uiKeyType = FB2UD( pucTempPtr); pucTempPtr += 4; pExtractedKey->uiFormatLen = FB2UD( pucTempPtr); pucTempPtr += 4; pExtractedKey->uiKeyLen = FB2UD( pucTempPtr); pucTempPtr += 4; m_uiEncKeySize = FB2UD( pucTempPtr); // Calculate a SHA1 checksum. algorithm.algorithm = (nuint8 *)oid_sha1; algorithm.parameter = NULL; algorithm.parameterLen = 0; if( RC_BAD( rc = CCS_DigestInit( m_hContext, &algorithm))) { rc = RC_SET( NE_XFLM_DIGEST_INIT_FAILED); m_hContext = 0; goto Exit; } pTemp = (FLMBYTE *)&pExtractedKey[ 1]; if( RC_BAD( rc = CCS_Digest( m_hContext, (nuint8 *)pTemp, pExtractedKey->uiFormatLen + pExtractedKey->uiKeyLen, (nuint8 *)ucDigest, &uiDigestLen))) { rc = RC_SET( NE_XFLM_DIGEST_FAILED); m_hContext = 0; goto Exit; } flmAssert( uiDigestLen == sizeof( ucDigest)); // Now compare the two digests. They must be equal! pTemp += pExtractedKey->uiKeyLen + pExtractedKey->uiFormatLen; if( f_memcmp( pTemp, ucDigest, uiDigestLen)) { rc = RC_SET( NE_XFLM_INVALID_ENCKEY_CRC); goto Exit; } pucFormat = (FLMBYTE *)&pExtractedKey[ 1]; pKey = pucFormat + pExtractedKey->uiFormatLen; uiIndx = 0; f_memset( &keyAttr[0], 0, sizeof(NICI_ATTRIBUTE) * 7); switch( pExtractedKey->uiKeyType) { case NICI_K_AES: { uiIndx = 0; keyAttr[uiIndx].type = NICI_A_KEY_TYPE; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = NICI_K_AES; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; keyAttr[uiIndx].u.v.valuePtr = pucFormat; keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiFormatLen; keyAttr[uiIndx].u.v.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_USAGE; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_SIZE; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = m_uiEncKeySize; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_VALUE; keyAttr[uiIndx].u.v.valuePtr = pKey; keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiKeyLen; keyAttr[uiIndx].u.v.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_CLASS; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_GLOBAL; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = N_TRUE; keyAttr[uiIndx].u.f.valueInfo = 0; break; } case NICI_K_DES3X: { uiIndx = 0; keyAttr[uiIndx].type = NICI_A_KEY_TYPE; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = NICI_K_DES3X; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; keyAttr[uiIndx].u.v.valuePtr = pucFormat; keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiFormatLen; keyAttr[uiIndx].u.v.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_USAGE; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_SIZE; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = m_uiEncKeySize; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_KEY_VALUE; keyAttr[uiIndx].u.v.valuePtr = pKey; keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiKeyLen; keyAttr[uiIndx].u.v.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_CLASS; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; keyAttr[uiIndx].u.f.valueInfo = 0; uiIndx++; keyAttr[uiIndx].type = NICI_A_GLOBAL; keyAttr[uiIndx].u.f.hasValue = 1; keyAttr[uiIndx].u.f.value = N_TRUE; keyAttr[uiIndx].u.f.valueInfo = 0; break; } default: { flmAssert( 0); rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); goto Exit; } } if( RC_BAD( rc = CCS_InjectKey( m_hContext, &keyAttr[0], 7, &m_keyHandle))) { rc = RC_SET( NE_XFLM_INJECT_KEY_FAILED); m_hContext = 0; goto Exit; } Exit: if( pucBuffer) { f_free( &pucBuffer); } f_mutexUnlock( m_hMutex); return( rc); } /**************************************************************************** Desc: getIVLen returns the correct length of the IV for the type of algorithm. ****************************************************************************/ FLMUINT F_NICICCS::getIVLen() { switch( m_uiAlgType) { case FLM_NICI_AES: { return( IV_SZ); } case FLM_NICI_DES3: { return( IV_SZ8); } default: { return( 0); } } } /**************************************************************************** Desc: generateIV will generate a random set of bytes to be used as IV. ****************************************************************************/ RCODE F_NICICCS::generateIV( FLMUINT uiIVLen, FLMBYTE * pucIV) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; NICI_ALGORITHM algorithm; FLMBYTE oid_sha1[] = {IDV_SHA1}; FLMBOOL bLocked = FALSE; FLMBYTE * pucIVPtr = m_ucRndIV; FLMBYTE ucIVBuffer[ IV_SZ * 2]; FLMUINT uiIVBufferLen = sizeof(ucIVBuffer); if( !uiIVLen) { goto Exit; } f_mutexLock( m_hMutex); bLocked = TRUE; // Create NICI Context if( !m_hContext) { if( RC_BAD( rc = CCS_CreateContext( 0, &m_hContext))) { rc = RC_SET( NE_XFLM_NICI_CONTEXT); m_hContext = 0; goto Exit; } } // See if it is time to reinitialize the Random IV. if( (m_uiIVFactor & 0x07FF) == 0) { // Generate the Random IV if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)&m_ucRndIV, IV_SZ))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } // Generate an adjustment factor for the IV if( RC_BAD( rc = CCS_GetRandom( m_hContext, (nuint8 *)&m_uiIVFactor, sizeof( FLMUINT)))) { rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); m_hContext = 0; goto Exit; } } // Increment each byte of the IV by the IV Factor for( uiLoop = 0; uiLoop < IV_SZ; uiLoop++) { (*pucIVPtr) += (FLMBYTE)m_uiIVFactor; pucIVPtr++; } // Now run the resulting IV through a SHA1 digest. algorithm.algorithm = (nuint8 *)oid_sha1; algorithm.parameter = NULL; algorithm.parameterLen = 0; if( RC_BAD( rc = CCS_DigestInit( m_hContext, &algorithm))) { rc = RC_SET( NE_XFLM_DIGEST_INIT_FAILED); m_hContext = 0; goto Exit; } if( RC_BAD( rc = CCS_Digest( m_hContext, (nuint8 *)m_ucRndIV, uiIVLen, (nuint8 *)ucIVBuffer, &uiIVBufferLen))) { rc = RC_SET( NE_XFLM_DIGEST_FAILED); m_hContext = 0; goto Exit; } // Return the new IV! f_memcpy( pucIV, ucIVBuffer, uiIVLen); m_uiIVFactor++; Exit: if( bLocked) { f_mutexUnlock( m_hMutex); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE flmAllocCCS( IF_CCS ** ppCCS) { RCODE rc = NE_XFLM_OK; F_NICICCS * pCCS = NULL; f_assert( (*ppCCS) == NULL); if( (pCCS = f_new F_NICICCS) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } *ppCCS = pCCS; pCCS = NULL; Exit: if( pCCS) { pCCS->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ #if !defined( FLM_UNIX) int CCSX_SetNewIV( int, // MODULEID FLMUINT32, // hContext pnuint8, // IV nuint32) // IVLen { return( NICI_E_FUNCTION_NOT_SUPPORTED); } #endif #endif /**************************************************************************** Desc: ****************************************************************************/ #ifndef FLM_USE_NICI void f_nici_dummy( void) { } #endif libxflaim-5.1.969/src/flclose.cpp0000644000175000017500000001161710511001742020165 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Contains the destructor for the F_Db object. // // Tabs: 3 // // Copyright (c) 1990-1992, 1995-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flclose.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Destructor for F_Db object ****************************************************************************/ F_Db::~F_Db() { if( m_eTransType != XFLM_NO_TRANS) { // Someone forgot to close their transaction! RC_UNEXPECTED_ASSERT( NE_XFLM_TRANS_ACTIVE); transAbort(); } // Free the super file. if (m_pSFileHdl) { // Opened files will be released back to the // file handle manager m_pSFileHdl->Release(); } // Free up statistics. if (m_bStatsInitialized) { flmStatFree( &m_Stats); } // Return the cached B-Tree (if any) to the // global pool if( m_pCachedBTree) { gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pCachedBTree); } if( m_pKrefTbl) { f_free( &m_pKrefTbl); m_uiKrefTblSize = 0; } if( m_pucKrefKeyBuf) { f_free( &m_pucKrefKeyBuf); } if (m_pKeyColl) { m_pKeyColl->Release(); } if (m_pIxClient) { m_pIxClient->Release(); } if (m_pIxStatus) { m_pIxStatus->Release(); } if (m_pDeleteStatus) { m_pDeleteStatus->Release(); } if (m_pCommitClient) { m_pCommitClient->Release(); } if (m_pOldNodeList) { m_pOldNodeList->Release(); } if (m_hWaitSem != F_SEM_NULL) { f_semDestroy( &m_hWaitSem); } m_tmpKrefPool.poolFree(); m_tempPool.poolFree(); // Unlink the F_Db from the F_Database and F_Dict structures. // IMPORTANT NOTE: The call to unlinkFromDatabase needs to // be the last thing that is done, because it may unlock // and relock the mutex. if (m_pDatabase) { m_pDatabase->lockMutex(); unlinkFromDict(); m_pDatabase->unlockMutex(); f_mutexLock( gv_XFlmSysData.hShareMutex); unlinkFromDatabase(); f_mutexUnlock( gv_XFlmSysData.hShareMutex); } } /**************************************************************************** Desc: Wait for a specific database to close ****************************************************************************/ RCODE FLMAPI F_DbSystem::waitToClose( const char * pszDbPath) { RCODE rc = NE_XFLM_OK; F_BUCKET * pBucket; FLMUINT uiBucket; F_Database * pDatabase = NULL; char szDbPathStr1[ F_PATH_MAX_SIZE]; FLMBOOL bMutexLocked = FALSE; F_SEM hWaitSem = F_SEM_NULL; if( RC_BAD( rc = f_semCreate( &hWaitSem))) { goto Exit; } if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathToStorageString( pszDbPath, szDbPathStr1))) { goto Exit; } Retry: if( !bMutexLocked) { f_mutexLock( gv_XFlmSysData.hShareMutex); bMutexLocked = TRUE; } pBucket = gv_XFlmSysData.pDatabaseHashTbl; uiBucket = f_strHashBucket( szDbPathStr1, pBucket, FILE_HASH_ENTRIES); pDatabase = (F_Database *)pBucket [uiBucket].pFirstInBucket; while( pDatabase) { // Compare the strings. On non-Unix platforms we must use // f_stricmp, because case does not matter for file names // on those platforms. #ifdef FLM_UNIX if( f_strcmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) #else if( f_stricmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) #endif { break; } pDatabase = pDatabase->m_pNext; } if( !pDatabase) { // Didn't find a matching database. We are done. goto Exit; } // If the file is in the process of being opened by another // thread, wait for the open to complete. if( pDatabase->m_uiFlags & DBF_BEING_OPENED) { f_notifyWait( gv_XFlmSysData.hShareMutex, hWaitSem, NULL, &pDatabase->m_pOpenNotifies); goto Retry; } else { // The database is open. Put ourselves into the close notify list // so that we will wake up when the database has been closed. if( RC_BAD( rc = f_notifyWait( gv_XFlmSysData.hShareMutex, hWaitSem, NULL, &pDatabase->m_pCloseNotifies))) { goto Exit; } } Exit: if( bMutexLocked) { f_mutexUnlock( gv_XFlmSysData.hShareMutex); } if( hWaitSem != F_SEM_NULL) { f_semDestroy( &hWaitSem); } return( rc); } libxflaim-5.1.969/src/translog.cpp0000644000175000017500000001761710511001742020375 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Routines to handle transaction logging // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: translog.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC void FLMAPI lgWriteComplete( IF_IOBuffer * pIOBuffer, void * pvData); #ifdef FLM_DBG_LOG /**************************************************************************** Desc: This routine is used to write out information about logged blocks to the log file. ****************************************************************************/ void scaLogWrite( F_Database * pDatabase, FLMUINT uiWriteAddress, FLMBYTE * pucBlkBuf, FLMUINT uiBufferLen, FLMUINT uiBlockSize, char * pszEvent ) { FLMUINT uiOffset = 0; FLMUINT32 ui32BlkAddr; FLMUINT64 ui64TransID; while (uiOffset < uiBufferLen) { ui32BlkAddr = ((F_BLK_HDR *)pucBlkBuf)->ui32BlkAddr; ui64TransID = ((F_BLK_HDR *)pucBlkBuf)->ui64TransID; // A uiWriteAddress of zero means we are writing exactly at the // block address - i.e., it is the data block, not the log block. flmDbgLogWrite( pDatabase, (FLMUINT)ui32BlkAddr, (FLMUINT)((uiWriteAddress) ? uiWriteAddress + uiOffset : (FLMUINT)ui32BlkAddr), ui64TransID, pszEvent); uiOffset += uiBlockSize; pucBlkBuf += uiBlockSize; } } #endif /**************************************************************************** Desc: This is the callback routine that is called when a disk write is completed. ****************************************************************************/ FSTATIC void FLMAPI lgWriteComplete( IF_IOBuffer * pIOBuffer, void * pvData) { F_Database * pDatabase = (F_Database *)pIOBuffer->getCallbackData( 0); #ifdef FLM_DBG_LOG FLMUINT uiBlockSize = pDatabase->getBlockSize(); FLMUINT uiLength = pIOBuffer->getBufferSize(); char * pszEvent; #endif XFLM_DB_STATS * pDbStats = (XFLM_DB_STATS *)pvData; #ifdef FLM_DBG_LOG pszEvent = (char *)(RC_OK( pIOBuffer->getCompletionCode()) ? (char *)"LGWRT" : (char *)"LGWRT-FAIL"); scaLogWrite( pDatabase, 0, pIOBuffer->getBuffer(), uiLength, uiBlockSize, pszEvent); #endif if (pDbStats) { // Must lock mutex, because this may be called from async write // completion at any time. pDatabase->lockMutex(); pDbStats->LogBlockWrites.ui64ElapMilli += pIOBuffer->getElapsedTime(); pDatabase->unlockMutex(); } } /**************************************************************************** Desc: This routine flushes a log buffer to the log file. ****************************************************************************/ RCODE F_Database::lgFlushLogBuffer( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl) { RCODE rc = NE_XFLM_OK; if (pDbStats) { pDbStats->bHaveStats = TRUE; pDbStats->LogBlockWrites.ui64Count++; pDbStats->LogBlockWrites.ui64TotalBytes += m_uiCurrLogWriteOffset; } m_pCurrLogBuffer->setCompletionCallback( lgWriteComplete, pDbStats); m_pCurrLogBuffer->addCallbackData( (void *)this); pSFileHdl->setMaxAutoExtendSize( m_uiMaxFileSize); pSFileHdl->setExtendSize( m_uiFileExtendSize); // NOTE: No guarantee that m_pCurrLogBuffer will still be around // after the call to writeBlock, unless we are doing // non-asynchronous write. if( RC_BAD( rc = pSFileHdl->writeBlock( m_uiCurrLogBlkAddr, m_uiCurrLogWriteOffset, m_pCurrLogBuffer))) { if (pDbStats) { pDbStats->uiWriteErrors++; } goto Exit; } Exit: m_uiCurrLogWriteOffset = 0; m_pCurrLogBuffer->Release(); m_pCurrLogBuffer = NULL; return( rc); } /**************************************************************************** Desc: This routine writes a block to the log file. ****************************************************************************/ RCODE F_Database::lgOutputBlock( XFLM_DB_STATS * pDbStats, F_SuperFileHdl * pSFileHdl, F_CachedBlock * pLogBlock, // Cached log block. F_BLK_HDR * pBlkHdr, // Pointer to the corresponding modified // block in cache. This block will be // modified to the logged version of // the block FLMUINT * puiLogEofRV) // Returns log EOF { RCODE rc = NE_XFLM_OK; FLMUINT uiFilePos = *puiLogEofRV; FLMBYTE * pucLogBlk; F_BLK_HDR * pLogBlkHdr; FLMUINT uiBlkAddress; FLMUINT uiLogBufferSize; // Time for a new block file? if (FSGetFileOffset( uiFilePos) >= m_uiMaxFileSize) { FLMUINT uiFileNumber; // Write out the current buffer, if it has anything in it. if (m_uiCurrLogWriteOffset) { if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl))) { goto Exit; } } uiFileNumber = FSGetFileNumber( uiFilePos); if (!uiFileNumber) { uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; } else { uiFileNumber++; } if (uiFileNumber > MAX_LOG_BLOCK_FILE_NUMBER) { rc = RC_SET( NE_XFLM_DB_FULL); goto Exit; } if (RC_BAD( rc = pSFileHdl->createFile( uiFileNumber ))) { goto Exit; } uiFilePos = FSBlkAddress( uiFileNumber, 0 ); } // Copy the log block to the log buffer. if (!m_uiCurrLogWriteOffset) { m_uiCurrLogBlkAddr = uiFilePos; // Get a buffer for logging. // NOTE: Buffers are not kept by the F_Database's buffer manager, // so once we are done with this buffer, it will be freed. uiLogBufferSize = MAX_LOG_BUFFER_SIZE; for( ;;) { if (RC_BAD( rc = m_pBufferMgr->getBuffer( uiLogBufferSize, &m_pCurrLogBuffer))) { // If we failed to get a buffer of the requested size, // reduce the buffer size by half and try again. if( rc == NE_XFLM_MEM) { uiLogBufferSize /= 2; if( uiLogBufferSize < m_uiBlockSize) { goto Exit; } rc = NE_XFLM_OK; continue; } goto Exit; } break; } } // Copy data from log block to the log buffer pucLogBlk = m_pCurrLogBuffer->getBufferPtr() + m_uiCurrLogWriteOffset; pLogBlkHdr = (F_BLK_HDR *)pucLogBlk; f_memcpy( pLogBlkHdr, pLogBlock->m_pBlkHdr, m_uiBlockSize); // If we are logging this block for the current update // transaction, set the BEFORE IMAGE (BI) flag in the block header // so we will know that this block is a before image block that // needs to be restored when aborting the current update // transaction. if (pLogBlock->m_ui16Flags & CA_WRITE_TO_LOG) { pLogBlkHdr->ui8BlkFlags |= BLK_IS_BEFORE_IMAGE; } uiBlkAddress = (FLMUINT)pLogBlkHdr->ui32BlkAddr; // Encrypt the block if needed if (RC_BAD( rc = encryptBlock( m_pDictList, (FLMBYTE *)pLogBlkHdr))) { goto Exit; } if (RC_BAD( rc = flmPrepareBlockToWrite( m_uiBlockSize, pLogBlkHdr))) { goto Exit; } // Set up for next log block write m_uiCurrLogWriteOffset += m_uiBlockSize; // If this log buffer is full, write it out. if (m_uiCurrLogWriteOffset == m_pCurrLogBuffer->getBufferSize()) { if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl))) { goto Exit; } } // Save the previous block address into the modified block's // block header area. Also save the transaction id. pBlkHdr->ui32PriorBlkImgAddr = (FLMUINT32)uiFilePos; *puiLogEofRV = uiFilePos + m_uiBlockSize; Exit: return( rc); } libxflaim-5.1.969/src/fxpath.cpp0000644000175000017500000014335110511001742020031 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Methods for parsing and evaluating XPATH queries // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fxpath.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #include "flaimsys.h" FSTATIC RCODE addCallbackFunc( IF_Query * pQuery); /**************************************************************************** Desc: Setup the tokenizer ****************************************************************************/ RCODE F_XPathTokenizer::setup( IF_IStream * pIStream) { if( m_pIStream) { m_pIStream->Release(); m_pIStream = NULL; } if( pIStream) { m_pIStream = pIStream; m_pIStream->AddRef(); } return( NE_XFLM_OK); } /**************************************************************************** Desc: Skips any whitespace characters starting at the current location ****************************************************************************/ RCODE F_XPathTokenizer::skipWhitespace( void) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; for( ;;) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( !gv_XFlmSysData.pXml->isWhitespace( uChar)) { if( RC_BAD( rc = ungetChar( uChar))) { goto Exit; } break; } } Exit: return( rc); } /**************************************************************************** Desc: Reads the next character from the stream. If no more characters are available, a NULL (0) character will be returned ****************************************************************************/ RCODE F_XPathTokenizer::getChar( FLMUNICODE * puChar) { RCODE rc = NE_XFLM_OK; if( m_uiUngetCount) { *puChar = m_uUngetBuf[ --m_uiUngetCount]; } else { if( RC_BAD( rc = f_readUTF8CharAsUnicode( m_pIStream, puChar))) { if( rc != NE_XFLM_EOF_HIT) { goto Exit; } rc = NE_XFLM_OK; *puChar = 0; } } Exit: return( rc); } /**************************************************************************** Desc: Returns the next character that will be read from the stream. If no more characters are available, a NULL (0) character will be returned ****************************************************************************/ RCODE F_XPathTokenizer::peekChar( FLMUNICODE * puChar) { RCODE rc = NE_XFLM_OK; if( m_uiUngetCount) { *puChar = m_uUngetBuf[ m_uiUngetCount - 1]; } else { if( RC_BAD( rc = f_readUTF8CharAsUnicode( m_pIStream, puChar))) { if( rc == NE_XFLM_EOF_HIT) { *puChar = 0; rc = NE_XFLM_OK; } goto Exit; } if( RC_BAD( rc = ungetChar( *puChar))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: Pushes the passed-in character onto the unget stack ****************************************************************************/ RCODE F_XPathTokenizer::ungetChar( FLMUNICODE uChar) { RCODE rc = NE_XFLM_OK; if( !uChar) { goto Exit; } if( m_uiUngetCount == XPATH_MAX_UNGET_CHARS) { rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); goto Exit; } m_uUngetBuf[ m_uiUngetCount++] = uChar; Exit: return( rc); } /**************************************************************************** Desc: Gets the next XPATH token from the query stream ****************************************************************************/ RCODE F_XPathTokenizer::getNextToken( F_XPathToken * pToken) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMBOOL bConsumeParens = FALSE; pToken->reset(); if( RC_BAD( rc = skipWhitespace())) { goto Exit; } if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } switch( uChar) { case 0: { pToken->m_eTokenType = END_TOKEN; break; } case FLM_UNICODE_LPAREN: { pToken->m_eTokenType = OP_LPAREN_TOKEN; break; } case FLM_UNICODE_RPAREN: { pToken->m_eTokenType = OP_RPAREN_TOKEN; break; } case FLM_UNICODE_LBRACKET: { pToken->m_eTokenType = OP_LBRACKET_TOKEN; break; } case FLM_UNICODE_RBRACKET: { pToken->m_eTokenType = OP_RBRACKET_TOKEN; break; } case FLM_UNICODE_PERIOD: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_PERIOD) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = DOUBLE_PERIOD_TOKEN; } else { pToken->m_eTokenType = PERIOD_TOKEN; } break; } case FLM_UNICODE_ATSIGN: { pToken->m_eTokenType = AXIS_ATSIGN_TOKEN; pToken->m_ui64Val = (FLMUINT64)ATTRIBUTE_AXIS; break; } case FLM_UNICODE_COMMA: { pToken->m_eTokenType = COMMA_TOKEN; break; } case FLM_UNICODE_COLON: { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( uChar != FLM_UNICODE_COLON) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } pToken->m_eTokenType = DOUBLE_COLON_TOKEN; break; } case FLM_UNICODE_FSLASH: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_FSLASH) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = OP_DOUBLE_FSLASH_TOKEN; } else { pToken->m_eTokenType = OP_FSLASH_TOKEN; } break; } case FLM_UNICODE_PIPE: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_PIPE) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = OP_OR_TOKEN; } else { pToken->m_eTokenType = OP_UNION_TOKEN; } break; } case FLM_UNICODE_AMP: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_AMP) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = OP_AND_TOKEN; } else { pToken->m_eTokenType = OP_BITAND_TOKEN; } break; } case FLM_UNICODE_PLUS: { pToken->m_eTokenType = OP_PLUS_TOKEN; break; } case FLM_UNICODE_HYPHEN: { pToken->m_eTokenType = OP_MINUS_TOKEN; break; } case FLM_UNICODE_TILDE: { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( uChar != FLM_UNICODE_EQ) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } pToken->m_eTokenType = OP_APPROX_EQ_TOKEN; break; } case FLM_UNICODE_EQ: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } // Check for double equal and allow it // in expressions if( uChar == FLM_UNICODE_EQ) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } } pToken->m_eTokenType = OP_EQ_TOKEN; break; } case FLM_UNICODE_BANG: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_EQ) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = OP_NE_TOKEN; } else { pToken->m_eTokenType = OP_NOT_TOKEN; } break; } case FLM_UNICODE_LT: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_EQ) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = OP_LE_TOKEN; } else { pToken->m_eTokenType = OP_LT_TOKEN; } break; } case FLM_UNICODE_GT: { if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_EQ) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = OP_GE_TOKEN; } else { pToken->m_eTokenType = OP_GT_TOKEN; } break; } case FLM_UNICODE_ASTERISK: { if( m_eLastTokenType != UNKNOWN_TOKEN && m_eLastTokenType != AXIS_ATSIGN_TOKEN && m_eLastTokenType != DOUBLE_COLON_TOKEN && m_eLastTokenType != OP_LPAREN_TOKEN && m_eLastTokenType != OP_LBRACKET_TOKEN && m_eLastTokenType != COMMA_TOKEN && !isOperator( m_eLastTokenType)) { pToken->m_eTokenType = OP_MULT_TOKEN; } else { pToken->m_eTokenType = NAME_TEST_WILD_TOKEN; } break; } case FLM_UNICODE_DOLLAR: { if( RC_BAD( rc = getName( pToken))) { goto Exit; } pToken->m_eTokenType = VAR_REF_TOKEN; break; } case FLM_UNICODE_0: case FLM_UNICODE_1: case FLM_UNICODE_2: case FLM_UNICODE_3: case FLM_UNICODE_4: case FLM_UNICODE_5: case FLM_UNICODE_6: case FLM_UNICODE_7: case FLM_UNICODE_8: case FLM_UNICODE_9: { if( RC_BAD( rc = ungetChar( uChar))) { goto Exit; } if( RC_BAD( rc = getNumber( pToken))) { goto Exit; } pToken->m_eTokenType = NUMBER_TOKEN; break; } case FLM_UNICODE_APOS: case FLM_UNICODE_QUOTE: { if( RC_BAD( rc = ungetChar( uChar))) { goto Exit; } if( RC_BAD( rc = getLiteral( pToken))) { goto Exit; } pToken->m_eTokenType = LITERAL_TOKEN; break; } case FLM_UNICODE_LBRACE: { pToken->m_eTokenType = LBRACE_TOKEN; break; } case FLM_UNICODE_RBRACE: { pToken->m_eTokenType = RBRACE_TOKEN; break; } default: { if( RC_BAD( rc = ungetChar( uChar))) { goto Exit; } if( RC_BAD( rc = getName( pToken))) { goto Exit; } if( pToken->m_puzPrefix) { pToken->m_eTokenType = NAME_TEST_QNAME_TOKEN; goto Exit; } if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } // Node type or function name? if( uChar == FLM_UNICODE_LPAREN) { switch( pToken->m_puzLocal[ 0]) { case FLM_UNICODE_b: if( f_uninativecmp( pToken->m_puzLocal, "binary") == 0) { pToken->m_eTokenType = BINARY_TOKEN; if (RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( RC_BAD( rc = skipWhitespace())) { goto Exit; } if (RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if (uChar != FLM_UNICODE_APOS && uChar != FLM_UNICODE_QUOTE) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = getBinary( pToken))) { goto Exit; } if( RC_BAD( rc = skipWhitespace())) { goto Exit; } if (RC_BAD( rc = getChar( &uChar))) { goto Exit; } if (uChar != FLM_UNICODE_RPAREN) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_c: if( f_uninativecmp( pToken->m_puzLocal, "ceiling") == 0) { pToken->m_eTokenType = FUNC_CEILING_TOKEN; } if( f_uninativecmp( pToken->m_puzLocal, "comment") == 0) { pToken->m_eTokenType = NODE_TYPE_COMMENT_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "concat") == 0) { pToken->m_eTokenType = FUNC_CONCAT_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "contains") == 0) { pToken->m_eTokenType = FUNC_CONTAINS_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "count") == 0) { pToken->m_eTokenType = FUNC_COUNT_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_f: if( f_uninativecmp( pToken->m_puzLocal, "false") == 0) { pToken->m_eTokenType = FUNC_FALSE_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "floor") == 0) { pToken->m_eTokenType = FUNC_FLOOR_TOKEN; } else if (f_uninativecmp( pToken->m_puzLocal, "funcCB") == 0) { pToken->m_eTokenType = FUNC_CB_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_i: if( f_uninativecmp( pToken->m_puzLocal, "id") == 0) { pToken->m_eTokenType = FUNC_ID_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_l: if( f_uninativecmp( pToken->m_puzLocal, "lang") == 0) { pToken->m_eTokenType = FUNC_LANG_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "last") == 0) { pToken->m_eTokenType = FUNC_LAST_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "local-name") == 0) { pToken->m_eTokenType = FUNC_LOCAL_NAME_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_n: if( f_uninativecmp( pToken->m_puzLocal, "name") == 0) { pToken->m_eTokenType = FUNC_NAME_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "namespace-uri") == 0) { pToken->m_eTokenType = FUNC_NAMESPACE_URI_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "node") == 0) { pToken->m_eTokenType = NODE_TYPE_NODE_TOKEN; bConsumeParens = TRUE; } else if( f_uninativecmp( pToken->m_puzLocal, "normalize-space") == 0) { pToken->m_eTokenType = FUNC_NORM_SPACE_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "not") == 0) { pToken->m_eTokenType = FUNC_NOT_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "number") == 0) { pToken->m_eTokenType = FUNC_NUMBER_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_p: if( f_uninativecmp( pToken->m_puzLocal, "position") == 0) { pToken->m_eTokenType = FUNC_POSITION_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "processing-instruction") == 0) { pToken->m_eTokenType = NODE_TYPE_PI_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_r: if( f_uninativecmp( pToken->m_puzLocal, "round") == 0) { pToken->m_eTokenType = FUNC_ROUND_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_s: if( f_uninativecmp( pToken->m_puzLocal, "starts-with") == 0) { pToken->m_eTokenType = FUNC_STARTS_WITH_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "string") == 0) { pToken->m_eTokenType = FUNC_STRING_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "string-length") == 0) { pToken->m_eTokenType = FUNC_STR_LEN_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "substring") == 0) { pToken->m_eTokenType = FUNC_SUBSTR_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "substring-after") == 0) { pToken->m_eTokenType = FUNC_SUBSTR_AFTER_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "substring-before") == 0) { pToken->m_eTokenType = FUNC_SUBSTR_BEFORE_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "sum") == 0) { pToken->m_eTokenType = FUNC_SUM_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_t: if( f_uninativecmp( pToken->m_puzLocal, "text") == 0) { pToken->m_eTokenType = NODE_TYPE_TEXT_TOKEN; bConsumeParens = TRUE; } else if( f_uninativecmp( pToken->m_puzLocal, "translate") == 0) { pToken->m_eTokenType = FUNC_TRANSLATE_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "true") == 0) { pToken->m_eTokenType = FUNC_TRUE_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_u: if( f_uninativecmp( pToken->m_puzLocal, "unknown") == 0) { pToken->m_eTokenType = FUNC_UNKNOWN_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; default: rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } goto Exit; } // Axis specifier or node name? if( uChar == FLM_UNICODE_COLON) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( RC_BAD( rc = peekChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_COLON) { if( RC_BAD( rc = ungetChar( FLM_UNICODE_COLON))) { goto Exit; } // If we have reached this point, we have a name followed // by '::'. Verify that the name is a valid axis specifier. switch( pToken->m_puzLocal[ 0]) { case FLM_UNICODE_a: if( f_uninativecmp( pToken->m_puzLocal, "ancestor") == 0) { pToken->m_eTokenType = AXIS_ANCESTOR_TOKEN; pToken->m_ui64Val = (FLMUINT64)ANCESTOR_AXIS; } else if( f_uninativecmp( pToken->m_puzLocal, "ancestor-or-self") == 0) { pToken->m_eTokenType = AXIS_ANCESTOR_OR_SELF_TOKEN; pToken->m_ui64Val = (FLMUINT64)ANCESTOR_OR_SELF_AXIS; } else if( f_uninativecmp( pToken->m_puzLocal, "attribute") == 0) { pToken->m_eTokenType = AXIS_ATTRIB_TOKEN; pToken->m_ui64Val = (FLMUINT64)ATTRIBUTE_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_c: if( f_uninativecmp( pToken->m_puzLocal, "child") == 0) { pToken->m_eTokenType = AXIS_CHILD_TOKEN; pToken->m_ui64Val = (FLMUINT64)CHILD_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_d: if( f_uninativecmp( pToken->m_puzLocal, "descendant") == 0) { pToken->m_eTokenType = AXIS_DESCENDANT_TOKEN; pToken->m_ui64Val = (FLMUINT64)DESCENDANT_AXIS; } else if( f_uninativecmp( pToken->m_puzLocal, "descendant-or-self") == 0) { pToken->m_eTokenType = AXIS_DESCENDANT_OR_SELF_TOKEN; pToken->m_ui64Val = (FLMUINT64)DESCENDANT_OR_SELF_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_f: if( f_uninativecmp( pToken->m_puzLocal, "following") == 0) { pToken->m_eTokenType = AXIS_FOLLOWING_TOKEN; pToken->m_ui64Val = (FLMUINT64)FOLLOWING_AXIS; } else if( f_uninativecmp( pToken->m_puzLocal, "following-sibling") == 0) { pToken->m_eTokenType = AXIS_FOLLOWING_SIB_TOKEN; pToken->m_ui64Val = (FLMUINT64)FOLLOWING_SIBLING_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_m: if( f_uninativecmp( pToken->m_puzLocal, "meta") == 0) { pToken->m_eTokenType = AXIS_META_TOKEN; pToken->m_ui64Val = (FLMUINT64)META_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_n: if( f_uninativecmp( pToken->m_puzLocal, "namespace") == 0) { pToken->m_eTokenType = AXIS_NAMESPACE_TOKEN; pToken->m_ui64Val = (FLMUINT64)NAMESPACE_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_p: if( f_uninativecmp( pToken->m_puzLocal, "parent") == 0) { pToken->m_eTokenType = AXIS_PARENT_TOKEN; pToken->m_ui64Val = (FLMUINT64)PARENT_AXIS; } else if( f_uninativecmp( pToken->m_puzLocal, "preceding") == 0) { pToken->m_eTokenType = AXIS_PRECEDING_TOKEN; pToken->m_ui64Val = (FLMUINT64)PRECEDING_AXIS; } else if( f_uninativecmp( pToken->m_puzLocal, "preceding-sibling") == 0) { pToken->m_eTokenType = AXIS_PRECEDING_SIB_TOKEN; pToken->m_ui64Val = (FLMUINT64)PRECEDING_SIBLING_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; case FLM_UNICODE_s: if( f_uninativecmp( pToken->m_puzLocal, "self") == 0) { pToken->m_eTokenType = AXIS_SELF_TOKEN; pToken->m_ui64Val = (FLMUINT64)SELF_AXIS; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; default: rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } goto Exit; } else if( uChar == FLM_UNICODE_ASTERISK) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } pToken->m_eTokenType = NAME_TEST_NCWILD_TOKEN; goto Exit; } else { if( RC_BAD( rc = ungetChar( FLM_UNICODE_COLON))) { goto Exit; } } } // Operator? if( m_eLastTokenType != UNKNOWN_TOKEN && m_eLastTokenType != LBRACE_TOKEN && m_eLastTokenType != RBRACE_TOKEN && m_eLastTokenType != DOUBLE_COLON_TOKEN && m_eLastTokenType != OP_LPAREN_TOKEN && m_eLastTokenType != OP_LBRACKET_TOKEN && m_eLastTokenType != COMMA_TOKEN && !isOperator( m_eLastTokenType) && !isAxisSpecifier( m_eLastTokenType)) { if( f_uninativecmp( pToken->m_puzLocal, "and") == 0) { pToken->m_eTokenType = OP_AND_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "or") == 0) { pToken->m_eTokenType = OP_OR_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "bitor") == 0) { pToken->m_eTokenType = OP_BITOR_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "bitand") == 0) { pToken->m_eTokenType = OP_BITAND_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "bitxor") == 0) { pToken->m_eTokenType = OP_BITXOR_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "mod") == 0) { pToken->m_eTokenType = OP_MOD_TOKEN; } else if( f_uninativecmp( pToken->m_puzLocal, "div") == 0) { pToken->m_eTokenType = OP_DIV_TOKEN; } else { rc = RC_SET( NE_XFLM_SYNTAX); } goto Exit; } // None of the above conditions matched, so we have a QName pToken->m_eTokenType = NAME_TEST_QNAME_TOKEN; break; } } // Get the operator flags (if any) if( tokenCanHaveFlags( pToken->m_eTokenType)) { FLMUNICODE uzTmpBuf[ 64]; FLMUNICODE uTmpChar; FLMUINT uiOffset; if( RC_BAD( rc = skipWhitespace())) { goto Exit; } if( RC_BAD( rc = peekChar( &uTmpChar))) { goto Exit; } if( uTmpChar == FLM_UNICODE_LBRACE) { if( RC_BAD( rc = getChar( &uTmpChar))) { goto Exit; } for( ;;) { if( RC_BAD( rc = skipWhitespace())) { goto Exit; } uiOffset = 0; for( ;;) { if( RC_BAD( rc = getChar( &uTmpChar))) { goto Exit; } if( !gv_XFlmSysData.pXml->isLetter( uTmpChar) && uTmpChar != FLM_UNICODE_UNDERSCORE) { uzTmpBuf[ uiOffset] = 0; if( f_uninativecmp( uzTmpBuf, "ci") == 0 || f_uninativecmp( uzTmpBuf, "caseinsensitive") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_CASE_INSENSITIVE; } else if( f_uninativecmp( uzTmpBuf, "cs") == 0 || f_uninativecmp( uzTmpBuf, "compressspace") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_COMPRESS_WHITESPACE; } else if( f_uninativecmp( uzTmpBuf, "ignls") == 0 || f_uninativecmp( uzTmpBuf, "ignoreleadingspace") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_IGNORE_LEADING_SPACE; } else if( f_uninativecmp( uzTmpBuf, "ignts") == 0 || f_uninativecmp( uzTmpBuf, "ignoretrailingspace") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_IGNORE_TRAILING_SPACE; } else if( f_uninativecmp( uzTmpBuf, "wstosp") == 0 || f_uninativecmp( uzTmpBuf, "whitespaceasspace") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_WHITESPACE_AS_SPACE; } else if( f_uninativecmp( uzTmpBuf, "ns") == 0 || f_uninativecmp( uzTmpBuf, "nospace") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_NO_WHITESPACE; } else if( f_uninativecmp( uzTmpBuf, "nu") == 0 || f_uninativecmp( uzTmpBuf, "nounderscores") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_NO_UNDERSCORES; } else if( f_uninativecmp( uzTmpBuf, "nd") == 0 || f_uninativecmp( uzTmpBuf, "nodashes") == 0) { pToken->m_uiTokenFlags |= XFLM_COMP_NO_DASHES; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } break; } uzTmpBuf[ uiOffset++] = uTmpChar; } if( !uTmpChar) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } else if( uTmpChar == FLM_UNICODE_RBRACE) { break; } else if( uTmpChar != FLM_UNICODE_COMMA) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } } } Exit: m_eLastTokenType = pToken->m_eTokenType; // Are we expecting a () sequence after the token that we need to // just consume? if (RC_OK( rc) && bConsumeParens) { if( RC_OK( rc = skipWhitespace())) { if( RC_OK( rc = getChar( &uChar))) { if (uChar != FLM_UNICODE_LPAREN) { rc = RC_SET( NE_XFLM_SYNTAX); } else if (RC_OK( rc = skipWhitespace())) { if (RC_OK( rc = getChar( &uChar))) { if (uChar != FLM_UNICODE_RPAREN) { rc = RC_SET( NE_XFLM_SYNTAX); } } } } } } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XPathTokenizer::getName( F_XPathToken * pToken) { RCODE rc = NE_XFLM_OK; FLMUINT uiOffset; FLMBOOL bFoundColon = FALSE; FLMUINT uiMaxChars; FLMUNICODE uChar; FLMUNICODE uTmpChar; uiOffset = 0; if( (uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE)) < 32) { if( RC_BAD( rc = pToken->resizeBuffer( 32 * sizeof( FLMUNICODE)))) { goto Exit; } uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE); } pToken->m_puzLocal = (FLMUNICODE *)pToken->m_pValBuf; if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( !gv_XFlmSysData.pXml->isLetter( uChar) && uChar != FLM_UNICODE_UNDERSCORE) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } ((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset++] = uChar; for( ;;) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( uiOffset == uiMaxChars) { if( RC_BAD( rc = pToken->resizeBuffer( pToken->m_uiValBufSize * sizeof( FLMUNICODE) * 2))) { goto Exit; } uiMaxChars *= 2; } if( uChar == FLM_UNICODE_COLON) { if( bFoundColon) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = peekChar( &uTmpChar))) { goto Exit; } if( !gv_XFlmSysData.pXml->isNCNameChar( uTmpChar)) { break; } uChar = 0; pToken->m_puzPrefix = (FLMUNICODE *)pToken->m_pValBuf; pToken->m_puzLocal = &(((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset + 1]); bFoundColon = TRUE; } else if( !gv_XFlmSysData.pXml->isNCNameChar( uChar)) { break; } ((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset++] = uChar; } ((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset] = 0; if( bFoundColon && (*pToken->m_puzPrefix == 0 || *pToken->m_puzLocal == 0)) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = ungetChar( uChar))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XPathTokenizer::getNumber( F_XPathToken * pToken) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64Num = 0; FLMUNICODE uChar; for( ;;) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( uChar < FLM_UNICODE_0 || uChar > FLM_UNICODE_9) { if( RC_BAD( rc = ungetChar( uChar))) { goto Exit; } break; } #if defined ( FLM_LINUX) || defined ( FLM_NLM) || defined( FLM_OSX) if( ui64Num > ((0xFFFFFFFFFFFFFFFFULL / 10) + (uChar - FLM_UNICODE_0))) #else if( ui64Num > ((0xFFFFFFFFFFFFFFFF / 10) + (uChar - FLM_UNICODE_0))) #endif { rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); goto Exit; } ui64Num = (FLMUINT64)((ui64Num * 10) + (uChar - FLM_UNICODE_0)); } pToken->m_ui64Val = ui64Num; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XPathTokenizer::getBinary( F_XPathToken * pToken) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMBOOL bDoubleQuote = FALSE; FLMUINT uiOffset = 0; FLMUINT uiMaxBytes; FLMBYTE * pucBuf; FLMBOOL bHaveHighNibble = FALSE; FLMBYTE ucHighNibble = 0; FLMBYTE ucNibble; if( (uiMaxBytes = pToken->m_uiValBufSize) < 64) { if( RC_BAD( rc = pToken->resizeBuffer( 64))) { goto Exit; } uiMaxBytes = pToken->m_uiValBufSize; } pucBuf = (FLMBYTE *)pToken->m_pValBuf; if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_QUOTE) { bDoubleQuote = TRUE; } else if( uChar != FLM_UNICODE_APOS) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } for( ;;) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if ( uChar == 0) { rc = RC_SET( NE_XFLM_UNEXPECTED_END_OF_INPUT); goto Exit; } if( bDoubleQuote && uChar == FLM_UNICODE_QUOTE) { break; } else if( !bDoubleQuote && uChar == FLM_UNICODE_APOS) { break; } if (uChar >= FLM_UNICODE_0 && uChar <= FLM_UNICODE_9) { ucNibble = (FLMBYTE)(uChar - FLM_UNICODE_0); } else if (uChar >= FLM_UNICODE_A && uChar <= FLM_UNICODE_F) { ucNibble = (FLMBYTE)(uChar - FLM_UNICODE_A + 10); } else if (uChar >= FLM_UNICODE_a && uChar <= FLM_UNICODE_f) { ucNibble = (FLMBYTE)(uChar - FLM_UNICODE_a + 10); } else if (gv_XFlmSysData.pXml->isWhitespace( uChar)) { // Just skip whitespace, anything else we will treat as an // error. continue; } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if (!bHaveHighNibble) { ucHighNibble = (FLMBYTE)(ucNibble << 4); bHaveHighNibble = TRUE; } else { pucBuf [uiOffset++] = ucHighNibble | ucNibble; if( uiOffset == uiMaxBytes) { if( RC_BAD( rc = pToken->resizeBuffer( pToken->m_uiValBufSize * 2))) { goto Exit; } uiMaxBytes *= 2; } bHaveHighNibble = FALSE; } } if (bHaveHighNibble) { pucBuf [uiOffset++] = ucHighNibble; } pToken->m_uiValBufLen = uiOffset; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XPathTokenizer::getLiteral( F_XPathToken * pToken) { RCODE rc = NE_XFLM_OK; FLMUNICODE uChar; FLMBOOL bDoubleQuote = FALSE; FLMUINT uiOffset; FLMUINT uiMaxChars; uiOffset = 0; if( (uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE)) < 32) { if( RC_BAD( rc = pToken->resizeBuffer( 32 * sizeof( FLMUNICODE)))) { goto Exit; } uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE); } pToken->m_puzLocal = (FLMUNICODE *)pToken->m_pValBuf; if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if( uChar == FLM_UNICODE_QUOTE) { bDoubleQuote = TRUE; } else if( uChar != FLM_UNICODE_APOS) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } for( ;;) { if( RC_BAD( rc = getChar( &uChar))) { goto Exit; } if ( uChar == 0) { rc = RC_SET( NE_XFLM_UNEXPECTED_END_OF_INPUT); goto Exit; } if( bDoubleQuote && uChar == FLM_UNICODE_QUOTE) { break; } else if( !bDoubleQuote && uChar == FLM_UNICODE_APOS) { break; } pToken->m_puzLocal[ uiOffset++] = uChar; if( uiOffset == uiMaxChars) { if( RC_BAD( rc = pToken->resizeBuffer( pToken->m_uiValBufSize * sizeof( FLMUNICODE) * 2))) { goto Exit; } uiMaxChars *= 2; } } pToken->m_puzLocal[ uiOffset++] = 0; Exit: return( rc); } /*************************************************************************** Desc: Class for handling query validator calls ***************************************************************************/ class XFLAIM_QueryValFunc : public IF_QueryValFunc { public: XFLAIM_QueryValFunc() { } virtual ~XFLAIM_QueryValFunc() { } RCODE FLMAPI getValue( IF_Db * pDb, IF_DOMNode * pContextNode, ValIterator eValueToGet, eValTypes * peValType, FLMBOOL * pbLastValue, void * pvVal, F_DynaBuf * pDynaBuf = NULL); RCODE FLMAPI cloneSelf( IF_QueryValFunc ** ppNewObj); }; /**************************************************************************** Desc: Get the next value for a query function. Since this is really just code to test the callback, it always returns a value whose type is boolean and whose value is true. ****************************************************************************/ RCODE FLMAPI XFLAIM_QueryValFunc::getValue( IF_Db *, // pDb, IF_DOMNode *, // pContextNode, ValIterator eValueToGet, eValTypes * peValType, FLMBOOL * pbLastValue, void * pvVal, F_DynaBuf * pDynaBuf) { RCODE rc = NE_XFLM_OK; if (pDynaBuf) { (void)pDynaBuf->truncateData( 0); } if (eValueToGet != GET_FIRST_VAL && eValueToGet != GET_LAST_VAL) { rc = (eValueToGet == GET_NEXT_VAL) ? NE_XFLM_EOF_HIT : NE_XFLM_BOF_HIT; goto Exit; } *pbLastValue = TRUE; *peValType = XFLM_BOOL_VAL; *((XFlmBoolType *)pvVal) = XFLM_TRUE; Exit: return( rc); } /**************************************************************************** Desc: Copy self to create a new object. ****************************************************************************/ RCODE FLMAPI XFLAIM_QueryValFunc::cloneSelf( IF_QueryValFunc ** ppNewObj) { // No need to copy - simply reference, because it has no private // state data that needs to have its own copy. *ppNewObj = (IF_QueryValFunc *)this; (*ppNewObj)->AddRef(); return( NE_XFLM_OK); } /*************************************************************************** Desc: Add a callback function predicate. ***************************************************************************/ FSTATIC RCODE addCallbackFunc( IF_Query * pQuery) { RCODE rc = NE_XFLM_OK; XFLAIM_QueryValFunc * pFuncObj = NULL; // Create a callback function object if ((pFuncObj = f_new XFLAIM_QueryValFunc) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = pQuery->addFunction( pFuncObj, TRUE))) { goto Exit; } Exit: if (pFuncObj) { pFuncObj->Release(); } return( rc); } /**************************************************************************** Desc: Parse an XPATH query ****************************************************************************/ RCODE F_XPath::parseQuery( F_Db * pDb, IF_IStream * pIStream, IF_Query * pQuery) { RCODE rc = NE_XFLM_OK; F_NameTable * pNameTable = NULL; F_XMLNamespace * pNamespace = NULL; eXPathAxisTypes eCurrentAxis = CHILD_AXIS; eDomNodeType eNodeType = ELEMENT_NODE; FLMUINT uiDictNum; if( RC_BAD( rc = pDb->getNameTable( &pNameTable))) { goto Exit; } if( RC_BAD( rc = m_tokenizer.setup( pIStream))) { goto Exit; } // Process any namespace declarations popNamespaces( getNamespaceCount()); if( RC_BAD( rc = pushNamespace( NULL, NULL))) { goto Exit; } if( RC_BAD( rc = getNextToken())) { goto Exit; } if( m_curToken.getType() == LBRACE_TOKEN) { for( ;;) { if( (pNamespace = f_new F_XMLNamespace) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = getNextToken())) { goto Exit; } if( m_curToken.getType() != NAME_TEST_QNAME_TOKEN) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( m_curToken.getPrefixPtr()) { if( f_uninativecmp( m_curToken.getPrefixPtr(), "xmlns") != 0) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = pNamespace->setPrefix( m_curToken.getLocalPtr()))) { goto Exit; } } else { if( f_uninativecmp( m_curToken.getLocalPtr(), "xmlns") != 0) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } if( RC_BAD( rc = getNextToken())) { goto Exit; } if( m_curToken.getType() != OP_EQ_TOKEN) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = getNextToken())) { goto Exit; } if( m_curToken.getType() != LITERAL_TOKEN) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = pNamespace->setURI( m_curToken.getLocalPtr()))) { goto Exit; } if( RC_BAD( rc = pushNamespace( pNamespace))) { goto Exit; } pNamespace->Release(); pNamespace = NULL; if( RC_BAD( rc = getNextToken())) { goto Exit; } if( m_curToken.getType() == RBRACE_TOKEN) { if( RC_BAD( rc = getNextToken())) { goto Exit; } break; } else if( m_curToken.getType() != COMMA_TOKEN) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } } for( ;;) { if( m_curToken.m_eTokenType == END_TOKEN) { break; } switch( m_curToken.m_eTokenType) { case OP_AND_TOKEN: case OP_OR_TOKEN: case OP_EQ_TOKEN: case OP_APPROX_EQ_TOKEN: case OP_NOT_TOKEN: case OP_NE_TOKEN: case OP_LT_TOKEN: case OP_LE_TOKEN: case OP_GT_TOKEN: case OP_GE_TOKEN: case OP_BITAND_TOKEN: case OP_BITOR_TOKEN: case OP_BITXOR_TOKEN: case OP_MULT_TOKEN: case OP_DIV_TOKEN: case OP_MOD_TOKEN: case OP_PLUS_TOKEN: case OP_MINUS_TOKEN: case OP_LPAREN_TOKEN: case OP_RPAREN_TOKEN: case OP_COMMA_TOKEN: case OP_LBRACKET_TOKEN: case OP_RBRACKET_TOKEN: if( RC_BAD( rc = pQuery->addOperator( (eQueryOperators)m_curToken.m_eTokenType, m_curToken.m_uiTokenFlags))) { goto Exit; } break; case LITERAL_TOKEN: if( RC_BAD( rc = pQuery->addUnicodeValue( (FLMUNICODE *)m_curToken.m_pValBuf))) { goto Exit; } break; case BINARY_TOKEN: if( RC_BAD( rc = pQuery->addBinaryValue( m_curToken.m_pValBuf, m_curToken.m_uiValBufLen))) { goto Exit; } break; case NUMBER_TOKEN: if( RC_BAD( rc = pQuery->addUINT64Value( m_curToken.m_ui64Val))) { goto Exit; } break; case FUNC_TRUE_TOKEN: case FUNC_FALSE_TOKEN: case FUNC_UNKNOWN_TOKEN: { FLMBOOL bVal = (m_curToken.m_eTokenType == FUNC_TRUE_TOKEN) ? TRUE : FALSE; FLMBOOL bUnknown = (m_curToken.m_eTokenType == FUNC_UNKNOWN_TOKEN) ? TRUE : FALSE; if ( RC_BAD( rc = pQuery->addBoolean( bVal, bUnknown))) { goto Exit; } // consume the opening and closing paren if ( RC_BAD( rc = getNextToken())) { goto Exit; } if ( m_curToken.m_eTokenType != OP_LPAREN_TOKEN) { rc = RC_SET( NE_XFLM_Q_EXPECTING_LPAREN); goto Exit; } if ( RC_BAD( rc = getNextToken())) { goto Exit; } if ( m_curToken.m_eTokenType != OP_RPAREN_TOKEN) { rc = RC_SET( NE_XFLM_Q_EXPECTING_RPAREN); goto Exit; } } break; case FUNC_LAST_TOKEN: case FUNC_POSITION_TOKEN: case FUNC_COUNT_TOKEN: case FUNC_ID_TOKEN: case FUNC_LOCAL_NAME_TOKEN: case FUNC_NAMESPACE_URI_TOKEN: case FUNC_NAME_TOKEN: case FUNC_STRING_TOKEN: case FUNC_CONCAT_TOKEN: case FUNC_STARTS_WITH_TOKEN: case FUNC_CONTAINS_TOKEN: case FUNC_SUBSTR_BEFORE_TOKEN: case FUNC_SUBSTR_AFTER_TOKEN: case FUNC_SUBSTR_TOKEN: case FUNC_STR_LEN_TOKEN: case FUNC_NORM_SPACE_TOKEN: case FUNC_TRANSLATE_TOKEN: case FUNC_NOT_TOKEN: case FUNC_LANG_TOKEN: case FUNC_NUMBER_TOKEN: case FUNC_SUM_TOKEN: case FUNC_FLOOR_TOKEN: case FUNC_CEILING_TOKEN: case FUNC_ROUND_TOKEN: if( RC_BAD( rc = pQuery->addFunction( XFLM_FUNC_xxx))) { goto Exit; } break; case FUNC_CB_TOKEN: if (RC_BAD( rc = addCallbackFunc( pQuery))) { goto Exit; } break; case AXIS_ANCESTOR_TOKEN: case AXIS_ANCESTOR_OR_SELF_TOKEN: case AXIS_CHILD_TOKEN: case AXIS_DESCENDANT_TOKEN: case AXIS_DESCENDANT_OR_SELF_TOKEN: case AXIS_FOLLOWING_TOKEN: case AXIS_FOLLOWING_SIB_TOKEN: case AXIS_PARENT_TOKEN: case AXIS_PRECEDING_TOKEN: case AXIS_PRECEDING_SIB_TOKEN: case AXIS_SELF_TOKEN: case AXIS_ATTRIB_TOKEN: case AXIS_NAMESPACE_TOKEN: case AXIS_ATSIGN_TOKEN: case AXIS_META_TOKEN: { eCurrentAxis = (eXPathAxisTypes)m_curToken.m_ui64Val; eNodeType = ELEMENT_NODE; if (m_curToken.m_eTokenType == AXIS_ATSIGN_TOKEN) { eNodeType = ATTRIBUTE_NODE; } else { if (m_curToken.m_eTokenType == AXIS_META_TOKEN) { eNodeType = ANY_NODE_TYPE; } else if (m_curToken.m_eTokenType == AXIS_ATTRIB_TOKEN || m_curToken.m_eTokenType == AXIS_NAMESPACE_TOKEN) { eNodeType = ATTRIBUTE_NODE; } else if (m_curToken.m_eTokenType == AXIS_SELF_TOKEN) { eNodeType = ANY_NODE_TYPE; } if( RC_BAD( rc = getNextToken())) { goto Exit; } if( m_curToken.m_eTokenType != DOUBLE_COLON_TOKEN) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } if( RC_BAD( rc = getNextToken())) { goto Exit; } switch( m_curToken.m_eTokenType) { case NAME_TEST_WILD_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, eNodeType, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NAME_TEST_QNAME_TOKEN: { if (eCurrentAxis == META_AXIS) { if (f_uninativecmp( m_curToken.getLocalPtr(), "nodeid") == 0) { uiDictNum = XFLM_META_NODE_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "documentid") == 0) { uiDictNum = XFLM_META_DOCUMENT_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "parentid") == 0) { uiDictNum = XFLM_META_PARENT_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "firstchildid") == 0) { uiDictNum = XFLM_META_FIRST_CHILD_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "lastchildid") == 0) { uiDictNum = XFLM_META_LAST_CHILD_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "nextsiblingid") == 0) { uiDictNum = XFLM_META_NEXT_SIBLING_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "prevsiblingid") == 0) { uiDictNum = XFLM_META_PREV_SIBLING_ID; } else if (f_uninativecmp( m_curToken.getLocalPtr(), "value") == 0) { uiDictNum = XFLM_META_VALUE; } else { rc = RC_SET( NE_XFLM_Q_INVALID_META_DATA_TYPE); goto Exit; } } else { if( m_curToken.getPrefixPtr()) { if( RC_BAD( rc = findNamespace( m_curToken.getPrefixPtr(), &pNamespace))) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( pDb, eNodeType == ELEMENT_NODE ? ELM_ELEMENT_TAG : ELM_ATTRIBUTE_TAG, m_curToken.getLocalPtr(), NULL, pNamespace ? TRUE : FALSE, pNamespace ? pNamespace->getURIPtr() : NULL, &uiDictNum, NULL))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } if( !m_curToken.getPrefixPtr()) { if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( pDb, eNodeType == ELEMENT_NODE ? ELM_ELEMENT_TAG : ELM_ATTRIBUTE_TAG, m_curToken.getLocalPtr(), NULL, FALSE, NULL, &uiDictNum, NULL))) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } if( pNamespace) { pNamespace->Release(); pNamespace = NULL; } } if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, eNodeType, uiDictNum))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NODE_TYPE_NODE_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, ANY_NODE_TYPE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NODE_TYPE_TEXT_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, DATA_NODE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NAME_TEST_NCWILD_TOKEN: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } case NODE_TYPE_COMMENT_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, COMMENT_NODE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NODE_TYPE_PI_TOKEN: { rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } default: { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } break; } case NAME_TEST_QNAME_TOKEN: eNodeType = ELEMENT_NODE; if( RC_BAD( rc = findNamespace( m_curToken.getPrefixPtr(), &pNamespace))) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( pDb, ELM_ELEMENT_TAG, m_curToken.getLocalPtr(), NULL, pNamespace ? TRUE : FALSE, pNamespace ? pNamespace->getURIPtr() : NULL, &uiDictNum, NULL))) { if( rc != NE_XFLM_NOT_FOUND) { goto Exit; } if( !m_curToken.getPrefixPtr()) { if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( pDb, ELM_ELEMENT_TAG, m_curToken.getLocalPtr(), NULL, FALSE, NULL, &uiDictNum, NULL))) { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } else { rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } } if( pNamespace) { pNamespace->Release(); pNamespace = NULL; } if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, eNodeType, uiDictNum))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; case PERIOD_TOKEN: if( RC_BAD( rc = pQuery->addXPathComponent( SELF_AXIS, ANY_NODE_TYPE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; case DOUBLE_PERIOD_TOKEN: if ( RC_BAD( rc = pQuery->addXPathComponent( PARENT_AXIS, ANY_NODE_TYPE, 0))) { goto Exit; } eCurrentAxis = PARENT_AXIS; eNodeType = ELEMENT_NODE; break; case OP_FSLASH_TOKEN: eCurrentAxis = CHILD_AXIS; break; case OP_DOUBLE_FSLASH_TOKEN: eCurrentAxis = DESCENDANT_AXIS; break; case NODE_TYPE_NODE_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, ANY_NODE_TYPE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; break; } case NODE_TYPE_COMMENT_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, COMMENT_NODE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NODE_TYPE_TEXT_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, DATA_NODE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case NAME_TEST_WILD_TOKEN: { if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, ELEMENT_NODE, 0))) { goto Exit; } eCurrentAxis = CHILD_AXIS; eNodeType = ELEMENT_NODE; break; } case DOUBLE_COLON_TOKEN: case OP_UNION_TOKEN: case NODE_TYPE_PI_TOKEN: case NAME_TEST_NCWILD_TOKEN: case VAR_REF_TOKEN: case END_TOKEN: default: rc = RC_SET( NE_XFLM_SYNTAX); goto Exit; } if( RC_BAD( rc = getNextToken())) { goto Exit; } } Exit: m_tokenizer.setup( NULL); if( pNamespace) { pNamespace->Release(); } if( pNameTable) { pNameTable->Release(); } return( rc); } /**************************************************************************** Desc: Parse an XPATH query ****************************************************************************/ RCODE F_XPath::parseQuery( F_Db * pDb, char * pszQuery, IF_Query * pQuery) { RCODE rc = NE_XFLM_OK; IF_BufferIStream * pBufferStream; if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferStream))) { goto Exit; } if( RC_BAD( rc = pBufferStream->openStream( (const char *)pszQuery, f_strlen( pszQuery)))) { goto Exit; } if( RC_BAD( rc = parseQuery( pDb, pBufferStream, pQuery))) { goto Exit; } Exit: if( pBufferStream) { pBufferStream->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_XPath::getNextToken( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = m_tokenizer.getNextToken( &m_curToken))) { goto Exit; } Exit: return( rc); } libxflaim-5.1.969/src/flchkdb.cpp0000644000175000017500000005653610511001742020144 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Check a database for corruptions // // Tabs: 3 // // Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flchkdb.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Constructor ****************************************************************************/ F_DbCheck::~F_DbCheck() { if (m_pXRefRS) { m_pXRefRS->Release(); m_pXRefRS = NULL; } // Cleanup any temporary index check files if (m_pIxRSet) { m_pIxRSet->Release(); } f_free( &m_puiIxArray); if (m_pDb) { m_pDb->Release(); } if (m_pDbInfo) { m_pDbInfo->Release(); } (void)closeAndDeleteResultSetDb(); if (m_pRandGen) { m_pRandGen->Release(); } if (m_pBtPool) { m_pBtPool->Release(); } if (m_pBlkEntries) { f_free( &m_pBlkEntries); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_DbCheck::createAndOpenResultSetDb( void) { RCODE rc = NE_XFLM_OK; XFLM_CREATE_OPTS createOpts; if (m_pResultSetDb) { if (RC_BAD( rc = closeAndDeleteResultSetDb())) { goto Exit; } } f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); for (;;) { // Generate a random file name f_sprintf( m_szResultSetDibName, "%d.db", (int)m_pRandGen->getUINT32( 100, 20000)); if (RC_OK( rc = gv_pXFlmDbSystem->dbCreate( m_szResultSetDibName, NULL, NULL, NULL, NULL, &createOpts, TRUE, (IF_Db **)&m_pResultSetDb))) { break; } if (rc == NE_XFLM_FILE_EXISTS || rc == NE_FLM_IO_ACCESS_DENIED) { rc = NE_XFLM_OK; } else { goto Exit; } } // Shouldn't have an RFL object - don't want anything // logged by this database. flmAssert( !m_pResultSetDb->getDatabase()->m_pRfl); Exit: return rc; } /**************************************************************************** Desc: Close the database file and delete it. ****************************************************************************/ RCODE F_DbCheck::closeAndDeleteResultSetDb( void) { RCODE rc = NE_XFLM_OK; if (m_pResultSetDb) { if (m_pResultSetDb->getTransType() != XFLM_NO_TRANS) { m_pResultSetDb->transAbort(); } m_pResultSetDb->Release(); m_pResultSetDb = NULL; } if (RC_BAD( rc = gv_pXFlmDbSystem->dbRemove( m_szResultSetDibName, NULL, NULL, TRUE))) { goto Exit; } f_memset( m_szResultSetDibName, 0, sizeof( m_szResultSetDibName)); Exit: return rc; } /**************************************************************************** Desc: Method to get a new F_BtResultSet object. This method will create a new collection for the result set to use before handing it off. ****************************************************************************/ RCODE F_DbCheck::getBtResultSet( F_BtResultSet ** ppBtRSet ) { RCODE rc = NE_XFLM_OK; FLMUINT uiCollection; F_BtResultSet * pBtRSet = NULL; F_Database * pDatabase = NULL; // If there is already a BtResultSet, let's get rid of it. if (*ppBtRSet) { (*ppBtRSet)->Release(); *ppBtRSet = NULL; } // Create the new BtResultSet object first. if ((pBtRSet = f_new F_BtResultSet( m_pResultSetDb, m_pBtPool)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } pDatabase = m_pResultSetDb->m_pDatabase; for (;;) { // Now create a new collection. Randomly select a collection number to use. uiCollection = m_pRandGen->getUINT32( 100, XFLM_MAX_COLLECTION_NUM); // Check to see if it already exists. if (RC_OK( rc = pDatabase->lFileCreate( m_pResultSetDb, &pBtRSet->m_Collection.lfInfo, &pBtRSet->m_Collection, uiCollection, XFLM_LF_COLLECTION, FALSE, TRUE))) { break; } if (rc != NE_XFLM_EXISTS) { goto Exit; } rc = NE_XFLM_OK; } *ppBtRSet = pBtRSet; pBtRSet = NULL; Exit: if (pBtRSet) { pBtRSet->Release(); } return rc; } /**************************************************************************** Desc : Checks for physical corruption in a FLAIM database. DNote: The routine verifies the database by first reading through the database to count certain block types which are in linked lists. It then verifies the linked lists. It also verifies the B-TREEs in the database. The reason for the first pass is so that when we verify the linked lists, we can keep ourselves from getting into an infinite loop if there is a loop in the lists. ****************************************************************************/ RCODE F_DbCheck::dbCheck( const char * pszDbFileName, // [IN] Full path and file name of the database which // is to be checked. NULL can be passed as the value of // this parameter if pDb is non-NULL. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory. NULL can be passed as the value of // this parameter to indicate that the log files are located // in the same directory as the database or if pDb is non-NULL. const char * pszPassword, // [IN] Database password. Needed to open the database if the database // key has been wrapped in a password. NULL by default. FLMUINT uiFlags, // [IN] Check flags. Possible flags include: // // XFLM_ONLINE. This flag instructs the check to repair any // index corruptions it finds. The database must have been // opened in read/write mode in order for the check to // successfully repair corruptions. An update transaction // will be started whenever a corruption is repaired. // // XFLM_DO_LOGICAL_CHECK. This flag instructs the check to // perform a logical check of the databases's indexes // in addition to the structural check. // // XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip // verifying the DOM links. This check can take quite a long time // to execute. // // XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow // the database to be opened in limited mode if the database key is // wrapped in a password and the password we pass is incorrect // (or non-existent). IF_DbInfo ** ppDbInfo, // [IN] Pointer to a DB_INFO structure which is used to store // statistics collected during the database check. IF_DbCheckStatus * pDbCheckStatus // [IN] Status interface. Functions in this interface are called // periodically to iform the calling application of the progress // being made. This allows the application to monitor and/or display // the progress of the database check. NULL may be passed as the // value of this parameter if the callback feature is not needed. ) { RCODE rc = NE_XFLM_OK; FLMBYTE * pBlk = NULL; FLMUINT uiFileEnd; FLMUINT uiBlockSize; FLMUINT uiLoop; FLMUINT64 ui64TmpSize; FLMBOOL bStartOver; FLMBOOL bOkToCloseTrans = FALSE; FLMBOOL bAllowLimitedMode = ( uiFlags & XFLM_ALLOW_LIMITED_MODE) ? TRUE : FALSE; if (RC_BAD( rc = gv_pXFlmDbSystem->dbOpen( pszDbFileName, pszDataDir, pszRflDir, pszPassword, bAllowLimitedMode, (IF_Db **)&m_pDb))) { goto Exit; } if ((m_pDbInfo = f_new F_DbInfo) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (ppDbInfo) { *ppDbInfo = m_pDbInfo; (*ppDbInfo)->AddRef(); } m_pDbCheckStatus = pDbCheckStatus; m_LastStatusRc = NE_XFLM_OK; // Get the file size... if (uiFlags & XFLM_SKIP_DOM_LINK_CHECK) { m_bSkipDOMLinkCheck = TRUE; } // Initialize the information block and Progress structure. // Since we know that the check will start read transactions // during its processing, set the flag to indicate that the KRef table // should be cleaned up on exit if we are still in a read transaction. bOkToCloseTrans = TRUE; uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); // Allocate memory to use for reading through the data blocks. if( RC_BAD( rc = f_alloc( uiBlockSize, &pBlk))) { goto Exit; } if ((m_pBtPool = f_new F_BtPool) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = m_pBtPool->btpInit())) { goto Exit; } // Setup the result set database. if( RC_BAD( rc = FlmAllocRandomGenerator( &m_pRandGen))) { goto Exit; } m_pRandGen->setSeed( 9768); if (RC_BAD( rc = createAndOpenResultSetDb())) { goto Exit; } Begin_Check: // Initialize all statistics in the DB_INFO structure. rc = NE_XFLM_OK; bStartOver = FALSE; m_pDbInfo->m_ui64FileSize = 0; m_pDbInfo->freeLogicalFiles(); m_bPhysicalCorrupt = FALSE; m_bIndexCorrupt = FALSE; m_uiFlags = uiFlags; m_bStartedUpdateTrans = FALSE; f_memset( &m_pDbInfo->m_AvailBlocks, 0, sizeof( BLOCK_INFO)); f_memset( &m_pDbInfo->m_LFHBlocks, 0, sizeof( BLOCK_INFO)); f_memset( &m_Progress, 0, sizeof( XFLM_PROGRESS_CHECK_INFO)); /* Get the dictionary information for the file. */ if (RC_BAD( rc = getDictInfo())) { goto Exit; } m_Progress.ui64BytesExamined = 0; for (uiLoop = 1; uiLoop <= MAX_DATA_BLOCK_FILE_NUMBER; uiLoop++) { if (RC_BAD( m_pDb->m_pSFileHdl->getFileSize( uiLoop, &ui64TmpSize))) { break; } m_Progress.ui64FileSize += ui64TmpSize; } // See if we have a valid end of file uiFileEnd = m_pDb->m_uiLogicalEOF; if (FSGetFileOffset( uiFileEnd) % uiBlockSize != 0) { if (RC_BAD( rc = chkReportError( FLM_BAD_FILE_SIZE, XFLM_LOCALE_NONE, 0, 0, 0xFF, (FLMUINT32)uiFileEnd, 0, 0, 0))) { goto Exit; } } else if (m_Progress.ui64FileSize < FSGetSizeInBytes( m_pDb->m_pDatabase-> getMaxFileSize(), uiFileEnd)) { m_Progress.ui64FileSize = FSGetSizeInBytes( m_pDb->m_pDatabase->getMaxFileSize(), uiFileEnd); } m_pDbInfo->m_ui64FileSize = m_Progress.ui64FileSize; // Verify the LFH blocks, B-Trees, and the AVAIL list. if( RC_BAD( rc = verifyLFHBlocks( &bStartOver))) { goto Exit; } if (bStartOver) { goto Begin_Check; } // Check the b-trees. if (RC_BAD( rc = verifyBTrees( &bStartOver))) { goto Exit; } if (bStartOver) { goto Begin_Check; } // Check the avail list. if (RC_BAD( rc = verifyAvailList( &bStartOver))) { goto Exit; } if (bStartOver) { goto Begin_Check; } Exit: if ((m_bPhysicalCorrupt || m_bIndexCorrupt) && !gv_pXFlmDbSystem->errorIsFileCorrupt( rc)) { rc = RC_SET( NE_XFLM_DATA_ERROR); } if (RC_OK( rc) && RC_BAD( m_LastStatusRc)) { rc = m_LastStatusRc; } if (m_pDb) { // Close down the transaction, if one is going if( bOkToCloseTrans && m_pDb->getTransType( ) == XFLM_READ_TRANS) { m_pDb->krefCntrlFree(); m_pDb->transAbort(); } } // Free memory, if allocated if (pBlk) { f_free( &pBlk); } // Close the FLAIM database we opened. if (m_pDb) { m_pDb->Release(); m_pDb = NULL; } return( rc); } /*************************************************************************** Desc: This routine opens a file and reads its dictionary into memory. *****************************************************************************/ RCODE F_DbCheck::getDictInfo() { RCODE rc = NE_XFLM_OK; // Close down the transaction, if one is going. if (m_pDb->getTransType() != XFLM_UPDATE_TRANS) { if (m_pDb->getTransType() == XFLM_READ_TRANS) { (void)m_pDb->transAbort(); } // Start a read transaction on the file to ensure we are connected // to the file's dictionary structures. if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS, FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE, &m_pDbInfo->m_dbHdr))) { goto Exit; } } else { f_memcpy( &m_pDbInfo->m_dbHdr, m_pDb->m_pDatabase->getUncommittedDbHdr(), sizeof( XFLM_DB_HDR)); } Exit: return( rc); } /******************************************************************** Desc: This routine follows all of the blocks in a chain, verifying that they are properly linked. It also verifies each block's header. *********************************************************************/ RCODE F_DbCheck::verifyBlkChain( BLOCK_INFO * pBlkInfo, FLMUINT uiLocale, FLMUINT uiFirstBlkAddr, FLMUINT uiBlkType, FLMBOOL * pbStartOverRV ) { RCODE rc = NE_XFLM_OK; FLMINT32 i32VerifyCode = 0; F_CachedBlock * pSCache = NULL; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiPrevBlkAddress; FLMUINT uiBlkCount = 0; STATE_INFO StateInfo; FLMBOOL bStateInitialized = FALSE; FLMUINT64 ui64SaveBytesExamined; FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); FLMUINT uiMaxBlocks = (FLMUINT)(FSGetSizeInBytes( m_pDb->m_pDatabase->getMaxFileSize(), m_pDb->m_uiLogicalEOF) / (FLMUINT64)uiBlockSize); uiPrevBlkAddress = 0; /* There must be at least ONE block if it is the LFH chain. */ if ((uiBlkType == BT_LFH_BLK) && (uiFirstBlkAddr == 0)) { i32VerifyCode = FLM_BAD_LFH_LIST_PTR; (void)chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, 0, 0, 0, 0); goto Exit; } /* Read through all of the blocks, verifying them as we go. */ Restart_Chain: uiBlkCount = 0; flmInitReadState( &StateInfo, &bStateInitialized, (FLMUINT)m_pDb->m_pDatabase-> m_lastCommittedDbHdr.ui32DbVersion, m_pDb, NULL, (FLMUINT)((uiBlkType == BT_FREE) ? (FLMUINT)0xFF : (FLMUINT)0), uiBlkType, NULL); ui64SaveBytesExamined = m_Progress.ui64BytesExamined; StateInfo.ui32BlkAddress = (FLMUINT32)uiFirstBlkAddr; while ((StateInfo.ui32BlkAddress != 0) && (uiBlkCount < uiMaxBlocks)) { StateInfo.pBlkHdr = NULL; if( RC_BAD( rc = blkRead( StateInfo.ui32BlkAddress, &pBlkHdr, &pSCache, &i32VerifyCode))) { if (rc == NE_XFLM_OLD_VIEW) { FLMUINT uiSaveDictSeq = m_pDb->m_pDict->getDictSeq(); if (RC_BAD( rc = getDictInfo())) goto Exit; // If the dictionary ID changed, start over. if (m_pDb->m_pDict->getDictSeq() != uiSaveDictSeq) { *pbStartOverRV = TRUE; goto Exit; } m_Progress.ui64BytesExamined = ui64SaveBytesExamined; goto Restart_Chain; } pBlkInfo->i32ErrCode = i32VerifyCode; pBlkInfo->uiNumErrors++; rc = chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, StateInfo.ui32BlkAddress, 0, 0, 0); } StateInfo.pBlkHdr = pBlkHdr; uiBlkCount++; m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; if (RC_BAD( rc = chkCallProgFunc())) { goto Exit; } f_yieldCPU(); if ((i32VerifyCode = flmVerifyBlockHeader( &StateInfo, pBlkInfo, uiBlockSize, 0xFFFFFFFF, uiPrevBlkAddress, TRUE)) != 0) { pBlkInfo->i32ErrCode = i32VerifyCode; pBlkInfo->uiNumErrors++; chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, StateInfo.ui32BlkAddress, 0, 0, 0); goto Exit; } uiPrevBlkAddress = StateInfo.ui32BlkAddress; StateInfo.ui32BlkAddress = pBlkHdr->ui32NextBlkInChain; } if (StateInfo.ui32BlkAddress != 0 && RC_OK( m_LastStatusRc)) { switch (uiBlkType) { case BT_LFH_BLK: i32VerifyCode = FLM_BAD_LFH_LIST_END; break; case BT_FREE: i32VerifyCode = FLM_BAD_AVAIL_LIST_END; break; } pBlkInfo->i32ErrCode = i32VerifyCode; pBlkInfo->uiNumErrors++; chkReportError( i32VerifyCode, (FLMUINT32)uiLocale, 0, 0, 0xFF, (FLMUINT32)uiPrevBlkAddress, 0, 0, 0); goto Exit; } Exit: if( pSCache) { ScaReleaseCache( pSCache, FALSE); } else if( pBlkHdr) { f_free( &pBlkHdr); } if (RC_OK(rc) && (i32VerifyCode != 0)) { rc = RC_SET( NE_XFLM_DATA_ERROR); } return( rc); } /*************************************************************************** Desc: This routine verifies the LFH blocks. *****************************************************************************/ RCODE F_DbCheck::verifyLFHBlocks( FLMBOOL * pbStartOverRV) { RCODE rc = NE_XFLM_OK; m_Progress.ui32LfNumber = 0; m_Progress.ui32LfType = 0; m_Progress.i32CheckPhase = XFLM_CHECK_LFH_BLOCKS; m_Progress.bStartFlag = TRUE; if (RC_BAD( rc = chkCallProgFunc())) { goto Exit; } m_Progress.bStartFlag = FALSE; f_yieldCPU(); // Go through the LFH blocks. if (RC_BAD( rc = verifyBlkChain( &m_pDbInfo->m_LFHBlocks, XFLM_LOCALE_LFH_LIST, (FLMUINT)m_pDb->m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr, BT_LFH_BLK, pbStartOverRV)) || *pbStartOverRV) { goto Exit; } Exit: return( rc); } /*************************************************************************** Desc: This routine reads through the blocks in the AVAIL list and verifies that we don't have a loop or some other corruption in the list. *****************************************************************************/ RCODE F_DbCheck::verifyAvailList( FLMBOOL * pbStartOverRV) { RCODE rc = NE_XFLM_OK; m_Progress.ui32LfNumber = 0; m_Progress.ui32LfType = 0; m_Progress.i32CheckPhase = XFLM_CHECK_AVAIL_BLOCKS; m_Progress.bStartFlag = TRUE; if (RC_BAD( rc = chkCallProgFunc())) { goto Exit; } m_Progress.bStartFlag = FALSE; f_yieldCPU(); if (RC_BAD( rc = verifyBlkChain( &m_pDbInfo->m_AvailBlocks, XFLM_LOCALE_AVAIL_LIST, m_pDb->m_uiFirstAvailBlkAddr, BT_FREE, pbStartOverRV)) || *pbStartOverRV) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: Returns the next B-Tree information that was collected during the database check. ****************************************************************************/ void FLMAPI F_DbInfo::getBTreeInfo( FLMUINT uiNthLogicalFile, FLMUINT * puiLfNum, eLFileType * peLfType, FLMUINT * puiRootBlkAddress, FLMUINT * puiNumLevels ) { LF_HDR * pLfHdr; if (uiNthLogicalFile < m_uiNumLogicalFiles) { pLfHdr = &m_pLogicalFiles[ uiNthLogicalFile]; *puiLfNum = pLfHdr->uiLfNum; *peLfType = pLfHdr->eLfType; *puiRootBlkAddress = pLfHdr->uiRootBlk; *puiNumLevels = pLfHdr->uiNumLevels; } else { flmAssert( 0); *puiLfNum = 0; *puiRootBlkAddress = 0; *puiNumLevels = 0; } } /**************************************************************************** Desc: Returns block information on the specified b-tree and level within that b-tree. ****************************************************************************/ void FLMAPI F_DbInfo::getBTreeBlockStats( FLMUINT uiNthLogicalFile, FLMUINT uiLevel, FLMUINT64 * pui64KeyCount, FLMUINT64 * pui64BytesUsed, FLMUINT64 * pui64ElementCount, FLMUINT64 * pui64ContElementCount, FLMUINT64 * pui64ContElmBytes, FLMUINT * puiBlockCount, FLMINT32 * pi32LastError, FLMUINT * puiNumErrors ) { LF_HDR * pLfHdr; if (uiNthLogicalFile < m_uiNumLogicalFiles && uiLevel < m_pLogicalFiles [uiNthLogicalFile].uiNumLevels) { pLfHdr = &m_pLogicalFiles[ uiNthLogicalFile]; *pui64KeyCount = pLfHdr->pLevelInfo [uiLevel].ui64KeyCount; *pui64BytesUsed = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64BytesUsed; *pui64ElementCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ElementCount; *pui64ContElementCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ContElementCount; *pui64ContElmBytes = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ContElmBytes; *puiBlockCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.uiBlockCount; *pi32LastError = pLfHdr->pLevelInfo [uiLevel].BlockInfo.i32ErrCode; *puiNumErrors = pLfHdr->pLevelInfo [uiLevel].BlockInfo.uiNumErrors; } else { flmAssert( 0); *pui64KeyCount = 0; *pui64BytesUsed = 0; *pui64ElementCount = 0; *pui64ContElementCount = 0; *pui64ContElmBytes = 0; *puiBlockCount = 0; *pi32LastError = 0; *puiNumErrors = 0; } } /**************************************************************************** Desc: Checks for physical corruption in a FLAIM database. Note: The routine verifies the database by first reading through the database to count certain block types which are in linked lists. It then verifies the linked lists. It also verifies the B-TREEs in the database. The reason for the first pass is so that when we verify the linked lists, we can keep ourselves from getting into an infinite loop if there is a loop in the lists. ****************************************************************************/ RCODE FLMAPI F_DbSystem::dbCheck( const char * pszDbFileName, // [IN] Full path and file name of the database which // is to be checked. NULL can be passed as the value of // this parameter if pDb is non-NULL. const char * pszDataDir, // [IN] Directory for data files. const char * pszRflDir, // [IN] RFL directory. NULL can be passed as the value of // this parameter to indicate that the log files are located // in the same directory as the database or if pDb is non-NULL. const char * pszPassword, // [IN] Database password. This is necessary to open the database if // the key has been wrapped in a password. NULL by default. FLMUINT uiFlags, // [IN] Check flags. Possible flags include: // // XFLM_ONLINE. This flag instructs the check to repair any // index corruptions it finds. The database must have been // opened in read/write mode in order for the check to // successfully repair corruptions. An update transaction // will be started whenever a corruption is repaired. // // XFLM_DO_LOGICAL_CHECK. This flag instructs the check to // perform a logical check of the databases's indexes // in addition to the structural check. // // XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip // verifying the DOM links. This check can take quite a long time // to execute. // // XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow // the database to be opened in limited mode if the database key is // wrapped in a password and the password we pass is incorrect // (or non-existent). IF_DbInfo ** ppDbInfo, // [IN] Pointer to a DB_INFO structure which is used to store // statistics collected during the database check. IF_DbCheckStatus * pDbCheckStatus // [IN] Status interface. Functions in this interface are called // periodically to iform the calling application of the progress // being made. This allows the application to monitor and/or display // the progress of the database check. NULL may be passed as the // value of this parameter if the callback feature is not needed. ) { RCODE rc = NE_XFLM_OK; F_DbCheck * pCheckObj = NULL; if ((pCheckObj = f_new F_DbCheck) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } rc = pCheckObj->dbCheck( pszDbFileName, pszDataDir, pszRflDir, pszPassword, uiFlags, ppDbInfo, pDbCheckStatus); Exit: if (pCheckObj) { pCheckObj->Release(); } return( rc); } libxflaim-5.1.969/src/flconvrt.cpp0000644000175000017500000003774610511001742020406 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Database upgrade routines. // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flconvrt.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc : Upgrades a database to the latest FLAIM version. ****************************************************************************/ RCODE FLMAPI F_Db::upgrade( IF_UpgradeClient * // pUpgradeClient ) { RCODE rc = NE_XFLM_OK; FLMBOOL bStartedTrans = FALSE; FLMBOOL bLockedDatabase = FALSE; FLMUINT uiOldVersion = 0; F_Rfl * pRfl = m_pDatabase->m_pRfl; XFLM_DB_HDR * pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; FLMUINT64 ui64SaveTransId; FLMUINT uiRflToken = 0; FLMBOOL bUpgradeNeeded = FALSE; // Lock the database if not already locked. // Cannot lose exclusive access between the checkpoint and // the update transaction that does the conversion. if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) { if (RC_BAD( rc = dbLock( FLM_LOCK_EXCLUSIVE, 0, 15))) { goto Exit; } bLockedDatabase = TRUE; } // Cannot have any transaction already going. if (m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } // NOTE: Don't get the current version number until AFTER obtaining // the exclusive lock - to make sure nobody else can or will do // an upgrade while we are in here. uiOldVersion = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion; // Verify that we can do the upgrade switch (uiOldVersion) { case XFLM_CURRENT_VERSION_NUM: break; default: rc = RC_SET( NE_XFLM_UNALLOWED_UPGRADE); goto Exit; } if( !bUpgradeNeeded) { goto Exit; } // Change state of logging OFF to TRUE - don't want anything // logged during conversion except for the upgrade packet. pRfl->disableLogging( &uiRflToken); m_uiFlags |= FDB_UPGRADING; ui64SaveTransId = m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID; // Flush everything do disk so that the roll forward log is empty. // The upgrade doesn't put any special data in the roll forward log // so if the roll forward log had stuff in it, it would roll forward // on data that was a newer version - and never work! // Start an update transaction and commit it, forcing it to be // checkpointed. if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Don't want this transaction to change the transaction ID because // we are only trying to force a checkpoint. We don't want to change // the transaction ID until we have actually done the convert. m_pDatabase->m_uncommittedDbHdr.ui64CurrTransID = ui64SaveTransId; m_ui64CurrTransID = ui64SaveTransId; // Set up things in the FDB to indicate where we should move the // checkpoint file number and offset to. If we are in the middle // of a recovery or restore operation, move the pointer forward // to just BEFORE the upgrade packet. Down below when we do the // checkpoint at the end of the upgrade, we will move the pointer // forward to just AFTER the upgrade packet. if (m_uiFlags & FDB_REPLAYING_RFL) { m_uiUpgradeCPFileNum = pRfl->getCurrFileNum(); m_uiUpgradeCPOffset = pRfl->getCurrPacketAddress(); } // Commit the transaction, forcing it to be checkpointed. bStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, TRUE))) { goto Exit; } // Start an update transaction for the conversion. if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Make sure that commit does something. m_bHadUpdOper = TRUE; // NOTE: By this point, all conversions should be complete, except for // committing and changing the version number. // Log the upgrade packet to the RFL pRfl->enableLogging( &uiRflToken); // Log the upgrade packet. if( RC_BAD( rc = pRfl->logUpgrade( this, uiOldVersion))) { goto Exit; } // Turn logging off again pRfl->disableLogging( &uiRflToken); // Change the FLAIM version number to the new version number. pUncommittedDbHdr->ui32DbVersion = XFLM_CURRENT_VERSION_NUM; // Commit and force a checkpoint by passing TRUE. // Set up things in the FDB to indicate where we should move the // checkpoint file number and offset to. If we are in the middle // of a recovery or restore operation, move the pointer forward // to just AFTER the upgrade packet. if (m_uiFlags & FDB_REPLAYING_RFL) { m_uiUpgradeCPFileNum = pRfl->getCurrFileNum(); m_uiUpgradeCPOffset = pRfl->getCurrReadOffset(); } bStartedTrans = FALSE; if (RC_BAD( rc = commitTrans( 0, TRUE))) { goto Exit; } Exit: if( bStartedTrans) { // Failure condition, we jumped to exit pUncommittedDbHdr->ui32DbVersion = (FLMUINT32)uiOldVersion; m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion = (FLMUINT32)uiOldVersion; (void)abortTrans(); } if (uiRflToken) { pRfl->enableLogging( &uiRflToken); } // Turn off the upgrade flag, in case it was turned on above. m_uiFlags &= (~(FDB_UPGRADING)); if (bLockedDatabase) { (void)dbUnlock(); } return( rc ); } /************************************************************************ Desc : Enable encryption on the database. *************************************************************************/ RCODE FLMAPI F_Db::enableEncryption( void) { RCODE rc = NE_XFLM_OK; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMBYTE * pucWrappingKey = NULL; FLMUINT32 ui32KeyLen = 0; XFLM_DB_HDR * pucUncommittedLogHdr = &m_pDatabase->m_uncommittedDbHdr; FLMBOOL bLocked = FALSE; FLMBOOL bStartedTrans = FALSE; FLMUINT uiRflToken = 0; // We must will start our own transaction if( m_eTransType != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) { if ( RC_BAD( rc = dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bLocked = TRUE; } // Disable RFL logging pRfl->disableLogging( &uiRflToken); // Begin an update transaction. if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // If we don't have a wrapping key, then create one. Normally // this would be the case, since we are enabling encryption, // but the test is "just to be sure" we don't // overwrite an existing key. if (!m_pDatabase->m_pWrappingKey) { if ( RC_BAD( rc = createDbKey())) { goto Exit; } } if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( &pucWrappingKey, &ui32KeyLen, m_pDatabase->m_pszDbPasswd, NULL))) { goto Exit; } f_memcpy( pucUncommittedLogHdr->DbKey, pucWrappingKey, ui32KeyLen); pucUncommittedLogHdr->ui32DbKeyLen = ui32KeyLen; m_pDatabase->m_rcLimitedCode = NE_XFLM_OK; m_pDatabase->m_bInLimitedMode = FALSE; m_pDatabase->m_bHaveEncKey = TRUE; // If we have a dictionary, update its limited mode flag too. if (m_pDict) { m_pDict->m_bInLimitedMode = FALSE; } // Log the upgrade packet pRfl->enableLogging( &uiRflToken); if (RC_BAD( rc = pRfl->logEncryptionKey( this, RFL_ENABLE_ENCRYPTION_PACKET, pucWrappingKey, ui32KeyLen))) { goto Exit; } // Turn logging off again pRfl->disableLogging( &uiRflToken); // Commit the transaction and force a checkpoint if( RC_BAD( rc = commitTrans( 0, TRUE, NULL))) { goto Exit; } bStartedTrans = FALSE; Exit: if( bStartedTrans) { transAbort(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bLocked) { dbUnlock(); } if( pucWrappingKey) { f_free( &pucWrappingKey); } return( rc); } /**************************************************************************** Desc : Change the database key from wrapped in the NICI server key to shrouded in a password and vice-versa. If no password is specified, the key will be wrapped in the NICI server key. If a password is specified, the key will be shrouded in it. ****************************************************************************/ RCODE FLMAPI F_Db::wrapKey( const char * pszPassword) { RCODE rc = NE_XFLM_OK; XFLM_DB_HDR * pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; FLMBOOL bStartedTrans = FALSE; FLMBYTE * pucTmp = NULL; FLMUINT32 ui32KeyLen = XFLM_MAX_ENC_KEY_SIZE; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMBOOL bLocked = FALSE; FLMUINT uiRflToken = 0; if( getTransType() != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if( !(m_uiFlags & FDB_HAS_FILE_LOCK)) { if ( RC_BAD( rc = dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bLocked = TRUE; } // Turn off logging. We only want to log the wrap key packet. pRfl->disableLogging( &uiRflToken); // Start the transaction if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Wrap or shroud the key if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( &pucTmp, &ui32KeyLen, (FLMBYTE *)pszPassword, NULL))) { goto Exit; } f_memcpy( pUncommittedDbHdr->DbKey, pucTmp, ui32KeyLen); pUncommittedDbHdr->ui32DbKeyLen = ui32KeyLen; // Turn on logging. We only want to log the wrap key packet. pRfl->enableLogging( &uiRflToken); // Log a wrapped key packet to record that the key // has been wrapped/encrypted. if (RC_BAD( rc = pRfl->logEncryptionKey( this, RFL_WRAP_KEY_PACKET, pucTmp, ui32KeyLen))) { goto Exit; } // Turn logging off again pRfl->disableLogging( &uiRflToken); // Make sure the log header gets written out... m_bHadUpdOper = TRUE; // Commit the transaction and force a checkpoint if (RC_BAD( rc = transCommit())) { goto Exit; } bStartedTrans = FALSE; // Delete the old password if (m_pDatabase->m_pszDbPasswd) { f_free( &m_pDatabase->m_pszDbPasswd); } // Store the new password if( pszPassword) { if (RC_BAD( rc = f_calloc( f_strlen( pszPassword) + 1, &m_pDatabase->m_pszDbPasswd))) { goto Exit; } f_memcpy( m_pDatabase->m_pszDbPasswd, pszPassword, (FLMSIZET)f_strlen( pszPassword)); } Exit: if( bStartedTrans) { transAbort(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bLocked) { dbUnlock(); } if( pucTmp) { f_free( &pucTmp); } return( rc); } /*************************************************************************** * A private function that goes through all the potential trial-and-error * involved in getting the strongest possible key we can use for the * database key. * NOTE: If an update transaction is needed, it is the responsibility of * the caller to start the transaction! We can't check for this in the * function because sometimes a transaction is required, and other times * (such as during a db create) it's impossible. ***************************************************************************/ RCODE F_Db::createDbKey( void) { RCODE rc = NE_XFLM_OK; if( m_pDatabase->m_pWrappingKey) { m_pDatabase->m_pWrappingKey->Release(); m_pDatabase->m_pWrappingKey = NULL; } if( RC_BAD( rc = flmAllocCCS( &m_pDatabase->m_pWrappingKey))) { goto Exit; } if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->init( TRUE, FLM_NICI_AES))) { goto Exit; } // Try to get the strongest encryption supported on this platform. if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( XFLM_NICI_AES256))) { if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( XFLM_NICI_AES192))) { if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( XFLM_NICI_AES128))) { // Try using DES3 m_pDatabase->m_pWrappingKey->Release(); m_pDatabase->m_pWrappingKey = NULL; if( RC_BAD( rc = flmAllocCCS( &m_pDatabase->m_pWrappingKey))) { goto Exit; } if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->init( TRUE, FLM_NICI_DES3))) { goto Exit; } if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( XFLM_NICI_DES3X))) { // No more choices... goto Exit; } } } } Exit: return rc; } /**************************************************************************** Desc : Generate a new database key and re-wrap all existing keys in it NOTE: New database key will be wrapped in NICI server key, even if the previous key was wrapped in a password. ****************************************************************************/ RCODE FLMAPI F_Db::rollOverDbKey( void) { RCODE rc = NE_XFLM_OK; F_Rfl * pRfl = m_pDatabase->m_pRfl; FLMBYTE * pucWrappingKey = NULL; FLMUINT32 ui32KeyLen = 0; FLMUINT uiEncDefNum; F_ENCDEF * pEncDef; F_DOMNode * pNode = NULL; F_DOMNode * pAttrNode = NULL; FLMBYTE * pucBuf = NULL; FLMBOOL bLocked = FALSE; FLMBOOL bStartedTrans = FALSE; FLMUINT32 ui32BufLen = 0; FLMUINT uiRflToken = 0; if( getTransType() != XFLM_NO_TRANS) { rc = RC_SET( NE_XFLM_TRANS_ACTIVE); goto Exit; } if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) { if ( RC_BAD( rc = dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT))) { goto Exit; } bLocked = TRUE; } // Turn off logging pRfl->disableLogging( &uiRflToken); // Start the transaction if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bStartedTrans = TRUE; // Update the database header with the new key if( RC_BAD( rc = createDbKey())) { goto Exit; } if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( &pucWrappingKey, &ui32KeyLen, m_pDatabase->m_pszDbPasswd, NULL))) { goto Exit; } f_memcpy( m_pDatabase->m_uncommittedDbHdr.DbKey, pucWrappingKey, ui32KeyLen); m_pDatabase->m_uncommittedDbHdr.ui32DbKeyLen = ui32KeyLen; // Loop through all the keys in the dictionary for( uiEncDefNum = m_pDict->m_uiLowestEncDefNum; uiEncDefNum <= m_pDict->m_uiHighestEncDefNum; uiEncDefNum++) { // If we can't retrieve the encdef, it's no big deal. Just means // there was a gap in the enc def nums. if( RC_OK( rc = m_pDict->getEncDef( uiEncDefNum, &pEncDef))) { if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pEncDef->ui64DocumentId, &pNode))) { goto Exit; } // Get the attribute holding the actual key if( RC_BAD( rc = pNode->getAttribute( this, ATTR_ENCRYPTION_KEY_TAG, (IF_DOMNode **)&pAttrNode))) { goto Exit; } if( RC_BAD( rc = pEncDef->pCcs->getKeyToStore( &pucBuf, &ui32BufLen, NULL, m_pDatabase->m_pWrappingKey))) { goto Exit; } pAttrNode->removeModeFlags( this, FDOM_READ_ONLY); if( RC_BAD( rc = pAttrNode->setBinary( this, pucBuf, ui32BufLen))) { goto Exit; } pAttrNode->addModeFlags( this, FDOM_READ_ONLY); f_free( &pucBuf); ui32BufLen = 0; if( RC_BAD( rc = documentDone( XFLM_DICT_COLLECTION, pEncDef->ui64DocumentId))) { goto Exit; } } else { rc = NE_XFLM_OK; } } if( RC_BAD( rc = transCommit())) { goto Exit; } bStartedTrans = FALSE; pRfl->enableLogging( &uiRflToken); if( RC_BAD( rc = m_pDatabase->m_pRfl->logRollOverDbKey( this))) { goto Exit; } Exit: if( bStartedTrans) { transAbort(); } if( uiRflToken) { pRfl->enableLogging( &uiRflToken); } if( bLocked) { dbUnlock(); } if( pucWrappingKey) { f_free( &pucWrappingKey); } if( pucBuf) { f_free( &pucBuf); } if( pNode) { pNode->Release(); } if( pAttrNode) { pAttrNode->Release(); } return( rc); } libxflaim-5.1.969/src/f_ccs.cpp0000644000175000017500000000625010511001742017610 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Controlled Cryptographic Services (CCS) interface // // Tabs: 3 // // Copyright (c) 2004-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id$ //------------------------------------------------------------------------------ #include "flaimsys.h" /**************************************************************************** Desc: Controlled Cryptographic Services (CCS) interface ****************************************************************************/ #ifndef FLM_HAS_ENCRYPTION class F_NOCCS : public IF_CCS { public: virtual ~F_NOCCS() { } RCODE init( FLMBOOL, // bKeyIsWrappingKey, FLMUINT) // uiAlgType) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } RCODE generateEncryptionKey( FLMUINT) // uiEncKeySize) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } RCODE generateWrappingKey( FLMUINT) // uiEncKeySize) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } RCODE encryptToStore( FLMBYTE *, // pucIn, FLMUINT, // uiInLen, FLMBYTE *, // pucOut, FLMUINT *, // puiOutLen, FLMBYTE *) // pucIV) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } RCODE decryptFromStore( FLMBYTE *, // pucIn, FLMUINT, // uiInLen, FLMBYTE *, // pucOut, FLMUINT *, // puiOutLen, FLMBYTE *) // pucIV) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } RCODE getKeyToStore( FLMBYTE **, // ppucKeyInfo, FLMUINT32 *, // pui32BufLen, FLMBYTE *, // pzEncKeyPasswd, IF_CCS *) // pWrappingCcs) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } RCODE setKeyFromStore( FLMBYTE *, // pucKeyInfo, FLMBYTE *, // pszEncKeyPasswd, IF_CCS *) // pWrappingCcs) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } FLMUINT getIVLen( void) { return( 0); } RCODE generateIV( FLMUINT, // uiIVLen, FLMBYTE *) // pucIV) { return( RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE)); } }; #endif /**************************************************************************** Desc: ****************************************************************************/ #ifndef FLM_HAS_ENCRYPTION RCODE flmAllocCCS( IF_CCS ** ppCCS) { RCODE rc = NE_XFLM_OK; F_NOCCS * pCCS = NULL; f_assert( (*ppCCS) == NULL); if( (pCCS = f_new F_NOCCS) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } *ppCCS = pCCS; pCCS = NULL; Exit: if( pCCS) { pCCS->Release(); } return( rc); } #endif libxflaim-5.1.969/util/0000755000175000017500000000000010511001742016212 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/util/java/0000755000175000017500000000000010511001742017133 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/util/java/XFlaimTester/0000755000175000017500000000000010511001742021502 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/util/java/XFlaimTester/src/0000755000175000017500000000000010511001742022271 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/util/xmlfiles/0000755000175000017500000000000010511001742020035 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/util/xmlfiles/dictionary/0000755000175000017500000000000010511001742022202 5ustar ahodgkinsonahodgkinsonlibxflaim-5.1.969/util/xmlfiles/dictionary/tracksubstrix.xml0000644000175000017500000000032410511001742025633 0ustar ahodgkinsonahodgkinson libxflaim-5.1.969/util/xmlfiles/dictionary/titlevalueix.xml0000644000175000017500000000102510511001742025441 0ustar ahodgkinsonahodgkinson libxflaim-5.1.969/util/xmlfiles/7006030a.xml0000644000175000017500000000145410511001742021543 0ustar ahodgkinsonahodgkinson 7006030a 1542 Holly Singers, The / A Merry Christmas cddb/misc Silent Night Jingle Bells We Wish You a Merry Christmas O Come All Ye Faithful / Good King Wenceslas / Joy To The World / Hark! The Hearld Angels Sing White Christmas Little Drummer Boy Have A Merry Christmas Away In A Manger Deck The Halls The Infant King libxflaim-5.1.969/util/xmlfiles/70059408.xml0000644000175000017500000000124610511001742021502 0ustar ahodgkinsonahodgkinson 70059408 1430 Fleetwood Mac / Fleetwood Mac cddb/blues Sony Music Special Editions BUK 50031 My Heart Beat Like A Hammer Merry Go Round Long Grey Mare Hellhound On My Trail No Place to Go My Baby's Good To Me If I loved Another Woman Cold Black Night libxflaim-5.1.969/util/xmlfiles/70063908.xml0000644000175000017500000000110610511001742021475 0ustar ahodgkinsonahodgkinson 70063908 1595 King Biscuit Time / No Style cddb/rock i walk the earth untitled i love you time to get up fatheriver niggling discrepancy little white eye o' the dug libxflaim-5.1.969/util/xmlfiles/70069209.xml0000644000175000017500000000132310511001742021476 0ustar ahodgkinsonahodgkinson 70069209 1684 Mantovani / Mantovani The Legend cddb/classical Scarborough Fair Theme from "New York, New York" The Way We Were Greensleeves You Don't Bring Me Flowers Copacabana Don't Cry Out Loud Manhattan Skyline I'll Never Love This Way Again libxflaim-5.1.969/util/xmlfiles/7006ab0a.xml0000644000175000017500000000154110511001742021700 0ustar ahodgkinsonahodgkinson 7006ab0a 1709 Dave Williamson Big Band and Singers / That Christmas Swing cddb/jazz Jingle Bells / Over the River and Through the Woods Let It Snow Silent Night Deck the Halls Have Yourself a Merry Little Christmas Jolly Old St. Nicholas / Up on the Housetop What Child is This? The First Noel God Rest Ye Merry Gentlemen O Christmas Tree / We Wish You a Merry Christmas libxflaim-5.1.969/util/xmlfiles/7005cc0a.xml0000644000175000017500000000134510511001742021704 0ustar ahodgkinsonahodgkinson 7005cc0a 1486 Money Mark / Third Version EP cddb/jazz Sometimes you gotta make it alone (1996 Version) Revolt of the Octopi Slow Flames Hard Ass From The Beginning To The End function World Lesson Pt. II Inner Laugh The Grade Marks Keyboard Repair libxflaim-5.1.969/util/xmlfiles/7006240a.xml0000644000175000017500000000162010511001742021541 0ustar ahodgkinsonahodgkinson 7006240a 1574 Various Artists / Christmas Greatest Hits Vol. 2 cddb/misc Twelve Days of Christmas - Bing Crosby The First Noel - Frank Sinatra O Tannenbaum - Nat 'King' Cole I Saw Three Ships - Mario Lanza O Come All Ye Faithful - Jim Reeves Jingle Bells - Bing Crosby Silent Night - Frank Sinatra O Holy Night - Nat 'King' Cole Joy to The World - Mario Lanza O Little Town of Betlehem - Jim Reeves libxflaim-5.1.969/util/xmlfiles/70069609.xml0000644000175000017500000000141010511001742021477 0ustar ahodgkinsonahodgkinson 70069609 1688 Ronnie Milsap / Greatest Hits cddb/country (I'd Be) A Legend In My Time (I'm A) Stand By My Woman Man Pure Love Daydreams About Night Things It Was Almost Like A Song Smoky Mountain Rain Please Don't Tell Me How The Story Ends Back On My Mind Again What A Difference You've Made In My Life libxflaim-5.1.969/util/xmlfiles/7005640a.xml0000644000175000017500000000130110511001742021540 0ustar ahodgkinsonahodgkinson 7005640a 1382 Del Shannon / Greatest Hits cddb/rock Runaway Hats off to Larry So long baby Hey little girl Little town flirt Two kinds of teardrops The swiss maid Keep searchin Cry myself to sleep Two silhouettes libxflaim-5.1.969/util/xmlfiles/7006450a.xml0000644000175000017500000000130510511001742021544 0ustar ahodgkinsonahodgkinson 7006450a 1607 Teresa Brewer / The Best of Teresa Brewer cddb/misc Music! Music! Music! You Send Me Jilted Empty Arms I Gotta Go Get My Baby Ricochet Pledging My Love Bo Weevil Till I Waltz Again With You A Tear Fell libxflaim-5.1.969/util/xmlfiles/7006640a.xml0000644000175000017500000000143110511001742021545 0ustar ahodgkinsonahodgkinson 7006640a 1638 The Dead Milkmen / Cream Of The Crop: The Best Of The Dead Mi cddb/rock Bitchin' Camaro Punk Rock Girl The Thing That Only Eats Hippies Surfin' Cow Instant Club Hit (You'll Dance To Anything) Smokin' Banana Peels Stuart Beach Song Dean's Dream Laundromat Song libxflaim-5.1.969/util/xmlfiles/7006b009.xml0000644000175000017500000000122510511001742021546 0ustar ahodgkinsonahodgkinson 7006b009 1714 Dorival Caymmi & Tom Jobim / Caymmi Visita Tom cddb/misc Das rosas Ss tinha de ser com vocj Inztil paisagem Vai de vez Cangco da noiva Saudade de Bahia Tristeza de nss dois Berimbau Sem vocj libxflaim-5.1.969/util/xmlfiles/7006830a.xml0000644000175000017500000000164110511001742021551 0ustar ahodgkinsonahodgkinson 7006830a 1669 Electric Eel Shock x The Get Go / Split cddb/rock Electric Eel Shock - Nothing Electric Eel Shock - My Tiger Electric Eel Shock - Speedy Joe Electric Eel Shock - Rock'n'Roll Can Rescue The World Electric Eel Shock - I Have No Money The Get Go - Sleepy Lisa The Get Go - Squinting With Yr Ears The Get Go - My Gurl (Copkiller #263) The Get Go - The Kids Who Took Forever The Get Go - March of the Get-Go Hoodlums libxflaim-5.1.969/util/xmlfiles/7006b409.xml0000644000175000017500000000123010511001742021546 0ustar ahodgkinsonahodgkinson 7006b409 1718 The Casket Lottery / Moving Mountains cddb/rock A Dead Dear Rip Van Winkle Vista Point Jealosy On Tap A Thousand Oaks (Away from home) Ancient Injury Stolen Honda Keep Searching Optimist Honor Roll libxflaim-5.1.969/util/xmlfiles/7005a10a.xml0000644000175000017500000000157710511001742021627 0ustar ahodgkinsonahodgkinson 7005a10a 1443 Various / The Rock 'n' Roll Years: Number 1 Hits cddb/misc Bill Haley & The Comets / Rock Around the Clock Pat Boone / Ain't that a Shame Chubby Checker / The Twist Wilbert Harrison / Kansas City The Platters / The Great Pretender Del Shannon / Runaway Mark Dinning / Teen Angel Joey Dee / Peppermint Twist Danny & The Juniors / At the Hop The Coasters / Yakety Yak libxflaim-5.1.969/util/xmlfiles/7001e10c.xml0000644000175000017500000000163010511001742021617 0ustar ahodgkinsonahodgkinson 7001e10c 483 V.A. / BONUS CD SEB 1-4 NON STOP MEGA MIX cddb/misc BIG BROTHER / ALEPH SUPER SONIC LEVEL / ANTONELLA BYE BYE BABY / MAX COVERI HELP ME / MELA MY WORLD / SOPHIE DIVINE / MIKE HAMMER POWER OF MAGIC / ALPHA TOWN JUST A GAME / VANESSA LOVE & PASSION / GIPSY & QUEEN GUNFIRE / MARK FARINA BAD DESIRE / F.C.F. TOUCH ME / LINDA ROSS libxflaim-5.1.969/util/xmlfiles/7004f809.xml0000644000175000017500000000146210511001742021563 0ustar ahodgkinsonahodgkinson 7004f809 1274 Ammer meets Console / Loopspool cddb/misc Wann und wo Herr Professor? Würden Sie ihn bitte beschreiben? Brezel | Pause | Firlefanz Sein Gesicht war das eines Gelehrten. Freiheit | Garten | Nadelöhr Da müsse sich sogleich alles verändern. Ecke | Charakter | Schublade Geschichte ist ein Engel ... being blown ... rückwärts. Tischtuch | Erdteil | Ewigkeit libxflaim-5.1.969/util/xmlfiles/7006a70a.xml0000644000175000017500000000164310511001742021630 0ustar ahodgkinsonahodgkinson 7006a70a 1705 Slim Whitman / Birmingham Jail cddb/country YEAR: 1966 ID3G: 2 Birmingham Jail Walbash Waltz Paint A Rose On The Garden Wall I'll Do As Much For You Someday Let's Go To Church (Next Sunday Morning) I'm Casting My Lasso Towards The Sky There's A Rainbow In Every Teardrop Tears can Never Drown The Flame (That's In My Heart) I'm Crying For You I'll Never Pass This Way Again libxflaim-5.1.969/util/xmlfiles/7005c60b.xml0000644000175000017500000000153710511001742021633 0ustar ahodgkinsonahodgkinson 7005c60b 1480 Johnny Cash / The singing storyteller cddb/country Goodbye little darling Give my love to Rose Hey good looking I can't help it I could never be ashamed of you I couldn't keep from crying I love you because The ways of a woman in love You're the nearest thing to heaven Come in, stranger Next in line libxflaim-5.1.969/util/xmlfiles/7005a90a.xml0000644000175000017500000000161010511001742021623 0ustar ahodgkinsonahodgkinson 7005a90a 1451 Various Artists / 50s Rock N' Roll - Disc 2 cddb/rock ID3G: 17 Jimmy Clanton: Just A Dream The Coasters: Yakety Yak Duane Eddy: Revel Rouser The Elegants: Little Star Danny & The Juniors: Rock N Roll is Here to Stay Bobby Freeman: Do you Wanna Dance Mark Dinning: Teen Angel The Drifters: There Goes My Baby The Feetwoods: Mr. Blue The Impalas: Sorry [I Ran All The Way Home] libxflaim-5.1.969/util/xmlfiles/70065e0a.xml0000644000175000017500000000132710511001742021631 0ustar ahodgkinsonahodgkinson 70065e0a 1632 JOAN SEBASTIAN / NORTEÑO cddb/soundtrack QUIEREME LA POLKA DE REYNOSA VERDAD QUE DUELE EN TU SONRISA EL CORRIDO DE ZENON MAS QUE A NINGUNA LLORAR ENTRE SOMBRAS TRAFICANTE DE BESOS LEVANTADO EN ARMAS OJITOS DE GOLONDRINA libxflaim-5.1.969/util/xmlfiles/7006a90a.xml0000644000175000017500000000140710511001742021630 0ustar ahodgkinsonahodgkinson 7006a90a 1707 The Grass Roots / The Grass Roots Greatest Hits Vol. 1 cddb/rock Let's Live For Today Where Were You When I Needed You This Precious Time Two Divided By Love The River Is Wide Sooner Or Later Walkin' Through The Country Temptation Eyes Feelings Bella Linda libxflaim-5.1.969/util/xmlfiles/7004e70c.xml0000644000175000017500000000153310511001742021632 0ustar ahodgkinsonahodgkinson 7004e70c 1257 Mjölk / Ammuntakiekko cddb/misc YEAR: 1996 ID3G: 100 Jättehejssan Loviisa-Porvoo-hymni Naksutellaan sieraimia Pulkka Luulit että oon Hannu Sha Jatsiukko Jättiläinen kohtaa Dag Hammarskiöldin Äiti me ollaan telkkarissa Makkaraa Joulupukin kouluvessa Paavin poika libxflaim-5.1.969/util/xmlfiles/70050608.xml0000644000175000017500000000113510511001742021470 0ustar ahodgkinsonahodgkinson 70050608 1288 S.o.K. / Spicier Demo! cddb/rock Secret Bonus Track!!! I'm In Love When You Smile At Me The Abby Song Not Anymore (Jellybeans) I ... I! All About Jesus Made Perfect (Acoustic) libxflaim-5.1.969/util/xmlfiles/70062308.xml0000644000175000017500000000126210511001742021471 0ustar ahodgkinsonahodgkinson 70062308 1573 Collette Wise / Faces Of Love cddb/country Blinded By The Love Little Things There Ain't Gonna Be A You And Me Forever's Never Long Enough What Am I Gonna Do With A Love Like That Leavin' On Your Mind Faith In Love Meet Me Where They Play The Blues libxflaim-5.1.969/util/xmlfiles/70060608.xml0000644000175000017500000000113110511001742021465 0ustar ahodgkinsonahodgkinson 70060608 1544 Girlschool / C'mon let's go cddb/rock C'mon lets go Fresh and Blood Not for sale Midnight Ride Deadline Don't call it love Demolition Boys Take it from me libxflaim-5.1.969/util/xmlfiles/7005ea09.xml0000644000175000017500000000110110511001742021622 0ustar ahodgkinsonahodgkinson 7005ea09 1516 Kindred / Kindred - File 01 cddb/rock Forecast Apathy Mirror Blood Bible Concept Controlled Skyward Disembodied libxflaim-5.1.969/util/xmlfiles/7005e90a.xml0000644000175000017500000000154210511001742021633 0ustar ahodgkinsonahodgkinson 7005e90a 1515 Various / Billboard Top R & B Hits - 1961 cddb/rock The Miracles / Shop Around Chris Kenner / I Like It Like That - Pt. 2 Ben E. King / Stand By Me Lee Dorsey / Ya Ya Pips / Every Beat Of My Heart Bobby Bland / Turn On Your Love Light Fats Domino / Let The Four Winds Blow The Jive Five / My True Story Dee Clark / Raindrops The Impressions / Gypsy Woman libxflaim-5.1.969/util/xmlfiles/70068008.xml0000644000175000017500000000126010511001742021472 0ustar ahodgkinsonahodgkinson 70068008 1666 Lieberman Winfried, Watanabe Katsuya / Sonata for Oboe, V-Cello and 2 Cembalos cddb/misc YEAR: 2000 ID3G: 105 Trio Sonata Eb-Maj Largo-Vivace Mest-Vivace Torio Sonata G-Min Allegro Largo Allegro Sonata No.50 G-Min Largo Allegro Andante Presto assai libxflaim-5.1.969/util/xmlfiles/70044808.xml0000644000175000017500000000113710511001742021477 0ustar ahodgkinsonahodgkinson 70044808 1098 Champion / Come Out Swinging cddb/rock Phyte Records Come Out Swingintro Harrison and Broadway Assume the Worst The Insider Left Your Mark A Thank You Note 1 to 2 (Hidden) libxflaim-5.1.969/util/xmlfiles/70064609.xml0000644000175000017500000000111410511001742021473 0ustar ahodgkinsonahodgkinson 70064609 1608 Satellite 77 / Everyone's A Critic cddb/blues Adam Shame Saturn Boy Nine Shore Icon Tomorrow It's Over Lawnware libxflaim-5.1.969/util/xmlfiles/70046709.xml0000644000175000017500000000127210511001742021501 0ustar ahodgkinsonahodgkinson 70046709 1129 al Score) cddb/soundtrack Arabian Nights, Reprise #1 Babkak, Omar, Alladin, Kassim Arabian Nights, Reprise #2 Friend Like Me Proud Of Your Boy How Quick they Forget Arabian Nights, Reprise #3 High Adventure Arabian Nights, Reprise #4 libxflaim-5.1.969/util/xmlfiles/7006ba0a.xml0000644000175000017500000000142010511001742021674 0ustar ahodgkinsonahodgkinson 7006ba0a 1724 Elvis Presley / Promised Land cddb/rock PROMISED LAND THERE'S A HONKY TONK ANGEL [WHO WILL TAKE ME BACK IN] HELP ME MR. SONGMAN LOVE SONG OF THE YEAR I'TS MIDNIGHT YOUR LOVE'S BEEN A LONG TIME COMING IF YOU TALK IN YOUR SLEEP THINKING ABOUT YOU YOU ASKED ME TO libxflaim-5.1.969/util/xmlfiles/7005db0a.xml0000644000175000017500000000144010511001742021700 0ustar ahodgkinsonahodgkinson 7005db0a 1501 Kitty Wells / Greatest Hits (Front Row Entertainment) cddb/country It Wasn't God Who Made Honky Tonk Angels Heartbreak, U.S.A. I Can't Stop Loving You Amigo's Guitar Left To Right Makin' Believe Mommy For A Day Password Searching (For Someone Like You) You Don't Hear libxflaim-5.1.969/util/xmlfiles/7004920a.xml0000644000175000017500000000145610511001742021553 0ustar ahodgkinsonahodgkinson 7004920a 1172 Nur Immer Sauber / Kuwait On Fire cddb/misc | p.shah | m.fischer | c.steinmann | zurich 2001 | Killing An Arab Killing Another Arab Azzuro (Blue Sky Over Kuwait) Kuwait On Fire Friday Night In Kuwait City Saturday Night In Kuwait City The Day After Another Night Domestic Workers Union More Workers libxflaim-5.1.969/util/xmlfiles/7006540a.xml0000644000175000017500000000134410511001742021547 0ustar ahodgkinsonahodgkinson 7006540a 1622 The Grass Roots / Tempation Eyes cddb/rock Midnight Confessions Sooner or Later Where Were You When I Needed You BElla Linda Feelings Temptation Eyes Look Out Girl Things I Should Have Said Heaven Knows I'd Wait a Million Years libxflaim-5.1.969/util/xmlfiles/7006900a.xml0000644000175000017500000000137310511001742021551 0ustar ahodgkinsonahodgkinson 7006900a 1682 Yves Duteil / Ton absence cddb/misc Pour les enfants du monde entier A ma mere Jusqu'ou je t'aime Le silence ou la verite La valse des etiquettes Ton absence La mer ressemble a ton amour La rumeur Les petits hommes verts Regard impressionniste libxflaim-5.1.969/util/xmlfiles/7005a209.xml0000644000175000017500000000131510511001742021546 0ustar ahodgkinsonahodgkinson 7005a209 1444 Mystik Journeymen / Mercury Rising Single cddb/rock YEAR: 1999 ID3G: 7 Ymbira Dreams Mercury Rising feat. The Bad News Bear Grouch The Odyssey Impact (Beatdie Delite) Guide Signal 7 feat. Robin Reasonz Blacksands Mercury Rising Instrumental libxflaim-5.1.969/util/xmlfiles/7006370a.xml0000644000175000017500000000134710511001742021553 0ustar ahodgkinsonahodgkinson 7006370a 1593 Buck Owens / Hot Dog cddb/country Don't Let Her Know A-11 Summertime Blues Memphis Blues Hot Dog Put A Quarter In The Jukebox Under Your Spell Again (Duet With Dwight Yoakam) Second Fiddle Sweethearts In Heaven Keys In The Mailbox libxflaim-5.1.969/util/xmlfiles/7006730a.xml0000644000175000017500000000134410511001742021550 0ustar ahodgkinsonahodgkinson 7006730a 1653 The Dubliners / The Dubliners #2 cddb/folk The Louse House At Kilkenny The Molly Maguires Johnny McGory Wheels Of The World The Aul Triangle Spancil Hill The Black Velvet Band Peat Bog Soldiers Farewell To Ireland Lord Of The Dance libxflaim-5.1.969/util/xmlfiles/7005e009.xml0000644000175000017500000000122410511001742021547 0ustar ahodgkinsonahodgkinson 7005e009 1506 Hyenas in the Desert / Die Laughing cddb/blues elephant graveyard can you feel it wild dogs the longest night (journal #1) concubinez why me fresh meat hyenas in the desert other side of midnight libxflaim-5.1.969/util/xmlfiles/7006750a.xml0000644000175000017500000000124010511001742021545 0ustar ahodgkinsonahodgkinson 7006750a 1655 Painkiller / Buried Secrets cddb/newage Tortured Souls One-eyed Pessary Trailmarker Blackhole dub Buried Secrets The Ladder Executioner Black Chamber Skinned The Toll libxflaim-5.1.969/util/xmlfiles/7006580a.xml0000644000175000017500000000130110511001742021544 0ustar ahodgkinsonahodgkinson 7006580a 1626 Bob Wills & the Texas Playboys / Roly Poly cddb/country Roly Poly Faded Love Ida Red I'm Gonna Be Boss C Jam Blues Take Me Back To Tulsa All Night Long Cotton Eyed Joe Honeysuckle Oh Monah libxflaim-5.1.969/util/xmlfiles/7005960a.xml0000644000175000017500000000127010511001742021552 0ustar ahodgkinsonahodgkinson 7005960a 1432 Lupe y Raúl cddb/misc Con el Mariachi Vargas de Tecalitlán Cosas del amor Dos almas Redención Flores negras Somos diferentes Deuda Ya no te quiero Falsa Mi pensamiento Fue mentira libxflaim-5.1.969/util/xmlfiles/70037c08.xml0000644000175000017500000000106110511001742021550 0ustar ahodgkinsonahodgkinson 70037c08 894 Snuff / Oishie Deh cddb/newage ID3G: 43 Yuki Romeo and Juliet Icy '97 Umpan Man Ambassador Fishy War March Angel Attack libxflaim-5.1.969/util/xmlfiles/7006770a.xml0000644000175000017500000000203210511001742021547 0ustar ahodgkinsonahodgkinson 7006770a 1657 Various Artists / Groovy 60's Hits cddb/rock YEAR: 2001 ID3G: 17 Gary Lewis & The Playboys / Sealed With A Kiss The Association / Cherish Edison Lighthouse / Love Grows (Where Rosemary Goes) Gerry & The Pacemakers / Don't Let The Sun Catch You Crying Wayne Fontana / A Groovy Kind Of Love Aaron Neville / Tell It Like It Is The Classics IV, featuring Dennis Yost / Spooky B.J. Thomas / Raindrops Keep Falling On My Head The Foundations / Build Me Up Buttercup Herman's Hermits / There's A Kind Of Hush libxflaim-5.1.969/util/xmlfiles/7006960a.xml0000644000175000017500000000132510511001742021554 0ustar ahodgkinsonahodgkinson 7006960a 1688 California Proline / Beach Party cddb/misc Submitted by Steve Clancy Surfin' USA Wipe Out Beach Baby Good Vibrations Sherry Barbara Ann Help Me Rhonda December '63 Here Comes Summer Summer Holiday libxflaim-5.1.969/util/xmlfiles/7006790a.xml0000644000175000017500000000153610511001742021561 0ustar ahodgkinsonahodgkinson 7006790a 1659 Various / Viva Italia! cddb/rock \nYEAR: 1998 ID3G: 98 Return to Me (Ritorna a Me) Torna a Surriento (Come Back to Sorrento) Italian Walz Just Say I Love Her (Dicitencello Vuie) Mona Lisa Volare (Nel Blu Dipinto Di Blu) Arrivederci, Roma Ciao, Ciao, Bambina Come Prima (For the First Time) Tell Me You're Mine (Per Un Bacio D'amor) libxflaim-5.1.969/util/xmlfiles/7005c909.xml0000644000175000017500000000127310511001742021562 0ustar ahodgkinsonahodgkinson 7005c909 1483 or 3 cddb/rock You've Got Your Troubles Here Comes That Rainy Day Feeling Again Here It Comes Again This Diamond Ring Count Me In Sealed With A Kiss Ferry Cross the Mersey Don't Let the Sun Catch You Crying Unchained Melody libxflaim-5.1.969/util/xmlfiles/70059d08.xml0000644000175000017500000000130710511001742021560 0ustar ahodgkinsonahodgkinson 70059d08 1439 Gosdin Vern / Rough Around The Edges cddb/country Lovin' You Is Music To My Mind I Wish I Had Something When Love Was All We Had To Share It's Beginning To Look like The End Rough Around The Edges We're Makin' Up For lost Time Rustin' Down Singer Of Sad Songs libxflaim-5.1.969/util/xmlfiles/00097210.xml0000644000175000017500000000217110511001742021462 0ustar ahodgkinsonahodgkinson 00097210 2420 Frank Sinatra / Blue skies cddb/jazz blue skies night and day somebody loves me you make me feel so young s'wonderful love me or leave me they say it's wonderful you do something to me on the sunny side of the street begin the beguine ol' man river don't blame me it all depends on you i fall in love with you the music stopped i don't stand a ghost of a chance libxflaim-5.1.969/util/xmlfiles/7005d30a.xml0000644000175000017500000000166210511001742021627 0ustar ahodgkinsonahodgkinson 7005d30a 1493 Various artists / Flashback 60 s cddb/misc When will i be love - The Every Brothers Barbara Ann - The Regents It keeps right on-a hurtin - Johnny Tillotson Since i feel for you - Lenny Welch Clap your hands - The Beau-Marks I'm gonna knock on your door - Eddie Hodges See you in september - The Happenings A lover's concerto - The Toys Daddy's home - Shep & The Limelites Last kiss - J. Frank Wilson & The Cavaliersd libxflaim-5.1.969/util/xmlfiles/7006b40a.xml0000644000175000017500000000134010511001742021620 0ustar ahodgkinsonahodgkinson 7006b40a 1718 The Moonglows / Super Hits cddb/rock Sincerely See Saw Ten Commandments Of Love Don't Say Goodbye Please Send Me Someone To Love Twelve Months Of Ther Year We Go Together Blue Velvet I'll Stop Wanting You Kiss Me Baby libxflaim-5.1.969/util/xmlfiles/7005f20a.xml0000644000175000017500000000202410511001742021621 0ustar ahodgkinsonahodgkinson 7005f20a 1524 Several / Country Music Classics Volume XIX (1970-1975) cddb/country Do You Remember These - The Statler Brothers Kiss An Angel Good Mornin' - Charley Pride The Most Beautiful Girl - Charlie Rich Bright Lights, Big City - Sonny James Hello Darlin' - Conway Twitty (Hey Won't You Play) Another Somebody Done Somebody Wrong Song - B.J. Thomas Dueling Banjos - Eric Weissberg and Steve Mandell When You're Hot, You're Hot - Jerry Reed I Love - Tom T. Hall Would You Lay With Me (In A Field Of Stone) - Tanya Tucker libxflaim-5.1.969/util/xmlfiles/70064c0a.xml0000644000175000017500000000131010511001742021616 0ustar ahodgkinsonahodgkinson 70064c0a 1614 Glenn Miller / tertas cddb/jazz Tuxedo Junction King Porter Stomp Oh Lady be Good The Way You Look Tonight Now We Know Time Alone Will Tell Irresistible You Spanish Shawl Somebody's Wrong In My Arms libxflaim-5.1.969/util/xmlfiles/7005d50a.xml0000644000175000017500000000175410511001742021633 0ustar ahodgkinsonahodgkinson 7005d50a 1495 Various / 60's Pop (Blockbuster Summer Series) cddb/rock Ricky Nelson: Travelin' Man Gary Lewis & the Playboys: This Diamond Ring Beach Boys: I Get Around Jackie DeShannon: What the World Needs Now Is Love Jan & Dean: Surf City Jay & the Americans: Come A Little Bit Closer Freddie & the Dreamers: I'm Telling You Now Gerry & the Pacemakers: Ferry Cross the Mersey Peter & Gordon: A World Without Love The Music Explosion: Little Bit O' Soul libxflaim-5.1.969/util/xmlfiles/70062f0a.xml0000644000175000017500000000143010511001742021622 0ustar ahodgkinsonahodgkinson 70062f0a 1585 Pearl Bailey / It's a Great Feeling cddb/misc He Didn't Ask Me Protect Me Ma! He's Making Eyes At Me It's A Great Feeling Some Days There Just Ain't No Fish Mamie Is Mimi He Didn't Have the Know How, No How If My Friends Could See Me Now Big Spender Mame libxflaim-5.1.969/util/xmlfiles/70066b0a.xml0000644000175000017500000000137410511001742021631 0ustar ahodgkinsonahodgkinson 70066b0a 1645 Bob Dylan / Nashville Skyline cddb/folk Girl From The North Country Nashville Skyline Rag To Be Alone With You I Threw It All Away Peggy Day Lay Lady Lay One More Night Tell Me That It Isn't True Country Pie Tonight I'll Be Staying Here With You libxflaim-5.1.969/util/xmlfiles/70064e0a.xml0000644000175000017500000000132410511001742021625 0ustar ahodgkinsonahodgkinson 70064e0a 1616 Paul Buskirk / Nacogdoches Waltz cddb/blues Nacogdoches Waltz Sophisticated Lady Little Rock Get-A-Way Intermezzo Dardanella Under Paris Ski Joy (Jesu Joy of Man's Desiring) Nola I will Wait for You Greensleeves libxflaim-5.1.969/util/xmlfiles/7004bb09.xml0000644000175000017500000000117610511001742021633 0ustar ahodgkinsonahodgkinson 7004bb09 1213 HISTEREZIS MOZGA / kadmozag bije u pozne jacije cddb/newage 3" cd D.I.Y. Chuka chups Lik na porcelanu Glp Epitaf Final duel Grr Separator Duum Vidovita Corka libxflaim-5.1.969/util/xmlfiles/7004f60a.xml0000644000175000017500000000124710511001742021632 0ustar ahodgkinsonahodgkinson 7004f60a 1272 SPEAK / Knee Deep In Guilt cddb/misc Unheard Truths Virus Bottom Line In From The Cold Now You See Me Good Clean Hate Free Yourself Always Throwing Stones Knee Deep In Guilt libxflaim-5.1.969/util/xmlfiles/7006b80a.xml0000644000175000017500000000136310511001742021631 0ustar ahodgkinsonahodgkinson 7006b80a 1722 Conjunto Primavera / Me Nacio Del Alma cddb/misc YEAR: 1996 ID3G: 86 Me Nacio Del Alma Tu Desastre Lo Vas a Pagar Tambien Tengo Celos Quiero Estar Loco Mala Mujer Tu Juego y Mi Amor Es Muy Tu Vida Sere Tu Sombra Mi Destino Fue Quererte libxflaim-5.1.969/util/xmlfiles/70061308.xml0000644000175000017500000000112510511001742021466 0ustar ahodgkinsonahodgkinson 70061308 1557 Crettins Puddle / Crettins Puddle cddb/rock See Your Smile Mortin's Sandals Vodka & Lime Yummy Gummy Candyland Electric Dreams Spice-E-Dice Moon? Moorh-Sum Cigam libxflaim-5.1.969/util/xmlfiles/70045109.xml0000644000175000017500000000121110511001742021463 0ustar ahodgkinsonahodgkinson 70045109 1107 MxPx / Renaissance E.P. cddb/rock Lonesome Town Letting Go Party II (Time To Go) Time Will Tell The Opposite Don't Look Back Talk Of The Town The Struggle Yuri Wakes Up Screaming libxflaim-5.1.969/util/xmlfiles/70031709.xml0000644000175000017500000000144410511001742021474 0ustar ahodgkinsonahodgkinson 70031709 793 The Stinky Puffs / A Little Tiny Smelly Little Bit of ...... cddb/rock Buddies Aren't Butts Menendez' Killed Their Parents I'll Love You Anyway I am Gross! / no You're Not! Buddies Aren't Butts Menendez' Killed Their Parents I'll Love You Anyway I am Gross! / no You're Not! Buddies Aren't Butts libxflaim-5.1.969/util/xmlfiles/70061508.xml0000644000175000017500000000116310511001742021472 0ustar ahodgkinsonahodgkinson 70061508 1559 John Andereson / Swingin' cddb/country Swingin' I've Got It Made Keep Your Hands To Yourself Can't Get Away From You Steamy Windows Solid Ground Where I Come From Who Got Your Love libxflaim-5.1.969/util/xmlfiles/7006aa0d.xml0000644000175000017500000000156410511001742021707 0ustar ahodgkinsonahodgkinson 7006aa0d 1708 Big 8 Drum Line / Slam Session cddb/misc YEAR: 1998 ID3G: 122 Come On and Go Get It On Zub-Dub Deacon Dairy Fresh Cap'n Cotton Street Time Tenor Overture Big Daddy Wet Love Bass Drum Feature Haitian Hustle Big 8 Sequence It's All Over libxflaim-5.1.969/util/xmlfiles/7006060a.xml0000644000175000017500000000232510511001742021544 0ustar ahodgkinsonahodgkinson 7006060a 1544 Various / Rock 'N' Roll Hall Of Fame cddb/rock Volume XII Wayne Fontana & The Mindbenders - Groovy Kind Of Love Mungo Jerry - In The Summertime Tommy Roe - Dizzy Shangri Las - Remember (Walking In The Sand) Desmond Dekker & The Aces - Israelites B.J. Thomas - Raindrops Keep Falling On My Head Jerry Butler - Moon River Herman's Hermits - Leaning On A Lamp Post Crests - The Angels Listened In Lovin' Spoonful - Did You Ever Have To Make Up Your Mind libxflaim-5.1.969/util/xmlfiles/7005cd0a.xml0000644000175000017500000000137010511001742021703 0ustar ahodgkinsonahodgkinson 7005cd0a 1487 Varios / Los mejores villancicos infantiles. V. 12 cddb/misc Rin, Rin Canta, Ríe y Bebe En el Portal de Belén Pastores venid Noche de Paz Ya Vienen los Reyes Madre en la puerta hay un Niño Campana sobre campana Los peces del Río Dime Niño de quién eres libxflaim-5.1.969/util/xmlfiles/7006420a.xml0000644000175000017500000000143610511001742021546 0ustar ahodgkinsonahodgkinson 7006420a 1604 Dion DiMucci / Drip Drop cddb/rock Drip Drop This Little Girl You're Mine He'll Only Hurt You Ruby Baby No One's Waiting For Me The Loneliest Man In The World Be Careful Of Stones That You Throw I Can't Believe (That You Don't Love Me Anymore) Donna The Prima Donna libxflaim-5.1.969/util/xmlfiles/70040b08.xml0000644000175000017500000000107710511001742021550 0ustar ahodgkinsonahodgkinson 70040b08 1037 The Beach Boys / Beach Boys Greatest Hits cddb/rock Judy Surfin' Surfin' Safari Luau Barbie Karate Surfer Girl What Is A Young Girl Make Of libxflaim-5.1.969/util/xmlfiles/70059908.xml0000644000175000017500000000110710511001742021503 0ustar ahodgkinsonahodgkinson 70059908 1435 Sibbe Fra Sandnes / Countri cddb/misc Countri Kyssa meg Nå e` det feri Kyssa meg igjen Ingenting e så ei feide dama Klinkekulå mi Alt hu ville Ballane e` sie libxflaim-5.1.969/util/xmlfiles/7006270a.xml0000644000175000017500000000155410511001742021552 0ustar ahodgkinsonahodgkinson 7006270a 1577 Various / Goodbye Vietnam Vol 1 cddb/misc Wayne Fontana & The Mindbenders / Groovy Kind of Love Tuneweavers / Happy Happy Birthday Baby Louis Armstrong / Black & Blue Mary Wells / Two Lovers Troy Shondell / This Time Vogues / You're the One Surfaris / Wipe Out Corsairs / Smoky Places Tams / What Kind of Fool Kingston Trio / Hang Down Your Head Tom Dooley libxflaim-5.1.969/util/xmlfiles/7005b308.xml0000644000175000017500000000113710511001742021551 0ustar ahodgkinsonahodgkinson 7005b308 1461 THE COLTS / SEVEN HEAVEN cddb/reggae -GUINGUETTE JAPNAISE- CIRCUS TRAIN LA CARNAVAL tengoku to jigoku ANYWAY(Gonna be Alright) DIG IT UP & GO HAPPY TOGETHER(Album Version) SMILeY libxflaim-5.1.969/util/xmlfiles/7006840a.xml0000644000175000017500000000137310511001742021554 0ustar ahodgkinsonahodgkinson 7006840a 1670 Various / The Sixties Generation 1968 cddb/rock Spooky Sealed With A Kiss Something's Happening Baby Now That I've Found You Hooked On A Feeling Everlasting Love I Thank You It's In His Kiss The Dock Of The Bay The Eyes Of A New York Woman libxflaim-5.1.969/util/xmlfiles/7005f709.xml0000644000175000017500000000125310511001742021561 0ustar ahodgkinsonahodgkinson 7005f709 1529 Jil Sobule / It's the Thought that Counts cddb/rock Christmas Time is Here Merry Christmas to the Family Jesus was a Dreidl Spinner This is Your Land One of These Days St. Francis Lucy at the Gym Kathy Lee Heroes libxflaim-5.1.969/util/xmlfiles/7005c10b.xml0000644000175000017500000000150710511001742021623 0ustar ahodgkinsonahodgkinson 7005c10b 1475 Ousters Band / Erap: The White Album cddb/rock \nYEAR: 2001 ID3G: 17 Huling Huli Jeuteng Lord Evil Ways ni Erap It Won't Be Long (Goodbye Erap) Wonderful Erap Contra-Vida Loca Sumpain si Erap Ang Baho mo Erap Jueteng Minute Bye-bye Erap Epilogue: Jinggoy Boy (Bonus Track) libxflaim-5.1.969/util/xmlfiles/7005c30a.xml0000644000175000017500000000137510511001742021627 0ustar ahodgkinsonahodgkinson 7005c30a 1477 Jim Reeves / The country side of Jim Reeves cddb/country A railroad bum Blue side of lonesome Waitin's for a train I won't forget you My lips are sealed Most of the time When two worlds colilde Yonder comes a sucjer A ffallen star Highway to nowhere libxflaim-5.1.969/util/xmlfiles/70057a0a.xml0000644000175000017500000000176710511001742021636 0ustar ahodgkinsonahodgkinson 70057a0a 1404 Arthur Godfrey And The Richard Wolfe Children's Chorus / All I Want For Christmas Is My Two Front Teeth cddb/misc YEAR: 1972 ID3G: 12 Christmas Is For Children I Saw Mommy Kissing Santa Claus Frosty The Snowman A Holly Jolly Christmas Medley: Jingle Bells, Up On The Housetop, Jolly Old St. Nicholas, We Wish You A Merry Christmas All I Want For Christmas (Is My Two Front Teeth) Santa Claus Is Comin' To Town Suzy Snowflake Twelve Days Of Christmas 'Twas The Night Before Christmas libxflaim-5.1.969/util/xmlfiles/70061f0a.xml0000644000175000017500000000136710511001742021632 0ustar ahodgkinsonahodgkinson 70061f0a 1569 Various Artists / Love to Rock 'N' Roll cddb/misc Daddy's Home That's My Desire Lovers Never Say Goodbye Tears On My Pillow Been So Long Don't Ask Me To Be Lonely Gee Crying In The Chapel Can I Come Over Tonight He's Gone libxflaim-5.1.969/util/xmlfiles/70065b0a.xml0000644000175000017500000000170510511001742021626 0ustar ahodgkinsonahodgkinson 70065b0a 1629 Patsy Cline / Patsy Cline Sings Song of Love cddb/country ID3G: 2 Faded Love You Belong to Me Back in Baby's Arms True love Someday (You'll Want Me to Love You) I Fall to Pieces Sweet Dreams (of You) Crazy He Called Me Baby I Love You So Much (It Hurts) libxflaim-5.1.969/util/xmlfiles/70052008.xml0000644000175000017500000000150710511001742021467 0ustar ahodgkinsonahodgkinson 70052008 1314 Various / Risque Blues - Keep On Churnin' cddb/blues Wynonie Harris - Keep On Churnin' (Till The Butter Comes) Dorothy Ellis - Drill, Daddy, Drill Roy Brown - Rockin' At Midnight Little Esther - Turn The Lamps Down Low Todd Rhodes with Connie Allen - Rocket 69 Hank Ballard - The Coffee Grind Pete "Guitar" Lewis - Chocolate Pork Chop Man Lucky Millinder with Myra Johnson - Silent George libxflaim-5.1.969/util/xmlfiles/70060109.xml0000644000175000017500000000113210511001742021462 0ustar ahodgkinsonahodgkinson 70060109 1539 Numbskulls / Heads in the Sand cddb/rock Dreaming Confused School Sux Time Enfaseema Rippin' Apart Waiting Ned is Dead Barbie Doll libxflaim-5.1.969/util/xmlfiles/7005e60a.xml0000644000175000017500000000135710511001742021634 0ustar ahodgkinsonahodgkinson 7005e60a 1512 Sub-Conscious / Lyric Luverz Deluxe: the Deep Dish Thesis cddb/misc The Daydream Journal Pages Whut Part of the Brain? Loose Lipped Diction Partial Carcass I Spit Sub-stance Tired and Talented Plyometrics Stick Shift Scripts (the) All Out (ro) libxflaim-5.1.969/util/xmlfiles/70059b0a.xml0000644000175000017500000000134310511001742021627 0ustar ahodgkinsonahodgkinson 70059b0a 1437 LANDSPEEDRECORD! / The Corporate Secret cddb/rock YEAR: 1999 ID3G: 17 Interoffice Copulation Dead Girlfriend Now! Freezie Poof Signs and Symptoms The Maw Chromatic God The Third Shadow Viagra Orgy Re: The Corporate Secret libxflaim-5.1.969/util/xmlfiles/7006af09.xml0000644000175000017500000000127610511001742021641 0ustar ahodgkinsonahodgkinson 7006af09 1713 Keith Whitley / The Best of cddb/country Brother Jukebox That Stuff I Wonder Do You Think of Me A Day In The Life of A Fool On The Other Hand Lady's Choice Some Old Side Road Turn This Thing Around I've Got The Heart For You libxflaim-5.1.969/util/xmlfiles/7005ed09.xml0000644000175000017500000000230210511001742021631 0ustar ahodgkinsonahodgkinson 7005ed09 1519 Christmas Comedy Classics Vol.2 / Christmas Comedy Classics Vol.2 cddb/misc DISC: 1\nUPC: 049925368222\nLABEL: Priority\nENSORECORD: 145140\n\nYEAR: 1993 ID3G: 57 We Wish You A Merry Christmas Christmas At Ground Zero The Twelve Days Of Christmas Yah Das Ist Ein Christmas Tree Deck The Stalls (With Oats And Barley) The Twelve Days Of Christmas The Night Before Christmas A Christmas Carol Jingle Cats Medley libxflaim-5.1.969/util/xmlfiles/70056409.xml0000644000175000017500000000145610511001742021503 0ustar ahodgkinsonahodgkinson 70056409 1382 Anne Murray / Both Sides Now cddb/country Previously released material from Anne's Both Sides Now album originally released in England on the Showcase Label for Castle Communications, Inc. 1985 It's Over Some Birds For Baby Paths Of Victory Both Sides Now Buffalo In The Park Last Thing On My Mind All The Time David's Song libxflaim-5.1.969/util/xmlfiles/70028663.xml0000644000175000017500000001770410511001742021507 0ustar ahodgkinsonahodgkinson 70028663 648 Soundscape / WES-05 Badmiton, Ping Pong, Swords cddb/misc Badminton Racquet (battledore) Hits Birdie (shuttlecock) In Serve Badminton Racquet (battledore) Hits Birdie (shuttlecock) In Serve Badminton Racquet Rim Hit Serve Badminton Soft Shuttlecock Return Badminton Soft Shuttlecock Return Badminton Soft Shuttlecock Return Badminton Soft Shuttlecock Return Badminton Hard Shuttlecock Return Badminton Hard Shuttlecock Return Badminton Hard Shuttlecock Return Badminton Battledore Rim Hit Badminton Battledore Rim Hit Badminton Battledore Swing With Whoosh Badminton Battledore Swing With Whoosh Badminton Battledore Dropped Onto Another Battledore Badminton Shuttlecock Hits Floor Badminton Shuttlecock Hits Floor Ping Pong Ball Serve Ping Pong Ball Serve Ping Pong Serve With Ball Hitting Table Twice Then Return Hit Ping Pong Serve With Ball Hitting Table Twice Then Return Hit Ping Pong Serve With Ball Hitting Table Twice Then Return Hit Ping Pong Served With Sandpaper Paddle Ball Hits Table Once Then Returned Ping Pong Serve With Ball Hitting Table Twice Then Return Hit Ping Pong Ball Return With Sandpaper Paddle Hits Table Twice And Then Returned Ping Pong Sandpaper Paddle Hits Ball Ping Pong Ball Bounced With Hand (bounces Used Before Serves) Ping Pong Single Ball Bounce Ping Pong Single Ball Bounce Ping Pong Single Ball Bounce And Then Caught Ping Pong Single Ball Bounce And Then Caught Ping Pong Ball Bounces On Table Twice Then Off Table Ping Pong Ball Being Bounced On Paddle Ping Pong Ball Being Bounced On Paddle Ping Pong Ball Bounces On Table With Paddle Ping Pong Ball Bounces And Rolls To A Stop Ping Pong Bouncing Ball On Table Stopped With Paddle Ping Pong Bouncing Ball On Table Stopped With Paddle Ping Pong Ball Drop And Then Bounces Until Stop Ping Pong Ball Drop And Then Bounces Until Stop Ping Pong Ball Drop And Then Bounces Until Stop Ping Pong Ball Drop And Then Bounces Until Stop Ping Pong Ball Drop And Then Bounces Until Stop Ping Pong Ball Spinning On Table Ping Pong Ball Roll Ping Pong Ball Roll Ping Pong Ball Catch Ping Pong Ball Pick Up With Slight Slide Ping Pong Ball Pick Up With Small Bounce Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Being Pick Up From Table Ping Pong Paddle Dropped On Table Ping Pong Paddle Set Down On Table Ping Pong Paddle Set Down On Table Ping Pong Paddle Tossed On Table Ping Pong Paddle Thrown On Table Ping Pong Paddle Slammed Down On Table Ping Pong Ball Hit Back And Forth Several Times Ping Pong Ball Hit Back And Forth Several Times Ping Pong Ball Hit Back And Forth Several Times Ping Pong Ball Hit Back And Forth Few Times Ping Pong Ball Hit Back And Forth Several Times Ping Pong Ball Hit Back And Forth Several Times Ping Pong Ball Hit Back And Forth Few Times Swords Kendo Bamboo Swords Rubbing Swords Kendo Bamboo Swords Hit Swords Kendo Bamboo Swords Hit Swords Kendo Bamboo Swords Hit Two Times Swords Kendo Bamboo Swords Hit Three Times Swords Kendo Bamboo Swords Hit Four Times Swords Kendo Bamboo Swords Hit Five Times Swords Kendo Bamboo Sword Combination Strikes And Blocks Swords Kendo Bamboo Sword Combination Strikes And Blocks Swords Broad Swords Rubbing Swords Broad Swords Rubbing Swords Broad Swords Rubbing Swords Broad Swords Clanging Swords Broad Swords Bounce Hit Swords Broad Swords Hit Swords Broad Swords Hit Swords Broad Swords Hit Two Times Swords Broad Swords Hit Three Times Swords Broad Swords Hit Four Times Swords Broad Swords Hit Five Times Swords Metal Swords Rubbing Swords Metal Swords Rubbing Swords Metal Swords Rubbing Swords Metal Swords Clanging Swords Metal Swords Hit And Clangs Swords Metal Swords Hit Swords Metal Swords Hit Two Times Swords Metal Swords Hit Three Times Swords Metal Swords Hit Four Times Swords Metal Swords Hit Five Times libxflaim-5.1.969/util/xmlfiles/7006130a.xml0000644000175000017500000000132210511001742021536 0ustar ahodgkinsonahodgkinson 7006130a 1557 Pop Tarts / Woman is the Fuehrer of the World cddb/rock Hallo Franzi Baby You Can Drive Your Car O.K., Vollgas Kindheit Jugend Sex Büro Film D'Amour Pop Starts So'n Scheiss Europa-Center I Turn My Radio On libxflaim-5.1.969/util/xmlfiles/70066609.xml0000644000175000017500000000117010511001742021477 0ustar ahodgkinsonahodgkinson 70066609 1640 Doom / The Greatest Invention.... cddb/rock Happy Pill Dig Your Grave Trash Breeds Trash Drop Out Worthless Nothing No Justice Silent Scream My Pornography Same Mind libxflaim-5.1.969/util/xmlfiles/7006320a.xml0000644000175000017500000000127710511001742021550 0ustar ahodgkinsonahodgkinson 7006320a 1588 With The Skeletons cddb/country Rockin' Bones Mystery Train No Help Wanted Memphis Train Of Love Boney Maroney Maybelline Pipeliner Blues Hank, You're Like Your Daddy Achy Breaky Heart libxflaim-5.1.969/util/xmlfiles/7004bf0a.xml0000644000175000017500000000141210511001742021700 0ustar ahodgkinsonahodgkinson 7004bf0a 1217 Varios / Villancicos populares cddb/misc YEAR: 1998 ID3G: 12 Ande, ande la marimorena En el portal de Belén La Virgen y San José Noche de Paz Ya viene la Vieja Campanas de Navidad Dime Niño de quien eres La Virgen va caminando Fum, Fum. Fum Jesusito de mi vida libxflaim-5.1.969/util/xmlfiles/7006bd0a.xml0000644000175000017500000000153010511001742021701 0ustar ahodgkinsonahodgkinson 7006bd0a 1727 British Rock / Volume 1 cddb/rock I'm Losin You - Rod Stewart Wild Thing - The Troggs All Day and All Of The Night - The Kinks Tell Her No - The Zombies Go Now - The Moody Blues I'm a Man - The Yardbirds Love Potion #9 - The Searchers Tired of Waiting For You - The Kinks You Turn On Me - Ian Whitcomb San Franciscan Nights - The Animals libxflaim-5.1.969/util/xmlfiles/7005530a.xml0000644000175000017500000000134010511001742021541 0ustar ahodgkinsonahodgkinson 7005530a 1365 Dukes Of Dixieland / Down By The Riverside cddb/jazz Down By The Riverside Lazy River Sunrise, Sunset Who's Sorry Now The Shadow Of Your Smile Whispering I've Found A New Baby Heartaches What's New? Ace In The Hole libxflaim-5.1.969/util/xmlfiles/7006700a.xml0000644000175000017500000000143610511001742021547 0ustar ahodgkinsonahodgkinson 7006700a 1650 Ray Coniff / Always in My Heart cddb/misc Maria Elena Ramona Don't Cry For Me Argentina (From Evita) La Violetera La Vie En Rose Are You Lonesome Tonight Theme From A Summer Place Valencia Blowin' In The Wind Adios Muchachos libxflaim-5.1.969/util/xmlfiles/7006720a.xml0000644000175000017500000000123710511001742021550 0ustar ahodgkinsonahodgkinson 7006720a 1652 Bob Marley / Satisfy My Soul cddb/reggae 400 Years Back Out Satisfy My Soul Small Axe Long Long Winter Love Life Mr. Chatterbox Stop That Train My Cup Soul Captive libxflaim-5.1.969/util/xmlfiles/7006190a.xml0000644000175000017500000000137210511001742021551 0ustar ahodgkinsonahodgkinson 7006190a 1563 Popular / Noche de Paz cddb/misc ID3G: 97 Ya vienen los reyes Campanas de Nochebuena La marimorena San José y el niño Jesús Como un lucero Hacia el portal de Belén Que suenen los panderos Ven conmigo pastorcillo Lo divino La Virgen y San José libxflaim-5.1.969/util/xmlfiles/7006c008.xml0000644000175000017500000000114410511001742021546 0ustar ahodgkinsonahodgkinson 7006c008 1730 For Pete's Sake / For Pete's Sake cddb/misc YEAR: 2000 ID3G: 21 Flatline Emo Boy Like Most Good Things Dave DeVaughn Bend or Break Run Around Comeback Peek-At-You libxflaim-5.1.969/util/xmlfiles/7005780a.xml0000644000175000017500000000213410511001742021552 0ustar ahodgkinsonahodgkinson 7005780a 1402 Various Artists / Platinum: Legendary Hot Rod Hits (Volume 3) cddb/rock Robert Mitchum / The Ballad Of Thunder Road The Beach Boys / Shut Down Robin Ward / In His Car Duane Eddy / 40 Miles Of Bad Road The Matadors / I Gotta Drive Jan & Dean / The Little Old Lady From Pasadena Bruce & Terry / Custom Machine Ronny & The Daytonas / G.T.O. Gary Usher / Mag Wheels Johnny Fortune / Dragster libxflaim-5.1.969/util/xmlfiles/7006590a.xml0000644000175000017500000000134010511001742021550 0ustar ahodgkinsonahodgkinson 7006590a 1627 Floyd Cramer / Floyd Cramer - Super Hits cddb/country Stood Up Last Date Maple Leaf Rag Georgia On My Mind San Antonio Rose Rhythm Of The Rain Today I Started Loving You Again The Gambler Help Me Make It Through The Night Dallas libxflaim-5.1.969/util/xmlfiles/7004c609.xml0000644000175000017500000000133110511001742021551 0ustar ahodgkinsonahodgkinson 7004c609 1224 orange cake mix / warm velvet static cddb/rock like warrm velvet static vocal band through the mirror stardust beach spaced out spaceman land of a million swimming pools theme from faze across the pine lake flying saucer dreams the last rays of a soft october sun libxflaim-5.1.969/util/xmlfiles/70045e09.xml0000644000175000017500000000101510511001742021551 0ustar ahodgkinsonahodgkinson 70045e09 1120 Heimir Björgúlfsson / the opposite cddb/misc 1 2 3 4 5 6 7 8 9 libxflaim-5.1.969/util/xmlfiles/70065c09.xml0000644000175000017500000000122310511001742021552 0ustar ahodgkinsonahodgkinson 70065c09 1630 Steve Calif / Steve Calif cddb/rock YEAR: 1995 ID3G: 14 Oysters The Breakfast Blues Gonna Have Some Fun Hard Times Yo Daddy If You See Kay Spank Me I'm A Hog For You Tip On In libxflaim-5.1.969/util/xmlfiles/70050b0a.xml0000644000175000017500000000130710511001742021616 0ustar ahodgkinsonahodgkinson 70050b0a 1293 Buddy Holly / Buddy Holly Golden Hits cddb/rock It Doesn't Matter Anymore That'll Be The Day Peggy Sue Crying, Waiting, Hoping Oh Boy Maybe Baby Everyday Well All Right It's So Easy Rave One libxflaim-5.1.969/util/xmlfiles/70060b0a.xml0000644000175000017500000000131710511001742021620 0ustar ahodgkinsonahodgkinson 70060b0a 1549 The Hollies / The Best Of cddb/rock Just One Look Look Through Any Window I Can't Let Go Bus Stop Stop, Stop, Stop Running Through The Night It's You On A Carousel Pay You Back With Interest Carrie Ann libxflaim-5.1.969/util/xmlfiles/70052a0a.xml0000644000175000017500000000140010511001742021611 0ustar ahodgkinsonahodgkinson 70052a0a 1324 Gene Autry / Greatest Hits cddb/country (Alla En) El Rancho Grande (My Ranch) In A Little Spanish Town My Adobe Hacienda Under Fiesta Stars Mexicali Rose A Gay Ranchero It Happened In Monterey Rancho Pillow Vaya Con Dios (May God Be With You) South Of The Border libxflaim-5.1.969/util/xmlfiles/7005e709.xml0000644000175000017500000000124310511001742021557 0ustar ahodgkinsonahodgkinson 7005e709 1513 Ron Feingold / One Man Acappella Jam cddb/misc It's All Me 5 Chances And You're Out Boy My PP Hurts Ice Chewin' Fool I'm Not Seinfeld Acappella Blues Live and Let Die 100 Proof Grown Man Cry libxflaim-5.1.969/util/xmlfiles/7005d20a.xml0000644000175000017500000000134010511001742021617 0ustar ahodgkinsonahodgkinson 7005d20a 1492 Wayne Newton / Greatest Hits cddb/misc Danke Schoen Daddy Don't Walk So Fast Red Roses For A Blue Lady I'll Be With You In Apple Blossom Time Summer Wind Remember When Games That Lovers Play Hello Dolly More Shangri-La libxflaim-5.1.969/util/xmlfiles/70052c0b.xml0000644000175000017500000000137210511001742021624 0ustar ahodgkinsonahodgkinson 70052c0b 1326 ãúéä áï-ãåø / îçñï äùèåæéí ùì ãúéä cddb/misc ùéø äúôøéè àðçðå ùèåæåðéí - àøåê îé ùìà îáéè îôñéã ëê ðåìã äöáò ëåìðå ø÷ãðéí ÷éôåã òì äæ÷ï òì äáîä øàô îåøéí îåøéí äîåï àôùøåéåú àðçðå ùèåæåðéí - ÷öø ùéø ÷èï äåà ðñ âãåì libxflaim-5.1.969/util/xmlfiles/70062c0a.xml0000644000175000017500000000133410511001742021622 0ustar ahodgkinsonahodgkinson 70062c0a 1582 Various / The Sixties Generation 1963 cddb/rock Good Golly Miss Molly Little Town Flirt How Do You Do It Greenback Dollar Two Faces Have I I Who Have Nothing 24 Hours From Tulsa Louie Louie Surf City Talk Back Trembling Lips libxflaim-5.1.969/util/xmlfiles/70054b0b.xml0000644000175000017500000000142610511001742021625 0ustar ahodgkinsonahodgkinson 70054b0b 1357 Walls Of Jericho / The Bound Feed The Gagged cddb/rock Playing Soldier Again Home Is Where The Heart Is Changing Times Unwanted Resistance Misanthropy Beneath The Exterior Full Disclosure Family Values Why Father Angel Inevitable Repercussions libxflaim-5.1.969/util/xmlfiles/70069e09.xml0000644000175000017500000000135210511001742021563 0ustar ahodgkinsonahodgkinson 70069e09 1696 Supergrass / In It for the Money (Bonus Disc 2) cddb/rock Limited Edition Bonus CD YEAR: 1997 ID3G: 20 Caught By The Fuzz (Acoustic) Sitting Up Straight Melanie Davis Odd? Wait For The Sun Nothing More's Gonna Get In My Way Sex 20ft Halo Je Suis Votre Papa Sucre libxflaim-5.1.969/util/xmlfiles/70066c0a.xml0000644000175000017500000000151110511001742021623 0ustar ahodgkinsonahodgkinson 70066c0a 1646 Screaming Fat Rat / Idiomatic Breakdown (nine r0x!) cddb/rock See Where We Led the Underground Resistance Round Midnight, Congested Life Evaporated Tense Since I Saw Your Smile Last Lark Can't Fail: Wanna Be Perfect 28 Source I Can't Accept You Roof say "We are Not Free!" The Kids are All Dead I'm Not Down libxflaim-5.1.969/util/xmlfiles/7005b90a.xml0000644000175000017500000000175110511001742021632 0ustar ahodgkinsonahodgkinson 7005b90a 1467 The Ultimate Rock n' Roll Collection / Cruising Classic cddb/rock Save the Last Dance for Me Crimson and Clover Do You Want to Dance Gimme Little Sign Mary Lou Bye Bye Love The Loco-Motion The Shoop Shoop Song (It's in His Kiss) At My Front Door I Fought the Law libxflaim-5.1.969/util/xmlfiles/7006b90a.xml0000644000175000017500000000205410511001742021630 0ustar ahodgkinsonahodgkinson 7006b90a 1723 Various Artists / Billboard Top R&B Hits -- 1967 cddb/rock I Heard It Through The Grapevine Tell It Like It Is Jimmy Mack I Never Loved A Man (The Way I Loved You) (Your Love Keeps Lifting Me) Higher And Higher Get On Up Your Precious Love Love Is Here And Now You're Gone Skinny Legs And All Cold Sweat (Part 1) libxflaim-5.1.969/util/xmlfiles/70053308.xml0000644000175000017500000000114110511001742021465 0ustar ahodgkinsonahodgkinson 70053308 1333 Various Artists / Hits From 1969 cddb/rock Heaven Knows Raindrops Keep Falling On My Head Israelites Jean Dizzy What Kind Of Fool Do You Think I Am Traces I'm Gonna Make You Mine libxflaim-5.1.969/util/xmlfiles/70068d0a.xml0000644000175000017500000000162610511001742021635 0ustar ahodgkinsonahodgkinson 70068d0a 1679 Various / Billboard Top R & B Hits - 1963 cddb/rock Jackie Wilson / Baby Workout Mary Wells / Two Lovers Martha & The Vandellas / Heat Wave Barbara Lewis / Hello Stranger The Impressions / It's All Right Ruby & The Romantics / Our Day Will Come Inez Foxx / Mockingbird Marvin Gaye / Pride And Joy The Miracles / You've Really Got A Hold On Me Garnet Mimms & The Enchanters / Cry Baby libxflaim-5.1.969/util/xmlfiles/70063309.xml0000644000175000017500000000127210511001742021474 0ustar ahodgkinsonahodgkinson 70063309 1589 Coal Train / Run and tell yer ma: The St. Valentine's Day Recordings cddb/jazz Carbolic in my Coffee Fiddlosophy China Doll Ohio Blues De Donde Express B&O Express Boatman's Reel Devil's Train Meatskins, % for $1 libxflaim-5.1.969/util/xmlfiles/7004df09.xml0000644000175000017500000000127610511001742021642 0ustar ahodgkinsonahodgkinson 7004df09 1249 Last of the Juanitas / Time's Up cddb/rock Of Course - Nowadays - They Call It Stalking Make You Cry Look Bolt the Door Here It Comes Time's Up Big Eyed Space Girl There's A U In Binoculars Self Loathing-isms Freedom Now! libxflaim-5.1.969/util/xmlfiles/7005010a.xml0000644000175000017500000000135510511001742021540 0ustar ahodgkinsonahodgkinson 7005010a 1283 Psi Upsilon Fraternity / Songs of Psi Upsilon cddb/misc Welcome, Brothers, Old & Young The College Chorus Psi U Joys After the Battle The Ever-Lovely Maiden Psi Upsilon Smoking Song Psi U Fellowship Psi U Doxology Dear Old Shrine Evensong libxflaim-5.1.969/util/xmlfiles/70069008.xml0000644000175000017500000000110510511001742021471 0ustar ahodgkinsonahodgkinson 70069008 1682 El último ke zierre / Soldadito español cddb/misc Soldadito español Viejo barrio El cazador Tu sexo Olor a muerte Botellín de agua Botas y cadenas Lujuria libxflaim-5.1.969/util/flmunittest.cpp0000644000175000017500000012562010511001742021302 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Unit test driver // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flmunittest.cpp 3138 2006-01-25 12:27:05 -0700 (Wed, 25 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_WIN) #if defined( FLM_64BIT) #define PLATPROC_STR "w64ia64" #else #define PLATPROC_STR "w32x86" #endif #elif defined( FLM_NLM) #define PLATPROC_STR "nwx86" #elif defined( FLM_LINUX) #define PLATPROC_STR "lxx86" #elif defined( FLM_OSX) #define PLATPROC_STR "osx" #elif defined( FLM_SOLARIS) #define PLATPROC_STR "solaris" #endif FLMBOOL gv_bShutdown = FALSE; extern RCODE getTest( IFlmTest ** ppTest); #ifdef FLM_RING_ZERO_NLM #define main nlm_main #endif struct TEST_INFO { bool bLog; char pszLogfile[ 256]; bool bDisplay; bool bVerboseDisplay; char pszEnvironment[ 32]; char pszBuild[ 32]; char pszUser[ 32]; char pszConfig[ 256]; TEST_INFO * pNext; TEST_INFO() { bLog = FALSE; bDisplay = FALSE; bVerboseDisplay = FALSE; pNext = NULL; pszLogfile[0] = '\0'; f_strcpy( pszEnvironment, PLATPROC_STR); f_strcpy( pszBuild, __DATE__); f_strcpy( pszUser, "defaultUser"); pszConfig [0] = 0; } }; /**************************************************************************** Desc: ****************************************************************************/ RCODE ITestReporter::init( const char * configFile, const char * buildNum, const char * environment, const char * userName) { RCODE rc = NE_XFLM_OK; if( (rc = createUnitTest( configFile, buildNum, environment, userName, &(this->m_uTD))) != 0) { goto Exit; } m_bInitialized = true; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ ITestReporter::~ITestReporter() { } /**************************************************************************** Desc: ****************************************************************************/ RCODE ITestReporter::recordUnitTestResults( const char * testName, const char * testDescr, const char * steps, const char * status, const char * resultDetails, const char * elapsedTime) { return ::recordUnitTestResults( &(this->m_uTD), testName, testDescr, steps, status, resultDetails, elapsedTime); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IFlmTestLogger::init( const char * pszFilename) { f_strcpy( m_szFilename, pszFilename); m_bInitialized = TRUE; return (NE_XFLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IFlmTestLogger::appendString( const char * pszString) { RCODE rc = NE_XFLM_OK; char * pszTemp = NULL; if ( RC_BAD( rc = f_alloc( f_strlen( pszString) + 3, &pszTemp))) { goto Exit; } f_sprintf( pszTemp, "%s\n", pszString); if ( RC_BAD( rc = f_filecat( m_szFilename, pszString))) { goto Exit; } Exit: if ( pszTemp) { f_free( &pszTemp); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ IFlmTestDisplayer::IFlmTestDisplayer() { } /**************************************************************************** Desc: ****************************************************************************/ RCODE IFlmTestDisplayer::init( void) { return( NE_XFLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ void IFlmTestDisplayer::appendString( const char * pszString) { f_printf( pszString); } /**************************************************************************** Desc: ****************************************************************************/ void printHelp() { f_printf( "\nCommand-line usage:"); f_printf( "\n\n[-l] [-d]"); f_printf( "[-c] [-b] [-u]"); f_printf( "\n-l - Specifies a log file to print to"); f_printf( "\n-d - Display output"); f_printf( "\n-t - Specifies configuration file for reporting"); f_printf( "\n-b - Specifies the build number"); f_printf( "\n-u - Specifies the user running the unit test"); f_printf( "\n-h - Shows this screen"); } /**************************************************************************** Desc: ****************************************************************************/ extern "C" int main( int argc, char ** argv) { RCODE rc = NE_XFLM_OK; IFlmTest * pTest = NULL; unsigned int i = 1; ArgList args; TEST_INFO testInfo; //parse the command line //format: //[-l] [-d] [-c] [-b] [-u] /* if ( argc < 2) { f_printf("You must specify at least one test to run"); printHelp(); goto Exit; } */ if ( argc > 1) { if ( ( f_strcmp( argv[1], "--help") == 0)|| ( f_strcmp( argv[1], "-h") == 0)) { printHelp(); goto Exit; } } args.expandArgs( argv, argc); while( i < args.getNumEntries()) { if ( args[i][0] != '-') { goto Exit; } if ( ( args[i][1] == 'l') || ( args[i][1] == 'L')) { testInfo.bLog = true; f_strcpy( testInfo.pszLogfile, &args[i][2]); } else if ( ( args[i][1] == 'd') || ( args[i][1] == 'D')) { testInfo.bDisplay = true; } else if ( ( args[i][1] == 'c') || ( args[i][1] == 'C')) { //config file f_strcpy( testInfo.pszConfig, &args[i][2]); } else if ( ( args[i][1] == 'b') || ( args[i][1] == 'B')) { //build f_strcpy( testInfo.pszBuild, &args[i][2]); } else if ( ( args[i][1] == 'u') || ( args[i][1] == 'U')) { //user f_strcpy( testInfo.pszUser, &args[i][2]); } else if ( ( args[i][1] == 'v') || ( args[i][1] == 'V')) { //verbose testInfo.bVerboseDisplay = TRUE; } else { f_printf( "\nInvalid parameter"); printHelp(); goto Exit; } i++; } f_printf("Running %s\n", argv[0]); if ( RC_BAD( rc = getTest( &pTest))) { f_printf( "ERROR: Unable to create test instance\n"); goto Exit; } if ( pTest->init( testInfo.bLog, testInfo.pszLogfile, testInfo.bDisplay, testInfo.bVerboseDisplay, testInfo.pszConfig, testInfo.pszEnvironment, testInfo.pszBuild, testInfo.pszUser) != 0) { f_printf( "\nTest initialization failed"); goto Exit; } if ( RC_BAD( rc = pTest->execute())) { // f_printf("\nTest Failed."); goto Exit; } Exit: if ( pTest) { pTest->Release(); } return( (int)rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE KeyIterator::next() { RCODE rc = NE_XFLM_OK; IF_DataVector * pTemp = NULL; if ( m_bFirstCall) { if ( RC_BAD( rc = m_pDb->keyRetrieve( m_uiIndex, NULL, XFLM_FIRST, m_pFoundKey))) { goto Exit; } m_bFirstCall = FALSE; } else { pTemp = m_pSearchKey; m_pSearchKey = m_pFoundKey; m_pFoundKey = pTemp; if ( RC_BAD( rc = m_pDb->keyRetrieve( m_uiIndex, m_pSearchKey, XFLM_EXCL, m_pFoundKey))) { goto Exit; } } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE KeyIterator::getCurrentKeyVal( FLMUINT uiComponent, FLMBYTE * pszKey, FLMUINT uiBufSize, FLMUINT * puiKeyLen, FLMUINT64 * pui64Id) { RCODE rc = NE_XFLM_OK; if ( RC_BAD( rc = m_pFoundKey->getUTF8( uiComponent, pszKey, &uiBufSize))) { goto Exit; } if ( puiKeyLen) { *puiKeyLen = uiBufSize; } if ( pui64Id) { *pui64Id = m_pFoundKey->getID( uiComponent); } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ KeyIterator::~KeyIterator() { if ( m_pFoundKey) { m_pFoundKey->Release(); } if ( m_pSearchKey) { m_pSearchKey->Release(); } } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FlagSet::removeElem( FLMBYTE * pElem) { FLMBOOL bElemExisted = FALSE; for ( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) { if ( f_strcmp( (char *)pElem, (char *)m_ppucElemArray[ uiLoop]) == 0) { bElemExisted = TRUE; if ( uiLoop < m_uiNumElems - 1) { f_free( &m_ppucElemArray[ uiLoop]); f_memmove( &m_ppucElemArray[ uiLoop], &m_ppucElemArray[ uiLoop + 1], (m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); f_memmove( &m_pbFlagArray[ uiLoop], &m_pbFlagArray[ uiLoop + 1], (m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); } // Otherwise, we're at the end and decrementing to counter will suffice m_uiNumElems--; } } return bElemExisted; } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FlagSet::removeElemContaining( FLMBYTE * pszSubString) { FLMBOOL bElemExisted = FALSE; for ( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; ) { if ( containsSubstring( m_ppucElemArray[ uiLoop], pszSubString)) { bElemExisted = TRUE; if ( uiLoop < m_uiNumElems - 1) { f_memmove( &m_ppucElemArray[ uiLoop], &m_ppucElemArray[ uiLoop + 1], ( m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); f_memmove( &m_pbFlagArray[ uiLoop], &m_pbFlagArray[ uiLoop + 1], ( m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); } // Otherwise, we're at the end and decrementing to counter will suffice m_uiNumElems--; } else { uiLoop++; } } return bElemExisted; } /**************************************************************************** Desc: ****************************************************************************/ FLMBOOL FlagSet::setElemFlag( FLMBYTE * pElem) { FLMBOOL bIsInSet = FALSE; for ( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) { if ( f_strcmp( (char *)pElem, (char *)m_ppucElemArray[ uiLoop]) == 0 && !m_pbFlagArray [uiLoop]) { m_pbFlagArray[ uiLoop] = TRUE; bIsInSet = TRUE; break; } } return bIsInSet; } /**************************************************************************** Desc: ****************************************************************************/ FlagSet FlagSet::crossProduct( FlagSet& fs2) { FlagSet fsCross; FLMUINT uiLoop1; FLMUINT uiCrossProductElems = this->getNumElements() * fs2.getNumElements(); FLMBYTE ** ppszCross; f_alloc( sizeof( FLMBYTE *) * uiCrossProductElems, &ppszCross); for ( uiLoop1 = 0; uiLoop1 < this->getNumElements(); uiLoop1++) { for ( FLMUINT uiLoop2 = 0; uiLoop2 < fs2.getNumElements(); uiLoop2++) { FLMUINT uiIndex = uiLoop1 * fs2.getNumElements() + uiLoop2; f_alloc( f_strlen( (char *)this->m_ppucElemArray[ uiLoop1]) + f_strlen( (char *)fs2.m_ppucElemArray[ uiLoop2]) + 1, &ppszCross[ uiIndex]); f_strcpy( (char *)ppszCross[ uiIndex], (char *)this->m_ppucElemArray[ uiLoop1]); f_strcat( (char *)ppszCross[ uiIndex], (char *)fs2.m_ppucElemArray[ uiLoop2]); } } fsCross.init( ppszCross, uiCrossProductElems); for( uiLoop1 = 0; uiLoop1 < uiCrossProductElems; uiLoop1++) { f_free( &ppszCross[ uiLoop1]); } f_free( &ppszCross); return( fsCross); } /**************************************************************************** Desc: ****************************************************************************/ FlagSet& FlagSet::operator=( const FlagSet& fs) { if ( this != &fs) { if ( m_ppucElemArray || m_pbFlagArray) { this->reset(); } this->init( fs.m_ppucElemArray, fs.m_uiNumElems); } return *this; } /**************************************************************************** Desc: ****************************************************************************/ FlagSet::FlagSet( const FlagSet& fs) { f_alloc( sizeof( FLMBYTE *) * fs.m_uiNumElems, &m_ppucElemArray); f_alloc( sizeof( FLMBOOL) * fs.m_uiNumElems, &m_pbFlagArray); f_memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * fs.m_uiNumElems); for ( FLMUINT uiLoop = 0; uiLoop < fs.m_uiNumElems; uiLoop++) { f_alloc( f_strlen( (char *)fs.m_ppucElemArray[uiLoop]) + 1, &m_ppucElemArray[ uiLoop]); f_strcpy( (char *)m_ppucElemArray[uiLoop], (char *)fs.m_ppucElemArray[uiLoop]); } m_uiNumElems = fs.m_uiNumElems; } /**************************************************************************** Desc: ****************************************************************************/ void FlagSet::reset() { for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) { f_free( &m_ppucElemArray[ uiLoop]); } f_free( &m_ppucElemArray); f_free( &m_pbFlagArray); m_uiNumElems = 0; m_ppucElemArray = NULL; m_pbFlagArray = NULL; } /**************************************************************************** Desc: ****************************************************************************/ void FlagSet::init( FLMBYTE ** ppucElemArray, FLMUINT uiNumElems) { reset(); f_alloc( sizeof( FLMBYTE *) * uiNumElems, &m_ppucElemArray); f_alloc( sizeof( FLMBOOL) * uiNumElems, &m_pbFlagArray); f_memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * uiNumElems); for ( FLMUINT uiLoop = 0; uiLoop < uiNumElems; uiLoop++) { f_alloc( f_strlen( (char *)ppucElemArray[uiLoop]) + 1, &m_ppucElemArray[ uiLoop]); f_strcpy( (char *)m_ppucElemArray[uiLoop], (char *)ppucElemArray[uiLoop]); } m_uiNumElems = uiNumElems; } /**************************************************************************** Desc: ****************************************************************************/ RCODE createUnitTest( const char * configPath, const char * buildNum, const char * environment, const char * user, unitTestData * uTD) { RCODE rc = NE_XFLM_OK; IF_FileSystem * pFileSystem = NULL; IF_FileHdl * pConfigFileHdl = NULL; IF_FileHdl * pCSVFileHdl = NULL; char buffer[ MAX_BUFFER_SIZE] = ""; FLMUINT size = MAX_BUFFER_SIZE; FLMUINT64 ui64Tmp; char * strPos1 = NULL; char * strPos2 = NULL; if( !configPath || !buildNum || !environment || !uTD || !user) { flmAssert(0); } if( f_strlen(user) > MAX_SMALL_BUFFER_SIZE) { rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); goto Exit; } else { f_strcpy(uTD->userName, user); } if( f_strlen(environment) > MAX_SMALL_BUFFER_SIZE) { rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); goto Exit; } else { f_strcpy(uTD->environment, environment); } if( f_strlen(buildNum) > MAX_SMALL_BUFFER_SIZE) { rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); goto Exit; } else { f_strcpy(uTD->buildNumber, buildNum); } if( RC_BAD( rc = FlmGetFileSystem( &pFileSystem))) { goto Exit; } if( configPath [0]) { if( RC_BAD( rc = pFileSystem->openFile( configPath, FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &pConfigFileHdl))) { goto Exit; } if ( RC_BAD( rc = pConfigFileHdl->size( &ui64Tmp))) { goto Exit; } size = (FLMUINT)ui64Tmp; if ( RC_BAD( rc = pConfigFileHdl->read( 0, size, buffer, &size))) { goto Exit; } #ifdef FLM_WIN { char * temp; f_alloc( size, &temp); char * tempbegin = temp; size_t newsize = size; for( unsigned int i = 0; i < size; i++) { if ( ( ( i + 1) < size) && ( buffer[i] == 0x0D && buffer[i + 1] == 0x0A)) { *temp++ = 0x0A; i++; newsize--; } else { *temp++ = buffer[i]; } } f_memcpy( buffer, tempbegin, (FLMSIZET)newsize); size = newsize; } #endif // Get the FOLDER strPos1 = f_strchr(buffer, ':'); strPos2 = f_strchr(strPos1, '\n'); if(!strPos1 || !strPos2) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } for( strPos1++; *strPos1 == ' ' || *strPos1 == '\t'; strPos1++); if(strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) { rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); goto Exit; } f_strncpy(uTD->folder, strPos1, strPos2-strPos1); uTD->folder[strPos2-strPos1] = '\0'; // Get the ATTRIBUTES strPos1 = f_strchr(strPos1, ':'); strPos2 = f_strchr(strPos1, '\n'); if(!strPos1 || !strPos2) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } for(strPos1++;*strPos1 == ' ' || *strPos1 == '\t';strPos1++); if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } f_strncpy(uTD->attrs, strPos1, strPos2-strPos1); uTD->attrs[strPos2-strPos1] = '\0'; // Get the CSVFILE strPos1 = f_strchr(strPos1, ':'); strPos2 = f_strchr(strPos1, '\n'); // Allow for possible \r if( *( --strPos2) != '\r') { strPos2++; } if( !strPos1 || !strPos2) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } for( strPos1++;*strPos1 == ' ' || *strPos1 == '\t';strPos1++); if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } f_strncpy(uTD->csvFilename, strPos1, strPos2-strPos1); uTD->csvFilename[strPos2-strPos1] = '\0'; rc = pFileSystem->openFile( uTD->csvFilename, FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pCSVFileHdl); if( RC_BAD( rc)) { if ( rc == NE_FLM_IO_PATH_NOT_FOUND) { // Create the file and write the header if (RC_BAD( rc = f_filecat( uTD->csvFilename, DATA_ORDER))) { goto Exit; } } } else { goto Exit; } } Exit: if( pConfigFileHdl) { pConfigFileHdl->Release(); } if( pCSVFileHdl) { pCSVFileHdl->Release(); } if( pFileSystem) { pFileSystem->Release(); } return( rc); } /**************************************************************************** Desc: After each unit test call this to record the unit test status to a CSV file. uTD - contains the configuration information testName - a Unique for this module unite test name. testDescr - A description of the unit test. steps - The steps the unit test performs. status - Maybe be PASS or FAIL resultDetails - details that explain the result status. ****************************************************************************/ RCODE recordUnitTestResults( unitTestData * uTD, const char * testName, const char * testDescr, const char * steps, const char * status, const char * resultDetails, const char * elapsedTime) { RCODE rc = NE_XFLM_OK; char buffer[MAX_BUFFER_SIZE]; if( !testName || !testDescr || !steps || !status || !resultDetails || !uTD ) { flmAssert(0); } //VISIT - re-enable the elapsed time reporting when the TCB can support it (void)elapsedTime; f_sprintf( buffer, "%s,%s,%s,%s,%s,%s,%s,%s,"/*%s,*/"%s,%s\n", testName, uTD->userName, testDescr, steps, uTD->buildNumber, status, uTD->environment, resultDetails, /*elapsedTime ? elapsedTime : "",*/ uTD->attrs, uTD->folder); if( RC_BAD( rc = f_filecat( uTD->csvFilename, buffer))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::init( FLMBOOL bLog, const char * pszLogfile, FLMBOOL bDisplay, FLMBOOL bVerboseDisplay, const char * pszConfigFile, const char * pszEnvironment, const char * pszBuild, const char * pszUser) { RCODE rc = NE_XFLM_MEM; if( RC_BAD( rc = FlmAllocDbSystem( &m_pDbSystem))) { goto Exit; } m_bLog = bLog; m_bDisplay = bDisplay; // Set up logger and displayer if true if( m_bLog) { if( ( m_pLogger = f_new IFlmTestLogger) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = m_pLogger->init( pszLogfile))) { goto Exit; } } if( m_bDisplay) { if( (m_pDisplayer = f_new IFlmTestDisplayer) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = m_pDisplayer->init())) { goto Exit; } } if( (m_pReporter = f_new ITestReporter) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = m_pReporter->init( pszConfigFile, pszBuild, pszEnvironment, pszUser))) { goto Exit; } m_bDisplayVerbose = bVerboseDisplay; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::beginTest( const char * pszTestName, const char * pszTestDesc, const char * pszTestSteps, const char * pszDetails) { char szTemp[ 256]; m_pszTestName = pszTestName; m_pszTestDesc = pszTestDesc; m_pszSteps = pszTestSteps; if( m_bDisplayVerbose) { displayLine( "========================================" "======================================="); f_sprintf( szTemp, "Test Name: %s", m_pszTestName); displayLine( szTemp); f_sprintf( szTemp, "Test Description: %s", m_pszTestDesc); displayLine( szTemp); f_sprintf( szTemp, "Steps: %s", m_pszSteps); displayLine( szTemp); } else { f_sprintf( szTemp, "Test Name: %s ... ", m_pszTestName); display( szTemp); } f_strcpy( m_szDetails, pszDetails); m_ui64StartMs = FLM_TIMER_UNITS_TO_SECS( FLM_GET_TIMER()); } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::endTest( const char * pszTestResult) { m_ui64EndMs = FLM_TIMER_UNITS_TO_SECS( FLM_GET_TIMER()); outputAll( pszTestResult, m_ui64EndMs - m_ui64StartMs); } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::log( const char * pszString) { if( m_bLog) { m_pLogger->appendString( pszString); } } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::display( const char * pszString) { if( m_bDisplay) { m_pDisplayer->appendString( pszString); } } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::displayLine( const char * pszString) { if( m_bDisplay) { m_pDisplayer->appendString( pszString); m_pDisplayer->appendString( "\n"); } } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::displayTime( FLMUINT64 ui64Milli, const char * pszIntro) { const char * pszDefault = "Elapsed Time: "; char szTimeBuf[ 64]; char * pszTempBuf = NULL; if( pszIntro) { f_alloc( f_strlen( pszIntro) + sizeof( szTimeBuf), &pszTempBuf); f_strcpy( pszTempBuf, pszIntro); } else { f_alloc( f_strlen( pszDefault) + sizeof( szTimeBuf), &pszTempBuf); f_strcpy( pszTempBuf, pszDefault); } normalizeTime( ui64Milli, szTimeBuf); f_strcat( pszTempBuf, szTimeBuf); displayLine( pszTempBuf); } /**************************************************************************** Desc: ****************************************************************************/ void TestBase::normalizeTime( FLMUINT64 ui64Milli, char * pszString) { FLMUINT64 ui64Ms; FLMUINT64 ui64S; FLMUINT64 ui64M; FLMUINT64 ui64H; ui64Ms = ui64Milli % 1000; ui64S = ui64Milli / 1000; ui64M = ui64S / 60; ui64S %= 60; ui64H = ui64M / 60; ui64M %= 60; f_sprintf( pszString, "%02I64u:%02I64u:%02I64u.%03I64u", ui64H, ui64M, ui64S, ui64Ms); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::initCleanTestState( const char * pszDibName) { RCODE rc = NE_XFLM_OK; XFLM_CREATE_OPTS createOpts; // Create the database f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); if ( RC_BAD( rc = m_pDbSystem->dbCreate( pszDibName, NULL, NULL, NULL, NULL, &createOpts, &m_pDb))) { if( rc == NE_XFLM_FILE_EXISTS) { if( RC_BAD( rc = m_pDbSystem->dbRemove( pszDibName, NULL, NULL, TRUE))) { goto Exit; } } if( RC_BAD( rc = m_pDbSystem->dbCreate( pszDibName, NULL, NULL, NULL, NULL, &createOpts, &m_pDb))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::openTestState( const char * pszDibName) { XFLM_CREATE_OPTS createOpts; IF_FileSystem * pFileSys = NULL; RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = FlmGetFileSystem( &pFileSys))) { goto Exit; } if ( RC_BAD( rc = pFileSys->doesFileExist( pszDibName))) { // Create the database f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); if ( RC_BAD( rc = m_pDbSystem->dbCreate( pszDibName, NULL, NULL, NULL, NULL, &createOpts, &m_pDb))) { goto Exit; } } else { // Open the existing database if( RC_BAD( rc = m_pDbSystem->dbOpen( pszDibName, NULL, NULL, NULL, FALSE, &m_pDb))) { goto Exit; } } Exit: if( pFileSys) { pFileSys->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::shutdownTestState( const char * pszDibName, FLMBOOL bRemoveDib) { RCODE rc = NE_XFLM_OK; FLMUINT uiRefCount; if( bRemoveDib) { if( m_pDb) { uiRefCount = m_pDb->Release(); flmAssert( uiRefCount == 0); m_pDb = NULL; } if( RC_BAD( rc = m_pDbSystem->dbRemove( pszDibName, NULL, NULL, TRUE))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::checkQueryResults( const char ** ppszResults, FLMUINT uiNumResultsExpected, IF_Query * pQuery, char * pszDetails) { RCODE rc = NE_XFLM_OK; FlagSet flagSet; IF_DOMNode * pReturn = NULL; FLMUINT uiLoop; char szBuffer[ 500]; flagSet.init( (FLMBYTE **)ppszResults, uiNumResultsExpected); for( uiLoop = 0; ; uiLoop++) { if( !uiLoop) { if( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", pszDetails, rc); goto Exit; } } else { if( RC_BAD( rc = pQuery->getNext( m_pDb, &pReturn))) { if( uiLoop < uiNumResultsExpected - 1) { MAKE_FLM_ERROR_STRING( "getNext failed.", pszDetails, rc); goto Exit; } else { rc = NE_XFLM_OK; break; } } } if( RC_BAD( rc = pReturn->getUTF8( m_pDb, (FLMBYTE *)szBuffer, sizeof(szBuffer), 0, sizeof( szBuffer) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", pszDetails, rc); goto Exit; } if ( !flagSet.setElemFlag( (FLMBYTE *)szBuffer)) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected result received.", pszDetails, rc); goto Exit; } } if( !flagSet.allElemFlagsSet()) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Expected results not received.", pszDetails, rc); goto Exit; } Exit: if( pReturn) { pReturn->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::doQueryTest( const char * pszQueryString, const char ** ppszExpectedResults, FLMUINT uiNumResultsExpected, IF_Query * pQuery, char * pszDetails, FLMUINT uiRequestedIndex, FLMUINT * puiIndexUsed) { RCODE rc = NE_XFLM_OK; FLMBOOL bHaveMult; if( pszQueryString) { if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, pszQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", pszDetails, rc); goto Exit; } } if( uiRequestedIndex) { if( RC_BAD( rc = pQuery->setIndex( uiRequestedIndex))) { MAKE_FLM_ERROR_STRING( "setIndex failed.", pszDetails, rc); goto Exit; } } if( RC_BAD( rc = checkQueryResults( ppszExpectedResults, uiNumResultsExpected, pQuery, pszDetails))) { goto Exit; } if( puiIndexUsed) { if( RC_BAD( rc = pQuery->getIndex( m_pDb, puiIndexUsed, &bHaveMult))) { goto Exit; } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::logTestResults( const char * pszTestResult, FLMUINT64 ui64ElapsedTime) { RCODE rc = NE_XFLM_OK; char * pszTemp = NULL; char szTime[ 64]; if( RC_BAD( rc = f_alloc( DETAILS_BUF_SIZ + 64, &pszTemp))) { goto Exit; } if( ui64ElapsedTime != ~((FLMUINT64)0)) { normalizeTime( ui64ElapsedTime, szTime); } else { f_strcpy( szTime, "Not Recorded"); } log( "========================================" "======================================="); f_sprintf( pszTemp, "Test Name: %s", m_pszTestName); log( pszTemp); f_sprintf( pszTemp, "Test Description: %s", m_pszTestDesc); log( pszTemp); f_sprintf( pszTemp, "Steps: %s", m_pszSteps); log( pszTemp); f_sprintf( pszTemp, "Test Result: %s", pszTestResult); log( pszTemp); f_sprintf( pszTemp, "Details: %s", m_szDetails); log( pszTemp); f_sprintf( pszTemp, "Elapsed Time: %s", szTime); log( pszTemp); log( "========================================" "======================================="); Exit: if( pszTemp) { f_free( &pszTemp); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::displayTestResults( const char * pszTestResult, FLMUINT64 ui64ElapsedTime) { RCODE rc = NE_XFLM_OK; char * pszTemp = NULL; char szTime[ 64]; if( RC_BAD( rc = f_alloc( DETAILS_BUF_SIZ + 64, &pszTemp))) { goto Exit; } if( ui64ElapsedTime != ~((FLMUINT64)0)) { normalizeTime( ui64ElapsedTime, szTime); } else { f_strcpy( szTime, "Not Recorded"); } if( m_bDisplayVerbose) { f_sprintf( pszTemp, "Test Result: %s", pszTestResult); displayLine( pszTemp); f_sprintf( pszTemp, "Details: %s", m_szDetails); displayLine( pszTemp); f_sprintf( pszTemp, "Elapsed Time: %s", szTime); displayLine( pszTemp); displayLine( "========================================" "======================================="); } else { f_sprintf( pszTemp, "Result: %s", pszTestResult); displayLine( pszTestResult); } Exit: if( pszTemp) { f_free( &pszTemp); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::outputAll( const char * pszTestResult, FLMUINT64 ui64ElapsedTime) { RCODE rc = NE_XFLM_OK; char * pszTime = NULL; if( ui64ElapsedTime != ~((FLMUINT64)0)) { if( RC_BAD( rc = f_alloc( 64, &pszTime))) { goto Exit; } normalizeTime( ui64ElapsedTime, pszTime); } if( RC_BAD( rc = displayTestResults( pszTestResult, ui64ElapsedTime))) { goto Exit; } if( RC_BAD( rc = logTestResults( pszTestResult, ui64ElapsedTime))) { goto Exit; } if( RC_BAD( rc = m_pReporter->recordUnitTestResults( m_pszTestName, m_pszTestDesc, m_pszSteps, pszTestResult, m_szDetails, pszTime))) { goto Exit; } Exit: if( pszTime) { f_free( &pszTime); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::importBuffer( const char * pszBuffer, FLMUINT uiCollection) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = m_pDbSystem->openBufferIStream( pszBuffer, f_strlen( pszBuffer), &m_pInputStream))) { MAKE_FLM_ERROR_STRING( "openBufferIStream failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->import( m_pInputStream, uiCollection))) { goto Exit; } Exit: if( m_pInputStream) { m_pInputStream->Release(); m_pInputStream = NULL; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::importDocument( const char * pszBuffer, FLMUINT uiCollection) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = m_pDbSystem->openBufferIStream( pszBuffer, f_strlen( pszBuffer), &m_pInputStream))) { MAKE_FLM_ERROR_STRING( "openBufferIStream failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->importDocument( m_pInputStream, uiCollection))) { goto Exit; } Exit: if( m_pInputStream) { m_pInputStream->Release(); m_pInputStream = NULL; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::importFile( const char * pszFilename, FLMUINT uiCollection) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = m_pDbSystem->openFileIStream( pszFilename, &m_pInputStream))) { MAKE_FLM_ERROR_STRING( "openFileIStream failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->import( m_pInputStream, uiCollection))) { MAKE_FLM_ERROR_STRING( "file import failed.", m_szDetails, rc); goto Exit; } Exit: if( m_pInputStream) { m_pInputStream->Release(); m_pInputStream = NULL; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ TestBase::~TestBase() { if( m_pLogger) { m_pLogger->Release(); } if( m_pDisplayer) { m_pDisplayer->Release(); } if( m_pReporter) { m_pReporter->Release(); } if( m_pInputStream) { m_pInputStream->Release(); } if( m_pDb) { m_pDb->Release(); } if( m_pDbSystem) { m_pDbSystem->Release(); } } /**************************************************************************** Desc: ****************************************************************************/ FLMINT TestBase::unicmp( FLMUNICODE * puzStr1, FLMUNICODE * puzStr2) { while( *puzStr1 == *puzStr2 && *puzStr1) { puzStr1++; puzStr2++; } return( (FLMINT)*puzStr1 - (FLMINT)*puzStr2); } /**************************************************************************** Desc: ****************************************************************************/ RCODE TestBase::createCompoundDoc( ELEMENT_NODE_INFO * pElementNodes, FLMUINT uiNumElementNodes, FLMUINT64 * pui64DocId) { IF_DOMNode * pDocRoot = NULL; IF_DOMNode * pNode = NULL; FLMUINT uiLoop; RCODE rc = NE_XFLM_OK; if( pui64DocId) { *pui64DocId = 0; } if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, ELM_ANY_TAG, &pDocRoot))) { MAKE_FLM_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } for( uiLoop = 0; uiLoop < uiNumElementNodes; uiLoop++) { if( isPlaceHolder(pElementNodes[uiLoop])) { continue; } if( RC_BAD( rc = pDocRoot->createNode( m_pDb, ELEMENT_NODE, pElementNodes[uiLoop].uiDictNum, XFLM_LAST_CHILD, &pNode))) { MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } switch( pElementNodes[uiLoop].uiDataType) { case XFLM_TEXT_TYPE: { if( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)pElementNodes[uiLoop].pvData))) { goto Exit; } break; } case XFLM_NUMBER_TYPE: { if ( RC_BAD( rc = pNode->setUINT( m_pDb, (FLMUINT)pElementNodes[uiLoop].pvData))) { goto Exit; } break; } case XFLM_BINARY_TYPE: { if ( RC_BAD( rc = pNode->setBinary( m_pDb, (FLMBYTE*)pElementNodes[uiLoop].pvData, pElementNodes[uiLoop].uiDataSize))) { goto Exit; } break; } default: { flmAssert( 0); } } } if( RC_BAD ( rc = m_pDb->documentDone( pDocRoot))) { MAKE_FLM_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if( pui64DocId) { if( RC_BAD( rc = pDocRoot->getNodeId( m_pDb, pui64DocId))) { goto Exit; } } Exit: if( pNode) { pNode->Release(); } if( pDocRoot) { pDocRoot->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ ArgList::ArgList() { m_uiCapacity = INIT_SIZE; m_uiNumEntries = 0; f_alloc( m_uiCapacity * sizeof( char *), &m_ppszArgs); } /**************************************************************************** Desc: ****************************************************************************/ ArgList::~ArgList() { FLMUINT uiLoop; if( m_ppszArgs) { for( uiLoop = 0; uiLoop < m_uiNumEntries; uiLoop++) { f_free( &( m_ppszArgs[uiLoop])); } f_free( &m_ppszArgs); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE ArgList::resize( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; char ** ppszTemp = NULL; if( RC_BAD( rc = f_alloc( m_uiCapacity * GROW_FACTOR * sizeof(char*), &ppszTemp))) { goto Exit; } m_uiCapacity *= GROW_FACTOR; for( uiLoop = 0; uiLoop < m_uiNumEntries; uiLoop++) { if( RC_BAD( rc = f_alloc( f_strlen( m_ppszArgs[uiLoop]) + 1, &ppszTemp[uiLoop]))) { f_free( &ppszTemp); goto Exit; } f_strcpy( ppszTemp[uiLoop], m_ppszArgs[uiLoop]); } f_free( &m_ppszArgs); m_ppszArgs = ppszTemp; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ FLMUINT ArgList::getNumEntries( void) { return( m_uiNumEntries); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ArgList::addArg( const char * pszArg) { RCODE rc = NE_XFLM_OK; if( m_uiNumEntries >= m_uiCapacity) { if( RC_BAD( rc = resize())) { goto Exit; } } if( RC_BAD( rc = f_alloc( f_strlen( pszArg) + 1, &m_ppszArgs[m_uiNumEntries]))) { goto Exit; } f_strcpy( m_ppszArgs[ m_uiNumEntries++], pszArg); Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * ArgList::getArg( FLMUINT uiIndex) { return( m_ppszArgs[ uiIndex]); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ArgList::expandFileArgs( const char * pszFilename) { RCODE rc = NE_XFLM_OK; char token[64]; IF_FileSystem * pFileSystem = NULL; IF_FileHdl * pFileHdl = NULL; if( RC_BAD( rc = FlmGetFileSystem( &pFileSystem))) { goto Exit; } if( RC_BAD( rc = pFileSystem->openFile( pszFilename, FLM_IO_RDWR, &pFileHdl))) { goto Exit; } while( RC_OK( rc = getTokenFromFile(token, pFileHdl))) { if( token[0] == '@') { if( RC_BAD( rc = expandFileArgs( &token[1]))) { goto Exit; } } else { flmAssert(*token); if( RC_BAD( rc = addArg( token))) { goto Exit; } } } if( rc == NE_FLM_IO_END_OF_FILE) { rc = NE_XFLM_OK; } Exit: if( pFileHdl) { pFileHdl->Release(); } if( pFileSystem) { pFileSystem->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ArgList::expandArgs( char ** ppszArgs, FLMUINT uiNumArgs) { RCODE rc = NE_XFLM_OK; FLMUINT uiLoop; for( uiLoop = 0; uiLoop < uiNumArgs; uiLoop++) { if( ppszArgs[uiLoop][0] == '@') { if( RC_BAD( rc = expandFileArgs( &ppszArgs[uiLoop][ 1]))) { goto Exit; } } else { if( RC_BAD( rc = addArg( ppszArgs[ uiLoop]))) { goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * ArgList::operator []( FLMUINT uiIndex) { return( m_ppszArgs[ uiIndex]); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ArgList::getTokenFromFile( char * pszToken, IF_FileHdl * pFileHdl) { RCODE rc = NE_XFLM_OK; FLMUINT uiSize = 1; FLMUINT64 ui64Offset = 0; FLMUINT64 ui64TrueOffset = 0; char c; for(;;) { Skip_WS_And_Comments: if ( RC_BAD( rc = pFileHdl->read( 0, 1, &c, &uiSize))) { goto Exit; } // Skip whitespace while( isWhitespace(c)) { if( RC_BAD( rc = pFileHdl->read( 0, 1, &c, &uiSize))) { goto Exit; } } if( c == '#') { // Skip comment for (;;) { if( RC_BAD( rc = pFileHdl->read( 0, 1, &c, &uiSize))) { goto Exit; } #ifdef FLM_UNIX // On unix platforms, an EOL is indicated by an LF if( c == 0x0A) { break; } #else // On Windows and NetWare we need to look for CR/LF if( c == 0x0D) { if( RC_BAD( rc = pFileHdl->read( 0, 1, &c, &uiSize))) { goto Exit; } // Newline found if( c == 0x0A) { break; } else { // Rewind ui64Offset = 0; ui64TrueOffset = 0; if( RC_BAD( rc = pFileHdl->tell( &ui64Offset))) { goto Exit; } ui64Offset--; if( RC_BAD( rc = pFileHdl->seek( ui64Offset, FLM_IO_SEEK_SET, &ui64TrueOffset))) { goto Exit; } } } #endif } goto Skip_WS_And_Comments; } while( !isWhitespace( c)) { if( c == '#') { break; } *pszToken++ = c; if( RC_BAD( rc = pFileHdl->read( 0, 1, &c, &uiSize))) { goto Exit; } } // Put the char back if( RC_BAD( rc = pFileHdl->tell( &ui64Offset))) { goto Exit; } ui64Offset--; if( RC_BAD( rc = pFileHdl->seek( ui64Offset, FLM_IO_SEEK_SET, &ui64TrueOffset))) { goto Exit; } break; } Exit: *pszToken = '\0'; return( rc); } libxflaim-5.1.969/util/xshell.cpp0000644000175000017500000000466610511001742020231 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Interactive database shell // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: xshell.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "fshell.h" static FlmSharedContext * gv_pSharedContext = NULL; FLMBOOL gv_bShutdown = FALSE; FLMBOOL gv_bRunning = TRUE; #ifdef FLM_RING_ZERO_NLM #define main nlm_main #endif /*************************************************************************** Desc: Program entry point (main) ****************************************************************************/ extern "C" int main( int, // iArgC, char **) // ppucArgV { RCODE rc = NE_XFLM_OK; FlmShell * pShell = NULL; IF_DbSystem * pDbSystem = NULL; if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem))) { goto Exit; } if( RC_BAD( rc = FTXInit( "X-FLAIM Shell", (FLMBYTE)80, (FLMBYTE)50, FLM_BLUE, FLM_WHITE, NULL, NULL))) { goto Exit; } FTXSetShutdownFlag( &gv_bShutdown); if( (gv_pSharedContext = f_new FlmSharedContext) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = gv_pSharedContext->init( NULL))) { goto Exit; } gv_pSharedContext->setShutdownFlag( &gv_bShutdown); if( (pShell = f_new FlmShell) != NULL) { if( RC_OK( pShell->setup( gv_pSharedContext))) { gv_pSharedContext->spawn( pShell); } } gv_pSharedContext->wait(); Exit: gv_bShutdown = TRUE; if( gv_pSharedContext) { gv_pSharedContext->Release(); } FTXExit(); if( pDbSystem) { pDbSystem->Release(); } gv_bRunning = FALSE; return( 0); } libxflaim-5.1.969/util/sharutil.h0000644000175000017500000002407610511001742020227 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Shared utility routines // // Tabs: 3 // // Copyright (c) 1997, 1999-2001, 2003-2006 Novell, Inc. // All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: sharutil.h 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef SHARUTIL_H #define SHARUTIL_H /* Current utility version */ #define UTIL_VER ((FLMUINT)300) /* Current FLAIM code version */ #define SRC_VER_STR "Ver32 Alpha" /* Prototypes */ void flmUtilParseParams( char * pszCommandBuffer, FLMINT iMaxArgs, FLMINT * piArgc, char ** ppszArgv); RCODE flmUtilStatusHook( // source: sharutl2.cpp FLMUINT uiStatusType, void * Parm1, void * Parm2, void * UserData); #ifdef FLM_NLM #define flmUtilGiveUpCPU() f_yieldCPU() #else #define flmUtilGiveUpCPU() f_sleep( 0) #endif //convenience macros #define STREQ(s1,s2) ((f_strcmp( (s1), (s2))) == 0) #define STREQI(s1,s2) ((f_stricmp( (s1), (s2))) == 0) #define TEST_RC(rc) \ if (RC_BAD( (rc))) \ { \ goto Exit; \ } #define TEST_RC_LOCAL(rc) \ if (RC_BAD( (rc))) \ { \ goto Exit_local; \ } #define MAKE_BAD_RC_JUMP() \ { \ rc = RC_SET( NE_XFLM_FAILURE); \ goto Exit; \ } #ifndef ELEMCOUNT #define ELEMCOUNT( elem) sizeof(elem) / sizeof(elem[0]) #endif /**************************************************************************** Name: FlmVector Desc: treat this vector class like an array, except that you will never write to an item out-of-bounds. This is because the vector dynamically allocates enough space to cover at least up through the index you are setting. If you try to read out-of-bounds you will hit an assert rather than an access violation. You will need to keep track of your own length, as there is no concept of "length" internal to this class. You can exploit the fact that if you leave holes in the elements, the intermediate elements will be filled with 0's. ****************************************************************************/ class FlmVector : public F_Object { public: FlmVector() { m_pElementArray = NULL; m_uiArraySize = 0; } ~FlmVector() { if ( m_pElementArray) { f_free( &m_pElementArray); } } RCODE setElementAt( void * pData, FLMUINT uiIndex); void * getElementAt( FLMUINT uiIndex); private: void ** m_pElementArray; FLMUINT m_uiArraySize; }; /**************************************************************************** Name: FlmStringAcc Desc: a class to safely build up a string accumulation, without worrying about buffer overflows. ****************************************************************************/ #define FSA_QUICKBUF_BUFFER_SIZE 128 class FlmStringAcc : public F_Object { public: FlmStringAcc() { commonInit(); } FlmStringAcc( char * pszStr) { commonInit(); this->appendTEXT( pszStr); } FlmStringAcc( FLMBYTE * pszStr) { commonInit(); this->appendTEXT( pszStr); } ~FlmStringAcc() { if ( m_pszVal) { f_free( &m_pszVal); } } void clear() { if ( m_pszVal) { m_pszVal[ 0] = 0; } m_szQuickBuf[ 0] = 0; m_uiValStrLen = 0; } FLMUINT getLength() { return m_uiValStrLen; } RCODE printf( const char * pszFormatString, ...); RCODE appendCHAR( char ucChar, FLMUINT uiHowMany = 1); RCODE appendTEXT( const FLMBYTE * pszVal); RCODE appendTEXT( const char * pszVal) { return appendTEXT( (FLMBYTE*)pszVal); } RCODE appendf( const char * pszFormatString, ...); const char * getTEXT() { //use quick buffer if applicable if ( m_bQuickBufActive) { return m_szQuickBuf; } else if ( m_pszVal) { return m_pszVal; } else { return( ""); } } private: void commonInit() //called by all constructors { m_pszVal = NULL; m_uiValStrLen = 0; m_szQuickBuf[ 0] = 0; m_bQuickBufActive = FALSE; } RCODE formatNumber( FLMUINT uiNum, FLMUINT uiBase); //use a small buffer for small strings to avoid heap allocations char m_szQuickBuf[ FSA_QUICKBUF_BUFFER_SIZE]; FLMBOOL m_bQuickBufActive; char * m_pszVal; FLMUINT m_uiBytesAllocatedForPszVal; FLMUINT m_uiValStrLen; //save the strlen stored to avoid recomputing it }; /*=========================================================================== Class: FlmContext Desc: This class manages a context or environment of variables. ===========================================================================*/ class FlmContext : public F_Object { public: FlmContext( void); ~FlmContext( void); RCODE setup( FLMBOOL bShared); RCODE setCurrDir( FLMBYTE * pszCurrDir); RCODE getCurrDir( FLMBYTE * pszCurrDir); void lock( void); void unlock( void); private: // Data FLMBYTE m_szCurrDir[ F_PATH_MAX_SIZE]; FLMBOOL m_bIsSetup; F_MUTEX m_hMutex; // Semaphore for controlling multi-thread // access. }; class FlmSharedContext; class FlmThreadContext; typedef FLMUINT (* THREAD_FUNC_p)( FlmThreadContext * pThread, void * pvAppData); /*=========================================================================== Class: FlmThreadContext Desc: This class manages a thread. ===========================================================================*/ class FlmThreadContext : public F_Object { public: FlmThreadContext( void); virtual ~FlmThreadContext( void); RCODE setup( FlmSharedContext * pSharedContext, const char * pszThreadName, THREAD_FUNC_p pFunc, void * pvAppData); virtual RCODE execute( void); void shutdown(); // Needs to be thread-safe. FINLINE FlmContext * getLocalContext( void){ return m_pLocalContext;} FINLINE FlmSharedContext * getSharedContext( void){ return m_pSharedContext;} FINLINE FLMBOOL * getShutdownFlagAddr( void) { return( &m_bShutdown); } FINLINE void setShutdownFlag( void) { m_bShutdown = TRUE; } FINLINE FLMBOOL getShutdownFlag( void) { if( m_pThread && m_pThread->getShutdownFlag()) { m_bShutdown = TRUE; } return( m_bShutdown); } FINLINE void setNext( FlmThreadContext * pNext) { m_pNext = pNext; } FINLINE void setPrev( FlmThreadContext * pPrev) { m_pPrev = pPrev; } FINLINE FlmThreadContext * getNext( void) { return( m_pNext); } FINLINE FlmThreadContext * getPrev( void) { return( m_pPrev); } FINLINE void setID( FLMUINT uiID) { m_uiID = uiID; } FINLINE FLMUINT getID( void) { return( m_uiID); } FINLINE void setScreen( FTX_SCREEN * pScreen) { m_pScreen = pScreen; } FINLINE FTX_SCREEN * getScreen( void) { return( m_pScreen); } FINLINE void setWindow( FTX_WINDOW * pWindow) { m_pWindow = pWindow; } FINLINE FTX_WINDOW * getWindow( void) { return( m_pWindow); } FINLINE void setFlmThread( IF_Thread * pThread) { m_pThread = pThread; } FINLINE IF_Thread * getFlmThread( void) { return( m_pThread); } void getName( char * pszName, FLMBOOL bLocked = FALSE); RCODE exec( void); void lock( void); void unlock( void); FLMBOOL funcExited() { return m_bFuncExited; } FINLINE void setFuncExited() { m_bFuncExited = TRUE; } RCODE getFuncErrorCode() { flmAssert( this->funcExited()); return m_FuncRC; } protected: FTX_SCREEN * m_pScreen; FTX_WINDOW * m_pWindow; private: FLMBOOL m_bShutdown; FLMUINT m_uiID; FlmContext * m_pLocalContext; FlmSharedContext * m_pSharedContext; FlmThreadContext * m_pNext; FlmThreadContext * m_pPrev; F_MUTEX m_hMutex; IF_Thread * m_pThread; THREAD_FUNC_p m_pThrdFunc; void * m_pvAppData; #define MAX_THREAD_NAME_LEN 64 char m_szName[ MAX_THREAD_NAME_LEN + 1]; FLMBOOL m_bFuncExited; RCODE m_FuncRC; }; /*=========================================================================== Class: FlmSharedContext Desc: This class manages the shared context for a group of threads. ===========================================================================*/ class FlmSharedContext : public FlmContext { public: FlmSharedContext( void); ~FlmSharedContext( void); RCODE init( // Initialized the share object. FlmSharedContext * pSharedContext); FINLINE void setShutdownFlag( FLMBOOL * pbShutdownFlag) { m_pbShutdownFlag = pbShutdownFlag; } // Threads RCODE spawn( FlmThreadContext * pThread, FLMUINT * puiThreadID = NULL); // ID of spawned thread RCODE spawn( char * pszThreadName, THREAD_FUNC_p pFunc, void * pvUserData, FLMUINT * puiThreadID = NULL); // ID of spawned thread void wait( void); void shutdown(); // Shutdown all threads in this shared context. RCODE killThread( FLMUINT uiThreadID, FLMUINT uiMaxWait = 0); RCODE setFocus( FLMUINT uiThreadID); FLMBOOL isThreadTerminating( FLMUINT uiThreadID); RCODE getThread( FLMUINT uiThreadID, FlmThreadContext ** ppThread); RCODE registerThread( FlmThreadContext * pThread); RCODE deregisterThread( FlmThreadContext * pThread); private: FlmSharedContext * m_pParentContext; FLMBOOL m_bPrivateShare; F_MUTEX m_hMutex; F_SEM m_hSem; FlmThreadContext * m_pThreadList; FLMBOOL m_bLocalShutdownFlag; FLMBOOL * m_pbShutdownFlag; FLMUINT m_uiNextProcID; }; void utilOutputLine( char * pszData, void * pvUserData); void utilPressAnyKey( char * pszPressAnyKeyMessage, void * pvUserData); RCODE utilInitWindow( char * pszTitle, FLMUINT * puiScreenRows, FTX_WINDOW ** ppMainWindow, FLMBOOL * pbShutdown); void utilShutdownWindow(); #endif // SHARUTIL_H libxflaim-5.1.969/util/flm_dlst.cpp0000644000175000017500000003350710511001742020532 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Dynamic, interactive list manager // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flm_dlst.cpp 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "flm_dlst.h" #include "sharutil.h" /**************************************************************************** Name: N/A Desc: Default constructor *****************************************************************************/ F_DynamicList::F_DynamicList( void) { m_pFirst = NULL; m_pLast = NULL; m_pCur = NULL; m_pListWin = NULL; m_uiRow = 0; m_uiListRows = 0; m_uiListCols = 0; m_bChanged = TRUE; m_bShowHorizontalSelector = TRUE; } /**************************************************************************** Name: N/A Desc: Default destructor *****************************************************************************/ F_DynamicList::~F_DynamicList( void) { DLIST_NODE * pTmp = m_pFirst; DLIST_NODE * pTmp2; while( pTmp) { pTmp2 = pTmp; pTmp = pTmp->pNext; freeNode( pTmp2); } if( m_pListWin) { FTXWinFree( &m_pListWin); } } /**************************************************************************** Name: setup Desc: Allocates the list window and prepares the object for use *****************************************************************************/ RCODE F_DynamicList::setup( FTX_WINDOW * pInitializedWindow) { RCODE rc = NE_FLM_OK; flmAssert( pInitializedWindow != NULL); m_pListWin = pInitializedWindow; /* Create the list window */ FTXWinClear( m_pListWin); FTXWinSetCursorType( m_pListWin, FLM_CURSOR_INVISIBLE); FTXWinSetScroll( m_pListWin, FALSE); FTXWinSetLineWrap( m_pListWin, FALSE); FTXWinGetCanvasSize( m_pListWin, &m_uiListCols, &m_uiListRows); return( rc); } /**************************************************************************** Name: insert Desc: *****************************************************************************/ RCODE F_DynamicList::insert( FLMUINT uiKey, F_DLIST_DISP_HOOK pDisplayHook, void * pvData, FLMUINT uiDataLen) { RCODE rc = NE_FLM_OK; DLIST_NODE * pTmp; DLIST_NODE * pNew = NULL; if( getNode( uiKey) != NULL) { rc = RC_SET( NE_FLM_EXISTS); goto Exit; } // Allocate the new node if( RC_BAD( rc = f_alloc( sizeof( DLIST_NODE), &pNew))) { goto Exit; } f_memset( pNew, 0, sizeof( DLIST_NODE)); // Set the members of the new node pNew->uiKey = uiKey; if( pDisplayHook) { pNew->pDispHook = pDisplayHook; } else { pNew->pDispHook = dlistDefaultDisplayHook; } if( uiDataLen) { if( RC_BAD( rc = f_alloc( uiDataLen, &pNew->pvData))) { goto Exit; } f_memcpy( pNew->pvData, pvData, uiDataLen); pNew->uiDataLen = uiDataLen; } // Find the insertion point if( !m_pFirst) { m_pFirst = m_pLast = m_pCur = pNew; } else { pTmp = m_pFirst; while( pTmp && pTmp->uiKey < uiKey) { pTmp = pTmp->pNext; } if( pTmp) { if( pTmp == m_pFirst) { pNew->pNext = m_pFirst; m_pFirst->pPrev = pNew; m_pFirst = pNew; } else { pNew->pNext = pTmp; pNew->pPrev = pTmp->pPrev; if( pTmp->pPrev) { pTmp->pPrev->pNext = pNew; } pTmp->pPrev = pNew; } } else { // Insert at end m_pLast->pNext = pNew; pNew->pPrev = m_pLast; m_pLast = pNew; } } m_bChanged = TRUE; Exit: if( RC_BAD( rc)) { if( pNew) { freeNode( pNew); } } return( rc); } /**************************************************************************** Name: update Desc: *****************************************************************************/ RCODE F_DynamicList::update( FLMUINT uiKey, F_DLIST_DISP_HOOK pDisplayHook, void * pvData, FLMUINT uiDataLen) { DLIST_NODE * pTmp; RCODE rc = NE_FLM_OK; if( (pTmp = getNode( uiKey)) == NULL) { rc = insert( uiKey, pDisplayHook, pvData, uiDataLen); goto Exit; } if( !pTmp->pvData || pTmp->uiDataLen != uiDataLen) { if( pTmp->pvData) { f_free( &pTmp->pvData); pTmp->uiDataLen = 0; } if( uiDataLen) { if( RC_BAD( rc = f_alloc( uiDataLen, &pTmp->pvData))) { goto Exit; } } } if( uiDataLen) { f_memcpy( pTmp->pvData, pvData, uiDataLen); pTmp->uiDataLen = uiDataLen; } m_bChanged = TRUE; Exit: return( rc); } /**************************************************************************** Name: remove Desc: *****************************************************************************/ RCODE F_DynamicList::remove( FLMUINT uiKey) { DLIST_NODE * pTmp; RCODE rc = NE_FLM_OK; if( (pTmp = getNode( uiKey)) == NULL) { rc = RC_SET( NE_FLM_NOT_FOUND); goto Exit; } if( pTmp->pPrev) { pTmp->pPrev->pNext = pTmp->pNext; } if( pTmp->pNext) { pTmp->pNext->pPrev = pTmp->pPrev; } if( m_pCur == pTmp) { if( pTmp->pNext) { m_pCur = pTmp->pNext; } else { m_pCur = pTmp->pPrev; } } if( m_pFirst == pTmp) { m_pFirst = pTmp->pNext; } if( m_pLast == pTmp) { m_pLast = pTmp->pPrev; if( m_uiRow) { m_uiRow--; } } freeNode( pTmp); m_bChanged = TRUE; Exit: return( rc); } /**************************************************************************** Name: refresh Desc: *****************************************************************************/ void F_DynamicList::refresh( void) { DLIST_NODE * pTmp; FLMUINT uiLoop; FTX_SCREEN * pScreen = NULL; if( !m_bChanged) { return; } if( RC_BAD( FTXWinGetScreen( m_pListWin, &pScreen))) { flmAssert( 0); goto Exit; } FTXSetRefreshState( TRUE); pTmp = m_pCur; uiLoop = m_uiRow; while( pTmp && uiLoop > 0) { pTmp = pTmp->pPrev; uiLoop--; } if( !pTmp) { pTmp = m_pFirst; } uiLoop = 0; while( pTmp && uiLoop < m_uiListRows) { pTmp->pDispHook( m_pListWin, (FLMBOOL)(pTmp == m_pCur ? TRUE : FALSE), uiLoop, pTmp->uiKey, pTmp->pvData, pTmp->uiDataLen, this); pTmp = pTmp->pNext; uiLoop++; f_yieldCPU(); } if( uiLoop < m_uiListRows) { FTXWinClearXY( m_pListWin, 0, uiLoop); } FTXSetRefreshState( FALSE); m_bChanged = FALSE; Exit: ; } /**************************************************************************** Name: cursorUp Desc: *****************************************************************************/ void F_DynamicList::cursorUp( void) { if( m_pCur && m_pCur->pPrev) { m_pCur = m_pCur->pPrev; if( m_uiRow > 0) { m_uiRow--; } m_bChanged = TRUE; } } /**************************************************************************** Name: cursorDown Desc: *****************************************************************************/ void F_DynamicList::cursorDown( void) { if( m_pCur && m_pCur->pNext) { m_pCur = m_pCur->pNext; if( m_uiRow < (m_uiListRows - 1)) { m_uiRow++; } m_bChanged = TRUE; } } /**************************************************************************** Name: pageUp Desc: *****************************************************************************/ void F_DynamicList::pageUp( void) { FLMUINT uiLoop = 0; DLIST_NODE * pTmp; while( m_pCur && uiLoop < m_uiListRows) { m_pCur = m_pCur->pPrev; uiLoop++; } if( m_pCur == NULL) { m_pCur = m_pFirst; m_uiRow = 0; } else { pTmp = m_pCur->pPrev; uiLoop = 0; while( pTmp && uiLoop < m_uiRow) { pTmp = pTmp->pPrev; uiLoop++; } if( uiLoop < m_uiRow) { m_uiRow = uiLoop; } } m_bChanged = TRUE; } /**************************************************************************** Name: pageDown Desc: *****************************************************************************/ void F_DynamicList::pageDown( void) { DLIST_NODE * pOldCur = m_pCur; FLMUINT uiLoop; if( m_pCur == m_pLast) { return; } uiLoop = 0; while( m_pCur && uiLoop < m_uiListRows) { m_pCur = m_pCur->pNext; uiLoop++; } if( m_pCur == NULL) { m_pCur = m_pLast; while( m_pCur != pOldCur && m_uiRow < (m_uiListRows - 1)) { pOldCur = pOldCur->pNext; m_uiRow++; } } m_bChanged = TRUE; } /**************************************************************************** Name: home Desc: *****************************************************************************/ void F_DynamicList::home( void) { m_pCur = m_pFirst; m_uiRow = 0; m_bChanged = TRUE; } /**************************************************************************** Name: end Desc: *****************************************************************************/ void F_DynamicList::end( void) { DLIST_NODE * pTmp; pTmp = m_pCur = m_pLast; m_uiRow = 0; while( pTmp && m_uiRow < m_uiListRows) { pTmp = pTmp->pPrev; m_uiRow++; } if( m_uiRow) { m_uiRow--; } m_bChanged = TRUE; } /**************************************************************************** Name: defaultKeyAction Desc: *****************************************************************************/ void F_DynamicList::defaultKeyAction( FLMUINT uiKey) { switch( uiKey) { case FKB_UP: cursorUp(); break; case FKB_DOWN: cursorDown(); break; case FKB_PGUP: pageUp(); break; case FKB_PGDN: pageDown(); break; case FKB_HOME: home(); break; case FKB_END: end(); break; case 'd': case 'D': { RCODE rc = NE_FLM_OK; rc = this->dumpToFile(); break; } } refresh(); } /**************************************************************************** Name: getNode Desc: *****************************************************************************/ DLIST_NODE * F_DynamicList::getNode( FLMUINT uiKey) { DLIST_NODE * pTmp = m_pFirst; while( pTmp) { if( pTmp->uiKey == uiKey) { break; } pTmp = pTmp->pNext; } return( pTmp); } /**************************************************************************** Name: freeNode Desc: *****************************************************************************/ void F_DynamicList::freeNode( DLIST_NODE * pNode) { if( pNode->pvData) { f_free( &pNode->pvData); f_free( &pNode); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE dlistDefaultDisplayHook( FTX_WINDOW * pWin, FLMBOOL bSelected, FLMUINT uiRow, FLMUINT uiKey, void * pvData, FLMUINT uiDataLen, F_DynamicList* pDynamicList) { eColorType uiBack = FLM_CYAN; eColorType uiFore = FLM_WHITE; F_UNREFERENCED_PARM( uiKey); F_UNREFERENCED_PARM( uiDataLen); FTXWinSetCursorPos( pWin, 0, uiRow); FTXWinClearToEOL( pWin); FTXWinPrintf( pWin, "%s", (FLMBYTE *) (pvData ? pvData : //the following cast is required by gcc 2.96 (FLMBYTE*)"Unknown")); if( bSelected && pDynamicList->getShowHorizontalSelector()) { FTXWinPaintRow( pWin, &uiBack, &uiFore, uiRow); } return( NE_FLM_OK); } /**************************************************************************** Desc: *****************************************************************************/ RCODE F_DynamicList::dumpToFile() { RCODE rc = NE_FLM_OK; DLIST_NODE * pTmp; FLMUINT uiLoop; IF_FileHdl * pFileHdl = NULL; #define DLST_RESP_SIZE 256 char szResponse[ DLST_RESP_SIZE]; FLMUINT uiTermChar; FTX_SCREEN * pScreen; IF_FileSystem * pFileSystem = NULL; if( RC_BAD( rc = FlmGetFileSystem( &pFileSystem))) { goto Exit; } f_strcpy( szResponse, (const char *)DLIST_DUMPFILE_PATH); FTXWinGetScreen( m_pListWin, &pScreen); FTXGetInput( pScreen, "enter filename to dump to", szResponse, DLST_RESP_SIZE-1, &uiTermChar); if ( uiTermChar != FKB_ENTER) { goto Exit; } if (RC_BAD( rc = pFileSystem->doesFileExist( szResponse))) { //create file if it doesn't already exist if ( rc == NE_FLM_IO_PATH_NOT_FOUND) { rc = pFileSystem->createFile( szResponse, FLM_IO_RDWR, &pFileHdl); } else { goto Exit_local; } } else { rc = pFileSystem->openFile( szResponse, FLM_IO_RDWR, &pFileHdl); } TEST_RC_LOCAL( rc); { FLMUINT64 ui64FileSize = 0; FLMUINT uiBytesWritten = 0; //figure out size of file currently, so you can append to it pFileHdl->size( &ui64FileSize); pTmp = m_pFirst; uiLoop = 0; while( pTmp) { FLMBYTE * pszNextLine = (FLMBYTE*)(pTmp->pvData); TEST_RC_LOCAL( rc = pFileHdl->write( ui64FileSize, //offset to current file size f_strlen( (const char *)pszNextLine), pszNextLine, &uiBytesWritten)); ui64FileSize += uiBytesWritten; TEST_RC_LOCAL( rc = pFileHdl->write( ui64FileSize, //add in newline 1, (FLMBYTE*)"\n", &uiBytesWritten)); ui64FileSize += uiBytesWritten; pTmp = pTmp->pNext; } (void)pFileHdl->closeFile(); } Exit_local: {//give success/fail message char szMessage[ 256]; FLMUINT uiChar; FTXWinGetScreen( m_pListWin, &pScreen); if ( RC_OK( rc)) { f_sprintf( szMessage, "contents of focused list appended to %s", DLIST_DUMPFILE_PATH); } else { f_sprintf( szMessage, "error rc=%u dumping to file %s", (unsigned)rc, DLIST_DUMPFILE_PATH); } FTXDisplayMessage( pScreen, FLM_RED, FLM_WHITE, szMessage, "press ESC or ENTER to close dialog", &uiChar); } Exit: if (pFileHdl) { pFileHdl->Release(); pFileHdl = NULL; } if( pFileSystem) { pFileSystem->Release(); } return rc; } libxflaim-5.1.969/util/view.cpp0000644000175000017500000004663110511001742017702 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file is the main for the database view utility // // Tabs: 3 // // Copyright (c) 1992-1995, 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: view.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #define MAIN_MODULE #include "view.h" #define UTIL_ID "VIEW" // Main Menu options #define MAIN_MENU_DB_HEADER 1 #define MAIN_MENU_LOGICAL_FILES 2 // Local function prototypes FSTATIC void ViewShowHelp( FLMBOOL bShowFullUsage); FSTATIC FLMUINT ViewGetChar( const char * pszMessage1, const char * pszMessage2, FLMUINT uiDefaultChar); FSTATIC FLMBOOL ViewGetFileName( FLMUINT uiCol, FLMUINT uiRow, FLMBOOL bDispOnly); FSTATIC FLMBOOL ViewOpenFile( void); FSTATIC void ViewDoMainMenu( void); FSTATIC FLMBOOL ViewSetupMainMenu( void); FSTATIC FLMBOOL ViewOpenFileDirect( void); static FLMBOOL bPauseBeforeExiting = FALSE; FLMUINT gv_uiTopLine = 0; FLMUINT gv_uiBottomLine = 0; #ifdef FLM_RING_ZERO_NLM #define main nlm_main #endif /******************************************************************** Desc: ? *********************************************************************/ extern "C" int main( int iArgC, char ** ppszArgV) { #define MAX_ARGS 30 RCODE rc = NE_XFLM_OK; FLMUINT uiArg; FLMINT iArgCnt = (FLMINT)iArgC; char * ppszArgs [MAX_ARGS]; char szCommandBuffer [300]; if( RC_BAD( rc = FlmAllocDbSystem( &gv_pDbSystem))) { goto Exit; } // Setup defaults for fixing the file header if necessary gv_ViewFixOptions.ui32BlockSize = XFLM_DEFAULT_BLKSIZ; gv_ViewFixOptions.ui32VersionNum = XFLM_CURRENT_VERSION_NUM; gv_ViewFixOptions.ui32MinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; gv_ViewFixOptions.ui32MaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; gv_ViewFixOptions.bKeepRflFiles = XFLM_DEFAULT_KEEP_RFL_FILES_FLAG; gv_ViewFixOptions.bLogAbortedTransToRfl = XFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG; gv_ViewFixOptions.ui32DefaultLanguage = XFLM_DEFAULT_LANG; // See if a file name was passed in gv_szViewFileName [0] = '\0'; gv_szDataDir [0] = 0; gv_szRflDir [0] = 0; gv_szPassword [0] = 0; gv_bViewExclusive = FALSE; gv_bViewFileOpened = FALSE; gv_bViewHdrRead = FALSE; gv_bViewHaveDictInfo = FALSE; gv_bShutdown = FALSE; gv_bRunning = TRUE; gv_pSFileHdl = NULL; f_conInit( 0xFFFF, 0xFFFF, "FLAIM Database Viewer"); f_conGetScreenSize( NULL, &gv_uiBottomLine); gv_uiTopLine = 2; gv_uiBottomLine -= 3; // Ask the user to enter parameters if none were entered on the command // line. if (iArgCnt < 2) { for (;;) { f_conStrOut( "\nView Params (enter ? for help): "); szCommandBuffer [0] = 0; f_conLineEdit( szCommandBuffer, sizeof( szCommandBuffer) - 1); if (f_stricmp( szCommandBuffer, "?") == 0) { ViewShowHelp( FALSE); } else { break; } if (gv_bShutdown) { goto Exit; } } flmUtilParseParams( szCommandBuffer, MAX_ARGS, &iArgCnt, &ppszArgs [1]); ppszArgs [0] = ppszArgV [0]; iArgCnt++; ppszArgV = &ppszArgs [0]; } uiArg = 1; while (uiArg < (FLMUINT)iArgCnt) { #ifdef FLM_UNIX if (ppszArgV [uiArg][0] == '-') #else if ((ppszArgV [uiArg][0] == '/') || (ppszArgV [uiArg][0] == '-')) #endif { switch( ppszArgV [uiArg][1]) { case 'x': case 'X': gv_bViewExclusive = TRUE; break; case 'b': case 'B': gv_ViewFixOptions.ui32BlockSize = (FLMUINT32)f_atoi( &ppszArgV [uiArg][2]); break; case 'd': case 'D': switch (ppszArgV [uiArg][2]) { case 'r': case 'R': f_strcpy( gv_szRflDir, &ppszArgV [uiArg][3]); break; case 'd': case 'D': f_strcpy( gv_szDataDir, &ppszArgV [uiArg][3]); break; default: break; } break; case 'l': case 'L': gv_ViewFixOptions.ui32MaxRflFileSize = (FLMUINT32)f_atol( &ppszArgV [uiArg][2]); break; case 'm': case 'M': gv_ViewFixOptions.ui32MinRflFileSize = (FLMUINT32)f_atol( &ppszArgV [uiArg][2]); break; case 'p': case 'P': switch( ppszArgV [uiArg][2]) { case 0: bPauseBeforeExiting = TRUE; break; case 'w': case 'W': if ( ppszArgV [uiArg][3]) { f_strcpy( gv_szPassword, &ppszArgV [uiArg][3]); } else { ViewShowHelp( TRUE); bPauseBeforeExiting = TRUE; goto Exit; } break; default: break; } break; case '?': ViewShowHelp( TRUE); bPauseBeforeExiting = TRUE; goto Exit; default: break; } } else if (f_stricmp( ppszArgV [uiArg], "?") == 0) { ViewShowHelp( TRUE); bPauseBeforeExiting = TRUE; goto Exit; } else if (!gv_szViewFileName [0]) { f_strcpy( gv_szViewFileName, ppszArgV [uiArg]); } uiArg++; } if( (gv_pViewPool = f_new F_Pool) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } gv_pViewPool->poolInit(2048); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, 0); // Open the file if (ViewOpenFile()) { // Execute the main menu ViewDoMainMenu(); ViewFreeMenuMemory(); // Close the file if (gv_bViewDbInitialized) { if (RC_BAD ( rc = gv_hViewDb->transAbort( ))) { ViewShowRCError( "calling transAbort()", rc); goto Exit; } gv_hViewDb->Release(); } } Exit: if (gv_pSFileHdl) { gv_pSFileHdl->Release(); gv_pSFileHdl = NULL; } if ((bPauseBeforeExiting) && (!gv_bShutdown)) { f_conStrOut( "\nPress any character to exit VIEW: "); for (;;) { if (gv_bShutdown) { break; } if (f_conHaveKey()) { f_conGetKey(); break; } viewGiveUpCPU(); } } if( gv_pViewPool) { gv_pViewPool->Release(); } f_conExit(); if( gv_pDbSystem) { gv_pDbSystem->Release(); } gv_bRunning = FALSE; return 0; } /******************************************************************** Desc: Display a help screen. *********************************************************************/ FSTATIC void ViewShowHelp( FLMBOOL bShowFullUsage ) { f_conStrOut( "\n"); if (bShowFullUsage) { f_conStrOut( "Usage: view [Options]\n"); } else { f_conStrOut( "Parameters: [Options]\n\n"); } f_conStrOut( " DbName = Name of database to view.\n"); f_conStrOut( " Options =\n"); f_conStrOut( " -dr = RFL directory.\n"); f_conStrOut( " -dd = Data directory.\n"); f_conStrOut( " -x = Open database in exclusive mode.\n"); f_conStrOut( " -f = Fix database header. If the options below are not set,\n"); f_conStrOut( " defaults will be used.\n"); f_conStrOut( " -b = Set block size to Size (only used if -f is specified).\n"); f_conStrOut( " -m = Set minimum RFL file size to Size (only used if -f\n"); f_conStrOut( " option is used).\n"); f_conStrOut( " -l = Set maximum RFL file size to Size (only used if -f\n"); f_conStrOut( " option is used).\n"); f_conStrOut( " used).\n"); f_conStrOut( " -p = Pause before exiting.\n"); f_conStrOut( " -pw = Database password.\n"); f_conStrOut( " -? = A '?' anywhere in the command line will cause this\n"); f_conStrOut( " screen to be displayed, with or without the leading '-'.\n"); f_conStrOut( "Options may be specified anywhere in the command line.\n"); } /*************************************************************************** Desc: Prompt user for a single character response and get the response. *****************************************************************************/ FSTATIC FLMUINT ViewGetChar( const char * pszMessage1, const char * pszMessage2, FLMUINT uiDefaultChar) { FLMUINT uiChar; FLMUINT uiNumCols; FLMUINT uiNumRows; f_conGetScreenSize( &uiNumCols, &uiNumRows); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, uiNumRows - 2); f_conSetBackFore( FLM_RED, FLM_WHITE); if (pszMessage1) { f_conStrOutXY( pszMessage1, 0, uiNumRows - 2); } f_conStrOutXY( pszMessage2, 0, 23); for (;;) { if (gv_bShutdown) { uiChar = FKB_ESCAPE; break; } else if (f_conHaveKey()) { uiChar = f_conGetKey(); break; } viewGiveUpCPU(); } f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, uiNumRows - 2); if (uiChar == FKB_ENTER) { uiChar = uiDefaultChar; } if (uiChar >= 'a' && uiChar <= 'z') { uiChar = uiChar - 'a' + 'A'; } return( uiChar); } /*************************************************************************** Desc: This routine reads and verifies the information contained in the file header and log header of a FLAIM database. *****************************************************************************/ FINLINE RCODE ViewReadAndVerifyHdrInfo( FLMUINT32 * pui32CalcCRC ) { return( flmGetHdrInfo( gv_pSFileHdl, &gv_ViewDbHdr, pui32CalcCRC)); } /*************************************************************************** Desc: Read the header information from the database -- this includes the file header and the log header. *****************************************************************************/ void ViewReadHdr( FLMUINT32 * pui32CalcCRC) { RCODE rc; FLMUINT uiNumCols; FLMUINT uiNumRows; f_conGetScreenSize( &uiNumCols, &uiNumRows); gv_bViewHdrRead = TRUE; if (RC_OK( rc = ViewReadAndVerifyHdrInfo( pui32CalcCRC))) { return; } // Had some sort of error ViewShowRCError( "reading header information", rc); // Make sure we have a valid block size if( gv_ViewDbHdr.ui16BlockSize != 4096 && gv_ViewDbHdr.ui16BlockSize != 8192) { gv_ViewDbHdr.ui16BlockSize = XFLM_DEFAULT_BLKSIZ; } } /******************************************************************** Desc: Ask for input from the user *********************************************************************/ void ViewAskInput( const char * pszPrompt, char * pszBuffer, FLMUINT uiBufLen) { char szTempBuf [80]; f_conStrOut( pszPrompt); if (uiBufLen > sizeof( szTempBuf)) { uiBufLen = sizeof( szTempBuf); } szTempBuf [0] = 0; f_conLineEdit( szTempBuf, uiBufLen); f_strcpy( (char *)pszBuffer, szTempBuf); } /*************************************************************************** Desc: This routine asks the user for the file name to be viewed. *****************************************************************************/ FSTATIC FLMBOOL ViewGetFileName( FLMUINT uiCol, FLMUINT uiRow, FLMBOOL bDispOnly) { const char * pszPrompt = "Enter database file name: "; f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( uiCol, uiRow); if (bDispOnly) { f_conStrOutXY( pszPrompt, uiCol, uiRow); f_conStrOutXY( gv_szViewFileName, uiCol + f_strlen( pszPrompt), uiRow); } else { f_conSetCursorPos( uiCol, uiRow); ViewAskInput( pszPrompt, gv_szViewFileName, 40); if (!gv_szViewFileName [0] || f_strcmp( gv_szViewFileName, "\\") == 0) { return( FALSE); } } return( TRUE); } /**************************************************************************** Desc: This routine opens a database file in DIRECT mode - because we couldn't get it open by calling the normal FLAIM functions. ****************************************************************************/ FSTATIC FLMBOOL ViewOpenFileDirect( void) { RCODE rc; IF_FileHdl * pCFileHdl = NULL; if (RC_BAD( rc = gv_pSFileHdl->getFileHdl( 0, FALSE, &pCFileHdl))) { ViewShowRCError( "opening file in direct mode", rc); return( FALSE); } gv_bViewFileOpened = TRUE; return( TRUE); } /*************************************************************************** Desc: This routine opens the database file which is to be viewed. *****************************************************************************/ FSTATIC FLMBOOL ViewOpenFile( void) { RCODE rc; FLMBOOL bOk = FALSE; F_SuperFileClient * pSFileClient = NULL; Get_File_Name: // Prompt for file name if necessary f_conClearScreen( 0, 1); if (!gv_szViewFileName [0]) { if (!ViewGetFileName( 5, 5, FALSE)) { goto Exit; } } else { if (!ViewGetFileName( 5, 5, TRUE)) { goto Exit; } } if (gv_pSFileHdl) { gv_pSFileHdl->Release(); gv_pSFileHdl = NULL; } if ((pSFileClient = f_new F_SuperFileClient) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if (RC_BAD( rc = pSFileClient->setup( gv_szViewFileName, gv_szDataDir, gv_ViewDbHdr.ui32MaxFileSize))) { ViewShowRCError( "setting up super file handle", rc); goto Exit; } if ((gv_pSFileHdl = f_new F_SuperFileHdl) == NULL) { rc = RC_SET( NE_XFLM_MEM); ViewShowRCError( "creating super file handle", rc); goto Exit; } if (RC_BAD( rc = gv_pSFileHdl->setup( pSFileClient, gv_XFlmSysData.pFileHdlCache, gv_XFlmSysData.uiFileOpenFlags, gv_XFlmSysData.uiFileCreateFlags))) { ViewShowRCError( "setting up super file handle", rc); goto Exit; } rc = ViewReadAndVerifyHdrInfo( NULL); gv_pSFileHdl->releaseFiles(); if (RC_BAD( rc)) { if (rc == NE_XFLM_IO_PATH_NOT_FOUND) { goto Path_Not_Found; } else { goto Other_Error; } } if (RC_BAD( rc = gv_pDbSystem->dbOpen( gv_szViewFileName, gv_szDataDir, gv_szRflDir, gv_szPassword, XFLM_DONT_REDO_LOG, &gv_hViewDb))) { char szTBuf[ 100]; if (rc == NE_XFLM_IO_PATH_NOT_FOUND) { Path_Not_Found: if (ViewGetChar( NULL, "File not found, try another file name? (Y/N, Default=Y): ", 'Y') != 'Y') { goto Exit; } gv_szViewFileName [0] = 0; goto Get_File_Name; } else { Other_Error: f_sprintf( szTBuf, "Error opening file: 0x%04X", (unsigned)rc); if (ViewGetChar( szTBuf, "Open file in DIRECT MODE anyway? (Y/N, Default=Y): ", 'Y') == 'Y') { if (!ViewOpenFileDirect()) { goto Exit; } } else { goto Exit; } } } else { gv_bViewDbInitialized = TRUE; // if (RC_BAD ( rc = gv_hViewDb->transBegin( XFLM_READ_TRANS))) // { // ViewShowRCError( "calling transBegin()", rc); // goto Exit; // } if (!ViewOpenFileDirect()) { goto Exit; } } bOk = TRUE; Exit: if (!bOk) { if (gv_pSFileHdl) { gv_pSFileHdl->Release(); gv_pSFileHdl = NULL; } gv_bViewFileOpened = FALSE; } if( pSFileClient) { pSFileClient->Release(); } return( bOk); } /*************************************************************************** Desc: This routine gets the dictionary information for a database and locks it into memory. *****************************************************************************/ RCODE ViewGetDictInfo( void) { RCODE rc = NE_XFLM_OK; IF_Db * pDb = gv_hViewDb; // FLMUINT uiSaveFlags; FLMUINT uiFlags; if (gv_bViewDbInitialized) { // If we have a transaction going, abort it and start another one. if (pDb->getTransType() != XFLM_NO_TRANS) { pDb->transAbort(); } // Need to fake out flmBeginDbTrans to avoid an assert. // This may be the first time we read in a dictionary due to the // fact that we did not do recovery and rollback. flmBeginDbTrans // expects the DBF_BEING_OPENED flag to be set the first time // a dictionary is read in. Otherwise, it will assert. // uiSaveFlags = pDb->pFile->uiFlags; // pDb->pFile->uiFlags |= DBF_BEING_OPENED; uiFlags = DBF_BEING_OPENED; // VISIT: This needs the other flags... // Start a read transaction. if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, FLM_NO_TIMEOUT, uiFlags, NULL))) { gv_bViewHaveDictInfo = FALSE; goto Exit; } gv_bViewHaveDictInfo = TRUE; // pDb->pFile->uiFlags = uiSaveFlags; } Exit: return rc; } /*************************************************************************** Desc: This routine sets up the main menu for the VIEW program. *****************************************************************************/ FSTATIC FLMBOOL ViewSetupMainMenu( void) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; // Initialize the menu structures ViewMenuInit( "Main Menu"); uiRow = 3; uiCol = 20; // Add each menu item to the menu if (!ViewAddMenuItem( LBL_DB_HEADER, 0, VAL_IS_EMPTY, 0, 0, 0, 0xFFFFFFFF, 0, MOD_DISABLED, uiCol, uiRow++, MAIN_MENU_DB_HEADER, FLM_BLACK, FLM_WHITE, FLM_BLUE, FLM_WHITE)) { goto Exit; } if (gv_ViewDbHdr.ui32FirstLFBlkAddr == 0) { if (!ViewAddMenuItem( LBL_LOGICAL_FILES, 0, VAL_IS_LABEL_INDEX, (FLMUINT)LBL_NONE, 0, 0, 0xFFFFFFFF, 0, MOD_DISABLED, uiCol, uiRow++, 0, FLM_BLACK, FLM_LIGHTGRAY, FLM_BLUE, FLM_LIGHTGRAY)) { goto Exit; } } else { if (!ViewAddMenuItem( LBL_LOGICAL_FILES, 0, VAL_IS_EMPTY, 0, 0, 0, 0xFFFFFFFF, 0, MOD_DISABLED, uiCol, uiRow++, MAIN_MENU_LOGICAL_FILES, FLM_BLACK, FLM_WHITE, FLM_BLUE, FLM_WHITE)) { goto Exit; } } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine executes the main menu of the VIEW program. From here the user may view various parts of the database until he presses the ESC key. *****************************************************************************/ FSTATIC void ViewDoMainMenu( void) { FLMUINT uiOption; VIEW_INFO SaveView; FLMBOOL bRepaint = TRUE; FLMUINT uiBlkAddress; BLK_EXP BlkExp; // Loop getting commands until the ESC key is pressed ViewReset( &SaveView); for( ;;) { // Redisplay the main menu each time, because the other options will // have destroyed the menu. if (gv_bViewPoppingStack) { if (!gv_bViewHdrRead) { ViewReadHdr(); } ViewSearch(); } if (bRepaint) { if (!ViewSetupMainMenu()) { return; } } bRepaint = TRUE; uiOption = ViewGetMenuOption(); switch (uiOption) { case ESCAPE_OPTION: return; case MAIN_MENU_DB_HEADER: if (!gv_bViewHdrRead) { ViewReadHdr(); } ViewDbHeader(); break; case MAIN_MENU_LOGICAL_FILES: if (!gv_bViewHdrRead) { ViewReadHdr(); } ViewLogicalFiles(); break; case SEARCH_OPTION: if (!gv_bViewHdrRead) { ViewReadHdr(); } gv_uiViewSearchLfNum = XFLM_DATA_COLLECTION; gv_uiViewSearchLfType = XFLM_LF_COLLECTION; if (ViewGetKey()) { ViewSearch(); } break; case GOTO_BLOCK_OPTION: if (!gv_bViewHdrRead) { ViewReadHdr(); } if (GetBlockAddrType( &uiBlkAddress)) { BlkExp.uiType = 0xFF; BlkExp.uiLevel = 0xFF; BlkExp.uiNextAddr = 0xFFFFFFFF; BlkExp.uiPrevAddr = 0xFFFFFFFF; BlkExp.uiLfNum = 0; ViewBlocks( uiBlkAddress, uiBlkAddress, &BlkExp); } else { bRepaint = FALSE; } break; case EDIT_OPTION: default: bRepaint = FALSE; break; } } } libxflaim-5.1.969/util/dirtyexittest1srv.cpp0000644000175000017500000001637110511001742022467 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Dirty exit test 1 // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: dirtyexittest1srv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined(NLM) #define DB_NAME_STR "SYS:\\BAD.DB" #else #define DB_NAME_STR "bad.db" #endif /**************************************************************************** Desc: ****************************************************************************/ class IDirtyExitTest1Impl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IDirtyExitTest1Impl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IDirtyExitTest1Impl::getName( void) { return( "Dirty Exit Test Part 1"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IDirtyExitTest1Impl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bTransStarted = FALSE; IF_PosIStream * pBufferIStream = NULL; const char * pszDocument = "" " " " 01 Pull Me Under.mp3" " Pull Me Under" " Dream Theater" " Images & Words" " 1992" " Rock" " 1" " " " " " " " 02 Another Day.mp3" " Another Day" " Dream Theater" " Images & Words" " 1992" " Rock" " 2" " " " " " " " 03 Take the Time.mp3" " Take the Time" " Dream Theater" " Images & Words" " 1992" " Rock" " 3" " " " " " " " 04 Surrounded.mp3" " Surrounded" " Dream Theater" " Images & Words" " 1992" " Rock" " 4" " " " " " " " 05 Metropolis, Pt. 1.mp3" " Metropolis, Pt. 1" " Dream Theater" " Images & Words" " 1992" " Rock" " 5" " " " " " " " 06 Under a Glass Moon.mp3" " Under a Glass Moon" " Dream Theater" " Images & Words" " 1992" " Rock" " 6" " " " " " " " 07 Wait for Sleep.mp3" " Wait for Sleep" " Dream Theater" " Images & Words" " 1992" " Rock" " 7" " " " " " " " 08 Learning to Live.mp3" " Learning to Live" " Dream Theater" " Images & Words" " 1992" " Rock" " 8" " " " " ""; beginTest( "Init Test State", "Setup the necessary state for our test ", "(1) Get the DbSystem (2) Create a database", "No Additional Details."); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING("Failed to open test state", m_szDetails, rc); goto Exit; } endTest("PASS"); beginTest( "Import document test", "Import a document", "Import a document before the dirty shutdown", ""); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING("Failed to begin trans.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = m_pDbSystem->openBufferIStream( pszDocument, f_strlen( pszDocument), &pBufferIStream))) { MAKE_FLM_ERROR_STRING("Failed to open file istream", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->import( pBufferIStream, XFLM_DATA_COLLECTION))) { MAKE_FLM_ERROR_STRING("Failed to import document", m_szDetails, rc); goto Exit; } endTest("PASS"); #ifdef FLM_USE_NICI { FLMUINT uiDes3Def = 0; IF_DOMNode * pRoot = NULL; IF_DOMNode * pNode = NULL; beginTest( "Recover Encryption Test", "Make sure operations with encrypted values are being recovered properly", "(1) Create an encryption definition (2) Add an encrypted value " "(3) commit the trans (4) quit the program without calling exit()", ""); if ( RC_BAD( rc = m_pDb->createEncDef( "des3", "des3 definition", 0, &uiDes3Def))) { MAKE_FLM_ERROR_STRING("createEncDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->getFirstDocument( XFLM_DATA_COLLECTION, &pRoot))) { MAKE_FLM_ERROR_STRING("getFirstDocument", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRoot->createNode( m_pDb, ELEMENT_NODE, ELM_DOCUMENT_TITLE_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_FLM_ERROR_STRING("createNode", m_szDetails, rc); pRoot->Release(); goto Exit; } rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"My big ol' native text value to be encrypted", 0, TRUE, uiDes3Def); pRoot->Release(); pNode->Release(); if ( RC_BAD( rc)) { MAKE_FLM_ERROR_STRING("setNative failed", m_szDetails, rc); goto Exit; } endTest("PASS"); } #endif Exit: if ( bTransStarted) { RCODE tmpRc = m_pDb->transCommit(); if ( RC_OK( rc)) { rc = tmpRc; } } if ( RC_BAD( rc)) { endTest("FAIL"); } // superficial cleanup. Purposely not calling exit on the dbsystem if ( pBufferIStream) { pBufferIStream->Release(); } return rc; } libxflaim-5.1.969/util/xpathtest2srv.cpp0000644000175000017500000013545710511001742021576 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: More XPATH Query tests. These tests focus on the META axis and the // Node subscript ([]). // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: xpathtest2srv.cpp 3129 2006-01-25 11:46:17 -0700 (Wed, 25 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #include "flmunittest.h" #ifndef DB_NAME_STR #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\XP3.DB" #else #define DB_NAME_STR "xp3.db" #endif #endif /**************************************************************************** Desc: ****************************************************************************/ class IXPATHTest2Impl : public TestBase { public: const char * getName( void); RCODE execute( void); private: RCODE runSuite1( void); RCODE runSuite2( void); RCODE runSuite3( void); RCODE runSuite4( void); RCODE runSuite5( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IXPATHTest2Impl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IXPATHTest2Impl::getName( void) { return( "XPATH Test 2"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPATHTest2Impl::execute( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = runSuite1())) { goto Exit; } if( RC_BAD( rc = runSuite2())) { goto Exit; } if( RC_BAD( rc = runSuite3())) { goto Exit; } if( RC_BAD( rc = runSuite4())) { goto Exit; } if( RC_BAD( rc = runSuite5())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPATHTest2Impl::runSuite1( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; IF_PosIStream * pPosIStream = NULL; IF_Query * pQuery = NULL; IF_DOMNode * pResult = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pTempNode = NULL; char szQueryString[256]; FLMUINT64 ui64ParentId = 0; FLMUINT64 ui64ParentId2 = 0; FLMUINT64 ui64DocumentId = 0; FLMUINT64 ui64DocumentId2 = 0; FLMUINT64 ui64NodeId = 0; FLMUINT64 ui64FirstChildId = 0; FLMUINT64 ui64FirstChildId2 = 0; FLMUINT64 ui64LastChildId = 0; FLMUINT64 ui64LastChildId2 = 0; FLMUINT64 ui64NextSiblingId = 0; FLMUINT64 ui64NextSiblingId2 = 0; FLMUINT64 ui64PrevSiblingId = 0; FLMUINT64 ui64PrevSiblingId2 = 0; FLMUINT64 ui64Tmp; const char * ppszQueryThreeResults[] = {"John Thorson","Marea Angela Castaneda"}; const char * ppszFollowingQueryResults[] = {"Africa", "Asia", "Europe"}; const char * ppszDescendantQueryResults[] = {"Sales","Marketing","Production"}; const char * ppszAncestorQueryResults[] = {"emp9032", "emp7216", "emp4238", "emp3456"}; const char * ppszQuery19Results[] = {"Sales","", "", ""}; char szBuffer[ 128]; const char * pszQueryResult = NULL; FLMBOOL bTransStarted = FALSE; const char * pszOrgChart = "" "Kim Akers" " " " Steve Masters" " Domestic" " " " Shelly Szymanski" " Sales" " " " Cindy Durkin" " Northeast" " " " " " Michelle Votava" " Southeast" " " " " " John Tippett" " Southwest" " " " " " Alan Steiner" " Northwest" " " " " " " " John Thorson" " Marketing" " " " " " Josh Barnhill" " Production" " " " " " " " Katie McAskill-White" " International" " " " Neil Charney" " Sales" " " " Beth Silverberg" " Africa" " " " " " Lani Ota" " Asia" " " " " " Peter Porzuczek" " Europe" " " " " " " " Marea Angela Castaneda" " Marketing" " " " " ""; const char * pszNameRegionIndex = " " // HARD-CODED Dict Num for easy retrieval " " " xflaim:Required=\"1\" " " " " xflaim:Required=\"1\" " " "; m_szDetails[0] = '\0'; beginTest( "XPATH Test 2 Setup", "Set up test state for XPATH Query Test Suite #3", "", ""); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = importBuffer( pszOrgChart, XFLM_DATA_COLLECTION))) { goto Exit; } if( RC_BAD( rc = importBuffer( pszNameRegionIndex, XFLM_DICT_COLLECTION))) { goto Exit; } m_pDb->transCommit(); bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "Failed to create query object.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* First Query ************************************/ f_strcpy( szQueryString, "/chairman/president/groupvp[@empID == \"emp9801\"]/director[3]/name"); pszQueryResult = "John Tippett"; beginTest( "Node Subscript Test 1", "/chairman/president/groupvp[@empID == \"emp9801\"]/director[3]/name", "", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Second Query ************************************/ f_strcpy( szQueryString, "/chairman/president[2]/groupvp[1]/director[3]/name"); pszQueryResult = "Peter Porzuczek"; beginTest( "Node Subscript Test 2", "/chairman/president[2]/groupvp[1]/director[3]/name", "", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Third Query ************************************/ f_strcpy( szQueryString, "/chairman/president/groupvp[4 - 2]/name"); beginTest( "Node Subscript Test 2", "/chairman/president/groupvp[4 - 2]/name", "", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, ppszQueryThreeResults, sizeof( ppszQueryThreeResults) / sizeof( ppszQueryThreeResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Fourth Query ************************************/ f_strcpy( szQueryString, "/groupvp[ @empID == \"emp7216\"]/director[2 * 3 - 4]/region"); pszQueryResult = "Asia"; beginTest( "Node Subscript Test 4", "/groupvp[ @empID == \"emp7216\"]/director[2 * 3 - 4]/region", "", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Fifth Query ************************************/ beginTest( "Meta Axis Pretest #1", "Query for necessary values to run the Meta Axis tests.", "Get a node and save its nodeId parentId prevSibId and docId " "in preparation for the first meta axis test.", ""); f_strcpy( szQueryString, "/chairman/president[@empID == \"emp4238\"]" "/groupvp/director[@empID == \"emp7634\"]/region[. == \"Asia\"]"); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } // Save the node id if( RC_BAD( rc = pResult->getNodeId( m_pDb, &ui64NodeId))) { goto Exit; } // Save its parent id if( RC_BAD( rc = pResult->getParentId( m_pDb, &ui64ParentId))) { MAKE_FLM_ERROR_STRING( "getParentId failed.", m_szDetails, rc); goto Exit; } // Save its prev sibling id if ( RC_BAD( rc = pResult->getPrevSibId( m_pDb, &ui64PrevSiblingId))) { MAKE_FLM_ERROR_STRING( "getPrevSibId failed.", m_szDetails, rc); goto Exit; } //Save the documentid if ( RC_BAD( rc = pResult->getDocumentId(m_pDb, &ui64DocumentId))) { MAKE_FLM_ERROR_STRING( "getDocumentId failed.", m_szDetails, rc); goto Exit; } endTest("PASS"); // Generate a new query to validate the meta::parentid axis f_sprintf( szQueryString, "/chairman/president/groupvp/director/region[meta::parentid == %I64u]", ui64ParentId); /********************* Sixth Query ************************************/ beginTest( "Meta Axis #1", szQueryString, "Do a meta::parentid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getParentId( m_pDb, &ui64ParentId2))) { MAKE_FLM_ERROR_STRING( "getParentId failed.", m_szDetails, rc); goto Exit; } if ( ui64ParentId2 != ui64ParentId) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::parentid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Seventh Query ************************************/ // Generate a new query to validate the meta::nodeid axis f_sprintf( szQueryString, "/chairman/president//director/region[meta::nodeid == %I64u]", ui64NodeId); beginTest( "Meta Axis #2", szQueryString, "Do a meta::nodeid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pResult->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } if( ui64Tmp != ui64NodeId) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::nodeid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Eighth Query ************************************/ // Generate a new query to validate the meta::prevsiblingid axis f_sprintf( szQueryString, "///region[meta::prevsiblingid == %I64u]", ui64PrevSiblingId); beginTest( "Meta Axis #3", szQueryString, "Do a meta::prevsiblingid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getPrevSibId( m_pDb, &ui64PrevSiblingId2))) { MAKE_FLM_ERROR_STRING( "getPrevSibId failed.", m_szDetails, rc); goto Exit; } if ( ui64PrevSiblingId2 != ui64PrevSiblingId) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::prevsiblingid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Ninth Query ************************************/ // Generate a new query to validate the meta::documentid f_sprintf( szQueryString, "///region[meta::documentid == %I64u]", ui64DocumentId); beginTest( "Meta Axis #9", szQueryString, "Do a meta::documentid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getDocumentId( m_pDb, &ui64DocumentId2))) { MAKE_FLM_ERROR_STRING( "getDocumentId failed.", m_szDetails, rc); goto Exit; } if ( ui64DocumentId2 != ui64DocumentId) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::documentid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Tenth Query ************************************/ f_strcpy( szQueryString, "///director[@empID ==\"emp5443\"]"); beginTest( "Meta Axis Pretest #2", "Query for necessary values to run the rest of the Meta Axis" " tests.", "Get a node and save its firstchildId lastchildId nextSibId" " firstAttrId and lastAttrId in preparation for the first meta axis" " test.", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } //Save the first child if ( RC_BAD( rc = pResult->getFirstChildId( m_pDb, &ui64FirstChildId))) { MAKE_FLM_ERROR_STRING( "getFirstChildId failed.", m_szDetails, rc); goto Exit; } //Save the last child if ( RC_BAD( rc = pResult->getLastChildId( m_pDb, &ui64LastChildId))) { MAKE_FLM_ERROR_STRING( "getLastChildId failed.", m_szDetails, rc); goto Exit; } //Save the next sibling if ( RC_BAD( rc = pResult->getNextSibling( m_pDb, &pTempNode))) { MAKE_FLM_ERROR_STRING( "getNextSibling failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pTempNode->getNodeId( m_pDb, &ui64NextSiblingId))) { goto Exit; } endTest("PASS"); /********************* Eleventh Query ************************************/ // Generate a new query to validate the meta::firstchildid f_sprintf( szQueryString, "///director[meta::firstchildid == %I64u]", ui64FirstChildId); beginTest( "Meta Axis #3", szQueryString, "Do a meta::firstchildid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getFirstChildId( m_pDb, &ui64FirstChildId2))) { MAKE_FLM_ERROR_STRING( "getFirstChildId failed.", m_szDetails, rc); goto Exit; } if ( ui64FirstChildId != ui64FirstChildId2) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::firstchildid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Twelfth Query ************************************/ // Generate a new query to validate the meta::lastchildid f_sprintf( szQueryString, "///director[meta::lastchildid == %I64u]", ui64LastChildId); beginTest( "Meta Axis #4", szQueryString, "Do a meta::lastchildid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getLastChildId( m_pDb, &ui64LastChildId2))) { MAKE_FLM_ERROR_STRING( "getLastChildId failed.", m_szDetails, rc); goto Exit; } if ( ui64LastChildId != ui64LastChildId2) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::lastchildid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Thirteenth Query ************************************/ // Generate a new query to validate the meta::nextsiblingid f_sprintf( szQueryString, "///director[meta::nextsiblingid == %I64u]", ui64NextSiblingId); beginTest("Meta Axis #5", szQueryString, "Do a meta::nextsiblingid query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getNextSibling( m_pDb, &pTempNode))) { MAKE_FLM_ERROR_STRING( "getNextSibling failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pTempNode->getNodeId( m_pDb, &ui64NextSiblingId2))) { goto Exit; } if( ui64NextSiblingId != ui64NextSiblingId2) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "meta::nextsiblingid query returned wrong node.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Seventeenth Query ************************************/ f_sprintf( szQueryString, "name[. == \"John Thorson\"]/parent::*/[@empID==\"emp4320\"]/attribute::empdate"); beginTest( "Parent and Attribute Axes Test", szQueryString, "Do a parent axis query", ""); pszQueryResult = "1978-11-09"; if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Eighteenth Query ************************************/ f_sprintf( szQueryString, "division[. == \"Domestic\"]/preceding::name"); beginTest( "Previous Axis Test", szQueryString, "Do a previous axis query", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = pQuery->setIndex( 0))) { MAKE_FLM_ERROR_STRING( "setIndex failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getLast( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getLast failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getUTF8( m_pDb, (FLMBYTE *)szBuffer, sizeof( szBuffer), 0, sizeof( szBuffer) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } // results are returned in order moving backward from the current node. // Therefore, Kim Akers, who appears first in the document is actually // last in our query results. if ( f_strcmp( szBuffer, "Kim Akers") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getPrev( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getPrevious failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResult->getUTF8( m_pDb, (FLMBYTE *)szBuffer, sizeof( szBuffer), 0, sizeof( szBuffer) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuffer, "Steve Masters") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Nineteenth Query ************************************/ f_sprintf( szQueryString, "name[.==\"Neil Charney\"]/following-sibling::*"); beginTest( "Following-Sibling Test", szQueryString, "Do a parent axis query", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, ppszQuery19Results, 4, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Twentieth Query ************************************/ f_sprintf( szQueryString, "name[.==\"Neil Charney\"]/following::region"); beginTest("Following Axis Test", szQueryString, "Do a following axis query", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, ppszFollowingQueryResults, sizeof( ppszFollowingQueryResults) / sizeof( ppszFollowingQueryResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Twenty-first Query ************************************/ f_sprintf( szQueryString, "president[@empID==\"emp4390\"]/descendant::department"); beginTest( "Descendant Axis Test", szQueryString, "Do a descendant axis query", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, ppszDescendantQueryResults, sizeof( ppszDescendantQueryResults) / sizeof( ppszDescendantQueryResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Twenty-second Query ************************************/ f_sprintf( szQueryString, "region[.==\"Europe\"]/preceding-sibling::*"); pszQueryResult = "Peter Porzuczek"; beginTest( "Preceding-sibling Axis Test", szQueryString, "Do a preceding-sibling axis query", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Twenty-third Query ************************************/ f_sprintf( szQueryString, "name[.==\"Beth Silverberg\"]/ancestor::*/attribute::empID"); beginTest( "Ancestor Axis Test", szQueryString, "Do a Ancestor axis query", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, ppszAncestorQueryResults, sizeof( ppszAncestorQueryResults) / sizeof( ppszAncestorQueryResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Twenty-fourth Query ************************************/ f_sprintf( szQueryString, "name[.==\"Beth Silverberg\"]/preceding::president/name"); pszQueryResult = "Steve Masters"; beginTest( "Preceding Axis Test", szQueryString, "Do a Preceding axis query", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /*********************** Twenty-fifth Query ********************************/ f_sprintf( szQueryString, "true() and ///chairman/president/name[.== \"Steve Masters\"]" "/meta::documentid[.==%u]", (unsigned)ui64DocumentId); pszQueryResult = "Steve Masters"; beginTest( "TRUE Value Defect Test #1", szQueryString, "Verify defect involving TRUE values has been fixed.", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /*********************** Twenty-sixth Query ********************************/ f_sprintf( szQueryString, "(///groupvp[@empID==\"emp9801\" and @empdate==\"1981-09-01\"]" "/director[@empID==\"emp2348\" or @empdate==\"1995-05-26\"]" "/name[.==\"Michelle Votava\" or .==\"John Tippett\"]==\"John Tippett\"" " and true()) or ///region[.==\"NorthWest\"]/preceding-sibling::*"); beginTest( "TRUE Value Defect Test #2", szQueryString, "Verify defect involving TRUE values has been fixed.", ""); if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pResult))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pResult->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } if( ui64Tmp != ui64DocumentId) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } endTest("PASS"); /*********************** Twenty-seventh Query ********************************/ f_sprintf( szQueryString, "name[.==\"Marea Angela Castaneda\"]/following::department[preceding-sibling::*]"); pszQueryResult = "Marketing"; beginTest( "Wildcard in predicate after step", szQueryString, "BUG - Verify that contexts are created properly.", ""); if ( RC_BAD( rc = doQueryTest( szQueryString, &pszQueryResult, 1, pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); Exit: if ( RC_BAD( rc)) { endTest("FAIL"); } if ( pPosIStream) { pPosIStream->Release(); } if ( pQuery) { pQuery->Release(); } if ( pResult) { pResult->Release(); } if ( pTempNode) { pTempNode->Release(); } if ( pAttr) { pAttr->Release(); } if ( bTransStarted) { m_pDb->transCommit(); } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPATHTest2Impl::runSuite2( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransStarted = FALSE; IF_PosIStream * pPosIStream = NULL; IF_DOMNode * pResult = NULL; IF_Query * pQuery = NULL; char szQueryString[256]; const char * pszAlbum = "" "7005c60b" "1480" "Johnny Cash / The singing storyteller" "cddb/country" "Goodbye little darling" "Give my love to Rose" "Hey good looking" "I can't help it" "I could never be ashamed of you" "I couldn't keep from crying" "I love you because" "The ways of a woman in love" "You're the nearest thing to heaven" "Come in, stranger" "Next in line" ""; beginTest( "XPATH Defect Tests Setup", "Set up test state for XPATH Query Resolved Defect Test Suite", "", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = importBuffer( pszAlbum, XFLM_DATA_COLLECTION))) { goto Exit; } m_pDb->transCommit(); bTransStarted = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "Failed to create query object.", m_szDetails, rc); goto Exit; } endTest("PASS"); /****************************Wildcard Defect Test***************************/ f_sprintf( szQueryString, "track[.==\"Yo*re the nearest thing*heaven\"]"); beginTest( "Wildcard Defect Test", szQueryString, "Verify wildcard defect has been fixed", ""); { const char * ppszResults[] = { "You're the nearest thing to heaven"}; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } /****************************Double Period Defect Test**********************/ f_sprintf( szQueryString, "track[.==\"Hey good looking\"]/../track[@index==\"6\"]"); beginTest( "Double Period Defect Test", szQueryString, "Verify double period defect has been fixed", ""); { const char * ppszResults[] = { "I couldn't keep from crying"}; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } #if 0 // The following queries still do not work /****************************Anonymous Parent Axis Defect Test*****************/ f_sprintf( szQueryString, "//track[parent::*]"); beginTest( "Anonymous parent axis defect test", szQueryString, "Verify anonymous parent axis defect has been fixed. " "Using the parent axis anonymously in a predicate " "will lead to asserts and access violations", ""); { const char * ppszResults[] = { "Goodbye little darling", "Give my love to Rose", "Hey good looking", "I can't help it", "I could never be ashamed of you", "I couldn't keep from crying", "I love you because", "The ways of a woman in love", "You're the nearest thing to heaven", "Come in, stranger", "Next in line" }; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } /****************************Self Axis Defect Test**************************/ f_sprintf( szQueryString, "track[self::track==\"Come in, stranger\"]"); beginTest( "Self Axis Defect Test", szQueryString, "Verify self axis defect has been fixed", ""); { const char * ppszResults[] = { "Come in, stranger"}; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } /****************************Subscript Defect Test***************************/ // The XPATH specification does allow for multiple predicates per node. There // are examples of this in section 2.5 of the XPATH specification. // Usually this is not a problem because an equivalent query can be // built by and-ing the various expressions together in a single predicate. // However, this will not work if one of those expressions is a position. // The following query should select the first track node in the document // if it has an index attribute with a value equal to 1 (which it does). f_sprintf( szQueryString, "track[1][@index==\"1\"]"); beginTest( "Multiple subscript Defect Test", szQueryString, "Verify multiple subscript defect has been fixed", ""); { const char * ppszResults[] = { "Goodbye little darling"}; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } /****************************Preceding wildcard Defect Test*****************/ f_sprintf( szQueryString, "descendant::track[preceding::*]"); beginTest( "Preceding Wildcard Defect Test", szQueryString, "Verify preceding wildcard defect has been fixed", ""); { const char * ppszResults[] = { "Goodbye little darling", "Give my love to Rose", "Hey good looking", "I can't help it", "I could never be ashamed of you", "I couldn't keep from crying", "I love you because", "The ways of a woman in love", "You're the nearest thing to heaven", "Come in, stranger", "Next in line" }; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } /****************Descendant or Self Wildcard Defect Test**********************/ f_sprintf( szQueryString, "//descendant-or-self::*[self::id!=\"gibberish\" or self::id==\"7005c60b\"]"); beginTest( "Descendant or Self Wildcard Defect Test", szQueryString, "Verify descendant or self wildcard defect has been fixed. " "The following query causes an assert in fquery.cpp", ""); { const char * ppszResults[] = { "7005c60b"}; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } /****************Descendant or Self Assert Defect Test**********************/ f_sprintf( szQueryString, "descendant-or-self::track[.==\"Hey good looking\" or " "preceding-sibling::track==\"Give my love to Rose\"]/@offset[preceding::id!=\"gibberish\"]"); beginTest( "Descendant or Self Wildcard Defect Test", szQueryString, "Verify descendant or self assert defect has been fixed. " "The following query causes an assert in fdom.cpp", ""); { const char * ppszResults[] = { "22712"}; if ( RC_BAD( rc = doQueryTest( szQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } #endif Exit: if ( pPosIStream) { pPosIStream->Release(); } if ( pQuery) { pQuery->Release(); } if ( pResult) { pResult->Release(); } if ( bTransStarted) { m_pDb->transCommit(); } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } #define IX_DICT_NUM 123 #define NUM_DOCS 5 /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPATHTest2Impl::runSuite3( void) { RCODE rc = NE_XFLM_OK; char szIndex[400]; IF_Query * pQuery = NULL; IF_DOMNode * pNode = NULL; FLMUINT64 ui64DocId = 0; IF_DOMNode * pDoc = NULL; FLMUINT uiLoop; FLMBOOL bTransActive = FALSE; FLMBOOL bDibCreated = FALSE; const char * pszIndexFormat = " " " " " " " " " "; const char * pszDoc = "" " " ""; beginTest( "Search/Delete Defect Test", "Search and delete documents to ensure bug that was causing " "btree to not be setup properly has been fixed", "Add some documents/index them/search using the index/" "occasionally delete a document while iterating", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransActive = TRUE; // create several documents that will be indexed for( uiLoop = 0; uiLoop < NUM_DOCS; uiLoop++) { if ( RC_BAD( rc = importBuffer( pszDoc, XFLM_DATA_COLLECTION))) { MAKE_FLM_ERROR_STRING( "importBuffer failed", m_szDetails, rc); goto Exit; } } f_sprintf( szIndex, pszIndexFormat, IX_DICT_NUM); // create an index if ( RC_BAD( rc = importBuffer( szIndex, XFLM_DICT_COLLECTION))) { MAKE_FLM_ERROR_STRING( "importBuffer failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } bTransActive = FALSE; // pose a query with the index if ( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "createIFQuery failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->setIndex( IX_DICT_NUM))) { MAKE_FLM_ERROR_STRING( "setIndex failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "bar"))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransActive = TRUE; // next, next for( uiLoop = 0; uiLoop < NUM_DOCS; uiLoop++) { if ( RC_BAD( rc = pQuery->getNext( m_pDb, &pNode))) { MAKE_FLM_ERROR_STRING( "getNext failed", m_szDetails, rc); goto Exit; } if ( ( uiLoop % 2) == 0) { if ( RC_BAD( rc = pNode->getDocumentId( m_pDb, &ui64DocId))) { MAKE_FLM_ERROR_STRING( "getDocumentId failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->getDocument( XFLM_DATA_COLLECTION, XFLM_EXACT, ui64DocId, &pDoc))) { MAKE_FLM_ERROR_STRING( "getDocument failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pDoc->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); goto Exit; } // insert a new doc if ( RC_BAD( rc = importBuffer( pszDoc, XFLM_DATA_COLLECTION))) { MAKE_FLM_ERROR_STRING( "importBuffer failed", m_szDetails, rc); goto Exit; } } } endTest("PASS"); Exit: if (RC_BAD( rc)) { endTest("FAIL"); } if ( pQuery) { pQuery->Release(); } if ( pNode) { pNode->Release(); } if ( pDoc) { pDoc->Release(); } if ( bTransActive) { if (RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPATHTest2Impl::runSuite4( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bTransActive = FALSE; char szQueryString[100]; IF_Query * pQuery = NULL; IF_DOMNode * pDoc = NULL; char * pszResult = NULL; FLMUINT uiResultAttr = 0; FLMBOOL bFoundDesiredDoc = FALSE; const char * pszDoc1 = "" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" ""; const char * pszDoc2 = "" " Right" " Right" " Right" " Right" ""; const char * pszDoc3 = "" " Right" " Right" " Right" " Right" " Wrong" " Wrong" " Wrong" " Wrong" " Wrong" ""; FLMBOOL bDibCreated = FALSE; f_sprintf( szQueryString, "!(/foo/bar[@baz==1]) && (/foo/bar[@baz==42])"); beginTest( "Notted Optimization Defect Test", szQueryString, "Verify a defect that was causing an improper optimization " "with notted nodes has been fixed", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransActive = TRUE; // make sure baz attribute is imported as a number if ( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "baz", XFLM_NUMBER_TYPE, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "createAttributeDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "result", XFLM_TEXT_TYPE, &uiResultAttr, NULL))) { MAKE_FLM_ERROR_STRING( "createAttributeDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = importBuffer( pszDoc1, XFLM_DATA_COLLECTION))) { MAKE_FLM_ERROR_STRING( "importBuffer failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = importBuffer( pszDoc2, XFLM_DATA_COLLECTION))) { MAKE_FLM_ERROR_STRING( "importBuffer failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = importBuffer( pszDoc3, XFLM_DATA_COLLECTION))) { MAKE_FLM_ERROR_STRING( "importBuffer failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "createIFQuery failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, szQueryString))) { MAKE_FLM_ERROR_STRING( "setQueryExpr failed", m_szDetails, rc); goto Exit; } while ( RC_OK( rc = pQuery->getNext( m_pDb, &pDoc))) { if ( pszResult) { f_free( &pszResult); } if ( RC_BAD( rc = pDoc->getAttributeValueUTF8( m_pDb, uiResultAttr, (FLMBYTE **)&pszResult))) { MAKE_FLM_ERROR_STRING( "getAttributeValueUTF8 failed", m_szDetails, rc); goto Exit; } if ( f_strcmp( pszResult, "pass") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "unexpected document returned", m_szDetails, rc); goto Exit; } bFoundDesiredDoc = TRUE; } if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { goto Exit; } if ( !bFoundDesiredDoc) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "expected document returned", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if ( pQuery) { pQuery->Release(); } if ( pDoc) { pDoc->Release(); } if ( pszResult) { f_free( &pszResult); } if (RC_BAD( rc)) { endTest("FAIL"); } if ( bTransActive) { if (RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPATHTest2Impl::runSuite5( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransActive = FALSE; IF_Query * pQuery = NULL; const char * pszQueryString = NULL; FLMBOOL bIndexAdded = FALSE; char szTestName[ 100]; const char * pszDoc = "" " abc*" " abcdef" " abc\\" " abcd" ""; const char * pszIndex = " " " " " "; if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc= m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "createIFQuery failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransActive = TRUE; if (RC_BAD ( rc = importBuffer( pszDoc, XFLM_DATA_COLLECTION))) { goto Exit; } Run_Again: pszQueryString = "///Text[.==\"abc\\*\"]"; f_sprintf( szTestName, "Escaped wildcard test #1 (indexed=%u)", bIndexAdded); beginTest( szTestName, pszQueryString, "Verify an escaped wildcard only matches a literal asterisk" "Self-explanatory", ""); { const char * ppszResults[] = { "abc*"}; if ( RC_BAD( rc = doQueryTest( pszQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } pszQueryString = "///Text[.==\"abc*\"]"; f_sprintf( szTestName, "Escaped wildcard test #2 (indexed=%u)", bIndexAdded); beginTest( szTestName, pszQueryString, "Verify an non-escaped wildcard matches all" "Self-explanatory", ""); { const char * ppszResults[] = { "abc*", "abcdef", "abc\\", "abcd" }; if ( RC_BAD( rc = doQueryTest( pszQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } pszQueryString = "///Text[.==\"abc\\\\\"]"; f_sprintf( szTestName, "Escaped backslash test #1 (indexed=%u)", bIndexAdded); beginTest( szTestName, pszQueryString, "Verify an escaped backslash only matches a backslash" "Self-explanatory", ""); { const char * ppszResults[] = { "abc\\" }; if ( RC_BAD( rc = doQueryTest( pszQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } pszQueryString = "///Text[.==\"abc\\d\"]"; f_sprintf( szTestName, "Escaped backslash test #2 (indexed=%u)", bIndexAdded); beginTest( szTestName, pszQueryString, "Verify a non-escaped backslash is treated like an escape char" "Self-explanatory", ""); { const char * ppszResults[] = { "abcd" }; if ( RC_BAD( rc = doQueryTest( pszQueryString, ppszResults, sizeof( ppszResults) / sizeof( ppszResults[0]), pQuery, m_szDetails))) { endTest("FAIL"); } else { endTest("PASS"); } } if ( !bIndexAdded) { if ( RC_BAD( rc = importBuffer( pszIndex, XFLM_DICT_COLLECTION))) { goto Exit; } bIndexAdded = TRUE; goto Run_Again; } Exit: if ( pQuery) { pQuery->Release(); } if ( bTransActive) { if (RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } libxflaim-5.1.969/util/dirtyexittest2srv.cpp0000644000175000017500000000720310511001742022462 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Dirty exit test 2 // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: dirtyexittest2srv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\BAD.DB" #define REBUILD_DEST_NAME_STR "SYS:\\BLD.DB" #else #define DB_NAME_STR "bad.db" #define REBUILD_DEST_NAME_STR "bld.db" #endif /***************************************************************************** Desc: ******************************************************************************/ class IDirtyExitTest2Impl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /***************************************************************************** Desc: ******************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IDirtyExitTest2Impl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /***************************************************************************** Desc: ******************************************************************************/ const char * IDirtyExitTest2Impl::getName( void) { return( "Dirty Exit Test 2"); } /***************************************************************************** Desc: ******************************************************************************/ RCODE IDirtyExitTest2Impl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMUINT64 ui64TotalNodes; FLMUINT64 ui64NodesRecovered; // Open the test state created by Dirty Exit Test 1. if ( RC_BAD( rc = openTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; beginTest( "Database check test", "Make sure dib was not corrupted by dirty shutdown", "Self explanatory", "No Additional Details."); // Make sure the database is still consistent if( RC_BAD( rc = m_pDbSystem->dbCheck( DB_NAME_STR, NULL, NULL, NULL, XFLM_DO_LOGICAL_CHECK, NULL, NULL))) { MAKE_FLM_ERROR_STRING("dbCheck failed", m_szDetails, rc); goto Exit; } endTest("PASS"); beginTest( "Database rebuild test", "Make sure the rebuild code works", "Self explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbRebuild( DB_NAME_STR, NULL, REBUILD_DEST_NAME_STR, NULL, NULL, NULL, NULL, NULL, &ui64TotalNodes, &ui64NodesRecovered, NULL, NULL))) { MAKE_FLM_ERROR_STRING("dbRebuild failed", m_szDetails, rc); goto Exit; } if( ui64TotalNodes != ui64NodesRecovered) { MAKE_FLM_ERROR_STRING("dbRebuild failed", m_szDetails, NE_XFLM_FAILURE); goto Exit; } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/importtestsrv.cpp0000644000175000017500000002146110511001742021667 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Import unit test // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: importtestsrv.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\IMP.DB" #else #define DB_NAME_STR "imp.db" #endif /**************************************************************************** Desc: ****************************************************************************/ class IImportTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IImportTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IImportTestImpl::getName( void) { return( "Import Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IImportTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransStarted = FALSE; IF_DirHdl * pDirHdl = NULL; IF_FileSystem * pFileSystem = NULL; FLMUINT uiCharCount = 0; char szTemp[ 64]; const char * pszDoc1 = "" " " "00054613 " "1352 " "Margret Birkenfeld / Zachaus " "cddb/misc " " ID3G: 77 " "Wie erzahlen euch 1. Srophe " "Wir erzahlen Euch 2. Strophe " "Zachaus ist ein reicher Mann 1+2 Str " "Jericho " "Haruck, schnauf schnauf 1+2 Strophe " "Haruck, schnauf schnauf 3 Strophe " "Zachaus ist ein reicher Mann 3. Stophe " "Zachaus komm herunter! " "Wir erzahlen euch " "Leer ab jetzt Playback " "Wie erzahlen euch 1. Srophe Pb " "Wir erzahlen Euch 2. Strophe Pb " "Zachaus ist ein reicher Mann 1+2 Str Pb " "Jericho Pb " "Haruck, schnauf schnauf 1+2 Strophe Pb " "Haruck, schnauf schnauf 3 Strophe Pb " "Zachaus ist ein reicher Mann 3. Stophe Pb " "Zachaus komm herunter! Pb " "Wir erzahlen euch Pb " " "; const char * pszDoc2 = " " " " "0008a40f " "2214 " "rundu... - Visur Ur Vinsabokinni " "cddb/misc " "Blessuo Solin Elskar Allt - Ur Augum Stirur Strjukio Fljott " "Heioloarkvaeoi " "Buxur, Vesti, Brok og Sko " "Gekk Eg Upp A Holinn" "Nu Blanar Yfir Berjamo - A Berjamo " "Orninn Flygur Fugla Haest - Solskrikjan - Min " "Nu Er Glatt I Borg Og Bae " "Smaladrengurinn - Klappa Saman Lofunum " "Stigur Hun Vio Stokkinn " "Dansi, Dansi, Dukkan Min " "Rioum Heim Til Hola - Gott Er Ao Rioa Sandana Mjuka " "Gryla - Jolasveinar Ganga Um Golf " "Erla, Gooa Erla " "Vio Skulum Ekki Hafa Hatt " "Sofa Urtu Born " " "; const char * pszIndexDef1 = " " " " " " " " " " " " " "; f_strcpy( m_szDetails, "No additional details."); beginTest( "Import Test Init", "Perform necessary initializations to carry out the import tests.", "(1) Get an F_DbSystem (2) Create a database (3) Start an " "update transaction.", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to init test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "Document Buffer Import Test", "Import some documents from in-memory XML data", "Self-Explanatory", ""); if( RC_BAD( rc = importBuffer( pszDoc1, XFLM_DATA_COLLECTION))) { goto Exit; } if( RC_BAD( rc = importBuffer( pszDoc2, XFLM_DATA_COLLECTION))) { goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "Document File Import Test", "Import some documents from XML data files", "Import all .xml files in the current directory.", ""); m_pDbSystem->getFileSystem( &pFileSystem); if( RC_BAD( rc = pFileSystem->openDir( ".", ".xml", &pDirHdl))) { MAKE_FLM_ERROR_STRING( "OpenDir failed.", m_szDetails, rc); goto Exit; } m_szDetails[ 0] = '\0'; for (;;) { if( RC_BAD( rc = pDirHdl->next())) { if ( rc == NE_XFLM_IO_NO_MORE_FILES) { rc = NE_XFLM_OK; break; } MAKE_FLM_ERROR_STRING("F_DirHdl::Next failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = importFile( pDirHdl->currentItemName(), XFLM_DATA_COLLECTION))) { goto Exit; } f_sprintf( szTemp, "Imported: %s. ", pDirHdl->currentItemName()); uiCharCount += f_strlen( szTemp); if( uiCharCount < DETAILS_BUF_SIZ) { f_strcat( m_szDetails, szTemp); } } endTest("PASS"); beginTest( "Index Definition Import Test", "Import an index definition.", "Self-explanatory", "No Additional Info"); if( RC_BAD( rc = importBuffer( pszIndexDef1, XFLM_DICT_COLLECTION))) { goto Exit; } endTest("PASS"); Exit: if( pDirHdl) { pDirHdl->Release(); } if( pFileSystem) { pFileSystem->Release(); } if( RC_BAD( rc)) { endTest("FAIL"); } if( bTransStarted) { m_pDb->transCommit(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/metaphonetestsrv.cpp0000644000175000017500000003371710511001742022344 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Metaphone unit test // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: metaphonetestsrv.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif struct WordPair { const char * pszWord1; const char * pszWord2; }; /**************************************************************************** Desc: ****************************************************************************/ class IMetaphoneTestImpl : public TestBase { public: const char * getName( void); RCODE suite1( void); RCODE suite2( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IMetaphoneTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IMetaphoneTestImpl::getName( void) { return( "MetaPhone Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IMetaphoneTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; if ( RC_BAD( rc = suite1())) { goto Exit; } if ( RC_BAD( rc = suite2())) { goto Exit; } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IMetaphoneTestImpl::suite1( void) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pRoot = NULL; IF_DOMNode * pChild = NULL; FLMBOOL bTransStarted = FALSE; FLMBOOL bDibCreated = FALSE; FLMUINT uiLoop = 0; FLMUINT uiWordDictNum = 0; IF_PosIStream * pMetaphoneIStream = NULL; IF_DataVector * pSearchKey = NULL; IF_DataVector * pFoundKey = NULL; FLMUINT uiMetaphone1 = 0; FLMUINT uiMetaphone2 = 0; FLMUINT uiIxMetaVal; const char * pszIndexDef = " " // HARD-CODED Dict Num for easy retrieval " " " "; WordPair commonMisspellings[] = { //correct,incorrect {"night","nite"}, {"anoint","annoint"}, {"coolly","cooly"}, {"supercede","supersede"}, {"irresistible","irresistable"}, {"development","developement"}, {"separate","seperate"}, {"tyranny ","tyrrany"}, {"harass", "harrass"}, {"desiccate", "dessicate"}, {"indispensable", "indispensible"}, {"receive","recieve"}, {"pursue", "persue"}, {"recommend","reccomend"}, {"desperate","desparate"}, {"liquefy","liquify"}, {"seize", "sieze"}, {"cemetery","cemetary"}, {"subpoena", "subpena"}, {"definitely", "definately"}, {"occasion","ocassion"}, {"consensus", "concensus"}, {"inadvertent","inadvertant"}, {"miniscule","minuscule"}, {"judgment","judgement"}, {"inoculate","innoculate"}, {"drunkenness","drunkeness"}, {"occurrence","occurence"}, {"dissipate","disippate"}, {"weird","wierd"}, {"accommodate","accomodate"}, {"embarrassment","embarassment"}, {"ecstasy","ecstacy"}, {"repetition","repitition"}, {"batallion","battalion"}, {"despair","dispair"}, {"irritable","irritible"}, {"accidentally","accidently"}, {"liaison","liason"}, {"memento","momento "}, {"broccoli","brocolli"}, {"millennium","millenium"}, {"yield","yeild"}, {"existence","existance"}, {"independent","independant"}, {"sacrilegious","sacreligious"}, {"insistent","insistant"}, {"excee","excede"}, {"privilege","priviledge"}, }; FLMUINT uiNumWords = sizeof( commonMisspellings)/sizeof( commonMisspellings[0]); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to init test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "Word", XFLM_TEXT_TYPE, &uiWordDictNum))) { MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = importBuffer( pszIndexDef, XFLM_DICT_COLLECTION))) { goto Exit; } beginTest( "Metaphone Index Key Creation Test", "Make sure mispelled words can be used to retrieve index keys " "created for correctly-spelled words.", "1) Create nodes with correctly-spelled words " "2) search for the index keys using mispelled versions of those words ", ""); //Create a node with a word attribute with a value of a correctly-spelled word if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, ELM_ELEMENT_TAG, &pRoot))) { MAKE_FLM_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } for( uiLoop = 0; uiLoop < uiNumWords; uiLoop++) { if( RC_BAD( rc = pRoot->createNode( m_pDb, ELEMENT_NODE, uiWordDictNum, XFLM_FIRST_CHILD, &pChild))) { MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pChild->setUTF8( m_pDb, (FLMBYTE *)commonMisspellings[uiLoop].pszWord1))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } } // Now, we should be able to locate every misspelled word in our list if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pFoundKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed.", m_szDetails, rc); goto Exit; } f_strcpy( m_szDetails, "Words: "); for ( uiLoop = 0; uiLoop < uiNumWords; uiLoop++) { if ( RC_BAD( rc = m_pDbSystem->openBufferIStream( commonMisspellings[uiLoop].pszWord1, f_strlen( commonMisspellings[uiLoop].pszWord1), &pMetaphoneIStream))) { MAKE_FLM_ERROR_STRING( "openBufferIStream failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDbSystem->getNextMetaphone( pMetaphoneIStream, &uiMetaphone1))) { MAKE_FLM_ERROR_STRING( "getNextMetaphone failed.", m_szDetails, rc); goto Exit; } pMetaphoneIStream->Release(); pMetaphoneIStream = NULL; if ( RC_BAD( rc = m_pDbSystem->openBufferIStream( commonMisspellings[uiLoop].pszWord2, f_strlen( commonMisspellings[uiLoop].pszWord2), &pMetaphoneIStream))) { MAKE_FLM_ERROR_STRING( "openBufferIStream failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDbSystem->getNextMetaphone( pMetaphoneIStream, &uiMetaphone2))) { MAKE_FLM_ERROR_STRING( "getNextMetaphone failed.", m_szDetails, rc); goto Exit; } pMetaphoneIStream->Release(); pMetaphoneIStream = NULL; // No sense in testing the index if the metaphone algorithm itself yields // different codes for these two words. if ( uiMetaphone1 == uiMetaphone2) { if ( (sizeof( m_szDetails) - f_strlen( m_szDetails)) > (f_strlen( commonMisspellings[uiLoop].pszWord1) + f_strlen( " vs. ") + f_strlen( commonMisspellings[uiLoop].pszWord2) + f_strlen( " "))) { f_strcat( m_szDetails, commonMisspellings[uiLoop].pszWord1); f_strcat( m_szDetails, " vs. "); f_strcat( m_szDetails, commonMisspellings[uiLoop].pszWord2); f_strcat( m_szDetails, " "); } if ( RC_BAD( rc = pSearchKey->setUTF8( 0, (FLMBYTE *)commonMisspellings[uiLoop].pszWord2))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->keyRetrieve( 77, pSearchKey, XFLM_EXACT, pFoundKey))) { char szTemp[128]; f_sprintf( szTemp, "\n\"%s\" indexed but cannot find \"%s\"!", commonMisspellings[uiLoop].pszWord1, commonMisspellings[uiLoop].pszWord2); MAKE_FLM_ERROR_STRING( "keyRetrieve failed. ", m_szDetails, rc); f_strcpy( m_szDetails, szTemp); goto Exit; } if ( RC_BAD( rc = pFoundKey->getUINT( 0, &uiIxMetaVal))) { MAKE_FLM_ERROR_STRING( "getUINT failed. ", m_szDetails, rc); goto Exit; } if ( uiIxMetaVal != uiMetaphone1) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Unexpected metaphone code in index", m_szDetails, rc); goto Exit; } } } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } if( pMetaphoneIStream) { pMetaphoneIStream->Release(); } if( pSearchKey) { pSearchKey->Release(); } if( pFoundKey) { pFoundKey->Release(); } if( pRoot) { pRoot->Release(); } if( pChild) { pChild->Release(); } if( bTransStarted) { m_pDb->transCommit(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IMetaphoneTestImpl::suite2( void) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pRoot = NULL; IF_DOMNode * pChild = NULL; FLMBOOL bTransStarted = FALSE; FLMBOOL bDibCreated = FALSE; FLMUINT uiWordDictNum = 0; IF_DataVector * pSearchKey = NULL; FLMUINT64 pui64DocIds[4]; FLMUINT uiLoop; FLMUINT uiLoop2; const char * pszIndexDef = " " // HARD-CODED Dict Num for easy retrieval " " " xflaim:Limit=\"102\" /> " " "; if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to init test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "Word", XFLM_TEXT_TYPE, &uiWordDictNum))) { MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } beginTest( "Metaphone Missing Index Key Creation Test", "Ensure index keys created properly if node has no value.", "Self-explanatory", ""); for( uiLoop = 0; uiLoop < sizeof(pui64DocIds)/sizeof(pui64DocIds[0]); uiLoop++) { //Create a node with a word attribute with a value of a correctly-spelled word if ( RC_BAD( rc = m_pDb->createRootElement(XFLM_DATA_COLLECTION, ELM_ELEMENT_TAG, &pRoot))) { MAKE_FLM_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pRoot->getNodeId( m_pDb, &pui64DocIds[ uiLoop]))) { goto Exit; } if( RC_BAD( rc = pRoot->createNode( m_pDb, ELEMENT_NODE, uiWordDictNum, XFLM_FIRST_CHILD, &pChild))) { MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } } if ( RC_BAD( rc = importBuffer( pszIndexDef, XFLM_DICT_COLLECTION))) { goto Exit; } // Now, we should be able to locate every misspelled word in our list if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed.", m_szDetails, rc); goto Exit; } for( uiLoop = 0; uiLoop < sizeof(pui64DocIds)/sizeof(pui64DocIds[0]); uiLoop++) { if( uiLoop == 0) { if ( RC_BAD( rc = m_pDb->keyRetrieve( 77, NULL, XFLM_FIRST, pSearchKey))) { MAKE_FLM_ERROR_STRING( "keyRetrieve failed.", m_szDetails, rc); goto Exit; } } else { rc = m_pDb->keyRetrieve( 77, pSearchKey, XFLM_EXCL | XFLM_MATCH_DOC_ID, pSearchKey); } for( uiLoop2 = 0; uiLoop2 < sizeof(pui64DocIds)/sizeof(pui64DocIds[0]); uiLoop2++) { if ( pui64DocIds[uiLoop2] == pSearchKey->getDocumentID()) { // "tag" the document ID once we've found it pui64DocIds[uiLoop2] = 0; break; } } } // Make sure we got all the document ids for( uiLoop2 = 0; uiLoop2 < sizeof(pui64DocIds)/sizeof(pui64DocIds[0]); uiLoop2++) { if ( pui64DocIds[uiLoop2] != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Key iteration failed", m_szDetails, rc); goto Exit; } } rc = m_pDb->keyRetrieve( 77, pSearchKey, XFLM_EXCL | XFLM_MATCH_DOC_ID, pSearchKey); if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "Too many keys", m_szDetails, rc); rc = RC_SET( NE_XFLM_DATA_ERROR); goto Exit; } endTest("PASS"); Exit: if ( RC_BAD( rc)) { endTest("FAIL"); } if ( pSearchKey) { pSearchKey->Release(); } if( pRoot) { pRoot->Release(); } if( pChild) { pChild->Release(); } if ( bTransStarted) { m_pDb->transCommit(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/dbdiff.cpp0000644000175000017500000004450610511001742020145 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Utility to compare two databases for equivalence. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: dbdiff.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "sharutil.h" #include "dbdiff.h" /**************************************************************************** Name: compareNodes Desc: method to compare two F_DOMNodes. ****************************************************************************/ RCODE F_DbDiff::compareNodes( FLMBYTE * pszCompareInfo, //info about the records for output F_DOMNode * pNode1, F_DOMNode * pNode2, DBDIFF_CALLBACK outputCallback, void * pvData) { RCODE rc = NE_XFLM_OK; FlmStringAcc acc; char szErrBuff[ 100]; if (pNode1->compareNode( pNode2, m_pDb1, m_pDb2, &szErrBuff[0], sizeof( szErrBuff)) != 0) { acc.printf( "ERROR: {%s} %s\n", pszCompareInfo, szErrBuff); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } Exit: return rc; } /**************************************************************************** Name: compareCollections Desc: compare two databases' Collection ****************************************************************************/ RCODE F_DbDiff::compareCollections( FLMUINT uiCollection, DBDIFF_CALLBACK outputCallback, void * pvData) { FLMUINT64 ui64NodeId1 = 0; FLMUINT64 ui64NodeId2 = 0; F_DOMNode * pNode1 = NULL; F_DOMNode * pNode2 = NULL; FLMBOOL bScanFinished1 = FALSE; FLMBOOL bScanFinished2 = FALSE; FlmStringAcc acc; RCODE rc = NE_XFLM_OK; if ( uiCollection == XFLM_MAINT_COLLECTION) { // Ignore any differences in this collection goto Exit; } while ( (!bScanFinished1) && (!bScanFinished2)) { if ( RC_BAD( rc = m_pDb1->getNextNode( uiCollection, ui64NodeId1, &pNode1))) { if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; bScanFinished1 = TRUE; } else { goto Exit; } } else { if( RC_BAD( rc = pNode1->getNodeId( m_pDb1, &ui64NodeId1))) { goto Exit; } } if ( RC_BAD( rc = m_pDb2->getNextNode( uiCollection, ui64NodeId2, &pNode2))) { if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; bScanFinished2 = TRUE; } else { goto Exit; } } else { if( RC_BAD( rc = pNode1->getNodeId( m_pDb2, &ui64NodeId2))) { goto Exit; } } if ( bScanFinished1 && bScanFinished2) { break; } //if one of them is finished and not the other one if (bScanFinished1 != bScanFinished2) { FlmStringAcc acc; acc.appendf( "ERROR: database1 and database2 have a different # of nodes in " "Collection %u\n", uiCollection); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } //if the nodeId's are different, then there's a problem if ( ui64NodeId1 != ui64NodeId2) { FlmStringAcc acc; acc.appendf( "ERROR: database1's nodeId %u ", ui64NodeId1); acc.appendf( " != database2's nodeId %u!\n", ui64NodeId2); acc.appendf( "ERROR: nodeId mismatch in Collection %u\n", uiCollection); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if ( !bScanFinished1) //bScanFinished1==bScanFinished2 at this point { acc.printf( "NodeId %u of Collection %u", ui64NodeId1, uiCollection); //compare the two records for accuracy if ( RC_BAD( rc = compareNodes( (FLMBYTE *)acc.getTEXT(), pNode1, pNode2, outputCallback, pvData))) { goto Exit; } } #ifdef FLM_NLM f_yieldCPU(); #else f_sleep( 0); #endif } Exit: if ( pNode1) { pNode1->Release(); } if ( pNode2) { pNode2->Release(); } return rc; } /**************************************************************************** Name: flmDbDiff Desc: compare two databases for logical equivalence. ****************************************************************************/ RCODE F_DbDiff::diff( char * pszDb1, char * pszDb1Password, char * pszDb2, char * pszDb2Password, DBDIFF_CALLBACK outputCallback, void * pvData) { RCODE rc = NE_XFLM_OK; RCODE tmpRc = NE_XFLM_OK; FLMBOOL bTransActive1 = FALSE; FLMBOOL bTransActive2 = FALSE; F_DOMNode * pNode1 = NULL; F_DOMNode * pNode2 = NULL; FlmVector CollectionVec1; FlmVector CollectionVec2; FLMUINT uiCollectionLen1 = 0; FLMUINT uiCollectionLen2 = 0; FLMUINT uiIndexLen1 = 0; FLMUINT uiIndexLen2 = 0; FLMUINT uiLoop; F_COLLECTION * pCollection1; F_COLLECTION * pCollection2; F_Dict * pDict1; F_Dict * pDict2; FLMUINT uiCollectionNum; FLMUINT uiIndexNum; IXD * pIxd; FlmStringAcc acc; FlmVector IndexVec1; FlmVector IndexVec2; //try to unlock any files currently open if ( RC_BAD( rc = dbSystem.closeUnusedFiles(0))) { goto Exit; } //can't compare a db with itself if ( f_strcmp( pszDb1, pszDb2) == 0) { outputCallback( "ERROR: cannot compare a dib with itself!\n", pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } //open up the database, and all that it entails if( RC_BAD( rc = dbSystem.openDb( pszDb1, NULL, NULL, pszDb1Password, XFLM_DONT_RESUME_THREADS, (IF_Db **)&m_pDb1))) { acc.printf( "ERROR: cannot open dib '"); acc.appendTEXT( pszDb1); acc.appendTEXT( "'!\n"); outputCallback((char*)acc.getTEXT(), pvData); goto Exit; } if( RC_BAD( rc = dbSystem.openDb( pszDb2, NULL, NULL, pszDb2Password, XFLM_DONT_RESUME_THREADS, (IF_Db **)&m_pDb2))) { acc.printf( "ERROR: cannot open dib '"); acc.appendTEXT( pszDb2); acc.appendTEXT( "'!\n"); outputCallback((char*)acc.getTEXT(), pvData); goto Exit; } if( RC_BAD( rc = m_pDb1->transBegin( XFLM_READ_TRANS))) { outputCallback( "ERROR: cannot start trans #1\n", pvData); goto Exit; } bTransActive1 = TRUE; if( RC_BAD( rc = m_pDb2->transBegin( XFLM_READ_TRANS))) { outputCallback( "ERROR: cannot start trans #2\n", pvData); goto Exit; } bTransActive2 = TRUE; if (RC_BAD( rc = m_pDb1->getDictionary( &pDict1))) { outputCallback( "ERROR: retrieving dictionary #1\n", pvData); goto Exit; } if (RC_BAD( rc = m_pDb2->getDictionary( &pDict2))) { outputCallback( "ERROR: retrieving dictionary #2\n", pvData); goto Exit; } //prepare to read Collections from the dictionary if (RC_BAD( rc = pDict1->getCollection( XFLM_DICT_COLLECTION, &pCollection1))) { outputCallback( "ERROR: retrieving dictionary collection #1\n", pvData); goto Exit; } if (RC_BAD( rc = pDict2->getCollection( XFLM_DICT_COLLECTION, &pCollection2))) { outputCallback( "ERROR: retrieving dictionary collection #2\n", pvData); goto Exit; } outputCallback( "Starting dbdiff with databases '", pvData); outputCallback( (char*)pszDb1, pvData); outputCallback( "' and '", pvData); outputCallback( (char*)pszDb2, pvData); outputCallback( "'\n", pvData); outputCallback( "Building index and Collection lists...", pvData); //build up a list of each Collection from the first database for (uiCollectionNum = 0;;) { if ((pCollection1 = pDict1->getNextCollection( uiCollectionNum, TRUE)) == NULL) { break; } uiCollectionNum = pCollection1->lfInfo.uiLfNum; if ( RC_BAD( rc = CollectionVec1.setElementAt( (void*)uiCollectionNum, uiCollectionLen1++))) { goto Exit; } } //build up a list of each Collection from the second database for (uiCollectionNum = 0;;) { if ((pCollection2 = pDict2->getNextCollection( uiCollectionNum, TRUE)) == NULL) { break; } uiCollectionNum = pCollection2->lfInfo.uiLfNum; if ( RC_BAD( rc = CollectionVec2.setElementAt( (void*)uiCollectionNum, uiCollectionLen2++))) { goto Exit; } } //must have same # of Collections to be the same if ( uiCollectionLen1 != uiCollectionLen2) { acc.printf( "ERROR: the two databases have a different number of Collections(" "%u,%u)!\n", uiCollectionLen1, uiCollectionLen2); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } //must have same # of indexes to be the same //build up a list of each Collection from the first database for (uiIndexNum = 0;;) { if ((pIxd = pDict1->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; IndexVec1.setElementAt( (void*)uiIndexNum, uiIndexLen1++); } for (uiIndexNum = 0;;) { if ((pIxd = pDict2->getNextIndex( uiIndexNum, TRUE)) == NULL) { break; } uiIndexNum = pIxd->uiIndexNum; IndexVec2.setElementAt( (void*)uiIndexNum, uiIndexLen2++); } if ( uiIndexLen1 != uiIndexLen2) { acc.printf( "ERROR: the two databases have a different number of indexes(" "%u,%u)!\n", uiIndexLen1, uiIndexLen2); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } outputCallback( "done\n", pvData); outputCallback( "comparing collections\n", pvData); //loop through each Collection and compare the two for ( uiLoop = 0; uiLoop < uiCollectionLen1; uiLoop++) { FLMUINT uiCollection1 = (FLMUINT)CollectionVec1.getElementAt( uiLoop); FLMUINT uiCollection2 = (FLMUINT)CollectionVec2.getElementAt( uiLoop); if ( uiCollection1 != uiCollection2) { acc.printf( "ERROR: database1's Collection (%u) != " "database2's Collection (%u)!\n", uiCollection1, uiCollection2); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } else { acc.printf( "(%4u of %4u) processing Collection #%u\n", uiLoop+1, uiCollectionLen1, uiCollection1); outputCallback( (char*)acc.getTEXT(), pvData); if ( RC_BAD( rc = compareCollections( uiCollection1, //uiCollection1==uiCollection2 at this point outputCallback, pvData))) { goto Exit; } } } // Compare index keys outputCallback( "done\n", pvData); outputCallback( "comparing indexes\n", pvData); for( uiLoop = 0; uiLoop < uiIndexLen1; uiLoop++) { uiIndexNum = (FLMUINT)IndexVec1.getElementAt( uiLoop); if ( uiIndexNum != (FLMUINT)IndexVec2.getElementAt( uiLoop)) { acc.printf( "ERROR: database1's Index (%u) != " "database2's Index (%u)!\n", uiIndexNum, IndexVec2.getElementAt( uiLoop)); outputCallback( (char*)acc.getTEXT(), pvData); rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } acc.printf( "(%5u of %5u) processing Index #%u\n", uiLoop + 1, uiIndexLen1, uiIndexNum); outputCallback( acc.getTEXT(), pvData); if ( RC_BAD( rc = compareIndexes( uiIndexNum, outputCallback, pvData))) { goto Exit; } } Exit: if (pNode1) { pNode1->Release(); } if (pNode2) { pNode2->Release(); } if ( bTransActive1) { if ( RC_OK( rc)) { rc = m_pDb1->transCommit(); } else { (void)m_pDb1->transAbort(); } } if ( bTransActive2) { if ( RC_OK( rc)) { rc = m_pDb2->transCommit(); } else { (void)m_pDb2->transAbort(); } } //close any files currently open. This is so any smi open can be //done following this in the same process. tmpRc = dbSystem.closeUnusedFiles(0); if ( RC_OK( rc)) { rc = tmpRc; } return rc; } RCODE F_DbDiff::compareIndexes( FLMUINT uiIndexNum, DBDIFF_CALLBACK outputCallback, void * pvData) { RCODE rc = NE_XFLM_OK; F_DataVector searchKey1; F_DataVector searchKey2; FLMBOOL bDataComp; FLMBOOL bKeyComp; FLMUINT uiLoop; FLMUINT uiLoop2; FLMUINT uiDataType; FLMUINT uiDataLen1; FLMUINT uiDataLen2; FLMBYTE * pucVal1 = NULL; FLMBYTE * pucVal2 = NULL; FLMUINT64 ui64Val1 = 0; FLMUINT64 ui64Val2 = 0; for( uiLoop = 0;; uiLoop++) { if ( uiLoop == 0) { if (RC_BAD( rc = m_pDb1->keyRetrieve( uiIndexNum, NULL, XFLM_FIRST, &searchKey1))) { if ( rc == NE_XFLM_EOF_HIT) { // empty index. Make sure the other index is empty too. if (( rc = m_pDb2->keyRetrieve( uiIndexNum, NULL, XFLM_FIRST, &searchKey2)) != NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"key mismatch found.", pvData); goto Exit; } } rc = NE_XFLM_OK; goto Exit; } if (RC_BAD( rc = m_pDb2->keyRetrieve( uiIndexNum, NULL, XFLM_FIRST, &searchKey2))) { outputCallback( (char*)"keyRetrieve failed.", pvData); goto Exit; } if ( searchKey1.getDocumentID() != searchKey2.getDocumentID()) { // Error rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"index document id mismatch.", pvData); goto Exit; } } else { if (RC_BAD( rc = m_pDb1->keyRetrieve( uiIndexNum, &searchKey1, XFLM_EXCL, &searchKey1))) { if ( rc == NE_XFLM_EOF_HIT) { // No more keys. Make sure the other index is at the end too. if (( rc = m_pDb2->keyRetrieve( uiIndexNum, &searchKey2, XFLM_EXCL, &searchKey2)) != NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"key mismatch found.", pvData); goto Exit; } } rc = NE_XFLM_OK; goto Exit; } if (RC_BAD( rc = m_pDb2->keyRetrieve( uiIndexNum, &searchKey2, XFLM_EXCL, &searchKey2))) { outputCallback( (char*)"keyRetrieve failed.", pvData); goto Exit; } } for( uiLoop2 = 0;; uiLoop2++) { if ( ( bDataComp = searchKey1.isDataComponent(uiLoop2)) != searchKey2.isDataComponent(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isDataComponent mismatch.", pvData); goto Exit; } if ( ( bKeyComp = searchKey1.isKeyComponent(uiLoop2)) != searchKey2.isKeyComponent(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isKeyComponent mismatch.", pvData); goto Exit; } if ( !bDataComp && !bKeyComp) { // No more components to check break; } if ( searchKey1.isRightTruncated( uiLoop2) != searchKey2.isRightTruncated( uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isRightTruncated mismatch.", pvData); goto Exit; } if ( searchKey1.isLeftTruncated( uiLoop2) != searchKey2.isLeftTruncated( uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isLeftTruncated mismatch.", pvData); goto Exit; } if ( searchKey1.getID(uiLoop2) != searchKey2.getID(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"node id mismatch.", pvData); goto Exit; } if ( searchKey1.getNameId(uiLoop2) != searchKey2.getNameId(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"name id mismatch.", pvData); goto Exit; } if ( searchKey1.isAttr(uiLoop2) != searchKey2.isAttr(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"isAttr mismatch.", pvData); goto Exit; } if ( ( uiDataType = searchKey1.getDataType(uiLoop2)) != searchKey2.getDataType(uiLoop2)) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"data type mismatch.", pvData); goto Exit; } if ( ( uiDataLen1 = searchKey1.getDataLength(uiLoop2)) != ( uiDataLen2 = searchKey2.getDataLength(uiLoop2))) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"data length mismatch.", pvData); goto Exit; } if ( uiDataType == XFLM_TEXT_TYPE || uiDataType == XFLM_BINARY_TYPE) { if ( pucVal1) { f_free( &pucVal1); } if ( RC_BAD( rc = f_alloc( uiDataLen1, &pucVal1))) { goto Exit; } if ( pucVal2) { f_free( &pucVal2); } if ( RC_BAD( rc = f_alloc( uiDataLen2, &pucVal2))) { goto Exit; } } switch( uiDataType) { case XFLM_NUMBER_TYPE: { if ( RC_BAD( rc = searchKey1.getUINT64( uiLoop2, &ui64Val1))) { outputCallback( (char*)"getUINT64 failed.", pvData); goto Exit; } if ( RC_BAD( rc = searchKey2.getUINT64( uiLoop2, &ui64Val2))) { outputCallback( (char*)"getUINT64 failed.", pvData); goto Exit; } if ( ui64Val1 != ui64Val2) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"UINT64 val mismatch.", pvData); goto Exit; } break; } case XFLM_BINARY_TYPE: { if ( RC_BAD( rc = searchKey1.getBinary( uiLoop2, pucVal1, &uiDataLen1))) { outputCallback( (char*)"getBinary failed.", pvData); goto Exit; } if ( RC_BAD( rc = searchKey2.getBinary( uiLoop2, pucVal2, &uiDataLen2))) { outputCallback( (char*)"getBinary failed.", pvData); goto Exit; } if ( f_memcmp( pucVal1, pucVal2, uiDataLen2) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"binary val mismatch.", pvData); goto Exit; } break; } case XFLM_TEXT_TYPE: { if ( RC_BAD( rc = searchKey1.getUTF8( uiLoop2, pucVal1, &uiDataLen1))) { outputCallback( (char*)"getUTF8 failed.", pvData); goto Exit; } if ( RC_BAD( rc = searchKey2.getUTF8( uiLoop2, pucVal2, &uiDataLen2))) { outputCallback( (char*)"getUTF8 failed.", pvData); goto Exit; } if ( f_strcmp( pucVal1, pucVal2) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); outputCallback( (char*)"text val mismatch.", pvData); goto Exit; } break; } default: break; } } } Exit: if ( pucVal1) { f_free( &pucVal1); } if ( pucVal2) { f_free( &pucVal2); } return rc; } libxflaim-5.1.969/util/regressiontest.cpp0000644000175000017500000010716710511001742022012 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Regression tests for specific defects // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: regressiontest.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif /**************************************************************************** Desc: ****************************************************************************/ class IRegressionTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); RCODE nestedElementIndexDefectTest( void); RCODE leftoverIndexKeyDefectTest( void); RCODE dataNodeDeletionDefectTest( void); RCODE truncatedValueFromStoreDefectTest( void); RCODE rflRecoverDefectTests( void); RCODE compoundIndexedValueDeleteTest( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IRegressionTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IRegressionTestImpl::getName() { return( "Regression Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = rflRecoverDefectTests())) { goto Exit; } if( RC_BAD( rc = leftoverIndexKeyDefectTest())) { goto Exit; } if( RC_BAD( rc = compoundIndexedValueDeleteTest())) { goto Exit; } if( RC_BAD( rc = dataNodeDeletionDefectTest())) { goto Exit; } if( RC_BAD( rc = truncatedValueFromStoreDefectTest())) { goto Exit; } /* if ( RC_BAD( rc = nestedElementIndexDefectTest())) { goto Exit; } */ Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::dataNodeDeletionDefectTest( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransBegun = FALSE; FLMUINT uiTextDef = 0; IF_DOMNode * pTextNode = NULL; beginTest( "Data Node Deletion Defect Test", "", "Self-explanatory", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "text_val", XFLM_TEXT_TYPE, &uiTextDef))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiTextDef, &pTextNode))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } // Stream in the value so a data node will be created if ( RC_BAD( rc = pTextNode->setUTF8( m_pDb, (FLMBYTE *)"Streamed ", 9, FALSE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTextNode->setUTF8( m_pDb, (FLMBYTE *)"value", 5, TRUE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // delete the data node within this same transaction if ( RC_BAD( rc = pTextNode->deleteChildren( m_pDb))) { MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiTextDef, &pTextNode))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } // Stream in the value so a data node will be created if ( RC_BAD( rc = pTextNode->setUTF8( m_pDb, (FLMBYTE *)"Streamed ", 9, FALSE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTextNode->setUTF8( m_pDb, (FLMBYTE *)"value2", 6, TRUE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // delete the data node within a different transaction if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = pTextNode->deleteChildren( m_pDb))) { MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); Exit: if ( pTextNode) { pTextNode->Release(); } if ( RC_BAD( rc)) { endTest("FAIL"); } if ( bTransBegun) { if ( RC_OK( rc)) { rc = m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::leftoverIndexKeyDefectTest( void) { RCODE rc = NE_XFLM_OK; IF_DataVector * pSearchKey = NULL; IF_DOMNode * pNode = NULL; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransBegun = FALSE; IF_DOMNode * pDoc = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pIndex = NULL; IF_DOMNode * pComp = NULL; FLMUINT uiIxValName = 0; char szBuf[100]; FLMUINT uiTmp; beginTest( "Unlink Node Bad Ix Key Defect Test", "", "Self-explanatory", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "indexed val", XFLM_TEXT_TYPE, &uiIxValName))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createDocument( XFLM_DATA_COLLECTION, &pDoc))) { MAKE_ERROR_STRING( "createDocument failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pDoc->createNode( m_pDb, ELEMENT_NODE, uiIxValName, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pDoc))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } // create an index definition that references the elem we made if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"index_2"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 123))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // xflaim:ElementPath must have one or more xflaim:ElementComponent // or one or more xflaim:AttributeComponent sub-elements if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pComp))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pComp->createAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, uiIxValName))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pComp->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pComp->createAttribute( m_pDb, ATTR_REQUIRED_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed", m_szDetails, rc); goto Exit; } // we should now have one empty key for this index if ( RC_BAD( rc = m_pDb->keyRetrieve( 123, NULL, XFLM_FIRST, pSearchKey))) { MAKE_ERROR_STRING( "keyRetrieve failed.", m_szDetails, rc); goto Exit; } if ( pSearchKey->getDataLength( 0) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Invalid key found.", m_szDetails, rc); goto Exit; } // now set a value if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"new value"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pDoc))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } // we should have a key with the new value in it if ( RC_BAD( rc = m_pDb->keyRetrieve( 123, NULL, XFLM_FIRST, pSearchKey))) { MAKE_ERROR_STRING( "keyRetrieve failed.", m_szDetails, rc); goto Exit; } uiTmp = sizeof( szBuf); if ( RC_BAD( rc = pSearchKey->getUTF8( 0, (FLMBYTE *)szBuf, &uiTmp))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, "new value") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "invalid key found.", m_szDetails, rc); goto Exit; } // now delete pNode if ( RC_BAD( rc = pNode->deleteNode( m_pDb))) { MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pDoc))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } // there should be no key now if ( RC_OK( rc = m_pDb->keyRetrieve( 123, NULL, XFLM_FIRST, pSearchKey))) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Invalid key found.", m_szDetails, rc); goto Exit; } if ( rc != NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected rc from keyRetrieve", m_szDetails, rc); goto Exit; } // create the node again and stream in the data. This will force the // creation of a DATA_NODE to hold the data if ( RC_BAD( rc = pDoc->createNode( m_pDb, ELEMENT_NODE, uiIxValName, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"Streamed ", 9, FALSE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"in ", 3, FALSE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"value", 5, TRUE))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pDoc))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } // we should have a new key now if ( RC_BAD( rc = m_pDb->keyRetrieve( 123, NULL, XFLM_FIRST, pSearchKey))) { MAKE_ERROR_STRING( "keyRetrieve failed.", m_szDetails, rc); goto Exit; } uiTmp = sizeof( szBuf); if ( RC_BAD( rc = pSearchKey->getUTF8( 0, (FLMBYTE *)szBuf, &uiTmp))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, "Streamed in value") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "invalid key found.", m_szDetails, rc); goto Exit; } // delete the node if ( RC_BAD( rc = pNode->deleteNode( m_pDb))) { MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pDoc))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } // we should have no key left if ( RC_OK( rc = m_pDb->keyRetrieve( 123, NULL, XFLM_FIRST, pSearchKey))) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Invalid key found.", m_szDetails, rc); goto Exit; } if ( rc != NE_XFLM_EOF_HIT) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected rc from keyRetrieve", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "commitTrans failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); Exit: if( pSearchKey) { pSearchKey->Release(); } if( pNode) { pNode->Release(); } if( pAttr) { pAttr->Release(); } if( pIndex) { pIndex->Release(); } if( pDoc) { pDoc->Release(); } if( pComp) { pComp->Release(); } if( RC_BAD( rc)) { endTest("FAIL"); } if( bTransBegun) { if( RC_OK( rc)) { rc = m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::nestedElementIndexDefectTest( void) { RCODE rc = NE_XFLM_OK; const char * pszDoc = "" " 123" ""; const char * pszIndex = "" "" "" "" ""; IF_DataVector * pSearchKey = NULL; FLMBOOL bTransBegun = FALSE; FLMBOOL bDibCreated = FALSE; beginTest( "Nested Element Index Defect Test", "", "Self-explanatory", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "beginTrans failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = importBuffer( pszDoc, XFLM_DATA_COLLECTION))) { goto Exit; } if ( RC_BAD( rc = importBuffer( pszIndex, XFLM_DICT_COLLECTION))) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // A key better have been generated... if ( RC_BAD( rc = m_pDb->keyRetrieve( 99, NULL, XFLM_FIRST | XFLM_MATCH_DOC_ID, pSearchKey))) { MAKE_FLM_ERROR_STRING( "No index keys generated", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if ( RC_BAD( rc)) { endTest("FAIL"); } if ( pSearchKey) { pSearchKey->Release(); } if ( bTransBegun) { if ( RC_OK( rc)) { rc = m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } #define TEXT_VAL "NATIVE_VALUE" #define UINT64_VAL (FLM_MAX_UINT64-7) #define INT64_VAL (-(FLM_MAX_INT64/2)) #define UINT_VAL (FLM_MAX_INT-13) #define BIN_VAL {0x00,0x01,0x02,0x03,0x04,0x05} #define BIN_VAL_LEN 6 /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::truncatedValueFromStoreDefectTest( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bTransBegun = FALSE; FLMBOOL bDibCreated = FALSE; FLMUINT uiRootId = 0; FLMUINT uiTextValId = 0; FLMUINT uiNumVal1Id = 0; FLMUINT uiNumVal2Id = 0; FLMUINT uiNumVal3Id = 0; FLMUINT uiBinValId = 0; FLMBYTE pucBinVal[] = BIN_VAL; FLMUINT64 ui64RootId = 0; FLMUINT uiNameId = 0; IF_DOMNode * pRootNode = NULL; IF_DOMNode * pValNode = NULL; beginTest( "Truncated Value From Store Defect Test", "Make sure values make it back from disk intact", "Add values to database/close database/open database/verify values", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "root", XFLM_NODATA_TYPE, &uiRootId))) { MAKE_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "text_val", XFLM_TEXT_TYPE, &uiTextValId))) { MAKE_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "uint64_val", XFLM_NUMBER_TYPE, &uiNumVal1Id))) { MAKE_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "int64_val", XFLM_NUMBER_TYPE, &uiNumVal2Id))) { MAKE_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "uint_val", XFLM_NUMBER_TYPE, &uiNumVal3Id))) { MAKE_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "bin_val", XFLM_BINARY_TYPE, &uiBinValId))) { MAKE_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiRootId, &pRootNode))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pRootNode->getNodeId( m_pDb, &ui64RootId))) { goto Exit; } if ( RC_BAD( rc = pRootNode->createNode( m_pDb, ELEMENT_NODE, uiTextValId, XFLM_FIRST_CHILD, &pValNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pValNode->setUTF8( m_pDb, (FLMBYTE *)TEXT_VAL, f_strlen( TEXT_VAL)))) { MAKE_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRootNode->createNode( m_pDb, ELEMENT_NODE, uiNumVal1Id, XFLM_FIRST_CHILD, &pValNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pValNode->setUINT64( m_pDb, UINT64_VAL))) { MAKE_ERROR_STRING( "setUINT64 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRootNode->createNode( m_pDb, ELEMENT_NODE, uiNumVal2Id, XFLM_FIRST_CHILD, &pValNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pValNode->setINT64( m_pDb, INT64_VAL))) { MAKE_ERROR_STRING( "setINT64 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRootNode->createNode( m_pDb, ELEMENT_NODE, uiNumVal3Id, XFLM_FIRST_CHILD, &pValNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pValNode->setUINT( m_pDb, UINT_VAL))) { MAKE_ERROR_STRING( "setUINT failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRootNode->createNode( m_pDb, ELEMENT_NODE, uiBinValId, XFLM_FIRST_CHILD, &pValNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pValNode->setBinary( m_pDb, pucBinVal, BIN_VAL_LEN, TRUE))) { MAKE_ERROR_STRING( "setBinary failed", m_szDetails, rc); goto Exit; } // close the database to force the values to disk if ( RC_BAD( rc = m_pDb->transCommit())) { goto Exit; } bTransBegun = FALSE; pRootNode->Release(); pRootNode = NULL; pValNode->Release(); pValNode = NULL; m_pDb->Release(); m_pDb = NULL; if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { MAKE_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS))) { MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64RootId, &pRootNode))) { MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRootNode->getFirstChild( m_pDb, &pValNode))) { MAKE_ERROR_STRING( "getFirstChild failed", m_szDetails, rc); goto Exit; } for(;;) { if ( RC_BAD( rc = pValNode->getNameId( m_pDb, &uiNameId))) { MAKE_ERROR_STRING( "getNameId failed", m_szDetails, rc); goto Exit; } if ( uiNameId == uiTextValId) { char szTemp[100]; if ( RC_BAD( rc = pValNode->getUTF8( m_pDb, (FLMBYTE *)szTemp, sizeof( szTemp), 0, sizeof(szTemp) -1))) { MAKE_ERROR_STRING( "getUTF8 failed", m_szDetails, rc); goto Exit; } if ( f_strcmp( szTemp, TEXT_VAL) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected text value found", m_szDetails, rc); goto Exit; } // flag this name id as visited uiTextValId = 0; } else if ( uiNameId == uiNumVal1Id) { FLMUINT64 ui64Temp; if ( RC_BAD( rc = pValNode->getUINT64( m_pDb, &ui64Temp))) { MAKE_ERROR_STRING( "getUINT64 Failed", m_szDetails, rc); goto Exit; } if ( ui64Temp != UINT64_VAL) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected uint64 value found", m_szDetails, rc); goto Exit; } uiNumVal1Id = 0; } else if ( uiNameId == uiNumVal2Id) { FLMINT64 i64Temp; if ( RC_BAD( rc = pValNode->getINT64( m_pDb, &i64Temp))) { goto Exit; } if ( i64Temp != INT64_VAL) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected int64 value found", m_szDetails, rc); goto Exit; } uiNumVal2Id = 0; } else if ( uiNameId == uiNumVal3Id) { FLMUINT uiTemp; if ( RC_BAD( rc = pValNode->getUINT( m_pDb, &uiTemp))) { MAKE_ERROR_STRING( "getUINT failed", m_szDetails, rc); goto Exit; } if ( uiTemp != UINT_VAL) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected uint value found", m_szDetails, rc); goto Exit; } uiNumVal3Id = 0; } else if ( uiNameId == uiBinValId) { FLMBYTE pucTemp[BIN_VAL_LEN]; FLMUINT uiTmp; if ( RC_BAD( rc = pValNode->getBinary( m_pDb, pucTemp, 0, sizeof(pucTemp), &uiTmp))) { MAKE_FLM_ERROR_STRING( "getBinary failed.", m_szDetails, rc); goto Exit; } if ( uiTmp != BIN_VAL_LEN || f_memcmp( pucTemp, pucBinVal, uiTmp) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected binary value found", m_szDetails, rc); goto Exit; } uiBinValId = 0; } else { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Unexpected node found", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pValNode->getNextSibling( m_pDb, &pValNode))) { if ( rc != NE_XFLM_DOM_NODE_NOT_FOUND || uiTextValId || uiNumVal1Id || uiNumVal2Id || uiNumVal3Id || uiBinValId) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_ERROR_STRING( "Node not found", m_szDetails, rc); goto Exit; } else { rc = NE_XFLM_OK; break; } } } endTest("PASS"); Exit: if( pRootNode) { pRootNode->Release(); } if( pValNode) { pValNode->Release(); } if( RC_BAD( rc)) { endTest("FAIL"); } if( bTransBegun) { if( RC_OK( rc)) { rc = m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } #if defined( FLM_NLM) #define BACKUP_NAME_STR "SYS:\\TST.BAK" #define NEW_NAME_STR "SYS:\\NEW.DB" #else #define BACKUP_NAME_STR "tst.bak" #define NEW_NAME_STR "new.db" #endif /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::rflRecoverDefectTests( void) { RCODE rc = NE_XFLM_OK; IF_Backup * pBackup = NULL; IF_DOMNode * pNode = NULL; FLMUINT uiDefNum; FLMBOOL bStartedTrans = FALSE; FLMBOOL bDibCreated = FALSE; FLMUINT uiEncDef = 0; IF_DataVector * pSearchKey = NULL; char szTmp[ 100]; FLMUINT uiTmp = 0; const char * pszIndex = "" "" ""; beginTest( "RFL Recover Defect Test", "Ensure a bug that was causing a corruption when replaying set text value " "operations of zero length has been fixed/Ensure a bug that was causing indexes " "to not be updated when recovering node update packets has been fixed", "", "No Additional Details."); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = m_pDb->setRflKeepFilesFlag( TRUE))) { MAKE_FLM_ERROR_STRING( "setRflKeepFilesFlag failed", m_szDetails, rc); goto Exit; } // Backup the database if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, XFLM_READ_TRANS, 0, &pBackup))) { MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pBackup->backup( BACKUP_NAME_STR, NULL, NULL, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); goto Exit; } pBackup->Release(); pBackup = NULL; // Start an update transaction if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bStartedTrans = TRUE; // Create some schema definitions uiDefNum = 0; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "text_val", XFLM_TEXT_TYPE, &uiDefNum))) { MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiDefNum, &pNode))) { MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)""))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } // Create an index on text_val if ( RC_BAD( rc = importBuffer( pszIndex, XFLM_DICT_COLLECTION))) { goto Exit; } // create an encryption definition #ifdef FLM_USE_NICI if ( RC_BAD( rc = m_pDb->createEncDef("aes", "aes_def", 0, &uiEncDef))) { MAKE_FLM_ERROR_STRING( "createEncDef failed", m_szDetails, rc); goto Exit; } #endif // modify the node value a few times with encryption. // This will generate node update packets if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"text_val_1", 0, TRUE, uiEncDef))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"text_val_2", 0, TRUE, uiEncDef))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"text_val_3", 0, TRUE, uiEncDef))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } // validate the key if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->keyRetrieve( 99, NULL, XFLM_FIRST | XFLM_MATCH_DOC_ID, pSearchKey))) { MAKE_FLM_ERROR_STRING( "No index keys generated", m_szDetails, rc); goto Exit; } uiTmp = sizeof(szTmp); if ( RC_BAD( rc = pSearchKey->getUTF8( 0, (FLMBYTE *)szTmp, &uiTmp))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed", m_szDetails, rc); goto Exit; } pSearchKey->Release(); pSearchKey = NULL; if ( f_strcmp( szTmp, "text_val_3") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Invalid key found", m_szDetails, rc); goto Exit; } // Commit the transaction if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } bStartedTrans = FALSE; m_pDb->Release(); m_pDb = NULL; if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) { MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); goto Exit; } // Remove the database if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, FALSE))) { MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); goto Exit; } // Restore the database if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, NULL, NULL, BACKUP_NAME_STR, NULL, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } // Validate the key for the index to make sure it was restored properly if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->keyRetrieve( 99, NULL, XFLM_FIRST | XFLM_MATCH_DOC_ID, pSearchKey))) { MAKE_FLM_ERROR_STRING( "No index keys generated", m_szDetails, rc); goto Exit; } uiTmp = sizeof(szTmp); if ( RC_BAD( rc = pSearchKey->getUTF8( 0, (FLMBYTE *)szTmp, &uiTmp))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed", m_szDetails, rc); goto Exit; } if ( f_strcmp( szTmp, "text_val_3") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Invalid key found", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if ( RC_BAD( rc)) { endTest("FAIL"); } if (bStartedTrans) { m_pDb->transAbort(); } if ( pSearchKey) { pSearchKey->Release(); } if( pBackup) { pBackup->Release(); } if( pNode) { pNode->Release(); } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IRegressionTestImpl::compoundIndexedValueDeleteTest( void) { RCODE rc = NE_XFLM_OK; IF_DataVector * pSearchKey = NULL; FLMBOOL bTransBegun = FALSE; FLMBOOL bDibCreated = FALSE; IF_DOMNode * pRoot = NULL; IF_DOMNode * pNode = NULL; FLMUINT uiFooId = 0; FLMUINT uiBarId = 0; char szBuf[100]; FLMUINT uiTemp = 0; const char * pszIndex = "" "" "" "" "" ""; beginTest( "Compound Indexed Value Delete Test", "", "Self-explanatory", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "beginTrans failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "foo", XFLM_NODATA_TYPE, &uiFooId))) { MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "bar", XFLM_TEXT_TYPE, &uiBarId))) { MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiFooId, &pRoot))) { MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pRoot->createNode( m_pDb, ELEMENT_NODE, uiBarId, XFLM_FIRST_CHILD, &pNode))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"bar ", 4, FALSE))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"value", 5, TRUE))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = importBuffer( pszIndex, XFLM_DICT_COLLECTION))) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed", m_szDetails, rc); goto Exit; } // A key better have been generated... if ( RC_BAD( rc = m_pDb->keyRetrieve( 99, NULL, XFLM_FIRST | XFLM_MATCH_DOC_ID, pSearchKey))) { MAKE_FLM_ERROR_STRING( "No index keys generated", m_szDetails, rc); goto Exit; } uiTemp = sizeof( szBuf); if ( RC_BAD( rc = pSearchKey->getUTF8( 1, (FLMBYTE *)szBuf, &uiTemp))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, "bar value") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "unexpected index key value", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = pNode->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); goto Exit; } // There better be no second component now if ( RC_BAD( rc = m_pDb->keyRetrieve( 99, NULL, XFLM_FIRST | XFLM_MATCH_DOC_ID, pSearchKey))) { MAKE_FLM_ERROR_STRING( "No index keys generated", m_szDetails, rc); goto Exit; } uiTemp = sizeof( szBuf); if ( RC_OK( rc = pSearchKey->getUTF8( 1, (FLMBYTE *)szBuf, &uiTemp))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed", m_szDetails, rc); goto Exit; } if ( rc == NE_XFLM_NOT_FOUND) { rc = NE_XFLM_OK; } else { goto Exit; } endTest("PASS"); Exit: if ( RC_BAD( rc)) { endTest("FAIL"); } if ( pSearchKey) { pSearchKey->Release(); } if ( pNode) { pNode->Release(); } if ( pRoot) { pRoot->Release(); } if ( bTransBegun) { if ( RC_OK( rc)) { rc = m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } libxflaim-5.1.969/util/indexdeftestsrv.cpp0000644000175000017500000017174310511001742022154 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Index definition unit test. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: indexdeftestsrv.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif /**************************************************************************** Desc: ****************************************************************************/ class IIndexDefTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); private: RCODE importEncDefs( void); FLMUINT m_uiAESDef; FLMUINT m_uiDES3Def; }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IIndexDefTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IIndexDefTestImpl::getName( void) { return "Index Definition Test"; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IIndexDefTestImpl::importEncDefs( void) { RCODE rc = NE_XFLM_OK; char * ppszEncDefs[] = {XFLM_ENC_AES_OPTION_STR, XFLM_ENC_DES3_OPTION_STR}; FLMUINT puiEncDef[2]; char szEncDef[200]; FLMUINT uiLoop = 0; for( uiLoop = 0; uiLoop < sizeof( ppszEncDefs) / sizeof( ppszEncDefs[ 0]); uiLoop++) { f_sprintf( szEncDef, "", ppszEncDefs[uiLoop], ppszEncDefs[uiLoop]); if( RC_BAD( rc = importBuffer( szEncDef, XFLM_DICT_COLLECTION))) { MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); goto Exit; } f_sprintf( szEncDef, "%s definition", ppszEncDefs[uiLoop]); if( RC_BAD( rc = m_pDb->getEncDefId( (char *)szEncDef, (FLMUINT *)&puiEncDef[ uiLoop]))) { goto Exit; } } m_uiAESDef = puiEncDef[ 0]; m_uiDES3Def = puiEncDef[ 1]; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IIndexDefTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMUINT uiAttrDefDictNum = 0; FLMUINT uiElemDef1DictNum = 0; FLMUINT uiElemDef2DictNum = 0; FLMUINT64 ui64ElemComponent = 0; FLMUINT uiTemp = 0; IF_DOMNode * pIndex = NULL; IF_DOMNode * pNode = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pAttrDef = NULL; IF_DOMNode * pElemDef = NULL; IF_DOMNode * pTmpNode = NULL; FLMBOOL bTransBegun = FALSE; beginTest( "Index Def Test Init", "Perform necessary initializations so we can do the " "index definition tests.", "1) Get the necessary class factories " "2) Create a database 3) create some attribute and element " "definitions.", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; #ifdef FLM_USE_NICI if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = importEncDefs())) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; #endif // Start an update transaction if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // Create an attribute def and an element def for us to refer to in the index /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_ATTRIBUTE_TAG, &pAttrDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttrDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pAttrDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttrDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } // Get the dictionary number (used by later tests) if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiAttrDefDictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } // Create an element definition /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_ELEMENT_TAG, &pElemDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pElemDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pElemDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pElemDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } // Get the dictionary number (used by later tests) if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiElemDef1DictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } // Create an element definition /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_ELEMENT_TAG, &pElemDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pElemDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"baz"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Give baz a foo attribute if ( RC_BAD( rc = pElemDef->createAttribute( m_pDb, uiAttrDefDictNum, &pAttr))) { goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pElemDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pElemDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } // Get the dictionary number (used by later tests) if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiElemDef2DictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Root Element Tests", "Verify rules for having a \"/\" element name are enforced", "Verify that an element name of \"/\" is only used once and can " "only be used at the root component of an index definition. Verify that it " "is not allowed to have any siblings and if it is a key component that " "the \"indexOn\" attribute is required to be set to \"presence\". Furthermore " "verify that it cannot be used as a data component. ", "No additional info"); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } // Create the root element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"root element test index 1"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI // Create the encryption Id attribute if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_ID_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, m_uiAESDef))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create the key size parameter. if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 192))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } #endif // Create the name attribute and set it to "/" if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"/"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Make this the first key component if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // The indexOn attribute should be required to be set to "presence" if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"value"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // documentDone should fail since we attempted to set the indexOn attribute // to something other than "presence" with an element component of "/" rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_MUST_INDEX_ON_PRESENCE) { MAKE_ERROR_STRING( "Invalid rc. Expected: NE_XFLM_MUST_INDEX_ON_PRESENCE.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } // Create the root element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"root element test index 2"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI // Create the encryption Id attribute if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_ID_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, m_uiAESDef))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create the key size parameter. if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 128))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } #endif // Create the name attribute and set it to "/" if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"/"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Make this the first key component if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // Set to "presence" as we should if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"presence"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // We should not be able to create a sibling to this component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } // Have to give it at least one attribute or we'll fail in documentDone if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // documentDone should fail since the "/" element has a sibling rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG) { MAKE_ERROR_STRING( "Invalid rc. Expected: NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } // Create the root element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"root element test index 3"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI // Create the encryption Id attribute if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_ID_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, m_uiAESDef))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create the key size parameter. if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 256))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } #endif // Create the name attribute and set it to "/" if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"/"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Make this the first key component if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // Set to "presence" as we should if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"presence"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // We should not be able to create a sibling to this component if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin(XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } // Have to give it at least one attribute or we'll fail in documentDone if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG) { MAKE_ERROR_STRING( "Incorrect rc from documentDone." " Expected: NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // Create a subordinate element component to the "/" if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } // Try to set its name attribute to "/" as well if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"/"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"presence"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // This call to documentDone should fail since we tried to // re-use the "/" rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG) { MAKE_ERROR_STRING( "Incorrect rc from documentDone." " Expected: NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try one last time if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } // Create the root element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"root element test index 4"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI // Create the encryption Id attribute if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_ID_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, m_uiAESDef))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create the key size parameter. if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 256))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } #endif // Create the name attribute and set it to "/" if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"/"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Make this the first key component if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // Set to "presence" as we should if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"presence"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin(XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // We should not be able to create a sibling to this component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } // Have to give it at least one attribute or we'll fail in documentDone if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // This call should not work rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG) { MAKE_ERROR_STRING( "Unexpected rc from documentDone on bad index def.", m_szDetails, rc); if ( RC_OK( rc)) { rc = RC_SET( NE_XFLM_FAILURE); } goto Exit; } if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // This call should work since we corrected all of the errors if ( RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Sibling Element Uniqueness Test", m_pszTestDesc = "Test to verify uniqueness rules among sibling elements", "Verify that sibling elements and attributes are unique " "- that we cannot use the same one twice if they are sibling components.", "No additional info"); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"duplicate components index"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a duplicate if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // This should fail because there are duplicate key components rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_DUP_SIBLING_IX_COMPONENTS) { MAKE_ERROR_STRING( "Invalid rc from documentDone. Expected: " "NE_XFLM_DUP_SIBLING_IX_COMPONENTS.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD ( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"duplicate components index"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a duplicate if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Clean up our mess if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"baz"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_DATA_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a duplicate attribute component if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_DATA_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // This should fail because of our duplicate attr components rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_DUP_SIBLING_IX_COMPONENTS) { MAKE_ERROR_STRING( "Invalid rc from documentDone." "Expected: NE_XFLM_DUP_SIBLING_IX_COMPONENTS", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try one more time if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD ( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"duplicate components index"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a duplicate if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Clean up our mess if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"baz"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_DATA_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // Create a duplicate attribute component if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->createAttribute( m_pDb, ATTR_DATA_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_DUP_SIBLING_IX_COMPONENTS) { MAKE_ERROR_STRING( "Incorrect rc from documentDone." " Expected: NE_XFLM_DUP_SIBLING_IX_COMPONENTS.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // We should be okay now if ( RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Key/Data Component Value Test", "Verify that the key component number attribute " "(ATTR_KEY_COMPONENT_TAG) and the data component number attribute " "(ATTR_DATA_COMPONENT_TAG) are not set to a value of zero.", "Self-Explanatory.", ""); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = pIndex->getFirstChild( m_pDb, &pNode))) { MAKE_ERROR_STRING( "getFirstChild failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( pNode->getAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiTemp))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( uiTemp == 0) { MAKE_ERROR_STRING( "Invalid key component number.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->getFirstChild( m_pDb, &pNode))) { MAKE_ERROR_STRING( "getFirstChild failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( pNode->getAttribute( m_pDb, ATTR_DATA_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiTemp))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( uiTemp == 0) { MAKE_ERROR_STRING( "Invalid key component number.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Component Sequence Number Test", "Verify rules regarding key/data component sequence numbers " "are enforced. ", "Verify that FLAIM disallows a sequence of key component " "numbers or data component numbers that has gaps (e.g. 1 3 4 5 - 2 is " "missing) or that does not begin with 1. Also verify that each component " "number is used once and only once. Also verify that FLAIM requires at " "least one key component but does not require at least one data component.", ""); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Sequence Test"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // This should not work because FLAIM requires at least one key component rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_ILLEGAL_INDEX_DEF) { MAKE_ERROR_STRING( "Incorrect rc from documentDone. " "Expected: NE_XFLM_ILLEGAL_INDEX_DEF.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Sequence Test"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Try to give this first component a key comp != 1 if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 7))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // This should fail because the first index key component != 1 rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_MISSING_KEY_COMPONENT) { MAKE_ERROR_STRING( "Invalid rc from documentDone. Expected: " "NE_XFLM_MISSING_KEY_COMPONENT.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Sequence Test"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Try to give this first component a key comp != 1 if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 7))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Fix our mistake if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a new element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"baz"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // Try to give this component the same sequence number as the first if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // This should fail because of duplicate sequence numbers rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_DUPLICATE_KEY_COMPONENT) { MAKE_ERROR_STRING( "Invalid rc from documentDone. " "Expected: NE_XFLM_DUPLICATE_KEY_COMPONENT.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Sequence Test"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Try to give this first component a key comp != 1 if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 7))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Fix our mistake if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a new element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"baz"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // Try to give this component the same sequence number as the first if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Change the number for one out of sequence if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 8))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // This should fail because of holes in the sequence rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_MISSING_KEY_COMPONENT) { MAKE_ERROR_STRING( "Invalid rc from documentDone. " "Expected: NE_XFLM_MISSING_KEY_COMPONENT.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Sequence Test"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } // Try to give this first component a key comp != 1 if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttr->setUINT( m_pDb, 7))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Fix our mistake if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Create a new element component if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"baz"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } // Try to give this component the same sequence number as the first if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Change the number for one out of sequence if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 8))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Correct our mistake if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } rc = m_pDb->documentDone( pIndex); if( RC_BAD( rc)) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Attribute Component Test", "Verify that attribute components are not allowed to have child " "components. Verify that attribute components are required to be marked as " "either a key component or a data component.", "1) Create an Attribute Component 2) Create a child for that " "attribute component. 3) Verify documentDone fails with correct rc 4) " "Delete the child node and verify documentDone succeeds.", ""); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if( RC_BAD( rc = pNode->getNodeId( m_pDb, &ui64ElemComponent))) { goto Exit; } // pNode is pointing to an element component node. // Create an attr component underneath if ( RC_BAD( pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 3))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Try to add another attribute component under this one if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } // This should not work because of the nested attribute component rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_ILLEGAL_INDEX_DEF) { MAKE_ERROR_STRING( "Incorrect rc from documentDone. " "Expected: NE_XFLM_ILLEGAL_INDEX_DEF", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if( RC_BAD( rc = m_pDb->getNode( XFLM_DICT_COLLECTION, ui64ElemComponent, &pNode))) { goto Exit; } if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 3))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // Try to add another attribute component under this one if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pTmpNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pTmpNode->deleteNode( m_pDb))) { MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } // All of our errors should now be corrected // NOTE - this works even though we have no data components // (they should not be required). if ( RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Name/Dict Number Attribute Tests", "Tests to ensure name and dict number rules are being enforced", "1) Verify that if both the name and number attributes are " "specified that they reference the same element or attribute definition " "2) Verify that at least one of these is required to be specified for " "each component. ", ""); if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; //NOTE: pNode still references "foo" attribute component if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, uiAttrDefDictNum + 17))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // This should fail because name and dict number don't refer to the same thing rc = m_pDb->documentDone( pIndex); if ( rc != NE_XFLM_ATTRIBUTE_NAME_MISMATCH) { MAKE_ERROR_STRING( "Invalid rc from documentDone. Expected: " "NE_XFLM_ATTRIBUTE_NAME_MISMATCH", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Try again if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; //NOTE: pNode still references "foo" attribute component if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, uiAttrDefDictNum + 17))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, uiAttrDefDictNum))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } // We should be okay now if ( RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); Exit: if ( RC_BAD( rc)) { outputAll("FAIL"); } if ( pIndex) { pIndex->Release(); } if ( pNode) { pNode->Release(); } if ( pAttr) { pAttr->Release(); } if ( pAttrDef) { pAttrDef->Release(); } if ( pElemDef) { pElemDef->Release(); } if ( pTmpNode) { pTmpNode->Release(); } if ( bTransBegun) { if ( RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/diffbackups.cpp0000644000175000017500000001600110511001742021175 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Check differences between backups // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: diffbackups.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "dart_backup.h" #include "ftx.h" #include "flmarg.h" FLMBOOL gv_bShutdown = FALSE; FSTATIC FLMBOOL gv_bRunning = FALSE; FSTATIC FTX_INFO * gv_pFtxInfo = NULL; FSTATIC FTX_WINDOW * gv_pMainWindow = NULL; FINLINE FLMBOOL breakCallback( void * pvData) { F_UNREFERENCED_PARM( pvData); return FALSE; } FSTATIC RCODE utilMain( FLMUINT uiArgc, char ** ppszArgv); #ifdef FLM_NLM // Prototypes FSTATIC void utilCleanup( void); // End prototypes /**************************************************************************** Desc: This routine shuts down all threads in the NLM. ****************************************************************************/ FSTATIC void utilCleanup( void ) { gv_bShutdown = TRUE; while( gv_bRunning) { f_yieldCPU(); } } #endif #ifdef FLM_NLM FLMBOOL gv_bSynchronized = FALSE; #endif #ifdef FLM_WATCOM_NLM #define main nlm_main #endif /******************************************************************** Desc: main *********************************************************************/ #if defined( FLM_UNIX) || defined( FLM_NLM) int main( int iArgC, char ** ppucArgV ) #else int __cdecl main( int iArgC, char ** ppucArgV ) #endif { RCODE rc = NE_XFLM_OK; int iResCode = 0; gv_bShutdown = FALSE; gv_bRunning = TRUE; #ifdef FLM_NLM /* Setup the routine to be called when the NLM exits itself */ atexit( utilCleanup); #endif if ( RC_BAD( rc = dbSystem.init())) { goto Exit; } //main code which varies from util to util goes here rc = utilMain( iArgC, ppucArgV); Exit: dbSystem.exit(); #ifdef FLM_NLM if (!gv_bSynchronized) { SynchronizeStart(); gv_bSynchronized = TRUE; } gv_bRunning = FALSE; #endif if ( iResCode == 0) { iResCode = (int)rc; } return( iResCode); } /**************************************************************************** NOTE: UTILITY-SPECIFIC CODE STARTS HERE ****************************************************************************/ /**************************************************************************** Name: utilMain Desc: the 'main'-type method which varies per utility ****************************************************************************/ FSTATIC RCODE utilMain( FLMUINT uiArgc, char ** ppszArgv) { RCODE rc = NE_XFLM_OK; FlmArgSet * pArgSet = NULL; FLMBOOL bPrintedUsage; FLMBOOL bBatchMode = FALSE; FLMUINT uiScreenRows; char szBackupRoot[128]; char szRflRoot[128]; char szDestDib1[16]; char szDestDib2[16]; FLMUINT uiSet1 = 0; FLMUINT uiSet2 = 0; FLMUINT64 ui64LastTrans = 0; F_RandomGenerator randomGen; randomGen.randomSetSeed( 123); //initialize the windowing TEST_RC( rc = utilInitWindow( "ezutil", &uiScreenRows, &gv_pFtxInfo, &gv_pMainWindow, &gv_bShutdown)); if ( (pArgSet = new FlmArgSet( "diff backups test", //description of utility utilOutputLine, gv_pMainWindow, //output callback and data utilPressAnyKey, gv_pMainWindow, //pager callback and data uiScreenRows)) //rows per screen == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } TEST_RC( rc = pArgSet->addArg( "root", "root directory where backups are", TRUE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); TEST_RC( rc = pArgSet->addArg( "rflroot", "root directory where rfl backups are", TRUE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); TEST_RC( rc = pArgSet->addArg( "firstDb", "name of first database", FALSE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); TEST_RC( rc = pArgSet->addArg( "secondDb", "name of second database", FALSE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); #ifdef FLM_LINUX TEST_RC( rc = pArgSet->addArg( "trans", "last trans to replay", FALSE, FLMARG_OPTION, FLMARG_CONTENT_UNSIGNED_INT_64, (FLMUINT64)0, 0xFFFFFFFFFFFFFFFFLL)); #else TEST_RC( rc = pArgSet->addArg( "trans", "last trans to replay", FALSE, FLMARG_OPTION, FLMARG_CONTENT_UNSIGNED_INT_64, (FLMUINT64)0, 0xFFFFFFFFFFFFFFFF)); #endif //options #ifdef FLM_NLM TEST_RC( rc = pArgSet->addArg( "waitToSync", "wait to sync on Netware", TRUE, FLMARG_OPTION, FLMARG_CONTENT_NONE)); #endif //required args TEST_RC( rc = pArgSet->addArg( "firstSet", "first backup set number", FALSE, FLMARG_REQUIRED_ARG, FLMARG_CONTENT_UNSIGNED_INT, 0, 0xFFFFFFFF)); TEST_RC( rc = pArgSet->addArg( "secondSet", "second backup set number", FALSE, FLMARG_REQUIRED_ARG, FLMARG_CONTENT_UNSIGNED_INT, 0, 0xFFFFFFFF)); //feed in the true command line TEST_RC( rc = pArgSet->parseCommandLine( uiArgc, ppszArgv, &bPrintedUsage)); #ifdef FLM_NLM if (!gv_bSynchronized && !(pArgSet->argIsPresent( "waitToSync"))) { SynchronizeStart(); gv_bSynchronized = TRUE; } #endif if( pArgSet->argIsPresent("root")) { f_strcpy( szBackupRoot, pArgSet->getString("root")); } else { f_strcpy( szBackupRoot, "hcbstage\\backups"); } if( pArgSet->argIsPresent("rflroot")) { f_strcpy( szRflRoot, pArgSet->getString("rflroot")); } else { f_strcpy( szRflRoot, "hcbstage\\rfls"); } if( pArgSet->argIsPresent("firstDb")) { f_strcpy( szDestDib1, pArgSet->getString("firstDb")); } else { f_strcpy( szDestDib1, "db1.db"); } if( pArgSet->argIsPresent("secondDb")) { f_strcpy( szDestDib2, pArgSet->getString("secondDb")); } else { f_strcpy( szDestDib2, "db2.db"); } if ( pArgSet->argIsPresent("firstSet")) { uiSet1 = pArgSet->getUINT( "firstSet"); } else { flmAssert(0); } if ( pArgSet->argIsPresent("secondSet")) { uiSet2 = pArgSet->getUINT( "secondSet"); } else { flmAssert(0); } if( pArgSet->argIsPresent("trans")) { ui64LastTrans = pArgSet->getUINT64( "trans"); } if ( RC_BAD( rc = utilDiffBackupSets( szBackupRoot, szRflRoot, szDestDib1, szDestDib2, &randomGen, utilOutputLine, gv_pMainWindow, breakCallback, gv_pMainWindow, uiSet1, uiSet2, ui64LastTrans))) { goto Exit; } Exit: if ( !bBatchMode) { utilPressAnyKey( "press any key to exit...", gv_pMainWindow); } if ( pArgSet) { pArgSet->Release(); } utilShutdownWindow( gv_pFtxInfo); return rc; } libxflaim-5.1.969/util/viewlfil.cpp0000644000175000017500000003036510511001742020546 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the routines which display the logical file // blocks in a FLAIM database. // // Tabs: 3 // // Copyright (c) 1992-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: viewlfil.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "view.h" FSTATIC FLMBOOL ViewOutputLFH( FLMUINT uiCol, FLMUINT * puiRow, F_LF_HDR * pLfHdr, FLMUINT uiLfCount, FLMUINT uiFileOffset); FSTATIC FLMBOOL ViewSetupLogicalFileMenu( FLMUINT uiLfNum, FLMUINT uiLfType, F_LF_HDR * pLfHdr); /*************************************************************************** Desc: This routine formats a logical file type into an ASCII buffer. *****************************************************************************/ void FormatLFType( char * pszDestBuf, FLMUINT uiLfType) { switch (uiLfType) { case XFLM_LF_COLLECTION: f_strcpy( pszDestBuf, COLLECTION_STRING); break; case XFLM_LF_INDEX: f_strcpy( pszDestBuf, INDEX_STRING); break; case XFLM_LF_INVALID: f_strcpy( pszDestBuf, "Deleted"); break; default: f_sprintf( pszDestBuf, "Unknown: %u", (unsigned)uiLfType); break; } } /*************************************************************************** Desc: This routine outputs the information in a single LFH - for a single logical file - FLAIM 1.5 and above. *****************************************************************************/ FSTATIC FLMBOOL ViewOutputLFH( FLMUINT uiCol, FLMUINT * puiRow, F_LF_HDR * pLfHdr, FLMUINT uiLfCount, FLMUINT uiFileOffset) { FLMBOOL bOk = FALSE; FLMUINT uiLabelWidth = 35; FLMUINT uiRow = *puiRow; FLMUINT uiBlkAddress; char szTempBuf [80]; eColorType uiBackColor = FLM_BLACK; eColorType uiForeColor = FLM_LIGHTGRAY; eColorType uiUnselectBackColor = FLM_BLACK; eColorType uiUnselectForeColor = FLM_WHITE; eColorType uiSelectBackColor = FLM_BLUE; eColorType uiSelectForeColor = FLM_WHITE; FLMUINT uiOption; FLMUINT uiLfNum; // Output Logical File Name uiLfNum = (FLMUINT)pLfHdr->ui32LfNumber; if (pLfHdr->ui32LfType == XFLM_LF_COLLECTION) { switch (uiLfNum) { case XFLM_DATA_COLLECTION: f_strcpy( szTempBuf, "DATA_COLLECTION"); break; case XFLM_DICT_COLLECTION: f_strcpy( szTempBuf, "DICTIONARY_COLLECTION"); break; default: f_sprintf( (char *)szTempBuf, "COLLECTION_%u", (unsigned)uiLfNum); break; } } else if (pLfHdr->ui32LfType == XFLM_LF_INDEX) { switch (uiLfNum) { case XFLM_DICT_NUMBER_INDEX: f_strcpy( szTempBuf, "DICT_NUMBER_IX"); break; case XFLM_DICT_NAME_INDEX: f_strcpy( szTempBuf, "DICT_NAME_IX"); break; default: f_sprintf( (char *)szTempBuf, "INDEX_%u", (unsigned)uiLfNum); break; } } else { f_sprintf( (char *)szTempBuf, "UNKNOWN_TYPE[%u]_%u", (unsigned)pLfHdr->ui32LfType, (unsigned)uiLfNum); } if (!ViewAddMenuItem( LBL_LOGICAL_FILE_NAME, uiLabelWidth, VAL_IS_TEXT_PTR, (FLMUINT64)((FLMUINT)(&szTempBuf [0])), 0, 0, VIEW_INVALID_FILE_OFFSET, (FLMUINT)f_strlen( szTempBuf), MOD_DISABLED, uiCol, uiRow++, 0, FLM_GREEN, FLM_WHITE, FLM_GREEN, FLM_WHITE)) { goto Exit; } // Adjust column and label width so the rest is indented uiCol += 2; uiLabelWidth -= 2; // Output Logical File Number if (!ViewAddMenuItem( LBL_LOGICAL_FILE_NUMBER, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)uiLfNum, 0, 0, uiFileOffset + F_LF_HDR_ui32LfNumber_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output Logical File Type FormatLFType( szTempBuf, (FLMUINT)pLfHdr->ui32LfType); if (!ViewAddMenuItem( LBL_LOGICAL_FILE_TYPE, uiLabelWidth, VAL_IS_TEXT_PTR, (FLMUINT64)((FLMUINT)(&szTempBuf [0])), 0, 0, uiFileOffset + F_LF_HDR_ui32LfType_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the root block address if ((uiBlkAddress = (FLMUINT)pLfHdr->ui32RootBlkAddr) == 0) { uiOption = 0; } else { uiOption = LFH_OPTION_ROOT_BLOCK | uiLfCount; } if (!ViewAddMenuItem( LBL_ROOT_BLOCK_ADDRESS, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)uiBlkAddress, 0, 0, uiFileOffset + F_LF_HDR_ui32RootBlkAddr_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } // Output the next node id if (!ViewAddMenuItem( LBL_NEXT_NODE_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, pLfHdr->ui64NextNodeId, 0, 0, uiFileOffset + F_LF_HDR_ui64NextNodeId_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the next node id if (!ViewAddMenuItem( LBL_ENCRYPTION_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, pLfHdr->ui32EncId, 0, 0, uiFileOffset + F_LF_HDR_ui64NextNodeId_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } *puiRow = uiRow + 1; bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine sets up a menu for displaying a single logical file. *****************************************************************************/ FSTATIC FLMBOOL ViewSetupLogicalFileMenu( FLMUINT uiLfNum, FLMUINT uiLfType, F_LF_HDR * pLfHdr ) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; char szLfName [40]; FLMUINT uiFileOffset; // Retrieve the information for the logical file if (!ViewGetLFName( szLfName, uiLfNum, (eLFileType)uiLfType, pLfHdr, &uiFileOffset)) { ViewShowError( "Could not retrieve LFH information"); goto Exit; } ViewMenuInit( "Logical File"); uiRow = 3; uiCol = 5; // Output the items in the pLfHdr if (!ViewOutputLFH( uiCol, &uiRow, pLfHdr, 0, uiFileOffset)) { goto Exit; } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine displays a single logical file and allows the user to press menu keys while displaying the information. *****************************************************************************/ void ViewLogicalFile( FLMUINT uiLfNum, FLMUINT uiLfType ) { FLMUINT uiOption; VIEW_INFO SaveView; FLMBOOL bRepaint = TRUE; F_LF_HDR lfHdr; BLK_EXP BlkExp2; FLMUINT uiBlkAddress2; // Loop getting commands until the hit the exit key ViewReset( &SaveView); while (!gv_bViewPoppingStack) { if (bRepaint) { if (!ViewSetupLogicalFileMenu( uiLfNum, uiLfType, &lfHdr)) { goto Exit; } } bRepaint = TRUE; uiOption = ViewGetMenuOption(); switch (uiOption) { case ESCAPE_OPTION: goto Exit; case SEARCH_OPTION: { VIEW_MENU_ITEM_p pMenuItem = gv_pViewMenuCurrItem; // Determine which logical file, if any we are pointing at while (pMenuItem && pMenuItem->iLabelIndex != LBL_LOGICAL_FILE_NAME) { pMenuItem = pMenuItem->pPrevItem; } if (pMenuItem) { while (pMenuItem && pMenuItem->iLabelIndex != LBL_LOGICAL_FILE_NUMBER) { pMenuItem = pMenuItem->pNextItem; } } if (pMenuItem) { FLMBYTE * pszTmp; gv_uiViewSearchLfNum = (FLMUINT)pMenuItem->ui64Value; while (pMenuItem && pMenuItem->iLabelIndex != LBL_LOGICAL_FILE_TYPE) { pMenuItem = pMenuItem->pNextItem; } pszTmp = (FLMBYTE *)((FLMUINT)pMenuItem->ui64Value); if (f_stricmp( (const char *)pszTmp, COLLECTION_STRING) == 0) { gv_uiViewSearchLfType = XFLM_LF_COLLECTION; } else { gv_uiViewSearchLfType = XFLM_LF_INDEX; } if (ViewGetKey()) { gv_bViewPoppingStack = TRUE; } } else { ViewShowError( "Position cursor to a logical file before searching"); } } break; default: if (uiOption & LFH_OPTION_ROOT_BLOCK) { BlkExp2.uiLevel = 0xFF; BlkExp2.uiType = 0xFF; uiBlkAddress2 = (FLMUINT)lfHdr.ui32RootBlkAddr; BlkExp2.uiNextAddr = BlkExp2.uiPrevAddr = 0xFFFFFFFF; BlkExp2.uiLfNum = (FLMUINT)lfHdr.ui32LfNumber; ViewBlocks( uiBlkAddress2, uiBlkAddress2, &BlkExp2); } else if (uiOption & LOGICAL_INDEX_OPTION) { ViewLogicalFile( (FLMUINT)(uiOption & (~(LOGICAL_INDEX_OPTION))), XFLM_LF_INDEX); } else if (uiOption & LOGICAL_CONTAINER_OPTION) { ViewLogicalFile( (FLMUINT)(uiOption & (~(LOGICAL_CONTAINER_OPTION))), XFLM_LF_COLLECTION); } else { bRepaint = FALSE; } break; } } Exit: ViewRestore( &SaveView); } /*************************************************************************** Desc: This routine displays ALL of the logical files in an LFH block in the database. *****************************************************************************/ FLMBOOL ViewLFHBlk( FLMUINT uiReadAddress, FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, BLK_EXP_p pBlkExp ) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; FLMUINT uiEndOfBlock; FLMUINT uiPos; F_LF_HDR * pLfHdr; F_BLK_HDR * pBlkHdr; FLMUINT uiLfCount = 0; FLMUINT32 ui32CalcCRC; FLMUINT32 ui32BlkCRC; FLMUINT uiBytesRead; // Read the block into memory if (!ViewBlkRead( uiReadAddress, ppBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, &ui32CalcCRC, &ui32BlkCRC, &uiBytesRead, TRUE)) { goto Exit; } pBlkHdr = *ppBlkHdr; uiPos = SIZEOF_STD_BLK_HDR; if (uiBytesRead <= SIZEOF_STD_BLK_HDR) { uiEndOfBlock = SIZEOF_STD_BLK_HDR; } else { uiEndOfBlock = blkGetEnd( (FLMUINT)gv_ViewDbHdr.ui16BlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); if (uiEndOfBlock > uiBytesRead) { uiEndOfBlock = uiBytesRead; } } ViewMenuInit( "LFH Block"); // Output the block header first uiRow = 0; uiCol = 5; pBlkExp->uiType = BT_LFH_BLK; pBlkExp->uiLfNum = 0; pBlkExp->uiBlkAddr = uiBlkAddress; pBlkExp->uiLevel = 0xFF; if (!ViewOutBlkHdr( uiCol, &uiRow, pBlkHdr, pBlkExp, NULL, ui32CalcCRC, ui32BlkCRC)) { goto Exit; } // Now display the items uiLfCount = 0; pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR); while (uiPos < uiEndOfBlock) { FLMUINT uiLfType = pLfHdr->ui32LfType; if (uiLfType != XFLM_LF_INVALID) { if (!ViewOutputLFH( uiCol, &uiRow, pLfHdr, uiLfCount, uiReadAddress + uiPos)) { goto Exit; } } uiLfCount++; pLfHdr++; uiPos += sizeof( F_LF_HDR); } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine sets things up to display the logical files in a database. *****************************************************************************/ void ViewLogicalFiles( void) { BLK_EXP BlkExp; if (!gv_bViewHdrRead) { ViewReadHdr(); } // If there are no LFH blocks, show a message and return if (gv_ViewDbHdr.ui32FirstLFBlkAddr == 0) { ViewShowError( "No LFH blocks in database"); return; } BlkExp.uiType = BT_LFH_BLK; BlkExp.uiPrevAddr = 0; BlkExp.uiNextAddr = 0xFFFFFFFF; ViewBlocks( (FLMUINT)gv_ViewDbHdr.ui32FirstLFBlkAddr, (FLMUINT)gv_ViewDbHdr.ui32FirstLFBlkAddr, &BlkExp); } libxflaim-5.1.969/util/fshell.h0000644000175000017500000004415410511001742017650 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Command-line environment for FLAIM utilities // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fshell.h 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FSHELL_HPP #define FSHELL_HPP #include "flaimsys.h" #include "sharutil.h" // Types of clipboard data enum eClipboardDataType { CLIPBOARD_EMPTY, CLIPBOARD_GEDCOM, CLIPBOARD_TEXT }; typedef enum eClipboardDataType ClipboardDataType; class FlmShell; /*=========================================================================== struct: DB_CONTEXT Desc: This structure contains information for a particular database. ===========================================================================*/ typedef struct DBContextTag { IF_Db * pDb; FLMUINT uiCurrCollection; FLMUINT uiCurrIndex; FLMUINT uiCurrId; FLMUINT uiCurrSearchFlags; } DB_CONTEXT; /*=========================================================================== Desc: This class is used by the shell to perform commands it has parsed. ===========================================================================*/ class FlmCommand : public F_Object { public: FlmCommand( void){} virtual ~FlmCommand( void) {} // Methods that must be implemented in classes that extend this class. virtual FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) = 0; virtual void displayHelp( FlmShell * pShell, char * pszCommand) = 0; virtual FLMBOOL canPerformCommand( char * pszCommand) = 0; }; /*=========================================================================== Desc: This class manages a database context - FLAIM session and #N open databases. ===========================================================================*/ class FlmDbContext : public F_Object { #define MAX_DBCONTEXT_OPEN_DB 9 public: FlmDbContext( void); ~FlmDbContext( void); FINLINE FLMUINT getCurrDbId( void) { return m_uiCurrDbId; } FINLINE void setCurrDbId( FLMUINT uiDbId) { if (uiDbId < MAX_DBCONTEXT_OPEN_DB) { m_uiCurrDbId = uiDbId; } } FLMBOOL getAvailDbId( FLMUINT * puiDbId); FLMBOOL setDb( FLMUINT uiDbId, IF_Db * pDb); IF_Db * getDb( FLMUINT uiDbId); FLMBOOL setCurrCollection( FLMUINT uiDbId, FLMUINT uiCollection); FLMUINT getCurrCollection( FLMUINT uiDbId); FLMBOOL setCurrIndex( FLMUINT uiDbId, FLMUINT uiIndex); FLMUINT getCurrIndex( FLMUINT uiDbId); FLMBOOL setCurrId( FLMUINT uiDbId, FLMUINT uiId); FLMUINT getCurrId( FLMUINT uiDbId); FLMBOOL setCurrSearchFlags( FLMUINT uiDbId, FLMUINT uiSearchFlags); FLMUINT getCurrSearchFlags( FLMUINT uiDbId); private: FLMUINT m_uiCurrDbId; DB_CONTEXT m_DbContexts [MAX_DBCONTEXT_OPEN_DB]; }; /*=========================================================================== Desc: This class parses a command line ===========================================================================*/ class FlmParse : public F_Object { public: FlmParse( void); ~FlmParse( void) { } void setString( char * pszString); char * getNextToken( void); private: char m_szString[ 512]; char m_szToken[ 512]; char * m_pszCurPos; }; /*=========================================================================== Desc: This class manages a command-line shell ===========================================================================*/ class FlmShell : public FlmThreadContext { public: FlmShell( void); ~FlmShell( void); RCODE setup( FlmSharedContext * pSharedContext); // Methods that are invoked by the command objects RCODE registerDatabase( IF_Db * pDb, FLMUINT * puiDbId); RCODE getDatabase( FLMUINT uiDbId, IF_Db ** ppDb); RCODE deregisterDatabase( FLMUINT uiDbId); RCODE con_printf( const char * pucFormat, ...); FINLINE void displayCommand( const char * pszCommand, const char * pszDescription) { con_printf( " %-20s -- %s\n", pszCommand, pszDescription); } RCODE execute( void); RCODE registerCmd( FlmCommand * pCmd); RCODE addCmdHistory( char * pszCmd); FINLINE FTX_WINDOW * getWindow( void) { return m_pWindow; } FINLINE char * getOutputFileName( void) { return m_pszOutputFile; } private: #define MAX_SHELL_OPEN_DB 10 #define MAX_SHELL_HISTORY_ITEMS 5 #define MAX_REGISTERED_COMMANDS 50 #define MAX_CMD_LINE_LEN 256 FlmSharedContext * m_pSharedContext; FTX_WINDOW * m_pTitleWin; IF_Db * m_DbList[ MAX_SHELL_OPEN_DB]; F_Pool m_histPool; F_Pool m_argPool; FLMINT m_iCurrArgC; char ** m_ppCurrArgV; char * m_pszOutputFile; FLMINT m_iLastCmdExitCode; FLMBOOL m_bPagingEnabled; FlmCommand * m_ppCmdList[ MAX_REGISTERED_COMMANDS]; char * m_ppHistory[ MAX_SHELL_HISTORY_ITEMS]; // Private methods RCODE parseCmdLine( char * pszString); RCODE executeCmdLine( void); RCODE selectCmdLineFromList( // Pops up a selection list and allows // the user to choose a command line // from the history list char * pszCmdLineRV); }; /*=========================================================================== Desc: This class implements the database open command ===========================================================================*/ class FlmDbOpenCommand : public FlmCommand { public: FlmDbOpenCommand( void) {} ~FlmDbOpenCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the database close command ===========================================================================*/ class FlmDbCloseCommand : public FlmCommand { public: FlmDbCloseCommand( void) {} ~FlmDbCloseCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the trans command (for transactions) ===========================================================================*/ class FlmTransCommand : public FlmCommand { public: FlmTransCommand( void) {} ~FlmTransCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the dbCopy, dbRename, and dbRemove commands. ==========================================================================*/ class FlmDbManageCommand : public FlmCommand { public: FlmDbManageCommand( void) {} ~FlmDbManageCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the backup command (FlmDbBackup) ===========================================================================*/ class FlmBackupCommand : public FlmCommand { public: FlmBackupCommand( void) {} ~FlmBackupCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the restore command (FlmDbRestore) ===========================================================================*/ class FlmRestoreCommand : public FlmCommand { public: FlmRestoreCommand( void) {} ~FlmRestoreCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the IMPORT command ===========================================================================*/ class FlmImportCommand : public FlmCommand { public: FlmImportCommand( void) {} ~FlmImportCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: ===========================================================================*/ class FlmDbConfigCommand : public FlmCommand { public: FlmDbConfigCommand( void) {} ~FlmDbConfigCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: ===========================================================================*/ class FlmDbGetConfigCommand : public FlmCommand { public: FlmDbGetConfigCommand( void) {} ~FlmDbGetConfigCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the sysinfo command ===========================================================================*/ class FlmSysInfoCommand : public FlmCommand { public: FlmSysInfoCommand( void) {} ~FlmSysInfoCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: Converts a file to a hex-equivalent ASCII file ===========================================================================*/ class FlmHexConvertCommand : public FlmCommand { public: FlmHexConvertCommand( void) {} ~FlmHexConvertCommand( void) {} FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: Converts a file to or from base64 ===========================================================================*/ class FlmBase64ConvertCommand : public FlmCommand { public: FlmBase64ConvertCommand( void) { } ~FlmBase64ConvertCommand( void) { } FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the file copy command. ===========================================================================*/ class FlmCopyCommand : public FlmCommand { public: FlmCopyCommand( void); ~FlmCopyCommand( void); // Methods that must be implemented in classes that extend this class. FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the file system command. ===========================================================================*/ class FlmFileSysCommand : public FlmCommand { public: FlmFileSysCommand( void) { } ~FlmFileSysCommand( void) { } // Methods that must be implemented in classes that extend this class. FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the file delete command. ===========================================================================*/ class FlmDomEditCommand : public FlmCommand { public: FlmDomEditCommand( void) { } ~FlmDomEditCommand( void) { } // Methods that must be implemented in classes that extend this class. FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; class FlmExportCommand : public FlmCommand { public: FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: RCODE writeToFile( IF_FileHdl * pFileHdl, char * pszLine, FLMUINT uiLevel = 0); RCODE writeDocument( IF_Db * pDb, IF_FileHdl * pFileHdl, IF_DOMNode * pRootNode); RCODE processAttributes( IF_Db * pDb, IF_DOMNode * pNode, char ** pszLine); }; /*=========================================================================== Desc: This class implements the wrap db key command. ===========================================================================*/ class FlmWrapKeyCommand : public FlmCommand { public: FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the db key rollover command. ===========================================================================*/ class FlmKeyRolloverCommand : public FlmCommand { public: FlmKeyRolloverCommand( void); ~FlmKeyRolloverCommand( void); // Methods that must be implemented in classes that extend this class. FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the nodeinfo command. ===========================================================================*/ class FlmNodeInfoCommand : public FlmCommand { public: FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: This class implements the collectioninfo and indexinfo commands. ===========================================================================*/ class FlmBTreeInfoCommand : public FlmCommand { public: FLMINT execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); void displayHelp( FlmShell * pShell, char * pszCommand); FLMBOOL canPerformCommand( char * pszCommand); private: }; /*=========================================================================== Desc: ===========================================================================*/ class DirectoryIterator { public: DirectoryIterator() { m_bInitialized = FALSE; m_pszBaseDir = NULL; m_pszExtendedDir = NULL; m_pszResolvedDir = NULL; m_pDirHdl = NULL; m_ppszMatchList = NULL; m_uiCurrentMatch = 0; m_uiTotalMatches = 0; } ~DirectoryIterator() { reset(); } FINLINE char * getResolvedPath() { return m_pszResolvedDir; } void reset(); RCODE setupForSearch( char * pszBaseDir, char * pszExtendedDir, char * pszPattern); FINLINE FLMBOOL isInitialized() { return m_bInitialized; } void next( char * pszReturn, FLMBOOL bCompletePath); void prev( char * pszReturn, FLMBOOL bCompletePath); void first( char * pszReturn, FLMBOOL bCompletePath); void last( char * pszReturn, FLMBOOL bCompletePath); FLMBOOL isInSet( char * pszFilename); FINLINE FLMBOOL isEmpty( void) { return m_uiTotalMatches == 0; } private: enum {MAX_PATH_SIZE = 640}; RCODE setupDirectories( char * pszBaseDir, char * pszExtendedDir); static FINLINE FLMBOOL isQuoted( char * pszString) { return pszString[0] == '\"' && pszString[ f_strlen( pszString) - 1] == '\"'; } FLMBOOL isDriveSpec( char * pszPath); RCODE extractRoot( char * pszPath, char * pszRoot); RCODE resolveDir( void); FLMBOOL m_bInitialized; char * m_pszBaseDir; char * m_pszExtendedDir; char * m_pszResolvedDir; IF_DirHdl * m_pDirHdl; char ** m_ppszMatchList; FLMUINT m_uiCurrentMatch; FLMUINT m_uiTotalMatches; }; char * positionToPath( char * pszCommandLine); void extractBaseDirAndWildcard( char * pszPath, char * pszBase, char * pszWildcard); void removeChars( char * pszString, char cChar); #endif // #ifndef FSHELL_HPP libxflaim-5.1.969/util/flm_lutl.h0000644000175000017500000000234110511001742020201 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: FLAIM's utility routines for presenting selection and statistics lists // // Tabs: 3 // // Copyright (c) 2003,2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flm_lutl.h 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ RCODE FLMAPI flstIndexManagerThread( IF_Thread * pThread); RCODE FLMAPI flstMemoryManagerThread( IF_Thread * pThread); libxflaim-5.1.969/util/checkdb.cpp0000644000175000017500000013324510511001742020311 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Checks a database for corruptions // // Tabs: 3 // // Copyright (c) 1992-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: checkdb.cpp 3129 2006-01-25 11:46:17 -0700 (Wed, 25 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "sharutil.h" #define UTIL_ID "CHECKDB" #define LABEL_COLUMN 5 #define VALUE_COLUMN 30 #define LOG_FILE_ROW 1 #define SOURCE_ROW 2 #define DATA_DIR_ROW 3 #define RFL_DIR_ROW 4 #define CACHE_USED_ROW 5 #define DOING_ROW 6 #define FILE_SIZE_ROW 7 #define AMOUNT_DONE_ROW 8 #define TOTAL_DOM_NODES_ROW 9 #define DOM_LINKS_VERIFIED_ROW 10 #define TOTAL_BROKEN_LINKS_ROW 11 #define TOTAL_KEYS_ROW 12 #define TOTAL_DUPS_ROW 13 #define TOTAL_KEYS_EXAM_ROW 14 #define BAD_IXREF_ROW 15 #define MISSING_IXREF_ROW 16 #define CONFLICT_ROW 17 #define CORRUPT_ROW 18 #define TOTAL_CORRUPT_ROW 19 #define REPAIR_ROW 20 #define OLD_VIEW_ROW 21 #define MISMATCH_ROW 22 #define MAX_LOG_BUFF 2048 #define MISMATCH_READ_ERR 0 #define MISMATCH_ERROR_CODE 1 #define MISMATCH_ERR_LOCALE 2 #define MISMATCH_LF_NUMBER 3 #define MISMATCH_LF_NAME 4 #define MISMATCH_LF_TYPE 5 #define MISMATCH_LF_LEVEL 6 #define MISMATCH_BLK_ADDRESS 7 #define MISMATCH_PARENT_ADDRESS 8 #define MISMATCH_ELM_OFFSET 9 #define MISMATCH_DRN 10 #define MISMATCH_ELM_REC_OFFSET 11 #define MISMATCH_FIELD_NUM 12 #define MISMATCH_ERR_NOT_LOGGED 13 #define SCREEN_REFRESH_RATE 100 /******************************************************************** Desc: *********************************************************************/ class F_LocalCheckStatus : public IF_DbCheckStatus { public: F_LocalCheckStatus() { m_uiLastRefresh = 0; } RCODE FLMAPI reportProgress( XFLM_PROGRESS_CHECK_INFO * pProgCheck); RCODE FLMAPI reportCheckErr( XFLM_CORRUPT_INFO * pCorruptInfo, FLMBOOL * pbFix); private: FLMUINT m_uiLastRefresh; }; FSTATIC FLMBOOL CheckDatabase( void); FSTATIC FLMBOOL DoCheck( void); FSTATIC void CheckShowHelp( FLMBOOL bShowFullUsage); FSTATIC FLMBOOL GetParams( FLMINT iArgC, char ** ppszArgV); FSTATIC void OutLabel( FLMUINT uiCol, FLMUINT uiRow, const char * pszLabel, const char * pszValue, FLMUINT64 ui64NumValue, FLMBOOL bLogIt); FSTATIC void OutLine( const char * pszString); FSTATIC void LogFlush( void); FSTATIC void LogString( const char * pszString); FSTATIC void DisplayValue( FLMUINT uiRow, const char * pszValue); FSTATIC void DisplayNumValue( FLMUINT uiRow, FLMUINT64 ui64Number); FSTATIC void OutValue( const char * pszLabel, const char * pszValue); FSTATIC void OutUINT( const char * pszLabel, FLMUINT uiNum); FSTATIC void OutUINT64( const char * pszLabel, FLMUINT64 ui64Num); FSTATIC void OutBlkHeader( void); FSTATIC void OutOneBlockStat( const char * pszLabel, FLMUINT uiBlockSize, FLMUINT64 ui64KeyCount, FLMUINT64 ui64BytesUsed, FLMUINT64 ui64ElementCount, FLMUINT64 ui64ContElementCount, FLMUINT64 ui64ContElmBytes, FLMUINT uiBlockCount, FLMINT32 i32LastError, FLMUINT uiNumErrors); FSTATIC void OutLogicalFile( IF_DbInfo * pDbInfo, FLMUINT uiIndex); FSTATIC void PrintInfo( IF_DbInfo * pDbInfo); FSTATIC FLMUINT CheckShowError( const char * pszMessage, FLMBOOL bLogIt); FSTATIC RCODE GetUserInput( void); FSTATIC void LogStr( FLMUINT uiIndent, const char * pszStr); FSTATIC void LogCorruptError( XFLM_CORRUPT_INFO * pCorrupt); FSTATIC void LogKeyError( XFLM_CORRUPT_INFO * pCorrupt); FSTATIC FLMBOOL DisplayField( IF_DataVector * ifpKey, FLMUINT uiElementNumber, FLMUINT uiStartCol, FLMUINT uiLevelOffset); FSTATIC FLMBOOL NumToName( FLMUINT uiNum, FLMUINT uiType, char * pszBuf); FLMBOOL gv_bShutdown = FALSE; static IF_FileHdl * gv_pLogFile = NULL; static IF_DbInfo * gv_pDbInfo = NULL; static F_Db * gv_pDb = NULL; static F_NameTable * gv_pNameTable = NULL; static FLMUINT gv_uiMaxRow; static char gv_szLogFileName[ F_PATH_MAX_SIZE]; static char gv_szTmpDir[ F_PATH_MAX_SIZE]; static char gv_szLastError[ 256]; static FLMUINT gv_uiLineCount; static char gv_szDbFileName[ F_PATH_MAX_SIZE]; static char gv_szDataDir[ F_PATH_MAX_SIZE]; static char gv_szRflDir[ F_PATH_MAX_SIZE]; static char * gv_pszLogBuffer = NULL; static FLMUINT64 gv_ui64FileSize; static FLMUINT64 gv_ui64BytesDone; static FLMUINT gv_uiCorruptCount; static FLMUINT gv_uiRepairCount; static FLMUINT gv_uiTotalCorruptions; static FLMUINT gv_uiOldViewCount; static FLMUINT gv_uiMismatchCount; static FLMUINT gv_uiLogBufferCount = 0; static FLMBOOL gv_bMultiplePasses = FALSE; static FLMBOOL gv_bBatchMode; static FLMBOOL gv_bContinue; static FLMBOOL gv_bStartUpdate = FALSE; static FLMBOOL gv_bRepairCorruptions = FALSE; static FLMBOOL gv_bDoLogicalCheck = FALSE; static FLMBOOL gv_bLoggingEnabled; static FLMBOOL gv_bShowStats; static FLMBOOL gv_bRunning; static FLMBOOL gv_bPauseBeforeExiting = FALSE; static FLMBOOL gv_bSkipDomLinkVerify = FALSE; static char gv_szPassword[256]; static IF_DbSystem * gv_pDbSystem = NULL; #ifdef FLM_RING_ZERO_NLM #define main nlm_main #endif /******************************************************************** Desc: *********************************************************************/ extern "C" int main( int iArgC, char ** ppszArgV) { int iResCode = 0; F_Pool logPool; logPool.poolInit( 1024); gv_bBatchMode = FALSE; gv_bShutdown = FALSE; gv_bRunning = TRUE; gv_szLastError[ 0] = '\0'; if( RC_BAD( FlmAllocDbSystem( &gv_pDbSystem))) { f_conStrOut( "\nCould not initialize FLAIM.\n"); goto Exit; } f_conInit( 0xFFFF, 0xFFFF, "FLAIM Database Check"); f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conDrawBorder(); f_conClearScreen( 0, 0); f_conGetScreenSize( NULL, &gv_uiMaxRow); if (RC_BAD( logPool.poolAlloc( MAX_LOG_BUFF, (void **)&gv_pszLogBuffer))) { f_conStrOut( "\nFailed to allocatae memory pool\n"); goto Exit; } if( GetParams( iArgC, ppszArgV)) { if (!DoCheck()) { iResCode = 1; } } logPool.poolReset( NULL); if( (gv_bPauseBeforeExiting) && (!gv_bShutdown)) { f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conSetBackFore( FLM_RED, FLM_WHITE); if( gv_szLastError[ 0] != '\0') { f_conStrOut( gv_szLastError); } f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); f_conStrOut( "Press any character to exit CHECKDB: "); for (;;) { if( gv_bShutdown) { break; } if( f_conHaveKey()) { f_conGetKey(); break; } } } Exit: if (gv_pDbInfo) { gv_pDbInfo->Release(); } logPool.poolFree(); f_conExit(); if( gv_pDbSystem) { gv_pDbSystem->Release(); } gv_bRunning = FALSE; return( iResCode); } /******************************************************************** Desc: Check the database... *********************************************************************/ FSTATIC FLMBOOL CheckDatabase( void) { RCODE rc = NE_XFLM_OK; FLMUINT uiStatus; char szTmpBuf[ 100]; FLMUINT uiCheckFlags; FLMBOOL bOk = TRUE; IF_DbCheckStatus * pDbCheckStatus = NULL; // Open the database - so we can have access to its name table if (!gv_pDb) { if( RC_BAD( rc = gv_pDbSystem->dbOpen( gv_szDbFileName, gv_szDataDir, gv_szRflDir, gv_szPassword, XFLM_ALLOW_LIMITED_MODE, (IF_Db **)&gv_pDb))) { f_sprintf( szTmpBuf, "Error opening database: 0x%04X", (unsigned)rc); CheckShowError( szTmpBuf, TRUE); bOk = FALSE; goto Exit; } } if (gv_pNameTable) { gv_pNameTable->Release(); gv_pNameTable = NULL; } (void)gv_pDb->getNameTable( &gv_pNameTable); gv_uiCorruptCount = 0; gv_ui64BytesDone = 0; gv_ui64FileSize = 0; gv_uiOldViewCount = 0; f_conSetBackFore( FLM_BLUE, FLM_WHITE); if (gv_bLoggingEnabled) { LogString( NULL); LogString( NULL); LogString( NULL); LogString( "=========================================================================="); LogString( "CHECK PARAMETERS:"); } OutLabel( LABEL_COLUMN, SOURCE_ROW, "Database", gv_szDbFileName, 0, TRUE); OutLabel( LABEL_COLUMN, DATA_DIR_ROW, "Data Files Dir.", gv_szDataDir [0] ? &gv_szDataDir [0] : "", 0, TRUE); OutLabel( LABEL_COLUMN, RFL_DIR_ROW, "RFL Files Dir.", gv_szRflDir [0] ? &gv_szRflDir [0] : "", 0, TRUE); OutLabel( LABEL_COLUMN, LOG_FILE_ROW, "Log File", (gv_szLogFileName[ 0]) ? &gv_szLogFileName[ 0] : "", 0, FALSE); OutLabel( LABEL_COLUMN, CACHE_USED_ROW, "Cache Bytes Used", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, DOING_ROW, "Doing", "Opening Database File", 0, FALSE); OutLabel( LABEL_COLUMN, FILE_SIZE_ROW, "File Size", NULL, (FLMUINT)gv_ui64FileSize, FALSE); OutLabel( LABEL_COLUMN, CORRUPT_ROW, "File Corruptions", NULL, gv_uiCorruptCount, FALSE); OutLabel( LABEL_COLUMN, TOTAL_CORRUPT_ROW, "Total Corruptions", NULL, gv_uiTotalCorruptions, FALSE); OutLabel( LABEL_COLUMN, OLD_VIEW_ROW, "Old View Count", NULL, gv_uiOldViewCount, FALSE); OutLabel( LABEL_COLUMN, MISMATCH_ROW, "Mismatch Count", NULL, gv_uiMismatchCount, FALSE); OutLabel( LABEL_COLUMN, REPAIR_ROW, "Problems Repaired", NULL, gv_uiRepairCount, FALSE); OutLabel( LABEL_COLUMN, TOTAL_KEYS_ROW, "Total Index Keys", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, TOTAL_DUPS_ROW, "Total Duplicate Keys", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, CONFLICT_ROW, "Key Conflicts", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, TOTAL_KEYS_EXAM_ROW, "Num. Keys Checked", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, BAD_IXREF_ROW, "Invalid Index Keys", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, MISSING_IXREF_ROW, "Missing Index Keys", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, TOTAL_DOM_NODES_ROW, "Total DOM Nodes", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, DOM_LINKS_VERIFIED_ROW, "DOM Links Verified", NULL, 0, FALSE); OutLabel( LABEL_COLUMN, TOTAL_BROKEN_LINKS_ROW, "DOM Links Broken", NULL, 0, FALSE); if( gv_bLoggingEnabled) { LogString( NULL); LogString( "CHECK DETAILED RESULTS:"); LogString( NULL); } uiCheckFlags = 0; if( gv_bRepairCorruptions == TRUE) { uiCheckFlags |= XFLM_ONLINE; } if( gv_bDoLogicalCheck == TRUE) { uiCheckFlags |= XFLM_DO_LOGICAL_CHECK; } if (gv_bSkipDomLinkVerify) { uiCheckFlags |= XFLM_SKIP_DOM_LINK_CHECK; } if (RC_OK( rc)) { F_LocalCheckStatus dbCheckStatus; rc = gv_pDbSystem->dbCheck( gv_szDbFileName, gv_szDataDir, gv_szRflDir, NULL, uiCheckFlags, &gv_pDbInfo, &dbCheckStatus); } if( rc == NE_XFLM_FAILURE) { f_sprintf( szTmpBuf, "User pressed ESCAPE, check halted"); gv_bShutdown = TRUE; } else { f_sprintf( szTmpBuf, "RETURN CODE: 0x%04X", (unsigned)rc); } uiStatus = CheckShowError( szTmpBuf, TRUE); if( ((uiStatus != FKB_ESCAPE) || (gv_bLoggingEnabled)) && (gv_bShowStats) && ((rc == NE_XFLM_OK) || (rc == NE_XFLM_DATA_ERROR) || (rc == NE_XFLM_TRANS_ACTIVE))) { PrintInfo( gv_pDbInfo); } if( gv_bLoggingEnabled) { LogString( NULL); LogFlush(); } Exit: if( gv_pNameTable) { gv_pNameTable->Release(); gv_pNameTable = NULL; } if( gv_pDb) { gv_pDb->Release(); } if (pDbCheckStatus) { pDbCheckStatus->Release(); } return( bOk); } /******************************************************************** Desc: Function to coordinate check of the database. *********************************************************************/ FSTATIC FLMBOOL DoCheck( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bOk = TRUE; char szTmpBuf[ 100]; IF_FileSystem * pFileSystem = NULL; if( RC_BAD( rc = FlmGetFileSystem( &pFileSystem))) { goto Exit; } f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, 0); gv_bContinue = TRUE; gv_uiLineCount = 0; gv_bLoggingEnabled = FALSE; gv_uiCorruptCount = 0; gv_uiTotalCorruptions = 0; gv_uiRepairCount = 0; gv_uiMismatchCount = 0; gv_uiLogBufferCount = 0; if( gv_szLogFileName[ 0]) { pFileSystem->deleteFile( gv_szLogFileName); if( RC_OK( rc = pFileSystem->createFile( gv_szLogFileName, FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &gv_pLogFile))) { gv_bLoggingEnabled = TRUE; } else { f_sprintf( szTmpBuf, "Error creating log file: 0x%04X", (unsigned)rc); CheckShowError( szTmpBuf, FALSE); bOk = FALSE; goto Exit; } } f_conSetCursorType( FLM_CURSOR_INVISIBLE); for( ;;) { // Check the database... if( !CheckDatabase()) { bOk = FALSE; break; } if( (!gv_bMultiplePasses) || (gv_bShutdown)) { break; } } f_conSetCursorType( FLM_CURSOR_UNDERLINE); if( gv_bLoggingEnabled) { LogFlush(); gv_pLogFile->Release(); gv_pLogFile = NULL; } Exit: if( pFileSystem) { pFileSystem->Release(); } return( bOk); } /******************************************************************** Desc: Show the help screen. *********************************************************************/ FSTATIC void CheckShowHelp( FLMBOOL bShowFullUsage ) { f_conStrOut( "\n"); if (bShowFullUsage) { f_conStrOut( "Usage: checkdb [Options]\n"); } else { f_conStrOut( "Parameters: [Options]\n\n"); } f_conStrOut( " FileName = Name of database to check.\n"); f_conStrOut( " Options\n"); f_conStrOut( " -b = Run in Batch Mode.\n"); f_conStrOut( " -c = Repair logical corruptions.\n"); f_conStrOut( " -d = Display/log detailed statistics.\n"); f_conStrOut( " -dr = RFL directory.\n"); f_conStrOut( " -dd = Data directory.\n"); f_conStrOut( " -i = Perform a logical (index) check.\n"); f_conStrOut( " -l = Log detailed information to .\n"); f_conStrOut( " -m = Multiple passes (continuous check).\n"); f_conStrOut( " -o = Output binary log information to .\n"); f_conStrOut( " -p = Pause before exiting.\n"); f_conStrOut( " -s = Skip DOM link verification.\n"); f_conStrOut( " -t = Temporary directory.\n"); f_conStrOut( " -u = Run check in an update transaction.\n"); f_conStrOut( " -v = Verify binary log information in . NOTE:\n"); f_conStrOut( " The -v and -o options cannot both be specified.\n"); f_conStrOut( " -a = Database password.\n"); f_conStrOut( " -? = A '?' anywhere in the command line will cause this\n"); f_conStrOut( " screen to be displayed.\n"); f_conStrOut( "Options may be specified anywhere in the command line.\n"); } /******************************************************************** Desc: *********************************************************************/ FSTATIC FLMBOOL GetParams( FLMINT iArgC, char ** ppszArgV ) { #define MAX_ARGS 30 FLMUINT uiLoop; char szTmpBuf[ 100]; char * pszTmp; char * ppszArgs[ MAX_ARGS]; char szCommandBuffer[ 300]; gv_szDbFileName [0] = '\0'; gv_szDataDir [0] = '\0'; gv_szRflDir [0] = '\0'; gv_szLogFileName[ 0] = '\0'; gv_szTmpDir[ 0] = '\0'; gv_bShowStats = FALSE; gv_szPassword[0] = '\0'; /* Ask the user to enter parameters if none were entered on the command line. */ if( iArgC < 2) { for( ;;) { f_conStrOut( "CheckDB Params (enter ? for help): "); szCommandBuffer[ 0] = '\0'; f_conLineEdit( szCommandBuffer, sizeof( szCommandBuffer) - 1); if( gv_bShutdown) { return( FALSE); } if( f_stricmp( szCommandBuffer, "?") == 0) { CheckShowHelp( FALSE); } else { break; } } flmUtilParseParams( szCommandBuffer, MAX_ARGS, &iArgC, &ppszArgs [1]); ppszArgs [0] = ppszArgV [0]; iArgC++; ppszArgV = &ppszArgs [0]; } uiLoop = 1; while( uiLoop < (FLMUINT)iArgC) { pszTmp = ppszArgV[ uiLoop]; /* See if they specified an option */ #ifdef FLM_UNIX if( *pszTmp == '-') #else if( (*pszTmp == '-') || (*pszTmp == '/')) #endif { pszTmp++; if( (*pszTmp == 'l') || (*pszTmp == 'L')) { pszTmp++; if( *pszTmp) { f_strcpy( gv_szLogFileName, pszTmp); } else { if( CheckShowError( "Log file name not specified in parameter", FALSE) == FKB_ESCAPE) { return( FALSE); } } } else if( (*pszTmp == 't') || (*pszTmp == 'T')) { pszTmp++; if( *pszTmp) { f_strcpy( gv_szTmpDir, pszTmp); } else { if( CheckShowError( "Temporary directory not specified in parameter", FALSE) == FKB_ESCAPE) { return( FALSE); } } } else if( (*pszTmp == 'd') || (*pszTmp == 'D')) { pszTmp++; if (!(*pszTmp)) { gv_bShowStats = TRUE; } else if (*pszTmp == 'r' || *pszTmp == 'R') { f_strcpy( gv_szRflDir, pszTmp + 1); } else if (*pszTmp == 'd' || *pszTmp == 'D') { f_strcpy( gv_szDataDir, pszTmp + 1); } else { f_sprintf( szTmpBuf, "Invalid option %s", pszTmp - 1); if( CheckShowError( szTmpBuf, FALSE) == FKB_ESCAPE) { return( FALSE); } } } else if (*pszTmp == 'a' || *pszTmp == 'A') { f_strcpy( gv_szPassword, pszTmp + 1); } else if (f_stricmp( pszTmp, "B") == 0) { gv_bBatchMode = TRUE; } else if (f_stricmp( pszTmp, "C") == 0) { gv_bRepairCorruptions = TRUE; } else if (f_stricmp( pszTmp, "I") == 0) { gv_bDoLogicalCheck = TRUE; } else if (f_stricmp( pszTmp, "M") == 0) { gv_bMultiplePasses = TRUE; } else if (f_stricmp( pszTmp, "P") == 0) { gv_bPauseBeforeExiting = TRUE; } else if (f_stricmp( pszTmp, "S") == 0) { gv_bSkipDomLinkVerify = TRUE; } else if (f_stricmp( pszTmp, "U") == 0) { gv_bStartUpdate = TRUE; } else if (f_stricmp( pszTmp, "?") == 0 || f_stricmp( pszTmp, "HELP") == 0) { CheckShowHelp( TRUE); gv_bPauseBeforeExiting = TRUE; return( FALSE); } else { f_sprintf( szTmpBuf, "Invalid option %s", pszTmp); if( CheckShowError( szTmpBuf, FALSE) == FKB_ESCAPE) { return( FALSE); } } } else if( f_stricmp( pszTmp, "?") == 0) { Show_Help: CheckShowHelp( TRUE); gv_bPauseBeforeExiting = TRUE; return( FALSE); } else if( !gv_szDbFileName[ 0]) { f_strcpy( gv_szDbFileName, pszTmp); } uiLoop++; } if( !gv_szDbFileName[ 0]) { goto Show_Help; } else { return( TRUE); } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void LogFlush( void ) { FLMUINT uiBytesWritten; if( gv_uiLogBufferCount) { gv_pLogFile->write( FLM_IO_CURRENT_POS, gv_uiLogBufferCount, (FLMBYTE *)gv_pszLogBuffer, &uiBytesWritten); gv_uiLogBufferCount = 0; } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void LogString( const char * pszString) { FLMUINT uiLen; FLMUINT uiLoop; if( (gv_bLoggingEnabled) && (gv_pszLogBuffer != NULL)) { uiLen = (FLMUINT)((pszString != NULL) ? (FLMUINT)(f_strlen( pszString)) : 0); for( uiLoop = 0; uiLoop < uiLen; uiLoop++) { gv_pszLogBuffer[ gv_uiLogBufferCount++] = *pszString++; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { LogFlush(); } } gv_pszLogBuffer[ gv_uiLogBufferCount++] = '\r'; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { LogFlush(); } gv_pszLogBuffer[ gv_uiLogBufferCount++] = '\n'; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { LogFlush(); } } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutLine( const char * pszBuf) { FLMUINT uiChar; if( gv_bLoggingEnabled) { LogString( pszBuf); } if( !gv_bBatchMode) { if( gv_bContinue) { if( gv_uiLineCount == 20) { f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 1)); f_conSetBackFore( FLM_RED, FLM_WHITE); f_conStrOut( "Press: ESC to quit, anything else to continue"); for( ;;) { if( gv_bShutdown) { uiChar = FKB_ESCAPE; break; } else if( f_conHaveKey()) { uiChar = f_conGetKey(); break; } } if( uiChar == FKB_ESCAPE) { gv_bContinue = FALSE; } else { f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, 0); } gv_uiLineCount = 0; } } if( gv_bContinue) { f_conStrOutXY( pszBuf, 0, gv_uiLineCount); gv_uiLineCount++; } } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutValue( const char * pszLabel, const char * pszValue) { char szTmpBuf[ 100]; f_strcpy( szTmpBuf, "...................................... "); f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], pszValue); f_memcpy( szTmpBuf, pszLabel, (FLMSIZET)f_strlen( pszLabel)); OutLine( szTmpBuf); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutUINT( const char * pszLabel, FLMUINT uiNum) { char szValue [12]; if( uiNum == 0xFFFFFFFF) { f_strcpy( szValue, "0xFFFFFFFF"); } else { f_sprintf( szValue, "%u", (unsigned)uiNum); } OutValue( pszLabel, szValue); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutUINT64( const char * pszLabel, FLMUINT64 ui64Num) { char szValue [24]; if( ui64Num == (FLMUINT64)-1) { f_strcpy( szValue, "0xFFFFFFFFFFFFFFFF"); } else { f_sprintf( szValue, "%I64u", ui64Num); } OutValue( pszLabel, szValue); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutBlkHeader( void) { OutLine( " Blk Type Blk Count Total Bytes Bytes Used Prcnt Element Cnt Avg Elem"); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutOneBlockStat( const char * pszLabel, FLMUINT uiBlockSize, FLMUINT64 ui64KeyCount, FLMUINT64 ui64BytesUsed, FLMUINT64 ui64ElementCount, FLMUINT64 ui64ContElementCount, FLMUINT64 ui64ContElmBytes, FLMUINT uiBlockCount, FLMINT32 i32LastError, FLMUINT uiNumErrors) { char szTmpBuf[ 100]; FLMUINT64 ui64TotalBytes; FLMUINT uiPercent; FLMUINT uiAvgElementSize; ui64TotalBytes = (FLMUINT64)uiBlockCount * (FLMUINT64)uiBlockSize; if( ui64ElementCount) { uiAvgElementSize = (FLMUINT)( ui64BytesUsed / ui64ElementCount); } else { uiAvgElementSize = 0; } if( ui64BytesUsed > 40000000) { uiPercent = (FLMUINT)( ui64BytesUsed / (ui64TotalBytes / 100)); } else if( ui64TotalBytes) { uiPercent = (FLMUINT)((ui64BytesUsed * 100) / ui64TotalBytes); } else { uiPercent = 0; } f_sprintf( szTmpBuf, "%-12s %10u %11u %10u %5u %11u %8u", pszLabel, (unsigned)uiBlockCount, (unsigned)ui64TotalBytes, (unsigned)ui64BytesUsed, (unsigned)uiPercent, (unsigned)ui64ElementCount, (unsigned)uiAvgElementSize); OutLine( szTmpBuf); if( ui64ContElementCount) { uiAvgElementSize = (FLMUINT)( ui64ContElmBytes / ui64ContElementCount); if( ui64ContElmBytes > 40000000) { uiPercent = (FLMUINT)( ui64ContElmBytes / (ui64TotalBytes / 100)); } else if( ui64TotalBytes) { uiPercent = (FLMUINT)((ui64ContElmBytes * 100) / ui64TotalBytes); } else { uiPercent = 0; } f_sprintf( szTmpBuf, "%-12s " "%10u %5u %11u %8u", " ContElm", (unsigned)ui64ContElmBytes, (unsigned)uiPercent, (unsigned)ui64ContElementCount, (unsigned)uiAvgElementSize); OutLine( szTmpBuf); } if( ui64KeyCount) { f_sprintf( szTmpBuf, "%-12s %10u", " KeyCnt", (unsigned)ui64KeyCount); OutLine( szTmpBuf); } if( uiNumErrors) { f_strcpy( szTmpBuf, " LAST ERROR: "); f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], gv_pDbSystem->checkErrorToStr( (FLMINT)i32LastError)); OutLine( szTmpBuf); f_sprintf( szTmpBuf, " TOTAL ERRORS: %u", (unsigned)uiNumErrors); OutLine( szTmpBuf); } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutLogicalFile( IF_DbInfo * pDbInfo, FLMUINT uiIndex ) { char szTmpBuf[ 100]; FLMUINT uiLoop; char szLfName[ 30]; FLMUINT uiLfNum; eLFileType eLfType; FLMUINT uiRootBlkAddress; FLMUINT uiNumLevels; FLMUINT64 ui64KeyCount; FLMUINT64 ui64BytesUsed; FLMUINT64 ui64ElementCount; FLMUINT64 ui64ContElementCount; FLMUINT64 ui64ContElmBytes; FLMUINT uiBlockCount; FLMINT32 i32LastError; FLMUINT uiNumErrors; pDbInfo->getBTreeInfo( uiIndex, &uiLfNum, &eLfType, &uiRootBlkAddress, &uiNumLevels); switch( eLfType) { case XFLM_LF_COLLECTION: /* Data collection */ f_strcpy( szTmpBuf, "COLLECTION"); break; case XFLM_LF_INDEX: /* Index */ f_strcpy( szTmpBuf, "INDEX"); break; default: break; } (void)NumToName( uiLfNum, eLfType == XFLM_LF_COLLECTION ? ELM_COLLECTION_TAG : ELM_INDEX_TAG, szLfName); OutValue( szTmpBuf, szLfName); OutUINT( " Logical File Number", uiLfNum); OutUINT( " Root Block Address", uiRootBlkAddress); if (!uiNumLevels) { OutUINT( " Levels", uiNumLevels); } else { OutBlkHeader(); for( uiLoop = 0; uiLoop < uiNumLevels; uiLoop++) { f_sprintf( szTmpBuf, " Level %u", (unsigned)uiLoop); pDbInfo->getBTreeBlockStats( uiIndex, uiLoop, &ui64KeyCount, &ui64BytesUsed, &ui64ElementCount, &ui64ContElementCount, &ui64ContElmBytes, &uiBlockCount, &i32LastError, &uiNumErrors); OutOneBlockStat( szTmpBuf, pDbInfo->getDbHdr()->ui16BlockSize, ui64KeyCount, ui64BytesUsed, ui64ElementCount, ui64ContElementCount, ui64ContElmBytes, uiBlockCount, i32LastError, uiNumErrors); } } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void PrintInfo( IF_DbInfo * pDbInfo ) { FLMUINT uiLoop; FLMUINT uiNumLogicalFiles; FLMUINT64 ui64BytesUsed; FLMUINT64 ui64ElementCount; FLMUINT64 ui64ContElementCount; FLMUINT64 ui64ContElmBytes; FLMUINT uiBlockCount; FLMINT32 i32LastError; FLMUINT uiNumErrors; const XFLM_DB_HDR * pDbHdr = pDbInfo->getDbHdr(); f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, 0); OutUINT( "Default Language", (FLMUINT)pDbHdr->ui8DefaultLanguage); OutUINT64( "File Size", pDbInfo->getFileSize()); OutUINT( "Index Count", pDbInfo->getNumIndexes()); OutUINT( "Collection Count", pDbInfo->getNumCollections()); OutUINT( "Block Size", (FLMUINT)pDbHdr->ui16BlockSize); OutLine( "LOG HEADER"); OutUINT( " First LFH Block Address", (FLMUINT)pDbHdr->ui32FirstLFBlkAddr); OutLine( "MISCELLANEOUS BLOCK STATISTICS"); OutBlkHeader(); ui64ElementCount = 0; ui64ContElementCount = 0; ui64ContElmBytes = 0; pDbInfo->getAvailBlockStats( &ui64BytesUsed, &uiBlockCount, &i32LastError, &uiNumErrors); if( uiBlockCount) { OutOneBlockStat( " Avail", (FLMUINT)pDbHdr->ui16BlockSize, 0, ui64BytesUsed, ui64ElementCount, ui64ContElementCount, ui64ContElmBytes, uiBlockCount, i32LastError, uiNumErrors); } ui64ElementCount = 0; ui64ContElementCount = 0; ui64ContElmBytes = 0; pDbInfo->getLFHBlockStats( &ui64BytesUsed, &uiBlockCount, &i32LastError, &uiNumErrors); if( uiBlockCount) { OutOneBlockStat( " LFH", (FLMUINT)pDbHdr->ui16BlockSize, 0, ui64BytesUsed, ui64ElementCount, ui64ContElementCount, ui64ContElmBytes, uiBlockCount, i32LastError, uiNumErrors); } uiNumLogicalFiles = pDbInfo->getNumLogicalFiles(); for( uiLoop = 0; uiLoop < uiNumLogicalFiles; uiLoop++) { OutLogicalFile( pDbInfo, uiLoop); } } /******************************************************************** Desc: *********************************************************************/ FSTATIC FLMUINT CheckShowError( const char * pszMessage, FLMBOOL bLogIt) { FLMUINT uiResKey; f_sprintf( gv_szLastError, "%s", pszMessage); if( bLogIt) { LogString( pszMessage); } if( gv_bBatchMode) { uiResKey = 0; } else { f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conSetBackFore( FLM_RED, FLM_WHITE); f_conStrOut( pszMessage); f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); f_conStrOut( "Press ENTER to continue, ESC to quit"); for( ;;) { if( gv_bShutdown) { uiResKey = FKB_ESCAPE; break; } else if( f_conHaveKey()) { uiResKey = f_conGetKey(); if( (uiResKey == FKB_ENTER) || (uiResKey == FKB_ESCAPE)) { break; } } } f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 2)); } return( uiResKey); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void OutLabel( FLMUINT uiCol, FLMUINT uiRow, const char * pszLabel, const char * pszValue, FLMUINT64 ui64NumValue, FLMBOOL bLogIt) { char szTmpBuf[ 100]; FLMUINT uiLoop; f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conStrOutXY( pszLabel, uiCol, uiRow); for( uiLoop = f_conGetCursorColumn(); uiLoop < VALUE_COLUMN - 1; uiLoop++) { f_conStrOut( "."); } if( pszValue != NULL) { DisplayValue( uiRow, pszValue); } else { DisplayNumValue( uiRow, ui64NumValue); } if( (bLogIt) && (gv_bLoggingEnabled)) { f_strcpy( szTmpBuf, pszLabel); f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], ": "); if( pszValue != NULL) { f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], pszValue); } else { f_sprintf( &szTmpBuf[ f_strlen( szTmpBuf)], "%I64u", ui64NumValue); } LogString( szTmpBuf); } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void DisplayValue( FLMUINT uiRow, const char * pszValue) { f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conStrOutXY( pszValue, VALUE_COLUMN, uiRow); f_conClearLine( 255, 255); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void DisplayNumValue( FLMUINT uiRow, FLMUINT64 ui64Number) { char szTmpBuf[ 128]; f_sprintf( szTmpBuf, "%,23I64u 0x%016I64X", ui64Number, ui64Number); DisplayValue( uiRow, szTmpBuf); } /******************************************************************** Desc: *********************************************************************/ FSTATIC RCODE GetUserInput( void ) { FLMUINT uiChar; f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 1)); f_conSetBackFore( FLM_RED, FLM_WHITE); f_conStrOut( "Q,ESC=Quit, Other=Continue"); for( ;;) { if( gv_bShutdown) { uiChar = FKB_ESCAPE; break; } else if( f_conHaveKey()) { uiChar = f_conGetKey(); break; } } f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 1)); switch( uiChar) { case 'q': case 'Q': case FKB_ESCAPE: return( RC_SET( NE_XFLM_FAILURE)); default: break; } return( NE_XFLM_OK); } /******************************************************************** Desc: *********************************************************************/ RCODE F_LocalCheckStatus::reportProgress( XFLM_PROGRESS_CHECK_INFO * pProgCheck) { RCODE rc = NE_XFLM_OK; XFLM_CACHE_INFO cacheInfo; char szWhat[ 256]; char szLfName[ 128]; FLMUINT uiCurrentTime; uiCurrentTime = FLM_TIMER_UNITS_TO_MILLI( FLM_GET_TIMER()); if( (uiCurrentTime - m_uiLastRefresh < SCREEN_REFRESH_RATE) && !pProgCheck->bStartFlag) { goto Exit; } // We have exceeded our refresh interval or we have changed check phases, // therefore, we should refresh the screen. m_uiLastRefresh = uiCurrentTime; gv_pDbSystem->getCacheInfo( &cacheInfo); DisplayNumValue( CACHE_USED_ROW, cacheInfo.BlockCache.uiByteCount); if( gv_bShutdown) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Update the display first gv_ui64BytesDone = pProgCheck->ui64BytesExamined; DisplayNumValue( TOTAL_KEYS_ROW, pProgCheck->ui64NumKeys); DisplayNumValue( TOTAL_DUPS_ROW, pProgCheck->ui64NumDuplicateKeys); DisplayNumValue( TOTAL_KEYS_EXAM_ROW, pProgCheck->ui64NumKeysExamined); DisplayNumValue( CONFLICT_ROW, pProgCheck->ui64NumConflicts); DisplayNumValue( BAD_IXREF_ROW, pProgCheck->ui64NumKeysNotFound); DisplayNumValue( MISSING_IXREF_ROW, pProgCheck->ui64NumDocKeysNotFound); DisplayNumValue( TOTAL_DOM_NODES_ROW, pProgCheck->ui64NumDomNodes); DisplayNumValue( DOM_LINKS_VERIFIED_ROW, pProgCheck->ui64NumDomLinksVerified); DisplayNumValue( TOTAL_BROKEN_LINKS_ROW, pProgCheck->ui64NumBrokenDomLinks); DisplayNumValue( REPAIR_ROW, pProgCheck->ui32NumProblemsFixed); gv_uiRepairCount = (FLMUINT)pProgCheck->ui32NumProblemsFixed; if( pProgCheck->i32CheckPhase != XFLM_CHECK_RS_SORT) { OutLabel( LABEL_COLUMN, AMOUNT_DONE_ROW, "Bytes Checked", NULL, gv_ui64BytesDone, FALSE); } if( pProgCheck->bStartFlag) { gv_ui64FileSize = pProgCheck->ui64FileSize; DisplayNumValue( FILE_SIZE_ROW, gv_ui64FileSize); switch( pProgCheck->i32CheckPhase) { case XFLM_CHECK_LFH_BLOCKS: f_strcpy( szWhat, "LFH BLOCKS"); break; case XFLM_CHECK_B_TREE: *szLfName = '\0'; if( pProgCheck->ui32LfType == XFLM_LF_INDEX) { f_strcpy( szWhat, "INDEX: "); (void)NumToName( pProgCheck->ui32LfNumber, ELM_INDEX_TAG, szLfName); } else if( pProgCheck->ui32LfType == XFLM_LF_COLLECTION) { f_strcpy( szWhat, "COLLECTION: "); (void)NumToName( pProgCheck->ui32LfNumber, ELM_COLLECTION_TAG, szLfName); } else { f_strcpy( szWhat, "DICTIONARY: "); (void)NumToName( pProgCheck->ui32LfNumber, ELM_INDEX_TAG, szLfName); } f_strcpy( &szWhat[ f_strlen( szWhat)], szLfName); f_sprintf( &szWhat[ f_strlen( szWhat)], " (%u)", (unsigned)pProgCheck->ui32LfNumber); szWhat[ 50] = '\0'; break; case XFLM_CHECK_AVAIL_BLOCKS: f_strcpy( szWhat, "AVAIL BLOCKS"); break; case XFLM_CHECK_RS_SORT: f_strcpy( szWhat, "SORTING INDEX KEYS"); break; case XFLM_CHECK_DOM_LINKS: f_strcpy( szWhat, "COLLECTION: "); (void)NumToName( pProgCheck->ui32LfNumber, ELM_COLLECTION_TAG, szLfName); f_strcpy( &szWhat[ f_strlen( szWhat)], szLfName); f_sprintf( &szWhat[ f_strlen( szWhat)], " (%u)", (unsigned)pProgCheck->ui32LfNumber); szWhat[ 50] = '\0'; break; default: break; } szWhat[ 45] = '\0'; f_conSetBackFore( FLM_BLUE, FLM_WHITE); DisplayValue( DOING_ROW, szWhat); } else if( f_conHaveKey() && (f_conGetKey() == FKB_ESCAPE)) { f_conSetBackFore( FLM_BLUE, FLM_WHITE); f_conSetCursorPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conSetBackFore( FLM_RED, FLM_WHITE); f_conStrOut( "ESCAPE key pressed.\n"); rc = GetUserInput(); f_conClearScreen( 0, (FLMUINT)(gv_uiMaxRow - 2)); f_conSetBackFore( FLM_BLUE, FLM_WHITE); goto Exit; } Exit: return rc; } RCODE F_LocalCheckStatus::reportCheckErr( XFLM_CORRUPT_INFO * pCorruptInfo, FLMBOOL * pbFix) { RCODE rc = NE_XFLM_OK; XFLM_CACHE_INFO cacheInfo; gv_pDbSystem->getCacheInfo( &cacheInfo); DisplayNumValue( CACHE_USED_ROW, cacheInfo.BlockCache.uiByteCount); if( (gv_bLoggingEnabled) && ((gv_bShowStats) || (pCorruptInfo->i32ErrCode != FLM_OLD_VIEW))) { LogCorruptError( pCorruptInfo); } f_conSetBackFore( FLM_BLUE, FLM_WHITE); if( pCorruptInfo->i32ErrCode == FLM_OLD_VIEW) { gv_uiOldViewCount++; DisplayNumValue( OLD_VIEW_ROW, gv_uiOldViewCount); } else { gv_uiCorruptCount++; gv_uiTotalCorruptions++; DisplayNumValue( CORRUPT_ROW, gv_uiCorruptCount); DisplayNumValue( TOTAL_CORRUPT_ROW, gv_uiTotalCorruptions); } if (pbFix) { *pbFix = gv_bRepairCorruptions; } return( rc); } /******************************************************************** Desc: *********************************************************************/ FSTATIC void LogStr( FLMUINT uiIndent, const char * pszStr) { FLMUINT uiLoop; if( gv_bLoggingEnabled) { for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) { gv_pszLogBuffer[ gv_uiLogBufferCount++] = ' '; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { LogFlush(); } } LogString( pszStr); } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void LogCorruptError( XFLM_CORRUPT_INFO * pCorrupt) { char szWhat[ 20]; char szTmpBuf[ 100]; switch( pCorrupt->ui32ErrLocale) { case XFLM_LOCALE_LFH_LIST: { LogStr( 0, "ERROR IN LFH LINKED LIST:"); break; } case XFLM_LOCALE_AVAIL_LIST: { LogStr( 0, "ERROR IN AVAIL LINKED LIST:"); break; } case XFLM_LOCALE_B_TREE: { if( pCorrupt->i32ErrCode == FLM_OLD_VIEW) { LogStr( 0, "OLD VIEW"); } else { if( pCorrupt->ui64ErrNodeId) { f_strcpy( szWhat, "NODE"); } else if( pCorrupt->ui32ErrElmOffset) { f_strcpy( szWhat, "ELEMENT"); } else if( pCorrupt->ui32ErrBlkAddress) { f_strcpy( szWhat, "BLOCK"); } else { f_strcpy( szWhat, "LAST BLOCK"); } f_sprintf( szTmpBuf, "BAD %s", szWhat); LogStr( 0, szTmpBuf); } // Log the logical file number, name, and type f_sprintf( szTmpBuf, "Logical File Number: %u", (unsigned)pCorrupt->ui32ErrLfNumber); LogStr( 2, szTmpBuf); switch( pCorrupt->ui32ErrLfType) { case XFLM_LF_COLLECTION: { f_strcpy( szWhat, "Collection"); break; } case XFLM_LF_INDEX: { f_strcpy( szWhat, "Index"); break; } default: { f_sprintf( szWhat, "?%u", (unsigned)pCorrupt->ui32ErrLfType); break; } } f_sprintf( szTmpBuf, "Logical File Type: %s", szWhat); LogStr( 2, szTmpBuf); // Log the level in the B-Tree, if known if( pCorrupt->ui32ErrBTreeLevel != 0xFF) { f_sprintf( szTmpBuf, "Level in B-Tree: %u", (unsigned)pCorrupt->ui32ErrBTreeLevel); LogStr( 2, szTmpBuf); } break; } case XFLM_LOCALE_INDEX: { f_strcpy( szWhat, "Index"); LogKeyError( pCorrupt); break; } default: { pCorrupt->ui32ErrLocale = 0; break; } } // Log the block address, if known if( pCorrupt->ui32ErrBlkAddress) { f_sprintf( szTmpBuf, "Block Address: 0x%08X (%u)", (unsigned)pCorrupt->ui32ErrBlkAddress, (unsigned)pCorrupt->ui32ErrBlkAddress); LogStr( 2, szTmpBuf); } // Log the parent block address, if known if( pCorrupt->ui32ErrParentBlkAddress) { if( pCorrupt->ui32ErrParentBlkAddress != FLM_MAX_UINT32) { f_sprintf( szTmpBuf, "Parent Block Address: 0x%08X (%u)", (unsigned)pCorrupt->ui32ErrParentBlkAddress, (unsigned)pCorrupt->ui32ErrParentBlkAddress); } else { f_sprintf( szTmpBuf, "Parent Block Address: NONE, Root Block"); } LogStr( 2, szTmpBuf); } // Log the element offset, if known if( pCorrupt->ui32ErrElmOffset != FLM_MAX_UINT32) { f_sprintf( szTmpBuf, "Element Offset: %u", (unsigned)pCorrupt->ui32ErrElmOffset); LogStr( 2, szTmpBuf); } // Log the NodeId, if known if( pCorrupt->ui64ErrNodeId) { f_sprintf( szTmpBuf, "NodeId: %u", (unsigned)pCorrupt->ui64ErrNodeId); LogStr( 2, szTmpBuf); } f_strcpy( szTmpBuf, gv_pDbSystem->checkErrorToStr( (FLMINT)pCorrupt->i32ErrCode)); f_sprintf( &szTmpBuf[ f_strlen( szTmpBuf)], " (%d)", (int)pCorrupt->i32ErrCode); LogStr( 2, szTmpBuf); LogStr( 0, NULL); if( gv_bLoggingEnabled) { gv_pLogFile->flush(); } } /******************************************************************** Desc: *********************************************************************/ FSTATIC void LogKeyError( XFLM_CORRUPT_INFO * pCorrupt) { FLMUINT uiLogItem; IF_DataVector * ifpKey = NULL; FLMUINT uiIndent; FLMUINT uiLevelOffset; char szNameBuf[ 200]; char szTmpBuf[ 200]; FLMUINT uiElementNumber; (void)NumToName( (FLMUINT)pCorrupt->ui32ErrLfNumber, ELM_INDEX_TAG, szNameBuf); LogString( NULL); LogString( NULL); f_sprintf( szTmpBuf, "ERROR IN INDEX: %s", szNameBuf); LogString( szTmpBuf); uiLogItem = 'K'; uiLevelOffset = 0; for( ;;) { uiIndent = 2; if( uiLogItem == 'K') { if( (ifpKey = pCorrupt->ifpErrIxKey) == NULL) { uiLogItem = 'L'; continue; } LogString( NULL); LogString( " PROBLEM KEY"); } uiElementNumber = 0; while( ifpKey->getNameId( uiElementNumber)) { DisplayField( ifpKey, uiElementNumber, uiIndent, uiLevelOffset); uiElementNumber++; } if( uiLogItem == 'L') { break; } else { uiLogItem = 'L'; } } } /*************************************************************************** Name: DisplayField Desc: This routine displays a field to the screen. *****************************************************************************/ FSTATIC FLMBOOL DisplayField( IF_DataVector * ifpKey, FLMUINT uiElementNumber, FLMUINT uiStartCol, FLMUINT uiLevelOffset ) { char szTmpBuf[ 220]; FLMUINT uiLoop; FLMUINT uiLen; FLMUINT uiBinLen; FLMUINT uiTmpLen; char * pszTmp; FLMBYTE * pucTmp; FLMBYTE ucTmpBin [80]; FLMUINT uiNum; FLMUINT uiIndent = (uiLevelOffset * 2) + uiStartCol; FLMUINT64 ui64NodeId; // Insert leading spaces to indent for level for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) { szTmpBuf[ uiLoop] = ' '; } // Output level and tag if (ifpKey->isKeyComponent( uiElementNumber)) { f_sprintf( &szTmpBuf[ uiIndent], "K) "); } else { f_sprintf( &szTmpBuf[ uiIndent], "D) "); } (void)NumToName( ifpKey->getNameId( uiElementNumber), ifpKey->isAttr( uiElementNumber) ? ELM_ATTRIBUTE_TAG : ELM_ELEMENT_TAG, &szTmpBuf[ f_strlen( szTmpBuf)]); // Output what will fit of the value on the rest of the line uiLen = f_strlen( szTmpBuf); szTmpBuf[ uiLen++] = ' '; szTmpBuf[ uiLen] = 0; if (!ifpKey->getDataLength( uiElementNumber)) { goto Exit; } switch( ifpKey->getDataType( uiElementNumber)) { case XFLM_TEXT_TYPE: pszTmp = &szTmpBuf[ uiLen]; uiLen = 80 - uiLen; ifpKey->getUTF8( uiElementNumber, (FLMBYTE *)pszTmp, &uiLen); break; case XFLM_NUMBER_TYPE: ifpKey->getUINT( uiElementNumber, &uiNum); f_sprintf( &szTmpBuf [uiLen], "%u", (unsigned)uiNum); break; case XFLM_BINARY_TYPE: ifpKey->getBinary( uiElementNumber, NULL, &uiBinLen); uiTmpLen = sizeof( ucTmpBin); ifpKey->getBinary( uiElementNumber, ucTmpBin, &uiTmpLen); pucTmp = &ucTmpBin [0]; while (uiBinLen && uiLen < 77) { f_sprintf( &szTmpBuf [uiLen], "%02X ", (unsigned)*pucTmp); uiBinLen--; pucTmp++; uiLen += 3; } szTmpBuf [uiLen - 1] = 0; break; default: break; } // Get the Id to display if ((ui64NodeId = ifpKey->getID( uiElementNumber)) != 0) { uiLen = f_strlen( szTmpBuf); f_sprintf( &szTmpBuf[ uiLen], " %I64u", ui64NodeId); } // Output the line Exit: LogString( szTmpBuf); return( TRUE); } /******************************************************************** Desc: *********************************************************************/ FSTATIC FLMBOOL NumToName( FLMUINT uiNum, FLMUINT uiType, char * pszBuf ) { FLMUINT uiLen = 128; if (gv_pNameTable && RC_OK( gv_pNameTable->getFromTagTypeAndNum( gv_pDb, uiType, uiNum, NULL, pszBuf, &uiLen))) { return( TRUE); } switch (uiType) { case ELM_INDEX_TAG: { if (uiNum == XFLM_DICT_NUMBER_INDEX) { f_strcpy( pszBuf, "Dictionary Number Index"); return( TRUE); } else if (uiNum == XFLM_DICT_NAME_INDEX) { f_strcpy( pszBuf, "Dictionary Name Index"); return( TRUE); } } case ELM_COLLECTION_TAG: { if (uiNum == XFLM_DATA_COLLECTION) { f_strcpy( pszBuf, "Data Collection"); return( TRUE); } else if (uiNum == XFLM_DICT_COLLECTION) { f_strcpy( pszBuf, "Dictionary Collection"); return( TRUE); } } } f_sprintf( pszBuf, "#%u", (unsigned)uiNum); return( FALSE); } libxflaim-5.1.969/util/indextest2srv.cpp0000644000175000017500000003672010511001742021552 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Indexing Unit Test 2 // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: indextest2srv.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #ifndef DB_NAME_STR #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\IX2.DB" #else #define DB_NAME_STR "ix2.db" #endif #endif /**************************************************************************** Desc: ****************************************************************************/ class IIndexTest2Impl : public TestBase { public: const char * getName( void); RCODE runSuite1( void); RCODE runSuite2( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IIndexTest2Impl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IIndexTest2Impl::getName( void) { return( "Index Test 2"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IIndexTest2Impl::execute( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = runSuite1())) { goto Exit; } if( RC_BAD( rc = runSuite2())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IIndexTest2Impl::runSuite1( void) { RCODE rc = NE_XFLM_OK; FLMBYTE szBuf[ 128]; FLMBYTE pucTemp[256]; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransStarted = FALSE; IF_DOMNode * pDocument = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pNameNode = NULL; IF_DOMNode * pPrevResult = NULL; IF_DOMNode * pNextResult = NULL; FLMUINT uiNameDef = 0; FLMUINT uiFirstDef = 0; FLMUINT uiLastDef = 0; FLMUINT i = 0; FlagSet firstNamePlusLastNameFlags; IF_Query * pQuery = NULL; KeyIterator * pKeyIter = f_new KeyIterator(); const char * pszIndexDef1 = " " // HARD-CODED Dict Num for easy retrieval " " " " " " " " " "; // NOTE: If you make any modifications here, make sure these arrays have // the same number of elements. char * pszFirstNames[] = { "Gavin", "James", "John", "Heidi", "Darcey", "Hilary"}; char * pszLastNames[] = { "Jensen", "Stevenson", "Smith", "Christensen", "Miller", "Andersen"}; char ** pszFirstPlusLastNames = NULL; if( RC_BAD( rc = f_alloc( sizeof( char *) * sizeof( pszFirstNames) / sizeof( pszFirstNames[ 0]), &pszFirstPlusLastNames))) { goto Exit; } // Concatenate all pairs to for( i = 0; i < sizeof(pszFirstNames)/sizeof(pszFirstNames[0]); i++) { if( RC_BAD( rc = f_alloc( f_strlen( pszFirstNames[i]) + f_strlen( pszLastNames[i]) + 1, &pszFirstPlusLastNames[ i]))) { goto Exit; } f_strcpy( pszFirstPlusLastNames[i], pszFirstNames[i]); f_strcat( pszFirstPlusLastNames[i], pszLastNames[i]); } firstNamePlusLastNameFlags.init( (FLMBYTE **)pszFirstPlusLastNames, i); beginTest( "Attribute Key Removal Test", "Create a bunch of documents that will cause the generation of " "keys on our indexed attributes. Delete the documents and ensure the keys " "are removed as well.", "No further details.", ""); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = pKeyIter->init( 1, m_pDbSystem, m_pDb))) { MAKE_FLM_ERROR_STRING( "Failed to initialize index key iter.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "name", XFLM_NODATA_TYPE, &uiNameDef))) { MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createAttributeDef(NULL, "first", XFLM_TEXT_TYPE, &uiFirstDef, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create attribute Def.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createAttributeDef(NULL, "last", XFLM_TEXT_TYPE, &uiLastDef, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create attribute Def.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = importBuffer( pszIndexDef1, XFLM_DICT_COLLECTION))) { goto Exit; } // Make a bunch of documents for( i = 0; i < sizeof(pszFirstNames)/sizeof( pszFirstNames[0]); i++) { if ( RC_BAD( rc = m_pDb->createDocument( XFLM_DATA_COLLECTION, &pDocument))) { MAKE_FLM_ERROR_STRING( "createDocument failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pDocument->createNode( m_pDb, ELEMENT_NODE, uiNameDef, XFLM_FIRST_CHILD, &pNameNode))) { MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pNameNode->createAttribute( m_pDb, uiFirstDef, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)pszFirstNames[ i]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pNameNode->createAttribute( m_pDb, uiLastDef, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)pszLastNames[i]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->documentDone( pDocument))) { MAKE_FLM_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } } // First make sure all expected keys were generated pKeyIter->reset(); while( RC_OK( rc = pKeyIter->next())) { if( RC_BAD( rc = pKeyIter->getCurrentKeyVal( 0, szBuf, sizeof( szBuf), NULL))) { MAKE_FLM_ERROR_STRING( "Unable to get key value.", m_szDetails, rc); goto Exit; } f_strcpy( (char *)pucTemp, (char *)szBuf); if( RC_BAD( rc = pKeyIter->getCurrentKeyVal( 1, szBuf, sizeof( szBuf), NULL))) { MAKE_FLM_ERROR_STRING( "Unable to get key value.", m_szDetails, rc); goto Exit; } f_strcat( (char *)pucTemp, (char *)szBuf); if( !firstNamePlusLastNameFlags.setElemFlag( pucTemp)) { MAKE_FLM_ERROR_STRING( "Unexpected key found.", m_szDetails, rc); goto Exit; } } if ( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "Unexpected rc when iterating keys.", m_szDetails, rc); goto Exit; } if( !firstNamePlusLastNameFlags.allElemFlagsSet()) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Expected keys not generated.", m_szDetails, rc); goto Exit; } // Query for all the documents with last == "Miller" if( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "Failed to create query object.", m_szDetails, rc); goto Exit; } // This query should return me the document node if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "@last == \"Miller\""))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } // Delete all documents that had a last attr == "Miller" while( RC_OK( rc = pQuery->getNext( m_pDb, &pNextResult))) { if( pPrevResult) { if( RC_BAD( rc = pPrevResult->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } pPrevResult->Release(); } pPrevResult = pNextResult; pPrevResult->AddRef(); } // Delete the final node if( pPrevResult) { if( RC_BAD( rc = pPrevResult->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } } // remove Miller and make a new cross product firstNamePlusLastNameFlags.removeElem( (FLMBYTE *)"DarceyMiller"); firstNamePlusLastNameFlags.unsetAllFlags(); // Reset the key iterator and make sure we don't get anything unexpected // First make sure all expected keys were generated pKeyIter->reset(); while( RC_OK( rc = pKeyIter->next())) { if ( RC_BAD( rc = pKeyIter->getCurrentKeyVal( 0, szBuf, sizeof( szBuf), NULL))) { MAKE_FLM_ERROR_STRING( "Unable to get key value.", m_szDetails, rc); goto Exit; } f_strcpy( (char *)pucTemp, (char *)szBuf); if( RC_BAD( rc = pKeyIter->getCurrentKeyVal( 1, szBuf, sizeof( szBuf), NULL))) { MAKE_FLM_ERROR_STRING( "Unable to get key value.", m_szDetails, rc); goto Exit; } f_strcat( (char *)pucTemp, (char *)szBuf); if( !firstNamePlusLastNameFlags.setElemFlag( pucTemp)) { MAKE_FLM_ERROR_STRING( "Unexpected key found.", m_szDetails, rc); goto Exit; } } if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "Unexpected rc when iterating keys.", m_szDetails, rc); goto Exit; } if( !firstNamePlusLastNameFlags.allElemFlagsSet()) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Expected keys not generated.", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } for( i = 0; i < sizeof(pszFirstNames)/sizeof(pszFirstNames[0]); i++) { f_free( &pszFirstPlusLastNames[ i]); } f_free( &pszFirstPlusLastNames); if( bTransStarted) { m_pDb->transCommit(); } if( pDocument) { pDocument->Release(); } if( pAttr) { pAttr->Release(); } if( pNameNode) { pNameNode->Release(); } if( pNextResult) { pNextResult->Release(); } if( pPrevResult) { pPrevResult->Release(); } if( pQuery) { pQuery->Release(); } if( pKeyIter) { pKeyIter->Release(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IIndexTest2Impl::runSuite2( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransStarted = FALSE; IF_DataVector * pSearchKey = NULL; FLMUINT uiExtAttrId = 0; char szTemp[30]; FLMUINT uiTemp; const char * pszIndexDef = "" " " " " " " ""; const char * pszDoc = " " "" " 00097210" " 2420" " Frank Sinatra / Blue skies" " cddb/jazz" " blue skies" " night and day" ""; beginTest( "Missing Presence Index Key Test", "", "No further details.", ""); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "ext", XFLM_TEXT_TYPE, &uiExtAttrId))) { MAKE_FLM_ERROR_STRING( "createAttributeDef failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = importBuffer( pszDoc, XFLM_DATA_COLLECTION))) { goto Exit; } if( RC_BAD( rc = importBuffer( pszIndexDef, XFLM_DICT_COLLECTION))) { goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { MAKE_FLM_ERROR_STRING( "createIFDataVector failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->keyRetrieve( 13, NULL, XFLM_FIRST, pSearchKey))) { MAKE_FLM_ERROR_STRING( "keyRetrieve failed", m_szDetails, rc); goto Exit; } uiTemp = sizeof(szTemp); if( RC_BAD( rc = pSearchKey->getUTF8( 0, (FLMBYTE *)szTemp, &uiTemp))) { MAKE_FLM_ERROR_STRING( "getNative failed", m_szDetails, rc); goto Exit; } if( f_strcmp( szTemp, "blue skies") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Invalid key component value", m_szDetails, rc); goto Exit; } if( RC_OK( rc = pSearchKey->getUINT( 1, &uiTemp))) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Invalid key component value", m_szDetails, rc); goto Exit; } // first key - "Blue skies"/ if( RC_BAD( rc = m_pDb->keyRetrieve( 13, pSearchKey, XFLM_EXCL, pSearchKey))) { MAKE_FLM_ERROR_STRING( "keyRetrieve failed", m_szDetails, rc); goto Exit; } // second key "night and day"/ uiTemp = sizeof(szTemp); if( RC_BAD( rc = pSearchKey->getUTF8( 0, (FLMBYTE *)szTemp, &uiTemp))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed", m_szDetails, rc); goto Exit; } if( f_strcmp( szTemp, "night and day") != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Invalid key component value", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pSearchKey->getUINT( 1, &uiTemp))) { MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); goto Exit; } if( uiTemp != uiExtAttrId) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Invalid key component value", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if( pSearchKey) { pSearchKey->Release(); } if( RC_BAD( rc)) { endTest("FAIL"); } if( bTransStarted) { if( RC_OK(rc)) { rc = m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, !bDibCreated); return( rc); } libxflaim-5.1.969/util/binarytest.cpp0000644000175000017500000003135510511001742021111 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Unit test for testing binary values // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: binarytest.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\BIN.DB" #else #define DB_NAME_STR "bin.db" #endif #define BIN_FILE_SIZE 2000000 #define BIG_FILE "BINARYFILE" #define BIN_BUFFER_SIZE 6550 /**************************************************************************** Desc: ****************************************************************************/ class IBinaryTestImpl : public TestBase { public: RCODE verifyData( IF_FileHdl * pFileHdl, FLMUINT64 ui64NodeId); RCODE encryptionTest( IF_FileHdl * pFileHdl, FLMUINT64 ui64NodeId); RCODE buildBinaryFile( void); const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IBinaryTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ const char * IBinaryTestImpl::getName( void) { return( "Binary Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IBinaryTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; IF_FileSystem * pFileSystem = NULL; IF_FileHdl * pFileHdl = NULL; FLMUINT uiBytesRead = 0; FLMUINT uiTotalBytesRead = 0; FLMUINT uiNameId = 0; IF_DOMNode * pNode = NULL; FLMBOOL bLast = FALSE; char szBuffer[1024]; FLMBOOL bTransActive = FALSE; char * pszBuffer = NULL; FLMUINT64 ui64NodeId; beginTest( "Large Binary Value Test", "Insert a large binary value into a DOM Node ", "Self-explanatory", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_ERROR_STRING( "Failed to initialize test state", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = buildBinaryFile())) { MAKE_ERROR_STRING( "Failed to create binary test file", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "bin_data", XFLM_BINARY_TYPE, &uiNameId))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiNameId, &pNode, &ui64NodeId))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } m_pDbSystem->getFileSystem( &pFileSystem); if ( RC_BAD( rc = pFileSystem->openFile( BIG_FILE, FLM_IO_RDONLY, &pFileHdl))) { MAKE_ERROR_STRING( "Failed to open file.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransActive = TRUE; // Set a small binary value so we can test to make sure that the code // allows an element with an embedded value to be changed to one with // a streaming value f_strcpy( szBuffer, "Hello, World!"); if ( RC_BAD( rc = pNode->setBinary( m_pDb, szBuffer, f_strlen( szBuffer) + 1, TRUE))) { MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); goto Exit; } for(;;) { if ( RC_BAD( rc = pFileHdl->read( uiTotalBytesRead, sizeof(szBuffer), szBuffer, &uiBytesRead))) { if ( rc == NE_FLM_IO_END_OF_FILE) { bLast = TRUE; } else { MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); goto Exit; } } if ( RC_BAD( rc = pNode->setBinary( m_pDb, szBuffer, uiBytesRead, bLast))) { MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); goto Exit; } uiTotalBytesRead += uiBytesRead; if ( bLast) { break; } } pNode->Release(); pNode = NULL; bTransActive = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = verifyData( pFileHdl, ui64NodeId))) { goto Exit; } endTest("PASS"); beginTest( "Large Encrypted Binary Value Test", "Insert a large encrypted binary value into a DOM Node ", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransActive = TRUE; if ( RC_BAD( rc = encryptionTest( pFileHdl, ui64NodeId))) { goto Exit; } bTransActive = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } endTest("PASS"); beginTest( "6550 Byte Binary Value Test", "Insert a binary value of size 6550 into a DOM Node ", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransActive = TRUE; if ( RC_BAD( rc = f_alloc( BIN_BUFFER_SIZE, &pszBuffer))) { MAKE_ERROR_STRING( "f_alloc failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) { MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->setBinary( m_pDb, pszBuffer, BIN_BUFFER_SIZE, TRUE))) { MAKE_ERROR_STRING( "setBinary failed", m_szDetails, rc); goto Exit; } bTransActive = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } Exit: if( bTransActive) { if ( RC_BAD( rc)) { m_pDb->transAbort(); } else { m_pDb->transCommit(); } } if( RC_BAD( rc)) { endTest("FAIL"); } else { endTest("PASS"); } if ( pszBuffer) { f_free( &pszBuffer); } if ( pNode) { pNode->Release(); } pFileSystem->deleteFile( BIG_FILE); if ( pFileSystem) { pFileSystem->Release(); } if ( pFileHdl) { pFileHdl->Release(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IBinaryTestImpl::buildBinaryFile( void) { IF_FileSystem * pFileSystem = NULL; IF_FileHdl * pFileHdl = NULL; FLMUINT uiBytesToWrite; FLMUINT uiBytesWritten; FLMUINT uiSpaceLeft = BIN_FILE_SIZE; char szChunk[1000]; char c = 0; RCODE rc = NE_XFLM_OK; m_pDbSystem->getFileSystem( &pFileSystem); pFileSystem->deleteFile( BIG_FILE); if ( RC_BAD( rc = pFileSystem->createFile( BIG_FILE, FLM_IO_RDWR, &pFileHdl))) { MAKE_ERROR_STRING( "File create failed", m_szDetails, rc); goto Exit; } for( ;;) { uiBytesToWrite = f_min( uiSpaceLeft, sizeof(szChunk)); f_memset( szChunk, c++, uiBytesToWrite); if ( RC_BAD( rc = pFileHdl->write( FLM_IO_CURRENT_POS, uiBytesToWrite, szChunk, &uiBytesWritten))) { MAKE_ERROR_STRING( "Write failed", m_szDetails, rc); goto Exit; } uiSpaceLeft -= uiBytesToWrite; if ( !uiSpaceLeft) { break; } } Exit: if ( pFileHdl) { pFileHdl->Release(); } if ( pFileSystem) { pFileSystem->Release(); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IBinaryTestImpl::verifyData( IF_FileHdl * pFileHdl, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; char szBuf1[ 10000]; char szBuf2[ sizeof(szBuf1)]; FLMUINT uiTotalBytesRead = 0; FLMUINT uiBytesRead1 = 0; FLMUINT uiBytesRead2 = 0; FLMBOOL bLast = FALSE; IF_DOMNode * pNode = NULL; m_pDbSystem->clearCache( m_pDb); if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) { goto Exit; } for(;;) { if ( RC_BAD( rc = pFileHdl->read( uiTotalBytesRead, sizeof(szBuf1), szBuf1, &uiBytesRead1))) { if ( rc == NE_FLM_IO_END_OF_FILE) { bLast = TRUE; } else { MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); goto Exit; } } if ( RC_BAD( rc = pNode->getBinary( m_pDb, szBuf2, uiTotalBytesRead, sizeof(szBuf2), &uiBytesRead2))) { if ( !( rc == NE_XFLM_EOF_HIT && bLast)) { MAKE_ERROR_STRING( "getBinary failed.", m_szDetails, rc); goto Exit; } else { rc = NE_XFLM_OK; } } if ( (uiBytesRead1 != uiBytesRead2) || f_memcmp( szBuf1, szBuf2, uiBytesRead1) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "Values do not match", m_szDetails, rc); goto Exit; } uiTotalBytesRead += uiBytesRead1; if ( bLast) { break; } } Exit: if( pNode) { pNode->Release(); } return rc; } #define START_SEED 123 /**************************************************************************** Desc: ****************************************************************************/ RCODE IBinaryTestImpl::encryptionTest( IF_FileHdl * pFileHdl, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; IF_RandomGenerator * pRand = NULL; FLMUINT uiEncDef = 0; FLMUINT64 ui64TotalSize; char * pszBuffer = NULL; FLMBOOL bLast = FALSE; FLMUINT uiTotalBytesRead = 0; FLMUINT uiChunkSize; FLMUINT uiBytesRead; IF_DOMNode * pNode = NULL; #ifdef FLM_USE_NICI if ( RC_BAD( rc = m_pDb->createEncDef( "aes", "aes_def", 0, &uiEncDef))) { MAKE_ERROR_STRING( "Failed to create encryption definition", m_szDetails, rc); goto Exit; } #endif if( RC_BAD( rc = FlmAllocRandomGenerator( &pRand))) { goto Exit; } pRand->setSeed( START_SEED); if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) { MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFileHdl->size( &ui64TotalSize))) { MAKE_ERROR_STRING( "Failed to get file size", m_szDetails, rc); goto Exit; } for(;;) { if ( uiTotalBytesRead < ui64TotalSize) { uiChunkSize = pRand->getUINT32( 1, (FLMUINT32)(ui64TotalSize - uiTotalBytesRead)); if ( pszBuffer) { f_free( &pszBuffer); } if (RC_BAD( rc = f_alloc( uiChunkSize, &pszBuffer))) { MAKE_ERROR_STRING( "f_alloc failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFileHdl->read( uiTotalBytesRead, uiChunkSize, pszBuffer, &uiBytesRead))) { MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); goto Exit; } } else { bLast = TRUE; uiBytesRead = 0; } if( RC_BAD( rc = pNode->setBinary( m_pDb, pszBuffer, uiBytesRead, bLast, uiEncDef))) { MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); goto Exit; } uiTotalBytesRead += uiBytesRead; if ( bLast) { break; } } pNode->Release(); pNode = NULL; if( RC_BAD( rc = verifyData( pFileHdl, ui64NodeId))) { goto Exit; } if (RC_BAD( rc = f_realloc( uiTotalBytesRead, &pszBuffer))) { MAKE_ERROR_STRING( "f_realloc failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) { MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFileHdl->read( 0, uiTotalBytesRead, pszBuffer, &uiBytesRead))) { MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); goto Exit; } // Set the node to have a large non-streaming value if( RC_BAD( rc = pNode->setBinary( m_pDb, pszBuffer, uiBytesRead, TRUE, uiEncDef))) { goto Exit; } pNode->Release(); pNode = NULL; if ( RC_BAD( rc = verifyData( pFileHdl, ui64NodeId))) { goto Exit; } Exit: if( pszBuffer) { f_free( &pszBuffer); } if( pNode) { pNode->Release(); } if( pRand) { pRand->Release(); } return( rc); } libxflaim-5.1.969/util/rebuild.cpp0000644000175000017500000007132710511001742020356 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Rebuild a corrupted database // // Tabs: 3 // // Copyright (c) 1992-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: rebuild.cpp 3129 2006-01-25 11:46:17 -0700 (Wed, 25 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #include "xflaim.h" #include "sharutil.h" #define UTIL_ID "REBUILD" #define LABEL_COLUMN 5 #define VALUE_COLUMN 35 #define PARAM_ROW 1 #define SOURCE_ROW (PARAM_ROW + 1) #define SOURCE_DATA_DIR_ROW (SOURCE_ROW + 1) #define DEST_ROW (SOURCE_DATA_DIR_ROW + 1) #define DEST_DATA_DIR_ROW (DEST_ROW + 1) #define DEST_RFL_ROW (DEST_DATA_DIR_ROW + 1) #define DICT_ROW (DEST_RFL_ROW + 1) #define CACHE_ROW (DICT_ROW + 1) #define LOG_FILE_ROW (CACHE_ROW + 1) #define DOING_ROW (LOG_FILE_ROW + 1) #define TOTAL_REC_ROW (DOING_ROW + 1) #define RECOV_ROW (TOTAL_REC_ROW + 1) #define DICT_RECOV_ROW (RECOV_ROW + 1) #define DISCARD_ROW (DICT_RECOV_ROW + 1) #define MAX_LOG_BUFF 2048 // Local class definitions class F_LocalRebuildStatus : public IF_DbRebuildStatus { public: F_LocalRebuildStatus() { } RCODE FLMAPI reportRebuild( XFLM_REBUILD_INFO * pRebuild); RCODE FLMAPI reportRebuildErr( XFLM_CORRUPT_INFO * pCorruptInfo); private: }; // Local function prototypes FSTATIC FLMBOOL bldDoRebuild( void); FSTATIC void bldShowResults( const char * pszFuncName, RCODE rc, FLMUINT64 ui64TotalNodes, FLMUINT64 ui64NodesRecovered, FLMUINT64 ui64DictNodesRecovered, FLMUINT64 ui64DiscardedDocs); FSTATIC void bldShowHelp( void); FSTATIC FLMBOOL bldGetParams( FLMINT iArgC, char ** ppszArgV); FSTATIC FLMBOOL bldParseHdrInfo( char * pszBuffer); FSTATIC void bldOutLabel( FLMUINT uiCol, FLMUINT uiRow, const char * pszLabel, const char * pszValue, FLMUINT64 ui64NumValue, FLMBOOL bLogIt); FSTATIC void bldLogFlush( void); FSTATIC void bldLogString( const char * pszStr); FSTATIC void bldOutValue( FLMUINT uiRow, const char * pszValue); FSTATIC void bldOutNumValue( FLMUINT uiRow, FLMUINT64 ui64Number); FSTATIC RCODE bldGetUserInput( void); FSTATIC void bldLogStr( FLMUINT uiIndent, const char * pszStr); FSTATIC void bldLogCorruptError( XFLM_CORRUPT_INFO * pCorruptInfo); FSTATIC void bldShowError( const char * pszMessage); FLMBOOL gv_bShutdown = FALSE; static char * gv_pszLogBuffer = NULL; static FLMUINT gv_uiLogBufferCount = 0; static FLMBOOL gv_bBatchMode; static FLMINT32 gv_i32LastDoing; static FLMUINT64 gv_ui64BytesDone; static FLMUINT64 gv_ui64TotalNodes; static FLMUINT64 gv_ui64NodesRecovered; static FLMUINT64 gv_ui64DictNodesRecovered; static FLMUINT64 gv_ui64DiscardedDocs; static char gv_szPassword[ 100]; static char gv_szSrcFileName[ F_PATH_MAX_SIZE]; static char gv_szSrcDataDir [F_PATH_MAX_SIZE]; static char gv_szDestFileName[ F_PATH_MAX_SIZE]; static char gv_szDestDataDir [F_PATH_MAX_SIZE]; static char gv_szDestRflDir [F_PATH_MAX_SIZE]; static char gv_szDictFileName[ F_PATH_MAX_SIZE]; static char gv_szLogFileName[ F_PATH_MAX_SIZE]; static FLMUINT gv_uiCacheSize = 30000; static IF_FileHdl * gv_pLogFile; static FLMBOOL gv_bLoggingEnabled; static char * gv_pszDictPath; static XFLM_CREATE_OPTS gv_DefaultCreateOpts; static FLMBOOL gv_bFixHdrInfo; static FLMBOOL gv_bRunning; static FLMBOOL gv_bPauseBeforeExiting = FALSE; static IF_DbSystem * gv_pDbSystem = NULL; #ifdef FLM_WATCOM_NLM #define main nlm_main #endif /******************************************************************** Desc: ? *********************************************************************/ extern "C" int main( int iArgC, char ** ppszArgV) { int iRetCode = 0; F_Pool logPool; logPool.poolInit( 1024); gv_bBatchMode = FALSE; gv_bRunning = TRUE; if( RC_BAD( FlmAllocDbSystem( &gv_pDbSystem))) { goto Exit; } f_conInit( 0xFFFF, 0xFFFF, "XFLAIM Database Rebuild"); if (RC_BAD( logPool.poolAlloc( MAX_LOG_BUFF, (void **)&gv_pszLogBuffer))) { f_conStrOut( "\nCould not allocate log buffer\n"); goto Exit; } if (bldGetParams( iArgC, (char **)ppszArgV)) { if (!bldDoRebuild()) { iRetCode = 1; } } Exit: if (gv_bPauseBeforeExiting && !gv_bShutdown) { f_conStrOut( "\nPress any character to exit REBUILD: "); for (;;) { if (gv_bShutdown) { break; } if (f_conHaveKey()) { f_conGetKey(); break; } f_yieldCPU(); } } logPool.poolFree(); f_conExit(); if( gv_pDbSystem) { gv_pDbSystem->Release(); } gv_bRunning = FALSE; return( iRetCode); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC FLMBOOL bldDoRebuild( void) { FLMBOOL bOk = TRUE; char szErrMsg[ 100]; char * pszDestRflDir; RCODE rc; F_LocalRebuildStatus dbRebuildStatus; IF_FileSystem * pFileSystem = NULL; if( RC_BAD( rc = FlmGetFileSystem( &pFileSystem))) { goto Exit; } gv_ui64BytesDone = 0; gv_ui64DictNodesRecovered = 0; gv_ui64DiscardedDocs = 0; gv_i32LastDoing = -1; gv_ui64TotalNodes = 0; gv_ui64NodesRecovered = 0; f_conSetBackFore( FLM_BLACK, FLM_LIGHTGRAY); f_conClearScreen( 0, 0); gv_bLoggingEnabled = FALSE; gv_uiLogBufferCount = 0; if( gv_szLogFileName[ 0]) { pFileSystem->deleteFile( gv_szLogFileName); if (RC_OK( rc = pFileSystem->createFile( gv_szLogFileName, FLM_IO_RDWR, &gv_pLogFile))) { gv_bLoggingEnabled = TRUE; } else { f_sprintf( szErrMsg, "Error creating log file: 0x%04X", (unsigned)rc); bldShowError( szErrMsg); bOk = FALSE; goto Exit; } } /* Configure FLAIM */ if (RC_BAD( rc = gv_pDbSystem->setHardMemoryLimit( 0, FALSE, 0, gv_uiCacheSize * 1024, 0, FALSE))) { f_sprintf( szErrMsg, "Error setting cache size for FLAIM share: 0x%04X", (unsigned)rc); bldShowError( szErrMsg); bOk = FALSE; goto Exit; } f_conSetBackFore( FLM_BLACK, FLM_WHITE); if( gv_bLoggingEnabled) { bldLogString( NULL); bldLogString( NULL); bldLogString( NULL); bldLogString( "=========================================================================="); bldLogString( "REBUILD PARAMETERS:"); } f_conClearScreen( 0, PARAM_ROW); f_conStrOutXY( "REBUILD PARAMETERS:", LABEL_COLUMN, PARAM_ROW); bldOutLabel( LABEL_COLUMN + 2, SOURCE_ROW, "Source DB", gv_szSrcFileName, 0, TRUE); bldOutLabel( LABEL_COLUMN + 2, SOURCE_DATA_DIR_ROW, "Src. Data Dir", (gv_szSrcDataDir [0]) ? &gv_szSrcDataDir [0] : "", 0, TRUE); bldOutLabel( LABEL_COLUMN + 2, DEST_ROW, "Destination DB", gv_szDestFileName, 0, TRUE); bldOutLabel( LABEL_COLUMN + 2, DEST_DATA_DIR_ROW, "Dest. Data Dir", (gv_szDestDataDir [0]) ? &gv_szDestDataDir [0] : "", 0, TRUE); bldOutLabel( LABEL_COLUMN + 2, DEST_RFL_ROW, "Dest. RFL Dir", (gv_szDestRflDir [0]) ? &gv_szDestRflDir [0] : "", 0, TRUE); bldOutLabel( LABEL_COLUMN + 2, DICT_ROW, "Dictionary File", (gv_szDictFileName [0]) ? &gv_szDictFileName [0] : "", 0, TRUE); bldOutLabel( LABEL_COLUMN + 2, CACHE_ROW, "Cache (kb)", NULL, gv_uiCacheSize, TRUE); bldOutLabel( LABEL_COLUMN + 2, LOG_FILE_ROW, "Log File", (gv_szLogFileName [0]) ? &gv_szLogFileName [0] : "", 0, TRUE); bldOutLabel( LABEL_COLUMN, DOING_ROW, "Current Action", "Startup ", 0L, FALSE); bldOutLabel( LABEL_COLUMN, TOTAL_REC_ROW, "Total Nodes", NULL, gv_ui64TotalNodes, FALSE); bldOutLabel( LABEL_COLUMN, RECOV_ROW, "Nodes Recovered", NULL, gv_ui64NodesRecovered, FALSE); bldOutLabel( LABEL_COLUMN, DICT_RECOV_ROW, "Dict Items Recov", NULL, gv_ui64DictNodesRecovered, FALSE); bldOutLabel( LABEL_COLUMN, DISCARD_ROW, "Discarded Documents", NULL, gv_ui64DiscardedDocs, FALSE); if( gv_szDictFileName [0]) { gv_pszDictPath = &gv_szDictFileName [0]; } else { gv_pszDictPath = NULL; } pszDestRflDir = ((gv_szDestRflDir [0]) ? &gv_szDestRflDir [0] : NULL); //VISIT: Implement a proper IF_DbRebuildClient!!! rc = gv_pDbSystem->dbRebuild( gv_szSrcFileName, gv_szSrcDataDir, gv_szDestFileName, gv_szDestDataDir, pszDestRflDir, gv_pszDictPath, gv_szPassword, NULL, &gv_ui64TotalNodes, &gv_ui64NodesRecovered, &gv_ui64DiscardedDocs, &dbRebuildStatus); bldShowResults( "DbRebuild", rc, gv_ui64TotalNodes, gv_ui64NodesRecovered, gv_ui64DictNodesRecovered, gv_ui64DiscardedDocs); Exit: if( gv_bLoggingEnabled) { bldLogFlush(); gv_pLogFile->Release(); gv_pLogFile = NULL; } if( pFileSystem) { pFileSystem->Release(); } return( bOk); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldShowResults( const char * pszFuncName, RCODE rc, FLMUINT64 ui64TotalNodes, FLMUINT64 ui64NodesRecovered, FLMUINT64 ui64DictNodesRecovered, FLMUINT64 ui64DiscardedDocs) { char szErrMsg[ 100]; if( RC_BAD( rc)) { if( rc != NE_FLM_FAILURE) { f_strcpy( szErrMsg, "Error calling "); f_strcpy( &szErrMsg[ f_strlen( szErrMsg)], pszFuncName); f_sprintf( &szErrMsg[ f_strlen( szErrMsg)], ": 0x%04X", (unsigned)rc); bldShowError( szErrMsg); if( gv_bLoggingEnabled) { bldLogString( szErrMsg); } } else if( gv_bLoggingEnabled) { bldLogString( "REBUILD HALTED BY USER"); gv_bShutdown = TRUE; } } else { bldOutNumValue( TOTAL_REC_ROW, ui64TotalNodes); bldOutNumValue( RECOV_ROW, ui64NodesRecovered); bldOutNumValue( DICT_RECOV_ROW, ui64DictNodesRecovered); bldOutNumValue( DISCARD_ROW, ui64DiscardedDocs); if( gv_bLoggingEnabled) { f_sprintf( szErrMsg, "Total Nodes: %u", (unsigned)ui64TotalNodes); bldLogString( szErrMsg); f_sprintf( szErrMsg, "Nodes Recovered: %u", (unsigned)ui64NodesRecovered); bldLogString( szErrMsg); f_sprintf( szErrMsg, "Dict Items Recovered: %u", (unsigned)ui64DictNodesRecovered); f_sprintf( szErrMsg, "Discarded Documents: %u", (unsigned)ui64DiscardedDocs); bldLogString( szErrMsg); } f_strcpy( szErrMsg, "Recovery completed successfully"); bldShowError( szErrMsg); if( gv_bLoggingEnabled) { bldLogString( szErrMsg); } } } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldShowHelp( void ) { f_conStrOut( "\n"); f_conStrOut( "Parameters: [Options]\n\n"); f_conStrOut( "SourceName = Name of database which is to be recovered.\n"); f_conStrOut( "DestName = Name of destination database to recover data to. Recovered\n"); f_conStrOut( " records are put in this database.\n"); f_conStrOut( "Options = (may be specified anywhere on command line): \n"); f_conStrOut( " -c = Cache (kilobytes) to use.\n"); f_conStrOut( " -sd = Data directory for source DB.\n"); f_conStrOut( " -dc = Dictionary file to use to create destination DB.\n"); f_conStrOut( " -dd = Data directory for destination DB.\n"); f_conStrOut( " -dr = RFL directory for destination DB.\n"); f_conStrOut( " -l = Log detailed information to .\n"); f_conStrOut( " -w = Specifies a Security Password to be used.\n"); f_conStrOut( " -b = Run in Batch Mode.\n"); f_conStrOut( " -h = Fix file header information. HdrInfo is in the format\n"); f_conStrOut( " BlkSiz:MinRfl:MaxRfl:Lang:FlmVer\n"); f_conStrOut( " -q = Output binary log information to .\n"); f_conStrOut( " -v = Verify binary log information in . NOTE: The\n"); f_conStrOut( " -v and -q options cannot both be specified.\n"); f_conStrOut( " -p = Pause before exiting.\n"); f_conStrOut( " -? = A '?' anywhere in the command line will cause this help\n"); f_conStrOut( " screen to be displayed, with or without the leading '-'.\n"); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC FLMBOOL bldGetParams( FLMINT iArgC, char ** ppszArgV) { #define MAX_ARGS 30 FLMUINT uiLoop; char szErrMsg [100]; char * pszPtr; char * ppszArgs[ MAX_ARGS]; char szCommandBuffer [300]; gv_szSrcFileName [0] = 0; gv_szSrcDataDir [0] = 0; gv_szDestFileName [0] = 0; gv_szDestDataDir [0] = 0; gv_szDestRflDir [0] = 0; gv_szDictFileName [0] = 0; gv_szLogFileName [0] = 0; gv_bFixHdrInfo = FALSE; gv_DefaultCreateOpts.ui32BlockSize = XFLM_DEFAULT_BLKSIZ; gv_DefaultCreateOpts.ui32MinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; gv_DefaultCreateOpts.ui32MaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; gv_DefaultCreateOpts.bKeepRflFiles = XFLM_DEFAULT_KEEP_RFL_FILES_FLAG; gv_DefaultCreateOpts.bLogAbortedTransToRfl = XFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG; gv_DefaultCreateOpts.ui32DefaultLanguage = XFLM_DEFAULT_LANG; gv_DefaultCreateOpts.ui32VersionNum = XFLM_CURRENT_VERSION_NUM; gv_uiCacheSize = 30000; gv_bBatchMode = FALSE; gv_szPassword [0] = 0; // Ask the user to enter parameters if none were entered on the command // line. if( iArgC < 2) { for (;;) { f_conStrOut( "\nRebuild Params (enter ? for help): "); szCommandBuffer[ 0] = 0; f_conLineEdit( szCommandBuffer, sizeof( szCommandBuffer) - 1); if( gv_bShutdown) { return( FALSE); } if( f_stricmp( szCommandBuffer, "?") == 0) { bldShowHelp(); } else { break; } } flmUtilParseParams( szCommandBuffer, MAX_ARGS, &iArgC, &ppszArgs [1]); ppszArgs[ 0] = ppszArgV[ 0]; iArgC++; ppszArgV = &ppszArgs[ 0]; } uiLoop = 1; while (uiLoop < (FLMUINT)iArgC) { pszPtr = ppszArgV [uiLoop]; // See if they specified an option #ifdef FLM_UNIX if (*pszPtr == '-') #else if (*pszPtr == '-' || *pszPtr == '/') #endif { pszPtr++; if (*pszPtr == 'c' || *pszPtr == 'C') { gv_uiCacheSize = f_atoi( pszPtr + 1); } else if (*pszPtr == 'd' || *pszPtr == 'D') { pszPtr++; if (*pszPtr == 'r' || *pszPtr == 'R') { pszPtr++; if (*pszPtr) { f_strcpy( gv_szDestRflDir, pszPtr); } else { bldShowError( "Destination RFL directory not specified"); return( FALSE); } } else if (*pszPtr == 'd' || *pszPtr == 'D') { pszPtr++; if (*pszPtr) { f_strcpy( gv_szDestDataDir, pszPtr); } else { bldShowError( "Destination data directory not specified"); return( FALSE); } } else if (*pszPtr == 'c' || *pszPtr == 'C') { pszPtr++; if (*pszPtr) { f_strcpy( gv_szDictFileName, pszPtr); } else { bldShowError( "Dictionary file name not specified"); return( FALSE); } } else { f_sprintf( szErrMsg, "Invalid option %s", pszPtr-1); bldShowError( szErrMsg); return( FALSE); } } else if (*pszPtr == 's' || *pszPtr == 'S') { pszPtr++; if (*pszPtr == 'd' || *pszPtr == 'D') { pszPtr++; if (*pszPtr) { f_strcpy( gv_szSrcDataDir, pszPtr); } else { bldShowError( "Source data directory not specified"); return( FALSE); } } else { f_sprintf( szErrMsg, "Invalid option %s", pszPtr-1); bldShowError( szErrMsg); return( FALSE); } } else if (*pszPtr == 'h' || *pszPtr == 'H') { pszPtr++; if( *pszPtr) { if( !bldParseHdrInfo( pszPtr)) { return( FALSE); } } else { bldShowError( "Block sizes not specified"); return( FALSE); } } else if (*pszPtr == 'l' || *pszPtr == 'L') { pszPtr++; if (*pszPtr) { f_strcpy( gv_szLogFileName, pszPtr); } else { bldShowError( "Log file name not specified"); return( FALSE); } } else if (*pszPtr == 'w' || *pszPtr == 'W') { pszPtr++; if (*pszPtr) { f_strcpy( gv_szPassword, pszPtr); } else { bldShowError( "Password not specified"); return( FALSE); } } else if (f_stricmp( pszPtr, "P") == 0) { gv_bPauseBeforeExiting = TRUE; } else if (f_stricmp( pszPtr, "B") == 0) { gv_bBatchMode = TRUE; } else if (f_stricmp( pszPtr, "?") == 0) { goto Show_Help; } else { f_sprintf( szErrMsg, "Invalid option %s", pszPtr); bldShowError( szErrMsg); return( FALSE); } } else if (f_stricmp( pszPtr, "?") == 0) { Show_Help: bldShowHelp(); gv_bPauseBeforeExiting = TRUE; return( FALSE); } else if (!gv_szSrcFileName[ 0]) { f_strcpy( gv_szSrcFileName, pszPtr); } else if (!gv_szDestFileName[ 0]) { f_strcpy( gv_szDestFileName, pszPtr); } uiLoop++; } if (!gv_szSrcFileName [0] || !gv_szDestFileName [0]) { goto Show_Help; } return( TRUE); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC FLMBOOL bldParseHdrInfo( char * pszBuffer) { FLMUINT uiNum; FLMBOOL bHaveParam; XFLM_CREATE_OPTS CreateOpts; FLMUINT uiFieldNum; f_memcpy( &CreateOpts, &gv_DefaultCreateOpts, sizeof( XFLM_CREATE_OPTS)); uiFieldNum = 1; for (;;) { uiNum = 0; bHaveParam = FALSE; while ((*pszBuffer == ' ') || (*pszBuffer == ':') || (*pszBuffer == ',') || (*pszBuffer == ';') || (*pszBuffer == '\t')) { pszBuffer++; } if( uiFieldNum == 4) // Language { char szTmpBuf[ 100]; FLMUINT uiTmpLen = 0; while ((*pszBuffer) && (*pszBuffer != ':') && (*pszBuffer != ',') && (*pszBuffer != ';') && (*pszBuffer != ' ') && (*pszBuffer != '\t')) { szTmpBuf[ uiTmpLen++] = *pszBuffer++; } szTmpBuf[ uiTmpLen] = 0; if( uiTmpLen) { uiNum = f_languageToNum( szTmpBuf); if( (!uiNum) && (f_stricmp( szTmpBuf, "US") != 0)) { bldShowError( "Illegal language in header information"); return( FALSE); } bHaveParam = TRUE; } } else { while( (*pszBuffer >= '0') && (*pszBuffer <= '9')) { uiNum *= 10; uiNum += (FLMUINT)(*pszBuffer - '0'); pszBuffer++; bHaveParam = TRUE; } } if( ((*pszBuffer != 0) && (*pszBuffer != ' ') && (*pszBuffer != ':') && (*pszBuffer != ',') && (*pszBuffer != ';') && (*pszBuffer != '\t'))) { bldShowError( "Illegal value in header information"); return( FALSE); } if( bHaveParam) { switch( uiFieldNum) { case 1: if( uiNum != 0 && uiNum != 4096 && uiNum != 8192) { bldShowError( "Illegal block size"); return( FALSE); } CreateOpts.ui32BlockSize = (FLMUINT32)uiNum; break; case 2: CreateOpts.ui32MinRflFileSize = (FLMUINT32)uiNum; break; case 3: CreateOpts.ui32MaxRflFileSize = (FLMUINT32)uiNum; break; case 4: CreateOpts.ui32DefaultLanguage = (FLMUINT32)uiNum; break; case 5: CreateOpts.ui32VersionNum = (FLMUINT32)uiNum; break; default: bldShowError( "Too many parameters in header information"); return( FALSE); } } if( !(*pszBuffer)) { break; } uiFieldNum++; } gv_bFixHdrInfo = TRUE; f_memcpy( &gv_DefaultCreateOpts, &CreateOpts, sizeof( XFLM_CREATE_OPTS)); return( TRUE); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldOutLabel( FLMUINT uiCol, FLMUINT uiRow, const char * pszLabel, const char * pszValue, FLMUINT64 ui64NumValue, FLMBOOL bLogIt) { char szMsg[ 100]; FLMUINT uiLen = (FLMUINT)(VALUE_COLUMN - uiCol - 1); f_memset( szMsg, '.', uiLen); szMsg[ uiLen] = 0; f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conStrOutXY( szMsg, uiCol, uiRow); f_conStrOutXY( pszLabel, uiCol, uiRow); if( pszValue != NULL) { bldOutValue( uiRow, pszValue); } else { bldOutNumValue( uiRow, ui64NumValue); } if( (bLogIt) && (gv_bLoggingEnabled)) { f_strcpy( szMsg, pszLabel); f_strcpy( &szMsg[ f_strlen( szMsg)], ": "); if( pszValue != NULL) { f_strcpy( &szMsg[ f_strlen( szMsg)], pszValue); } else { f_sprintf( (&szMsg[ f_strlen( szMsg)]), "%I64u", ui64NumValue); } bldLogString( szMsg); } } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldOutValue( FLMUINT uiRow, const char * pszValue) { f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conStrOutXY( pszValue, VALUE_COLUMN, uiRow); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldOutNumValue( FLMUINT uiRow, FLMUINT64 ui64Number ) { char szMsg[ 80]; f_sprintf( szMsg, "%-20I64u (0x%16I64X)", ui64Number, ui64Number); bldOutValue( uiRow, szMsg); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC RCODE bldGetUserInput( void ) { FLMUINT uiChar; f_conStrOutXY( "Q,ESC=Quit, Other=Continue: ", 0, 23); for (;;) { if( gv_bShutdown) { uiChar = FKB_ESCAPE; break; } else if( f_conHaveKey()) { uiChar = f_conGetKey(); if( uiChar) { break; } } f_yieldCPU(); } f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conClearScreen( 0, 22); switch( uiChar) { case 'q': case 'Q': case FKB_ESCAPE: return( RC_SET( NE_XFLM_USER_ABORT)); default: break; } return( NE_XFLM_OK); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldLogStr( FLMUINT uiIndent, const char * pszStr) { FLMUINT uiLoop; if( gv_bLoggingEnabled) { for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) { gv_pszLogBuffer[ gv_uiLogBufferCount++] = ' '; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { bldLogFlush(); } } bldLogString( pszStr); } } /******************************************************************** Desc: Log a corruption error to file. *********************************************************************/ FSTATIC void bldLogCorruptError( XFLM_CORRUPT_INFO * pCorruptInfo) { char szBuf[ 100]; // Log the container number bldLogString( NULL); bldLogString( "ERROR IN DATABASE"); f_sprintf( szBuf, "Collection Number: %u", (unsigned)pCorruptInfo->ui32ErrLfNumber); bldLogStr( 2, szBuf); // Log the block address, if known if (pCorruptInfo->ui32ErrBlkAddress) { f_sprintf( szBuf, "Block Address: 0x%08X (%u)", (unsigned)pCorruptInfo->ui32ErrBlkAddress, (unsigned)pCorruptInfo->ui32ErrBlkAddress); bldLogStr( 2, szBuf); } // Log the parent block address, if known if (pCorruptInfo->ui32ErrParentBlkAddress) { f_sprintf( szBuf, "Parent Block Address: 0x%08X (%u)", (unsigned)pCorruptInfo->ui32ErrParentBlkAddress, (unsigned)pCorruptInfo->ui32ErrParentBlkAddress); bldLogStr( 2, szBuf); } // Log the element offset, if known if (pCorruptInfo->ui32ErrElmOffset) { f_sprintf( szBuf, "Offset of Element within Block: %u", (unsigned)pCorruptInfo->ui32ErrElmOffset); bldLogStr( 2, szBuf); } // Log the elment node Id, if known if (pCorruptInfo->ui64ErrNodeId) { f_sprintf( szBuf, "Node Id: %u", (unsigned)pCorruptInfo->ui64ErrNodeId); bldLogStr( 2, szBuf); } // Log the error message f_strcpy( szBuf, gv_pDbSystem->checkErrorToStr( (FLMINT)pCorruptInfo->i32ErrCode)); f_sprintf( (&szBuf [f_strlen( szBuf)]), " (%d)", (int)pCorruptInfo->i32ErrCode); bldLogStr( 2, szBuf); bldLogStr( 0, NULL); } /******************************************************************** Desc: ? *********************************************************************/ RCODE F_LocalRebuildStatus::reportRebuild( XFLM_REBUILD_INFO * pRebuild) { RCODE rc = NE_XFLM_OK; // First update the display if( gv_i32LastDoing != pRebuild->i32DoingFlag) { gv_i32LastDoing = pRebuild->i32DoingFlag; if( gv_i32LastDoing == REBUILD_GET_BLK_SIZ) { bldOutValue( DOING_ROW, "Determining Block Size "); } else if( gv_i32LastDoing == REBUILD_RECOVER_DICT) { bldOutValue( DOING_ROW, "Recovering Dictionaries "); } else { bldOutValue( DOING_ROW, "Recovering Data "); } } if( gv_i32LastDoing != REBUILD_GET_BLK_SIZ) { if( gv_ui64TotalNodes != pRebuild->ui64TotNodes) { gv_ui64TotalNodes = pRebuild->ui64TotNodes; bldOutNumValue( TOTAL_REC_ROW, gv_ui64TotalNodes); } if( gv_i32LastDoing == REBUILD_RECOVER_DICT) { if( gv_ui64DictNodesRecovered != pRebuild->ui64NodesRecov) { gv_ui64DictNodesRecovered = pRebuild->ui64NodesRecov; bldOutNumValue( DICT_RECOV_ROW, gv_ui64DictNodesRecovered); gv_ui64DiscardedDocs = pRebuild->ui64DiscardedDocs; bldOutNumValue( DISCARD_ROW, gv_ui64DiscardedDocs); } } else { if( gv_ui64NodesRecovered != pRebuild->ui64NodesRecov) { gv_ui64NodesRecovered = pRebuild->ui64NodesRecov; bldOutNumValue( RECOV_ROW, gv_ui64NodesRecovered); gv_ui64DiscardedDocs = pRebuild->ui64DiscardedDocs; bldOutNumValue( DISCARD_ROW, gv_ui64DiscardedDocs); } } } // See if they pressed an ESC character if ((f_conHaveKey()) && (f_conGetKey() == FKB_ESCAPE)) { f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conClearScreen( 0, 22); f_conSetBackFore (FLM_RED, FLM_WHITE); f_conStrOutXY( "ESCAPE key pressed", 0, 22); rc = bldGetUserInput(); goto Exit; } f_yieldCPU(); Exit: return( rc); } RCODE F_LocalRebuildStatus::reportRebuildErr( XFLM_CORRUPT_INFO * pCorruptInfo) { RCODE rc = NE_XFLM_OK; bldLogCorruptError( pCorruptInfo); // See if they pressed an ESC character if ((f_conHaveKey()) && (f_conGetKey() == FKB_ESCAPE)) { f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conClearScreen( 0, 22); f_conSetBackFore (FLM_RED, FLM_WHITE); f_conStrOutXY( "ESCAPE key pressed", 0, 22); rc = bldGetUserInput(); goto Exit; } f_yieldCPU(); Exit: return( rc); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldShowError( const char * pszMessage) { if( !gv_bBatchMode) { f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conClearScreen( 0, 22); f_conSetBackFore (FLM_RED, FLM_WHITE); f_conStrOutXY( pszMessage, 0, 22); f_conStrOutXY( "Press any character to continue, ESCAPE to quit: ", 0, 23); for (;;) { if (gv_bShutdown) { break; } else if (f_conHaveKey()) { if (f_conGetKey() == FKB_ESCAPE) { gv_bShutdown = TRUE; } break; } f_yieldCPU(); } f_conSetBackFore (FLM_BLACK, FLM_LIGHTGRAY); f_conClearScreen( 0, 22); } } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldLogFlush( void ) { FLMUINT uiBytesWritten; if( gv_uiLogBufferCount) { gv_pLogFile->write( FLM_IO_CURRENT_POS, gv_uiLogBufferCount, gv_pszLogBuffer, &uiBytesWritten); gv_uiLogBufferCount = 0; } } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void bldLogString( const char * pszStr) { FLMUINT uiLen; FLMUINT uiLoop; if( (gv_bLoggingEnabled) && (gv_pszLogBuffer != NULL)) { uiLen = (FLMUINT)((pszStr != NULL) ? (FLMUINT)f_strlen( pszStr) : 0); for( uiLoop = 0; uiLoop < uiLen; uiLoop++) { gv_pszLogBuffer[ gv_uiLogBufferCount++] = *pszStr++; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { bldLogFlush(); } } gv_pszLogBuffer[ gv_uiLogBufferCount++] = '\r'; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { bldLogFlush(); } gv_pszLogBuffer[ gv_uiLogBufferCount++] = '\n'; if( gv_uiLogBufferCount == MAX_LOG_BUFF) { bldLogFlush(); } } } libxflaim-5.1.969/util/namespacetestsrv.cpp0000644000175000017500000001405710511001742022314 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Namespace unit test // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: namespacetestsrv.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined(NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif /**************************************************************************** Desc: ****************************************************************************/ class INamespaceTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new INamespaceTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * INamespaceTestImpl::getName( void) { return( "Namespace Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE INamespaceTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; IF_PosIStream * pPosIStream = NULL; IF_Query * pQuery = NULL; IF_DOMNode * pReturn = NULL; char szNamespace[128]; FLMBOOL bTransStarted = FALSE; const char * pszDocument = "" " " " 27" " Male" " " " Dishwasher" " 10000" " " " 5'10" " 200" ""; m_szDetails[0] = '\0'; beginTest( "Default Namespace Test", "Set the default namespace of a document and ensure it " "is reflected in the various nodes when it is changed or overridden", "1) create a database 2) import a document containing " "different default namespaces at different levels 3) retrieve various " "nodes and ensure their namespaces are correct.", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "Failed to begin trans.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = importBuffer( pszDocument, XFLM_DATA_COLLECTION))) { goto Exit; } if( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "Failed to create query object.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "{xmlns=\"http://www.novell.com/Bogus\"}/person/name"))) { MAKE_FLM_ERROR_STRING( "Failed to setup query expression.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pReturn->getNamespaceURI( m_pDb, szNamespace, sizeof( szNamespace), NULL))) { MAKE_FLM_ERROR_STRING( "getNamespaceURI failed", m_szDetails, rc); goto Exit; } if ( f_strcmp( szNamespace, "http://www.novell.com/Bogus") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "element has unexpected namespace", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "/occupation/name"))) { MAKE_FLM_ERROR_STRING( "Failed to setup query expression.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pReturn->getNamespaceURI( m_pDb, szNamespace, sizeof( szNamespace), NULL))) { MAKE_FLM_ERROR_STRING( "unexpected rcode from getNamespaceURI.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "{xmlns=\"http://www.novell.com/Bogus\"}/person/height"))) { MAKE_FLM_ERROR_STRING( "Failed to setup query expression.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pReturn->getNamespaceURI( m_pDb, szNamespace, sizeof( szNamespace), NULL))) { MAKE_FLM_ERROR_STRING( "getNamespaceURI failed", m_szDetails, rc); goto Exit; } if ( f_strcmp( szNamespace, "http://www.novell.com/Bogus") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "element has unexpected namespace", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } if( bTransStarted) { RCODE tmpRc = m_pDb->transCommit(); if ( RC_OK( rc)) { rc = tmpRc; } } if( pPosIStream) { pPosIStream->Release(); } if( pQuery) { pQuery->Release(); } if( pReturn) { pReturn->Release(); } RCODE tmpRc = NE_XFLM_OK; tmpRc = shutdownTestState(DB_NAME_STR, TRUE); if( RC_OK( rc)) { rc = tmpRc; } return( rc); } libxflaim-5.1.969/util/dictchangetest.cpp0000644000175000017500000002054510511001742021715 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Dictionary change tests // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: dictchangetest.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif #define NAMESPACE "http://www.bogusnamespace.com" #define MAX_POLL_COUNT 100 /**************************************************************************** Desc: ****************************************************************************/ class IDictChangeTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IDictChangeTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ const char * IDictChangeTestImpl::getName( void) { return( "Dict Change Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IDictChangeTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMUINT uiElmId = 0; IF_DOMNode * pNode = NULL; IF_DOMNode * pDictDef = NULL; IF_DOMNode * pDoc = NULL; IF_DOMNode * pMyAttrNode = NULL; IF_DOMNode * pMyEntryNode = NULL; char szLocalName[100]; FLMUINT uiNumCharsReturned = 0; FLMBOOL bTransStarted = FALSE; FLMUINT uiMyEntryId = 0; FLMUINT uiMyAttrId = 0x80000001; FLMUINT64 ui64EntryId; beginTest( "Dictionary Item Change Test", "Ensure we can change the name of an element definition then reuse the old name", "Create the database/create an element definition/" "create root element/rename element/set element to \"purge\"/" "reuse the old element name", ""); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_ERROR_STRING( "Failed to init test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDb->createElementDef( NAMESPACE, "foo", XFLM_TEXT_TYPE, &uiElmId))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiElmId, &pNode))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"value1"))) { MAKE_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } // change the "foo" element's name then set its state to "purge" if( RC_BAD( rc = m_pDb->getDictionaryDef( ELM_ELEMENT_TAG, uiElmId, &pDictDef))) { MAKE_ERROR_STRING( "getDictionaryDef changed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pDictDef->setAttributeValueUTF8( m_pDb, ATTR_NAME_TAG, (FLMBYTE *)"deleted_foo"))) { MAKE_ERROR_STRING( "setAttributeValueUTF8 failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->documentDone( pDictDef))) { MAKE_ERROR_STRING( "documentDone failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->changeItemState( ELM_ELEMENT_TAG, uiElmId, XFLM_PURGE_OPTION_STR))) { MAKE_ERROR_STRING( "changeItemState failed", m_szDetails, rc); goto Exit; } uiElmId = 0; if ( RC_BAD( rc = m_pDb->createElementDef( NAMESPACE, "foo", XFLM_NUMBER_TYPE, &uiElmId))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiElmId, &pNode))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, sizeof( szLocalName), &uiNumCharsReturned))) { MAKE_ERROR_STRING( "getLocalName failed.", m_szDetails, rc); goto Exit; } if( bTransStarted) { if( RC_BAD( rc)) { m_pDb->transAbort(); } else { m_pDb->transCommit(); } } endTest("PASS"); beginTest( "Dictionary Attribute Purge Test", "Verify that setting a definition to \"purge\" works correctly", "Create the database/create an attribute definition/" "Create element/add an attribute/set attribute def to \"purge\"/" "Test for presence of attribute definition", ""); if( RC_BAD( rc = m_pDb->transBegin(XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "MyElement", XFLM_NODATA_TYPE, &uiMyEntryId))) { MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "MyAttribute", XFLM_NUMBER_TYPE, &uiMyAttrId))) { MAKE_FLM_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createDocument( XFLM_DATA_COLLECTION, &pDoc))) { MAKE_FLM_ERROR_STRING( "createDocument failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pDoc->createNode( m_pDb, ELEMENT_NODE, uiMyEntryId, XFLM_LAST_CHILD, &pMyEntryNode, &ui64EntryId))) { MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pMyEntryNode->createAttribute( m_pDb, uiMyAttrId, &pMyAttrNode))) { MAKE_FLM_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pMyAttrNode->setUINT( m_pDb, 0x18332))) { MAKE_FLM_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pMyEntryNode->setUINT64( m_pDb, 34))) { goto Exit; } if( RC_BAD( rc = m_pDb->documentDone( pDoc))) { MAKE_FLM_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } m_pDb->transCommit(); bTransStarted = FALSE; // Start a transaction and set the state of the uiMyAttrId to "purge" if( RC_BAD( rc = m_pDb->transBegin(XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if( RC_BAD( rc = m_pDb->changeItemState( ELM_ATTRIBUTE_TAG, uiMyAttrId, XFLM_PURGE_OPTION_STR))) { MAKE_ERROR_STRING( "changeItemState failed", m_szDetails, rc); goto Exit; } if( RC_BAD( m_pDb->transCommit())) { goto Exit; } bTransStarted = FALSE; for( FLMINT iCount = 0; iCount < MAX_POLL_COUNT; iCount++) { if( RC_BAD( rc = m_pDb->getDictionaryDef( ELM_ATTRIBUTE_TAG, uiMyAttrId, &pMyAttrNode))) { break; } f_sleep( 100); } if( rc != NE_XFLM_NOT_FOUND) { MAKE_ERROR_STRING( "purge attribute failed", m_szDetails, rc); goto Exit; } else { rc = NE_XFLM_OK; } endTest("PASS"); m_pDb->doCheckpoint( 0); Exit: if( bTransStarted) { if( RC_BAD( rc)) { m_pDb->transAbort(); } else { m_pDb->transCommit(); } } if( RC_BAD( rc)) { endTest("FAIL"); } if( pNode) { pNode->Release(); } if( pMyEntryNode) { pMyEntryNode->Release(); } if( pMyAttrNode) { pMyAttrNode->Release(); } if( pDoc) { pDoc->Release(); } if ( pDictDef) { pDictDef->Release(); } shutdownTestState( DB_NAME_STR, bDibCreated); return rc; } libxflaim-5.1.969/util/domedit.h0000644000175000017500000004220510511001742020013 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: DOM editor // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: domedit.h 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef DOMEDIT_HPP #define DOMEDIT_HPP class F_DomEditor; typedef F_DomEditor * F_DomEditor_p; /* Constants */ #define F_DOMEDIT_BUF_SIZE 0x0000FFFF #define F_DOMEDIT_MAX_TITLE_SIZE 64 /* System flags */ #define F_DOMEDIT_FLAG_NOPARENT 0x00000001 #define F_DOMEDIT_FLAG_NOCHILD 0x00000002 #define F_DOMEDIT_FLAG_LIST_ITEM 0x00000004 #define F_DOMEDIT_FLAG_HIDE_TAG 0x00000008 #define F_DOMEDIT_FLAG_HIDE_LEVEL 0x00000010 #define F_DOMEDIT_FLAG_HIDE_SOURCE 0x00000020 #define F_DOMEDIT_FLAG_READ_ONLY 0x00000040 #define F_DOMEDIT_FLAG_SELECTED 0x00000080 #define F_DOMEDIT_FLAG_ENDTAG 0x00000100 #define F_DOMEDIT_FLAG_NO_DELETE 0x00000200 #define F_DOMEDIT_FLAG_COLLAPSED 0x00000400 #define F_DOMEDIT_FLAG_HIDE_EXPAND 0x00000800 #define F_DOMEDIT_FLAG_COMMENT 0x00001000 #define F_DOMEDIT_FLAG_KEY_FROM 0x00002000 #define F_DOMEDIT_FLAG_KEY_UNTIL 0x00004000 #define F_DOMEDIT_FLAG_NODOM 0x00008000 // Don't fetch a dom object. #define F_DOMEDIT_FLAG_ELEMENT_DATA 0x00010000 // Display the data embedded in the element node (acting as the child) /* Index selection flags */ #define F_DOMEDIT_ISEL_NOIX 0x0001 // Show "no index" as an option /* Configuration options */ #define F_DOMEDIT_CONFIG_STATS_START 0x0001 #define F_DOMEDIT_CONFIG_STATS_STOP 0x0002 #define F_DOMEDIT_CONFIG_STATS_RESET 0x0003 /* Types, enums, etc. */ typedef enum { F_DOMEDIT_EVENT_RECREAD, F_DOMEDIT_EVENT_RECINSERT, F_DOMEDIT_EVENT_GETDISPVAL, F_DOMEDIT_EVENT_GETNEXTNODE, F_DOMEDIT_EVENT_GETPREVNODE, F_DOMEDIT_EVENT_IEDIT, // Interactive editor invoked F_DOMEDIT_EVENT_REFRESH, // Called prior to refresh F_DOMEDIT_EVENT_NAME_TABLE } eDomEventType; #define MAX_DISPLAY_SEGMENT 100 typedef struct { char szString[ MAX_DISPLAY_SEGMENT]; FLMUINT uiCol; eColorType uiForeground; eColorType uiBackground; } DME_DISP_COLUMN; // Structure to hold an entire row typedef struct rowInfo { FLMUINT uiLevel; FLMUINT64 ui64NodeId; FLMUINT64 ui64DocId; FLMUINT uiNameId; FLMBOOL bExpanded; FLMBOOL bHasChildren; FLMBOOL bHasAttributes; FLMBOOL bHasElementData; // When this flag is set, the node is acting as the parent. F_DOMNode * pDomNode; F_DataVector * pVector; eDomNodeType eType; FLMUNICODE * puzValue; FLMUINT uiLength; FLMUINT uiIndex; FLMUINT uiElementNumber; FLMBOOL bUseValue; FLMUINT uiFlags; struct rowInfo * pNext; struct rowInfo * pPrev; } DME_ROW_INFO; typedef struct { FLMBOOL bSingleLevel; FLMUINT uiAnchorLevel; FLMUINT64 ui64NodeId; } DME_ROW_ANCHOR; typedef struct { F_NameTable * pNameTable; FLMBOOL bInitialized; } DME_NAME_TABLE_INFO; /* Callbacks */ typedef RCODE (* F_DOMEDIT_DISP_HOOK)( F_DomEditor * pDomEditor, DME_ROW_INFO * pRow, FLMUINT * puiNumVals); typedef RCODE (* F_DOMEDIT_KEY_HOOK)( F_DomEditor * pDomEditor, DME_ROW_INFO * pCurRow, FLMUINT uiKeyIn, FLMUINT * puiKeyOut, void * pvKeyData); typedef RCODE (* F_DOMEDIT_HELP_HOOK)( F_DomEditor * pDomEditor, F_DomEditor * pHelpEditor); typedef RCODE (* F_DOMEDIT_EVENT_HOOK)( F_DomEditor * pDomEditor, eDomEventType eEventType, void * EventData, void * UserData); class F_DomEditor : public F_Object { public: F_DomEditor( void); ~F_DomEditor( void); void reset( void); RCODE Setup( FTX_SCREEN * pScreen); void setParent( F_DomEditor * pParent); F_DomEditor * getParentEditor( void); RCODE setSource( F_Db * pDb, FLMUINT uiCollection); F_Db * getSource( void ) { return m_pDb; } RCODE insertRow( DME_ROW_INFO * pRow, DME_ROW_INFO * pStartRow = NULL); RCODE setTitle( const char * pszTitle); void setCurrentAtTop( void); void setCurrentAtBottom( void); void setReadOnly( FLMBOOL bReadOnly); RCODE interactiveEdit( FLMUINT uiULX = 0, FLMUINT uiULY = 0, FLMUINT uiLRX = 0, FLMUINT uiLRY = 0, FLMBOOL bBorder = TRUE, FLMUINT uiStatusLines = 1, FLMUINT uiStartChar = 0); FLMBOOL isMonochrome( void); RCODE getPrevRow( DME_ROW_INFO * pCurRow, DME_ROW_INFO ** ppPrevRow, FLMBOOL bFetchPrevRow = FALSE); RCODE getNextRow( DME_ROW_INFO * pCurRow, DME_ROW_INFO ** ppNextRow, FLMBOOL bFetchNextRow = FALSE, FLMBOOL bIgnoreAnchor = FALSE); RCODE getNextDocument( DME_ROW_INFO * pCurRow, DME_ROW_INFO ** ppNextRow, FLMBOOL bFetchNextRow = FALSE, FLMBOOL bIgnoreAnchor = FALSE); void setScrFirstRow( DME_ROW_INFO * pScrFirstRow); DME_ROW_INFO * getScrFirstRow( void); DME_ROW_INFO * getScrLastRow( void); FLMUINT getCursorRow( void); FLMUINT getNumRows( void); FLMUINT getDispRows( void) { return m_uiNumRows; } void setNumDispRows( FLMUINT uiNumDispRows) { m_uiNumRows = uiNumDispRows; } RCODE getDisplayValue( DME_ROW_INFO * pRow, char * pszBuf, FLMUINT uiBufSize); DME_ROW_INFO * findRecord( FLMUINT uiCollection, FLMUINT uiDrn, DME_ROW_INFO * pStartRow = NULL); void setShutdown( FLMBOOL * pbShutdown); FLMBOOL * getShutdown( void); RCODE setCurrentRow( DME_ROW_INFO * pCurRow, FLMUINT uiCurRow); DME_ROW_INFO * getCurrentRow( FLMUINT * puiCurRow); RCODE setFirstRow( DME_ROW_INFO * pRow); RCODE setControlFlags( DME_ROW_INFO * pCurRow, FLMUINT uiFlags); RCODE getControlFlags( DME_ROW_INFO * pCurRow, FLMUINT * puiFlags); void setDisplayHook( F_DOMEDIT_DISP_HOOK pDispHook, void * DispData); void setKeyHook( F_DOMEDIT_KEY_HOOK pKeyHook, void * KeyData); void setHelpHook( F_DOMEDIT_HELP_HOOK pHelpHook, void * HelpData); void setEventHook( F_DOMEDIT_EVENT_HOOK pEventHook, void * EventData); F_Db * getDb( void); FLMUINT getCollection( void); FLMUINT getLastKey( void); RCODE getNumber( char * pszBuf, FLMUINT64 * pui64Value, FLMINT64 * pi64Value); RCODE getCollectionNumber( char * pszCollectionName, FLMUINT * puiCollectionNum); RCODE getIndexNumber( char * pszIndexName, FLMUINT * puiIndexNum); RCODE retrieveNodeFromDb( FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId); RCODE requestInput( const char * pszMessage, char * pszResponse, FLMUINT uiMaxRespLen, FLMUINT * puiTermChar); RCODE displayMessage( const char * pszMessage, RCODE rcOfMessage, FLMUINT * puiTermChar, eColorType uiBackground, eColorType uiForeground); RCODE globalConfig( FLMUINT uiOption); RCODE createStatusWindow( const char * pszTitle, eColorType uiBack, eColorType uiFore, FLMUINT * puiCols, FLMUINT * puiRows, FTX_WINDOW ** ppWindow); FLMUINT getULX( void); FLMUINT getULY( void); RCODE openNewDb(); RCODE refreshNameTable( void); RCODE retrieveDocumentList( FLMUINT uiCollection, FLMUINT64 * pui64NodeId, FLMUINT * puiTermChar); RCODE getDomNode( FLMUINT64 ui64NodeId, FLMUINT uiAttrNameId, F_DOMNode ** ppDomNode); void setDocList( FLMBOOL bDocList ) { m_bDocList = bDocList; } DME_DISP_COLUMN * getDispColumns( void) { return &m_dispColumns[0]; } FINLINE FLMUINT getEditCanvasCols( void); RCODE getNodeName( F_DOMNode * pDOMNode, DME_ROW_INFO * pRow, FLMUNICODE ** ppuzName, FLMUINT * puiBufSize = NULL); RCODE getNodeValue( F_DOMNode * pDOMNode, FLMUNICODE ** ppuzValue, FLMUINT * puiBufSize = NULL, FLMBOOL bStartTrans = TRUE); RCODE beginTransaction( eDbTransType eTransType); RCODE commitTransaction( void); RCODE abortTransaction( void); void doQuery( char * pszQuery, FLMUINT uiQueryBufSize); RCODE indexList( void); RCODE editIndexRow( DME_ROW_INFO * pCurRow); RCODE editIndexNode( DME_ROW_INFO * pCurRow); FLMBOOL canEditRow( DME_ROW_INFO * pCurRow); void releaseAllRows( void); FINLINE FLMBOOL isDocList( void) { return( m_bDocList); } FINLINE FLMUINT getCanvasCols( void) { return m_uiEditCanvasCols; } RCODE viewOnlyDeleteIxKey( void); FINLINE FTX_WINDOW * getStatusWindow( void) { return( m_pEditStatusWin); } private: /* Methods */ void checkDocument( DME_ROW_INFO ** ppDocRow, DME_ROW_INFO * pRow); RCODE asciiUCMixToUC( char * pszAscii, FLMUNICODE * puzUnicode, FLMUINT uiMaxUniChars); RCODE UCToAsciiUCMix( FLMUNICODE * puzUnicode, char * pszAscii, FLMUINT uiMaxAsciiChars); FINLINE DME_ROW_ANCHOR * getRowAnchor( void); FINLINE void setRowAnchor( DME_ROW_ANCHOR * pRowAnchor); RCODE editTextBuffer( char ** ppszBuffer, FLMUINT uiBufSize, FLMUINT * puiTermChar); RCODE addDocumentToList( FLMUINT uiCollection, FLMUINT64 ui64DocumentId); void releaseLastRow( void); RCODE expandRow( DME_ROW_INFO * pRow, FLMBOOL bOneLevel, DME_ROW_INFO ** ppLastRow); RCODE collapseRow( DME_ROW_INFO ** ppRow); RCODE refreshEditWindow( DME_ROW_INFO ** ppFirstRow, DME_ROW_INFO * pCursorRow, FLMUINT * puiCurRow); RCODE refreshRow( FLMUINT uiRow, DME_ROW_INFO * pNd, FLMBOOL bSelected); RCODE clearSelections( void); RCODE editRow( FLMUINT uiRow, DME_ROW_INFO * pRow, FLMBOOL bReadOnly = FALSE); FLMBOOL canDeleteRow( DME_ROW_INFO * pCurRow); RCODE addRowToDb( DME_ROW_INFO * pCurRow, FLMUINT uiCollection, FLMBOOL bAddInBackground, FLMBOOL bStartThread, FLMUINT * pudDrn); RCODE modifyRowInDb( DME_ROW_INFO * pCurRow, FLMBOOL bAddInBackground, FLMBOOL bStartThread); FLMBOOL isExiting( void); RCODE _insertRow( DME_ROW_INFO * pRecord, DME_ROW_INFO * pStartNd = NULL); RCODE selectCollection( FLMUINT * puiCollection, FLMUINT * puiTermChar); RCODE selectIndex( FLMUINT uiFlags, FLMUINT * puiIndex, FLMUINT * puiTermChar); RCODE displayAttributes( DME_ROW_INFO * pRow); RCODE displayNodeInfo( DME_ROW_INFO * pRow); RCODE exportNode( DME_ROW_INFO * pRow); RCODE showHelp( FLMUINT * puiKeyRV = NULL); RCODE getNextTitle( DME_ROW_INFO * pRow, DME_ROW_INFO ** ppNewRow); RCODE getPrevTitle( DME_ROW_INFO * pRow, DME_ROW_INFO ** ppNewRow); RCODE selectElementAttribute( DME_ROW_INFO * pRow, FLMUINT * puiTermChar); RCODE deleteRow( DME_ROW_INFO ** ppCurRow); RCODE addSomething( DME_ROW_INFO ** ppCurRow); RCODE createDocumentNode( DME_ROW_INFO ** ppCurRow); RCODE createRootElementNode( DME_ROW_INFO ** ppCurRow); RCODE createElementNode( DME_ROW_INFO ** ppCurRow); RCODE createTextNode( DME_ROW_INFO ** ppRow, eDomNodeType eNodeType); RCODE createAttributeNode( DME_ROW_INFO ** ppCurRow); RCODE selectNodeType( eDomNodeType * peNodeType, FLMUINT * puiTermChar); RCODE buildNewRow( FLMINT iLevel, F_DOMNode * pDomNode, DME_ROW_INFO ** ppNewRow); RCODE selectTag( FLMUINT uiTagType, FLMUINT * puiTag, FLMUINT * puiTermChar); RCODE selectEncDef( FLMUINT uiTagType, FLMUINT * puiEncDefId, FLMUINT * puiTermChar); RCODE addComment( DME_ROW_INFO ** ppCurRow, const char * pucFormat, ...); /* Data */ DME_ROW_INFO * m_pScrFirstRow; DME_ROW_INFO * m_pScrLastRow; DME_ROW_INFO * m_pCurRow; DME_ROW_ANCHOR * m_pRowAnchor; DME_ROW_INFO * m_pDocList; DME_ROW_INFO * m_pCurDoc; F_Db * m_pDb; FLMBOOL m_bOpenedDb; FLMUINT m_uiCollection; FLMUINT m_uiLastKey; char m_szTitle[ F_DOMEDIT_MAX_TITLE_SIZE + 1]; FLMUINT m_uiCurRow; FLMUINT m_uiEditCanvasRows; FLMUINT m_uiEditCanvasCols; FLMUINT m_uiNumRows; FLMUINT m_uiULX; FLMUINT m_uiULY; FLMUINT m_uiLRX; FLMUINT m_uiLRY; FLMBOOL m_bReadOnly; FLMBOOL m_bSetupCalled; FLMBOOL * m_pbShutdown; FLMBOOL m_bMonochrome; FTX_SCREEN * m_pScreen; FTX_WINDOW * m_pEditWindow; FTX_WINDOW * m_pEditStatusWin; F_DomEditor * m_pParent; F_DOMEDIT_DISP_HOOK m_pDisplayHook; void * m_DisplayData; F_DOMEDIT_KEY_HOOK m_pKeyHook; void * m_KeyData; F_DOMEDIT_HELP_HOOK m_pHelpHook; void * m_HelpData; F_DOMEDIT_EVENT_HOOK m_pEventHook; void * m_EventData; FLMBOOL m_bDocList; F_NameTable * m_pNameTable; DME_DISP_COLUMN m_dispColumns[ 16]; }; FINLINE FLMUINT F_DomEditor::getEditCanvasCols( void) { return m_uiEditCanvasCols; } FINLINE void F_DomEditor::setShutdown( FLMBOOL * pbShutdown) { flmAssert( m_bSetupCalled == TRUE); m_pbShutdown = pbShutdown; } FINLINE FLMBOOL * F_DomEditor::getShutdown( void) { flmAssert( m_bSetupCalled == TRUE); return( m_pbShutdown); } FINLINE FLMBOOL F_DomEditor::isExiting( void) { flmAssert( m_bSetupCalled == TRUE); if( m_pbShutdown && *m_pbShutdown) { return( TRUE); } return( FALSE); } FINLINE FLMBOOL F_DomEditor::isMonochrome( void) { flmAssert( m_bSetupCalled == TRUE); return( m_bMonochrome); } FINLINE void F_DomEditor::setParent( F_DomEditor * pParent) { flmAssert( m_bSetupCalled == TRUE); m_pParent = pParent; } FINLINE F_DomEditor * F_DomEditor::getParentEditor( void) { flmAssert( m_bSetupCalled == TRUE); return( m_pParent); } FINLINE void F_DomEditor::setReadOnly( FLMBOOL bReadOnly) { flmAssert( m_bSetupCalled == TRUE); m_bReadOnly = bReadOnly; } FINLINE DME_ROW_INFO * F_DomEditor::getScrFirstRow( void) { flmAssert( m_bSetupCalled == TRUE); return( m_pScrFirstRow); } FINLINE DME_ROW_INFO * F_DomEditor::getScrLastRow( void) { flmAssert( m_bSetupCalled == TRUE); return( m_pScrLastRow); } FINLINE FLMUINT F_DomEditor::getCursorRow( void) { flmAssert( m_bSetupCalled == TRUE); return( m_uiCurRow); } FINLINE DME_ROW_ANCHOR * F_DomEditor::getRowAnchor( void) { flmAssert( m_bSetupCalled == TRUE); return( m_pRowAnchor); } FINLINE void F_DomEditor::setRowAnchor( DME_ROW_ANCHOR * pRowAnchor) { flmAssert( m_bSetupCalled == TRUE); m_pRowAnchor = pRowAnchor; } FINLINE FLMUINT F_DomEditor::getNumRows( void) { flmAssert( m_bSetupCalled == TRUE && m_pEditWindow != NULL); return( m_uiEditCanvasRows); } FINLINE F_Db * F_DomEditor::getDb( void) { flmAssert( m_bSetupCalled == TRUE); return( m_pDb); } FINLINE FLMUINT F_DomEditor::getCollection( void) { flmAssert( m_bSetupCalled == TRUE); return( m_uiCollection); } FINLINE FLMUINT F_DomEditor::getLastKey( void) { flmAssert( m_bSetupCalled == TRUE); return( m_uiLastKey); } FINLINE FLMUINT F_DomEditor::getULX( void) { flmAssert( m_bSetupCalled == TRUE); return( m_uiULX); } FINLINE FLMUINT F_DomEditor::getULY( void) { flmAssert( m_bSetupCalled == TRUE); return( m_uiULY); } FINLINE void F_DomEditor::setDisplayHook( F_DOMEDIT_DISP_HOOK pDispHook, void * DispData) { flmAssert( m_bSetupCalled == TRUE); m_pDisplayHook = pDispHook; m_DisplayData = DispData; } FINLINE void F_DomEditor::setKeyHook( F_DOMEDIT_KEY_HOOK pKeyHook, void * KeyData) { flmAssert( m_bSetupCalled == TRUE); m_pKeyHook = pKeyHook; m_KeyData = KeyData; } FINLINE void F_DomEditor::setHelpHook( F_DOMEDIT_HELP_HOOK pHelpHook, void * HelpData) { flmAssert( m_bSetupCalled == TRUE); m_pHelpHook = pHelpHook; m_HelpData = HelpData; } FINLINE void F_DomEditor::setEventHook( F_DOMEDIT_EVENT_HOOK pEventHook, void * EventData) { flmAssert( m_bSetupCalled == TRUE); m_pEventHook = pEventHook; m_EventData = EventData; } /* Prototypes */ RCODE F_DomEditorDefaultDispHook( F_DomEditor * pDomEditor, DME_ROW_INFO * pRow, FLMUINT * puiNumVals); RCODE F_DomEditorViewOnlyKeyHook( F_DomEditor * pDomEditor, DME_ROW_INFO * pCurRow, FLMUINT uiKeyIn, FLMUINT * puiKeyOut, void * pvKeyData); RCODE F_DomEditorSelectionKeyHook( F_DomEditor * pDomEditor, DME_ROW_INFO * pCurRow, FLMUINT uiKeyIn, FLMUINT * puiKeyOut, void * pvKeyData); RCODE F_DomEditorFileKeyHook( F_DomEditor * pDomEditor, DME_ROW_INFO * pCurRow, FLMUINT uiKeyIn, FLMUINT * puiKeyOut, void * pvKeyData); FLMBOOL domDisplayNodeInfo( FTX_WINDOW * pWindow, char * pszOutputFileName, IF_Db * pDb, FLMUINT uiCollection, FLMUINT64 ui64NodeId, FLMBOOL bDoSubTree, FLMBOOL bWaitForKeystroke); FLMBOOL domDisplayEntryInfo( FTX_WINDOW * pWindow, char * pszOutputFileName, IF_Db * pDb, FLMUINT64 ui64NodeId, FLMBOOL bWaitForKeystroke); FLMBOOL domDisplayBTreeInfo( FTX_WINDOW * pWindow, char * pszOutputFileName, IF_Db * pDb, FLMUINT uiLfNum, FLMBOOL bDoCollection, FLMBOOL bDoIndex, FLMBOOL bWaitForKeystroke); #endif libxflaim-5.1.969/util/viewdisp.cpp0000644000175000017500000000463110511001742020554 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains display routines for the VIEW program. // // Tabs: 3 // // Copyright (c) 1992-1995, 1998-2000, 2002-2003,2005-2006 Novell, Inc. // All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: viewdisp.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "view.h" /*************************************************************************** Desc: This routine displays an error message to the screen. *****************************************************************************/ void ViewShowError( const char * pszMessage) { FLMUINT uiChar; FLMUINT uiNumCols; FLMUINT uiNumRows; f_conGetScreenSize( &uiNumCols, &uiNumRows); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, uiNumRows - 2); f_conSetBackFore( FLM_RED, FLM_WHITE); f_conStrOutXY( pszMessage, 0, uiNumRows - 2); f_conStrOutXY( "Press ENTER to continue: ", 0, uiNumRows - 1); for (;;) { uiChar = f_conGetKey(); if (uiChar == FKB_ENTER || uiChar == FKB_ESCAPE) { break; } } f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, uiNumRows - 2); ViewEscPrompt(); } /*************************************************************************** Desc: This routine displays a FLAIM error message to the screen. It formats the RCODE into a message and calls ViewShowError to display the error. *****************************************************************************/ void ViewShowRCError( const char * pszWhat, RCODE rc) { char szTBuf [100]; f_sprintf( szTBuf, "Error %s: 0x%04X", pszWhat, (unsigned)rc); ViewShowError( szTBuf); } libxflaim-5.1.969/util/sharutil.cpp0000644000175000017500000005746010511001742020565 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Shared utility routines // // Tabs: 3 // // Copyright (c) 1997-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: sharutil.cpp 3129 2006-01-25 11:46:17 -0700 (Wed, 25 Jan 2006) ahodgkinson $ //------------------------------------------------------------------------------ #include "flaimsys.h" #include "sharutil.h" FSTATIC RCODE FLMAPI _flmWrapperFunc( IF_Thread * pThread); /******************************************************************** Desc: Parses command-line parameters *********************************************************************/ void flmUtilParseParams( char * pszCommandBuffer, FLMINT iMaxArgs, FLMINT * iArgcRV, char ** ppArgvRV) { FLMINT iArgC = 0; for (;;) { /* Strip off leading white space. */ while ((*pszCommandBuffer == ' ') || (*pszCommandBuffer == '\t')) pszCommandBuffer++; if (!(*pszCommandBuffer)) break; if ((*pszCommandBuffer == '"') || (*pszCommandBuffer == '\'')) { char cQuoteChar = *pszCommandBuffer; pszCommandBuffer++; ppArgvRV [iArgC] = pszCommandBuffer; iArgC++; while ((*pszCommandBuffer) && (*pszCommandBuffer != cQuoteChar)) pszCommandBuffer++; if (*pszCommandBuffer) *pszCommandBuffer++ = 0; } else { ppArgvRV [iArgC] = pszCommandBuffer; iArgC++; while ((*pszCommandBuffer) && (*pszCommandBuffer != ' ') && (*pszCommandBuffer != '\t')) pszCommandBuffer++; if (*pszCommandBuffer) *pszCommandBuffer++ = 0; } /* Quit if we have reached the maximum allowable number of arguments. */ if (iArgC == iMaxArgs) break; } *iArgcRV = iArgC; } /**************************************************************************** Name: FlmVector::setElementAt Desc: a vector set item operation. ****************************************************************************/ #define FLMVECTOR_START_AMOUNT 16 #define FLMVECTOR_GROW_AMOUNT 2 RCODE FlmVector::setElementAt( void * pData, FLMUINT uiIndex) { RCODE rc = NE_XFLM_OK; if ( !m_pElementArray) { TEST_RC( rc = f_calloc( sizeof( void*) * FLMVECTOR_START_AMOUNT, &m_pElementArray)); m_uiArraySize = FLMVECTOR_START_AMOUNT; } if ( uiIndex >= m_uiArraySize) { TEST_RC( rc = f_recalloc( sizeof( void*) * m_uiArraySize * FLMVECTOR_GROW_AMOUNT, &m_pElementArray)); m_uiArraySize *= FLMVECTOR_GROW_AMOUNT; } m_pElementArray[ uiIndex] = pData; Exit: return rc; } /**************************************************************************** Name: FlmVector::getElementAt Desc: a vector get item operation ****************************************************************************/ void * FlmVector::getElementAt( FLMUINT uiIndex) { //if you hit this you are indexing into the vector out of bounds. //unlike a real array, we can catch this here! oh joy! flmAssert ( uiIndex < m_uiArraySize); return m_pElementArray[ uiIndex]; } /**************************************************************************** Name: FlmStringAcc::appendCHAR Desc: append a char (or the same char many times) to the string ****************************************************************************/ RCODE FlmStringAcc::appendCHAR( char ucChar, FLMUINT uiHowMany) { RCODE rc = NE_XFLM_OK; if ( uiHowMany == 1) { FLMBYTE szStr[ 2]; szStr[ 0] = ucChar; szStr[ 1] = 0; rc = this->appendTEXT( (const FLMBYTE*)szStr); } else { FLMBYTE * pszStr; if( RC_BAD( rc = f_alloc( uiHowMany + 1, &pszStr))) { goto Exit; } f_memset( pszStr, ucChar, uiHowMany); pszStr[ uiHowMany] = 0; rc = this->appendTEXT( pszStr); f_free( &pszStr); } Exit: return rc; } /**************************************************************************** Name: FlmStringAcc::appendTEXT Desc: appending text to the accumulator safely. all other methods in the class funnel through this one, as this one contains the logic for making sure storage requirements are met. ****************************************************************************/ RCODE FlmStringAcc::appendTEXT( const FLMBYTE * pszVal) { RCODE rc = NE_XFLM_OK; FLMUINT uiIncomingStrLen; FLMUINT uiStrLen; //be forgiving if they pass in a NULL if ( !pszVal) { goto Exit; } //also be forgiving if they pass a 0-length string else if( (uiIncomingStrLen = f_strlen( (const char *)pszVal)) == 0) { goto Exit; } //compute total size we need to store the new total if ( m_bQuickBufActive || m_pszVal) { uiStrLen = uiIncomingStrLen + m_uiValStrLen; } else { uiStrLen = uiIncomingStrLen; } //just use small buffer if it's small enough if ( uiStrLen < FSA_QUICKBUF_BUFFER_SIZE) { f_strcat( m_szQuickBuf, (const char *)pszVal); m_bQuickBufActive = TRUE; } //we are exceeding the quickbuf size, so get the bytes from the heap else { //ensure storage requirements are met (and then some) if ( m_pszVal == NULL) { FLMUINT uiNewBytes = (uiStrLen+1) * 4; if ( RC_OK ( rc = f_alloc( (FLMUINT)(sizeof( FLMBYTE) * uiNewBytes), &m_pszVal))) { m_uiBytesAllocatedForPszVal = uiNewBytes; m_pszVal[ 0] = 0; } else { goto Exit; } } else if ( (m_uiBytesAllocatedForPszVal-1) < uiStrLen) { FLMUINT uiNewBytes = (uiStrLen+1) * 4; if ( RC_OK( rc = f_realloc( (FLMUINT)(sizeof( FLMBYTE) * uiNewBytes), &m_pszVal))) { m_uiBytesAllocatedForPszVal = uiNewBytes; } else { goto Exit; } } //if transitioning from quick buf to heap buf, we need to //transfer over the quick buf contents and unset the flag if ( m_bQuickBufActive) { m_bQuickBufActive = FALSE; f_strcpy( m_pszVal, m_szQuickBuf); //no need to zero out m_szQuickBuf because it will never //be used again, unless a clear() is issued, in which //case it will be zeroed out then. } //copy over the string f_strcat( m_pszVal, (const char *)pszVal); } m_uiValStrLen = uiStrLen; Exit: return rc; } /**************************************************************************** Desc: printf into the FlmStringAcc ****************************************************************************/ RCODE FlmStringAcc::printf( const char * pszFormatString, ...) { f_va_list args; char * pDestStr = NULL; FLMSIZET iSize = 4096; RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = f_alloc( iSize, &pDestStr))) { goto Exit; } f_va_start( args, pszFormatString); f_vsprintf( pDestStr, pszFormatString, &args); f_va_end( args); this->clear(); TEST_RC( rc = this->appendTEXT( (FLMBYTE *)pDestStr)); Exit: if ( pDestStr) { f_free( &pDestStr); } return rc; } /**************************************************************************** Desc: formatted appender like sprintf ****************************************************************************/ RCODE FlmStringAcc::appendf( const char * pszFormatString, ...) { f_va_list args; char * pDestStr = NULL; FLMSIZET iSize = 4096; RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = f_alloc( iSize, &pDestStr))) { goto Exit; } f_va_start( args, pszFormatString); f_vsprintf( pDestStr, pszFormatString, &args); f_va_end( args); TEST_RC( rc = this->appendTEXT( (FLMBYTE *)pDestStr)); Exit: if ( pDestStr) { f_free( &pDestStr); } return rc; } /**************************************************************************** Desc: Constructor *****************************************************************************/ FlmContext::FlmContext() { m_szCurrDir[ 0] = '\0'; m_hMutex = F_MUTEX_NULL; m_bIsSetup = FALSE; } /**************************************************************************** Desc: Destructor *****************************************************************************/ FlmContext::~FlmContext( void) { if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); m_hMutex = F_MUTEX_NULL; } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::setup( FLMBOOL bShared) { RCODE rc = NE_XFLM_OK; if( bShared) { if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } } m_bIsSetup = TRUE; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::setCurrDir( FLMBYTE * pszCurrDir) { RCODE rc = NE_XFLM_OK; flmAssert( m_bIsSetup); lock(); f_strcpy( (char *)m_szCurrDir, (const char *)pszCurrDir); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmContext::getCurrDir( FLMBYTE * pszCurrDir) { RCODE rc = NE_XFLM_OK; flmAssert( m_bIsSetup); lock(); f_strcpy( (char *)pszCurrDir, (const char *)m_szCurrDir); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ void FlmContext::lock( void) { flmAssert( m_bIsSetup); if( m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmContext::unlock( void) { flmAssert( m_bIsSetup); if( m_hMutex != F_MUTEX_NULL) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ FlmThreadContext::FlmThreadContext( void) { m_pScreen = NULL; m_pWindow = NULL; m_bShutdown = FALSE; m_pLocalContext = NULL; m_pSharedContext = NULL; m_pNext = NULL; m_pPrev = NULL; m_uiID = 0; m_hMutex = F_MUTEX_NULL; m_pThrdFunc = NULL; m_pvAppData = NULL; m_pThread = NULL; m_bFuncExited = FALSE; } /**************************************************************************** Desc: *****************************************************************************/ FlmThreadContext::~FlmThreadContext( void) { // Free the local context if( m_pLocalContext) { m_pLocalContext->Release(); } // Destroy the semaphore if( m_hMutex != F_MUTEX_NULL) { f_mutexDestroy( &m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::lock( void) { if( m_hMutex != F_MUTEX_NULL) { f_mutexLock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::unlock( void) { if( m_hMutex != F_MUTEX_NULL) { f_mutexUnlock( m_hMutex); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmThreadContext::setup( FlmSharedContext * pSharedContext, const char * pszThreadName, THREAD_FUNC_p pFunc, void * pvAppData) { RCODE rc = NE_XFLM_OK; flmAssert( pSharedContext != NULL); m_pSharedContext = pSharedContext; m_pThrdFunc = pFunc; m_pvAppData = pvAppData; if( (m_pLocalContext = f_new FlmContext) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) { goto Exit; } if( pszThreadName && f_strlen( pszThreadName) <= MAX_THREAD_NAME_LEN) { f_strcpy( m_szName, pszThreadName); } else { f_sprintf( m_szName, "flmGenericThread"); } if( RC_BAD( rc = m_pLocalContext->setup( FALSE))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::getName( char * pszName, FLMBOOL bLocked) { if( !bLocked) { lock(); } f_strcpy( pszName, m_szName); if( !bLocked) { unlock(); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmThreadContext::execute( void) { flmAssert( m_pThrdFunc != NULL); m_FuncRC = m_pThrdFunc( this, m_pvAppData); return m_FuncRC; } /**************************************************************************** Desc: *****************************************************************************/ void FlmThreadContext::shutdown() { m_bShutdown = TRUE; } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmThreadContext::exec( void) { flmAssert( m_pThrdFunc != NULL); return( (RCODE)(m_pThrdFunc( this, m_pvAppData))); } /**************************************************************************** Desc: *****************************************************************************/ FlmSharedContext::FlmSharedContext( void) { m_pParentContext = NULL; m_pThreadList = NULL; m_bLocalShutdownFlag = FALSE; m_pbShutdownFlag = &m_bLocalShutdownFlag; m_hSem = F_SEM_NULL; m_uiNextProcID = 1; m_bPrivateShare = FALSE; } /**************************************************************************** Desc: *****************************************************************************/ FlmSharedContext::~FlmSharedContext( void) { // Clean up the thread list shutdown(); // Free the ESem if( m_hSem != F_SEM_NULL) { f_semDestroy( &m_hSem); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::init( FlmSharedContext * pSharedContext) { RCODE rc = NE_XFLM_OK; // Initialize the base class if( RC_BAD( rc = FlmContext::setup( TRUE))) { goto Exit; } if( RC_BAD( rc = f_semCreate( &m_hSem))) { goto Exit; } m_pParentContext = pSharedContext; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ void FlmSharedContext::shutdown( void) { FLMBOOL bLocked = FALSE; *m_pbShutdownFlag = TRUE; for( ;;) { lock(); bLocked = TRUE; if( m_pThreadList) { m_pThreadList->shutdown(); } else { break; } unlock(); bLocked = FALSE; (void)f_semWait( m_hSem, 1000); } if( bLocked) { unlock(); } } /**************************************************************************** Desc: *****************************************************************************/ void FlmSharedContext::wait( void) { FLMBOOL bLocked = FALSE; for( ;;) { lock(); bLocked = TRUE; if( !m_pThreadList) { break; } unlock(); bLocked = FALSE; (void)f_semWait( m_hSem, 1000); } if( bLocked) { unlock(); } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::spawn( FlmThreadContext * pThread, FLMUINT * puiThreadID) { RCODE rc = NE_XFLM_OK; char szName[ MAX_THREAD_NAME_LEN + 1]; IF_ThreadMgr * pThreadMgr = NULL; registerThread( pThread); pThread->getName( szName); if( RC_BAD( rc = FlmGetThreadMgr( &pThreadMgr))) { goto Exit; } if( RC_BAD( rc = pThreadMgr->createThread( NULL, _flmWrapperFunc, szName, 0, 0, pThread))) { goto Exit; } if( puiThreadID) { *puiThreadID = pThread->getID(); } Exit: if( pThreadMgr) { pThreadMgr->Release(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::spawn( char * pszThreadName, THREAD_FUNC_p pFunc, void * pvUserData, FLMUINT * puiThreadID) { FlmThreadContext * pThread; RCODE rc = NE_XFLM_OK; if( (pThread = f_new FlmThreadContext) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pThread->setup( this, pszThreadName, pFunc, pvUserData))) { goto Exit; } if( RC_BAD( rc = spawn( pThread, puiThreadID))) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::registerThread( FlmThreadContext * pThread) { RCODE rc = NE_XFLM_OK; lock(); pThread->setNext( m_pThreadList); if( m_pThreadList) { m_pThreadList->setPrev( pThread); } m_pThreadList = pThread; pThread->setID( m_uiNextProcID++); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::deregisterThread( FlmThreadContext * pThread) { FlmThreadContext * pTmpThrd; RCODE rc = NE_XFLM_OK; lock(); pTmpThrd = m_pThreadList; while( pTmpThrd) { if( pTmpThrd == pThread) { if( pTmpThrd->getPrev()) { pTmpThrd->getPrev()->setNext( pTmpThrd->getNext()); } if( pTmpThrd->getNext()) { pTmpThrd->getNext()->setPrev( pTmpThrd->getPrev()); } if( pTmpThrd == m_pThreadList) { m_pThreadList = pTmpThrd->getNext(); } pTmpThrd->Release(); break; } pTmpThrd = pTmpThrd->getNext(); } f_semSignal( m_hSem); unlock(); return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::killThread( FLMUINT uiThreadID, FLMUINT uiMaxWait) { FlmThreadContext * pThread; FLMUINT uiStartTime; RCODE rc = NE_XFLM_OK; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { pThread->shutdown(); break; } pThread = pThread->getNext(); } unlock(); // Wait for the thread to exit uiStartTime = FLM_GET_TIMER(); uiMaxWait = FLM_SECS_TO_TIMER_UNITS( uiMaxWait); for( ;;) { (void)f_semWait( m_hSem, 200); lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { break; } pThread = pThread->getNext(); } unlock(); if( !pThread) { break; } if( uiMaxWait) { if( FLM_GET_TIMER() - uiStartTime >= uiMaxWait) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } } } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::setFocus( FLMUINT uiThreadID) { FlmThreadContext * pThread; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { if( pThread->getScreen()) { FTXScreenDisplay( pThread->getScreen()); } break; } pThread = pThread->getNext(); } unlock(); return( NE_XFLM_OK); } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmSharedContext::isThreadTerminating( FLMUINT uiThreadID) { FLMBOOL bTerminating = FALSE; FlmThreadContext * pThread; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { if( pThread->getShutdownFlag()) { bTerminating = TRUE; } break; } pThread = pThread->getNext(); } unlock(); return( bTerminating); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmSharedContext::getThread( FLMUINT uiThreadID, FlmThreadContext ** ppThread) { FlmThreadContext * pThread; lock(); pThread = m_pThreadList; while( pThread) { if( pThread->getID() == uiThreadID) { if( ppThread) { *ppThread = pThread; } break; } pThread = pThread->getNext(); } unlock(); return( ((pThread != NULL) ? NE_XFLM_OK : RC_SET( NE_XFLM_NOT_FOUND))); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FLMAPI _flmWrapperFunc( IF_Thread * pFlmThread) { FlmThreadContext * pThread = (FlmThreadContext *)pFlmThread->getParm1(); FlmSharedContext * pSharedContext = pThread->getSharedContext(); pThread->setFlmThread( pFlmThread); if( RC_BAD( pThread->execute())) { goto Exit; } Exit: pThread->setFuncExited(); pThread->setFlmThread( NULL); // Unlink the thread from the shared context pSharedContext->deregisterThread( pThread); return( NE_XFLM_OK); } /**************************************************************************** Desc: callback to use to output a line ****************************************************************************/ void utilOutputLine( char * pszData, void * pvUserData) { FTX_WINDOW * pMainWindow = (FTX_WINDOW*)pvUserData; eColorType uiBack, uiFore; FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); FTXWinCPrintf( pMainWindow, uiBack, uiFore, "%s\n", pszData); } /**************************************************************************** Name: utilPressAnyKey Desc: callback to serve as a 'pager' function when the Usage: help is too long to fit on one screen. ****************************************************************************/ void utilPressAnyKey( char * pszMessage, void * pvUserData) { FTX_WINDOW * pMainWindow = (FTX_WINDOW*)pvUserData; FLMUINT uiChar; eColorType uiBack, uiFore; FTXWinGetBackFore( pMainWindow, &uiBack, &uiFore); FTXWinCPrintf( pMainWindow, uiBack, uiFore, pszMessage); while( RC_BAD( FTXWinTestKB( pMainWindow))) { f_sleep( 100); } FTXWinCPrintf( pMainWindow, uiBack, uiFore, "\r "); FTXWinCPrintf( pMainWindow, uiBack, uiFore, "\r"); FTXWinInputChar( pMainWindow, &uiChar); } /**************************************************************************** Name: utilInitWindow Desc: routine to startup the TUI ****************************************************************************/ RCODE utilInitWindow( char * pszTitle, FLMUINT * puiScreenRows, FTX_WINDOW ** ppMainWindow, FLMBOOL * pbShutdown) { FTX_SCREEN * pScreen = NULL; FTX_WINDOW * pTitleWin = NULL; FLMUINT uiCols; int iResCode = 0; if( RC_BAD( FTXInit( pszTitle, (FLMBYTE)80, (FLMBYTE)50, FLM_BLUE, FLM_WHITE, NULL, NULL))) { iResCode = 1; goto Exit; } FTXSetShutdownFlag( pbShutdown); if( RC_BAD( FTXScreenInit( pszTitle, &pScreen))) { iResCode = 1; goto Exit; } if( RC_BAD( FTXScreenGetSize( pScreen, &uiCols, puiScreenRows))) { iResCode = 1; goto Exit; } if( RC_BAD( FTXScreenInitStandardWindows( pScreen, FLM_RED, FLM_WHITE, FLM_BLUE, FLM_WHITE, FALSE, FALSE, pszTitle, &pTitleWin, ppMainWindow))) { iResCode = 1; goto Exit; } Exit: return (RCODE)iResCode; } /**************************************************************************** Name: utilShutdownWindow Desc: routine to shutdown the TUI ****************************************************************************/ void utilShutdownWindow() { FTXExit(); } libxflaim-5.1.969/util/dictdeftestsrv.cpp0000644000175000017500000007566110511001742021772 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Dictionary definition tests // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: dictdeftestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif /**************************************************************************** Desc: ****************************************************************************/ class IDictDefTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IDictDefTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IDictDefTestImpl::getName( void) { return( "Dictionary Definition Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IDictDefTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; int ps = 0; FLMUINT uiDefNum = 0; IF_DOMNode * pDocument = NULL; IF_DOMNode * pNode = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pAttrDef = NULL; IF_DOMNode * pElemDef = NULL; IF_DOMNode * pIndex = NULL; IF_DOMNode * pCollection = NULL; FLMUINT uiAttrDictNum = 0; FLMUINT uiElemDictNum = 0; FLMUINT uiIndexDictNum = 0; FLMBYTE szBuf[ 128]; FLMBOOL bTransStarted = FALSE; FLMBOOL bDibCreated = FALSE; FLMUINT64 ui64Tmp; beginTest( "Initialize FLAIM Test", "Perform initializations so we can test the dictionary definition", "(1)Get DbSystem class factory (2)call init() (3)create XFLAIM db", "No additional info."); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_ERROR_STRING( "Failed to initialize test state.", m_szDetails, ps); goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "Type Attribute Test", "Verify that deleting or modifying of the \"type\"" "attribute (ATTR_TYPE_TAG) of an element or attribute definition is " "denied with a proper error code", "(1) Create an element definition (2)Verify the node has a " "\"type\" attribute (3)Try to set it to an arbitrary value (4)Try " "to delete it.", ""); if( RC_BAD( rc = m_pDb->createElementDef( NULL, "element1", XFLM_TEXT_TYPE, &uiDefNum))) { MAKE_ERROR_STRING( "createElemDef failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->getDictionaryDef( ELM_ELEMENT_TAG, uiDefNum, &pNode))) { MAKE_ERROR_STRING( "getDictionaryDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->hasAttribute( m_pDb, ATTR_TYPE_TAG, &pAttr))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { MAKE_ERROR_STRING( "\"Type\" attribute not automatically created.", m_szDetails, rc); } else { MAKE_ERROR_STRING( "hasAttribute failed.", m_szDetails, rc); } goto Exit; } rc = pAttr->setUINT( m_pDb, 8); if ( rc != NE_XFLM_READ_ONLY) { MAKE_ERROR_STRING( "Succeeded in modifying \"type\" attribute.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } // need to begin a new transaction since attempting to delete // the Attribute node will probably require the transaction // to be aborted and we don't want to lose our modifications if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; rc = pAttr->deleteNode(m_pDb); if( rc != NE_XFLM_DELETE_NOT_ALLOWED) { MAKE_ERROR_STRING( "Incorrect rc when trying to delete \"type\" attribute.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } // The failed delete requires us to abort the trans // NOTE: This may no longer be necessary in the future if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "Name Attribute Test", "Verify that deleting of the \"name\" " "attribute (ATTR_NAME_TAG) of any definition is denied with a proper " "error code. Verify that modifying the name is allowed but deleting " "it should be denied.", "(1) Verify the node has a \"name\" attribute" "(2) Set it to an arbitrary value (3)Try to delete it.", ""); if( RC_BAD( rc = pNode->hasAttribute( m_pDb, ATTR_NAME_TAG))) { MAKE_ERROR_STRING( "hasAttribute failed. \"name\" attribute not created" " automatically.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pNode->getAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } // This attribute can be modified... if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"This should be okay"))) { MAKE_ERROR_STRING( "failed to modify \"name\" attribute.", m_szDetails, rc); goto Exit; } // Need to begin a new transaction since attempting to delete // the Attribute node will probably require the transaction // to be aborted and we don't want to lose our modifications if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; rc = pAttr->deleteNode(m_pDb); if( rc != NE_XFLM_DELETE_NOT_ALLOWED) { MAKE_ERROR_STRING( "Succeeded in deleting \"name\" attribute.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } // The failed delete requires us to abort the trans // NOTE: This may no longer be necessary in the future if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "State Attribute Test 1", " Verify that deleting or modifying the \"state\" attribute " "(ATTR_STATE_TAG) on an element attribute or index definition is denied " "if it is attemped using the \"regular\" DOM methods for updating nodes. ", "(1) Verify the node has a \"state\" attribute" "(2) Try to set it to an arbitrary value (3) Try to delete it.", ""); if( RC_BAD( rc = pNode->hasAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { MAKE_ERROR_STRING( "\"state\" attribute not automatically created.", m_szDetails, rc); rc = NE_XFLM_FAILURE; } else { MAKE_ERROR_STRING( "hasAttribute failed.", m_szDetails, rc); } goto Exit; } rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"This should not work"); if( rc != NE_XFLM_READ_ONLY) { MAKE_ERROR_STRING( "attempt to modify \"state\" attribute returned " "incorrect rc.", m_szDetails, rc); if( RC_OK( rc )) { rc = NE_XFLM_FAILURE; } goto Exit; } // Commit the trans here since we may have to abort the trans // in which we attempt to delete the pAttr node if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; rc = pAttr->deleteNode(m_pDb); if( rc != NE_XFLM_DELETE_NOT_ALLOWED) { MAKE_ERROR_STRING( "Attempt to delete \"state\" attribute " "returned incorrect rc.", m_szDetails, rc); goto Exit; } // The failed delete requires us to abort the trans // NOTE: This may no longer be necessary in the future if( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "State Attribute Test 2 / Dict Num Test", "Test creating an element or attribute definition without " "a \"state\" attribute (ATTR_STATE_TAG). In this case FLAIM should " "automatically create the \"state\" attribute and set its value to " "\"active\". Same should happen for indexes if the user did not create " "a state attribute for the index definition and value should be set " "to \"online\". Make sure that the \"DictNumber\" attribute " "(ATTR_DICT_NUMBER) is automatically created and assigned if the user " "omits it or sets it to zero. This applies to all types of definitions " "(elements attributes indexes collections and prefixes). Once set " "to a non-zero value make sure that the user cannot ever modify or " "delete the attribute.", "(1) Create an attribute definition (2) create an element " "definition (3) create an index definition (3) make sure attribute", ""); //create an attribute definition /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_ATTRIBUTE_TAG, &pAttrDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttrDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pAttrDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttrDef->getAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, sizeof(szBuf) - 1))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( (char *)szBuf, "active")) { MAKE_ERROR_STRING( "unexpected \"state\" value. Expected: \"active\"", m_szDetails, rc); goto Exit; } //create an element definition /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_ELEMENT_TAG, &pElemDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pElemDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pAttrDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiAttrDictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } rc = pAttr->setUINT( m_pDb, 123); if ( rc != NE_XFLM_READ_ONLY) { MAKE_ERROR_STRING( "Incorrect error code returned while trying to " "delete dict number attribute.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } //should have been set up automatically if ( uiAttrDictNum == 0) { MAKE_ERROR_STRING( "Invalid dictionary number.", m_szDetails, rc); rc = NE_XFLM_FAILURE; goto Exit; } if ( RC_BAD( rc = pElemDef->createAttribute( m_pDb, uiAttrDictNum, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->documentDone( pElemDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } //make sure state automatically set to "active" if ( RC_BAD( rc = pElemDef->getAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, sizeof(szBuf) - 1))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( (char *)szBuf, "active")) { MAKE_ERROR_STRING( "Invalid state value. Should be \"active\".", m_szDetails, rc); rc = NE_XFLM_FAILURE; goto Exit; } //make sure the dict num was set up automatically if ( RC_BAD ( rc = pElemDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiElemDictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( uiElemDictNum == 0) { MAKE_ERROR_STRING( "Invalid dictionary number.", m_szDetails, rc); rc = NE_XFLM_FAILURE; goto Exit; } rc = pAttr->setUINT( m_pDb, 123); if ( rc != NE_XFLM_READ_ONLY) { MAKE_ERROR_STRING( "Incorrect rc when trying to modify dict num.", m_szDetails, rc); if ( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } //create an index definition that references the elem and attr defs we made if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_INDEX_TAG, &pIndex))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar+foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } //xflaim:ElementPath must have one or more xflaim:ElementComponent //or one or more xflaim:AttributeComponent sub-elements if ( RC_BAD( rc = pIndex->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"value"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createNode( m_pDb, ELEMENT_NODE, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_FIRST_CHILD, &pNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8(m_pDb, (FLMBYTE *)"foo"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_INDEX_ON_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"value"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_KEY_COMPONENT_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 2))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = m_pDb->documentDone( pIndex))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD ( rc = pIndex->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiIndexDictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( uiIndexDictNum == 0) { MAKE_GENERIC_ERROR_STRING( "Invalid dict num", m_szDetails, uiIndexDictNum); rc = NE_XFLM_FAILURE; goto Exit; } rc = pAttr->setUINT( m_pDb, 123); if ( rc != NE_XFLM_READ_ONLY) { MAKE_ERROR_STRING( "Invalid rc when trying to change dict num.", m_szDetails, rc); if ( RC_OK( rc )) { rc = NE_XFLM_FAILURE; } goto Exit; } endTest("PASS"); beginTest( "Referenced Definitions Test", "Test to make sure that element and attribute definitions " "cannot be deleted if they are referenced from an index definition.", "Try to delete the Element and Attribute Definitions we created.", ""); //need to begin a new transaction since attempting to delete //the Element Definition node will probably require the transaction //to be aborted and we don't want to lose our modifications if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; rc = pElemDef->deleteNode( m_pDb); if ( rc != NE_XFLM_CANNOT_DEL_ELEMENT) { MAKE_ERROR_STRING( "Expected rc == NE_XFLM_CANNOT_DEL_ELEMENT", m_szDetails, rc); goto Exit; } //The failed delete requires us to abort the trans //NOTE: This may no longer be necessary in the future if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; //try to delete attribute rc = pAttrDef->deleteNode( m_pDb); if ( rc != NE_XFLM_CANNOT_DEL_ATTRIBUTE) { MAKE_ERROR_STRING( "Exected rc == NE_XFLM_CANNOT_DEL_ATTRIBUTE.", m_szDetails, rc); if ( RC_OK( rc )) { rc = NE_XFLM_FAILURE; } goto Exit; } //The failed delete requires us to abort the trans //NOTE: This may no longer be necessary in the future if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); beginTest( "State Attribute Test 3", "Verify that the state attribute can be modified using the " "special methods that are made for that purpose. For index definitions " "the indexSuspend and indexResume methods may be used to change the state " "of the index. For attribute and element definitions the changeItemState " "method may be used to change the state of the element or attribute. For " "changeItemState we need to verify that the state change is legal. Certain " "state transitions are illegal for a user to make even using changeItemState." "Test to make sure that element and attribute definitions cannot be deleted " "directly using the DOM APIs if the state attribute is not set to \"unused\"." "Verify that state is only settable to \"unused\" by the sweep method - users " "should not be able to do this. The sweep method should be tested to make sure " "it does not set the state to \"unused\" if the element or attribute definition " "is actually in use somewhere.", "(1) Call indexSuspend and indexResume on the index definition " "(2) Verify the state is changed correctly (3) Set state attribute of " "the index element and attribute definitions to \"checking\" (4) Call " "sweep (5) Verify all states are correct (6) Verify user cannot set state " "to \"unused\" directly (7) for each definition in reverse order of creation: " "(a) call sweep to set state to \"unused\" (b) delete definition.", ""); //suspend the index if ( RC_BAD( rc = m_pDb->indexSuspend( uiIndexDictNum))) { MAKE_ERROR_STRING( "indexSuspend failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->getAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof( szBuf), 0, sizeof(szBuf) - 1))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } //NOTE: There is a define FLM_INDEX_SUSPENDED_STR in flaim.h //should I have access to it in flaimpub.h? if ( f_strcmp( (char *)szBuf, "suspended")) { //MAKE_ERROR_STRING macro won't work here. f_sprintf( (char*)m_szDetails, "index state: %s. expected \"suspended\". " "rc == %X. File: %s. Line# %u. ", szBuf, (unsigned)rc, __FILE__, __LINE__); goto Exit; } //resume the index if ( RC_BAD( rc = m_pDb->indexResume( uiIndexDictNum))) { MAKE_ERROR_STRING( "indexResume failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pIndex->getAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, sizeof(szBuf) - 1))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( (char *)szBuf, "online") && f_strcmp( (char *)szBuf, "offline")) //VISIT - may still be offline ? { //MAKE_ERROR_STRING macro won't work here. f_sprintf( (char*)m_szDetails, "index state: %s. expected \"online\" or \"offline\". " "rc == %X. File: %s. Line# %u. ", szBuf, (unsigned)rc, __FILE__, __LINE__); goto Exit; } //now set it to unused the right way if ( RC_BAD( rc = m_pDb->changeItemState( ELM_ATTRIBUTE_TAG, uiAttrDictNum, "checking"))) { MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; for( ;;) { if ( RC_BAD( rc = pAttrDef->getAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, sizeof(szBuf) - 1))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if( !f_strcmp( (char *)szBuf, "active")) { break; } f_sleep( 250); } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = m_pDb->changeItemState( ELM_ELEMENT_TAG, uiElemDictNum, "checking"))) { MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; for( ;;) { if ( RC_BAD( rc = pElemDef->getAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, sizeof(szBuf) - 1))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if( !f_strcmp( (char *)szBuf, "active")) { break; } f_sleep( 250); } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; //shouldn't be able to set to "unused" this way rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"unused"); if ( rc != NE_XFLM_READ_ONLY) { MAKE_ERROR_STRING( "Invalid return code when trying to modify " "state attribute.", m_szDetails, rc); goto Exit; } //delete the IndexDef if ( RC_BAD( rc = pIndex->deleteNode( m_pDb))) { f_sprintf( (char*)m_szDetails, "failed to delete index definition." "Line# %u. rc == %X.", __LINE__, (unsigned)rc); goto Exit; } if ( RC_BAD( rc = m_pDb->changeItemState( ELM_ELEMENT_TAG, uiElemDictNum, "checking"))) { MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; for( ;;) { if( RC_BAD( rc = pElemDef->getNodeId( m_pDb, &ui64Tmp))) { if( rc != NE_XFLM_DOM_NODE_DELETED) { flmAssert( 0); goto Exit; } rc = NE_XFLM_OK; break; } f_sleep( 250); } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = m_pDb->changeItemState( ELM_ATTRIBUTE_TAG, uiAttrDictNum, "checking"))) { MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; for( ;;) { if( RC_BAD( rc = pAttrDef->getNodeId( m_pDb, &ui64Tmp))) { if( rc != NE_XFLM_DOM_NODE_DELETED) { flmAssert( 0); goto Exit; } rc = NE_XFLM_OK; break; } f_sleep( 250); } if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; endTest("PASS"); m_pszTestName = "Dictionary Doc #1 Test"; m_pszTestDesc = "Verify that no nodes in document #1 of the dictionary collection " "can be modified or deleted by an application. This is a special document " "used internally by FLAIM so it should not be changeable or deletable by " "an application. It is readable but not changeable or deletable."; m_pszSteps = "(1) Get Doc #1 (2) Try to delete it. "; beginTest( "Dictionary Doc #1 Test", "Verify that no nodes in document #1 of the dictionary collection " "can be modified or deleted by an application. This is a special document " "used internally by FLAIM so it should not be changeable or deletable by " "an application. It is readable but not changeable or deletable.", "(1) Get Doc #1 (2) Try to delete it. ", ""); //get document #1 if ( RC_BAD( rc = m_pDb->getNode( XFLM_DICT_COLLECTION, 1, &pDocument))) { MAKE_ERROR_STRING( "getNode failed.", m_szDetails, rc); goto Exit; } //should not be deleteable rc = pDocument->deleteNode( m_pDb); if ( rc != NE_XFLM_DELETE_NOT_ALLOWED) { MAKE_ERROR_STRING( "Unexpected rc from attempt to delete dict. doc #1." , m_szDetails, rc); goto Exit; } //this failed delete forces us to abort the trans if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; endTest("PASS"); m_pszTestName = "Collection Definition Test"; m_pszTestDesc = "Verify rules for collection definitions are enforced"; m_pszSteps = "(1) Create a collection definition (2) Try to delete its name"; beginTest( "Collection Definition Test", "Verify rules for collection definitions are enforced", "(1) Create a collection definition (2) Try to delete its name", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_COLLECTION_TAG, &pCollection))) { MAKE_ERROR_STRING( "createRootElement failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pCollection->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"My Collection"))) { MAKE_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->documentDone( pCollection))) { MAKE_ERROR_STRING( "documentDone failed", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pCollection->getAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed", m_szDetails, rc); goto Exit; } rc = pAttr->deleteNode( m_pDb); if ( rc != NE_XFLM_DELETE_NOT_ALLOWED) { MAKE_ERROR_STRING( "Invalid rc returned when trying to delete " "\"name\" attribute. Expected: NE_XFLM_DELETE_NOT_ALLOWED", m_szDetails, rc); if( RC_OK( rc)) { rc = NE_XFLM_FAILURE; } goto Exit; } if ( RC_BAD( rc = m_pDb->transAbort())) { MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); goto Exit; } bTransStarted = FALSE; endTest("PASS"); Exit: if ( RC_BAD( rc)) { endTest("FAIL"); } if (bTransStarted) { m_pDb->transCommit(); } if ( pNode) { pNode->Release(); } if ( pAttr) { pAttr->Release(); } if ( pAttrDef) { pAttrDef->Release(); } if ( pElemDef) { pElemDef->Release(); } if ( pDocument) { pDocument->Release(); } if ( pIndex ) { pIndex->Release(); } if( pCollection) { pCollection->Release(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/viewmenu.cpp0000644000175000017500000010303510511001742020557 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the routines which initialize and setup // menus in the VIEW program. // // Tabs: 3 // // Copyright (c) 1992-1995, 1998-2000, 2002-2006 Novell, Inc. // All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: viewmenu.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "view.h" // These variables are for displaying date and time FSTATIC char * Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; // Local Function Prototypes FSTATIC void ViewDispMenuItem( VIEW_MENU_ITEM_p pMenuItem); FSTATIC void ViewRefreshMenu( VIEW_MENU_ITEM_p pPrevItem); FSTATIC void UpdateHorizCursor( FLMBOOL bOnFlag); FSTATIC void DoUpArrow( void); FSTATIC void DoDownArrow( void); FSTATIC void DoPageDown( void); FSTATIC void DoPageUp( void); FSTATIC void DoHome( void); FSTATIC void DoEnd( void); FSTATIC void DoRightArrow( void); FSTATIC void DoLeftArrow( void); FSTATIC void ByteToHex( FLMBYTE ucChar, char * pszDestBuf, FLMBOOL bUpperCaseFlag); FSTATIC void ViewHelpScreen( void); extern FLMUINT gv_uiTopLine; extern FLMUINT gv_uiBottomLine; /*************************************************************************** Desc: This routine frees the memory used to hold menu items. *****************************************************************************/ void ViewFreeMenuMemory( void) { if (gv_pViewMenuFirstItem) { gv_pViewPool->poolFree(); gv_pViewMenuFirstItem = NULL; gv_pViewMenuLastItem = NULL; gv_pViewMenuCurrItem = NULL; } } /*************************************************************************** Desc: This routine initializes variables to start a new menu. *****************************************************************************/ void ViewMenuInit( const char * pszTitle) { FLMUINT uiCol; // Clear the screen and display the menu title f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, 0); // Display the title in the middle of the top line of the screen uiCol = (80 - f_strlen( pszTitle)) / 2; f_conStrOutXY( pszTitle, uiCol, 0); ViewUpdateDate( TRUE, &gv_ViewLastTime); // Deallocate any old memory, if any ViewFreeMenuMemory(); } /*************************************************************************** Desc: This routine converts a FLMBYTE value to two ASCII Hex characters. *****************************************************************************/ FSTATIC void ByteToHex( FLMBYTE ucChar, char * pszDestBuf, FLMBOOL bUpperCaseFlag ) { FLMBYTE ucTempChar; // Convert the upper four bits if ((ucTempChar = ((ucChar & 0xF0) >> 4)) <= 9) { *pszDestBuf = '0' + ucTempChar; } else if (bUpperCaseFlag) { *pszDestBuf = 'A' + ucTempChar - 10; } else { *pszDestBuf = 'a' + ucTempChar - 10; } pszDestBuf++; // Convert the lower four bits if ((ucTempChar = (ucChar & 0x0F)) <= 9) { *pszDestBuf = '0' + ucTempChar; } else if (bUpperCaseFlag) { *pszDestBuf = 'A' + ucTempChar - 10; } else { *pszDestBuf = 'a' + ucTempChar - 10; } pszDestBuf++; // Terminate with a NULL character *pszDestBuf = 0; } /*************************************************************************** Desc: This routine displays a menu item on the screen. *****************************************************************************/ FSTATIC void ViewDispMenuItem( VIEW_MENU_ITEM_p pMenuItem ) { FLMUINT uiRow; FLMUINT uiCol = pMenuItem->uiCol; FLMUINT uiLoop; char szTempBuf [80]; if (!gv_bViewEnabled) { return; } // Calculate row and column where the item is to be displayed uiRow = gv_uiTopLine + (pMenuItem->uiRow - gv_uiViewTopRow); // If it is a HEX display, output the address first if ((pMenuItem->uiValueType & 0x0F) == VAL_IS_BINARY_HEX) { f_sprintf( (char *)szTempBuf, "%03u:%08X", (unsigned)pMenuItem->uiModFileNumber, (unsigned)pMenuItem->uiModFileOffset); f_conSetBackFore( FLM_WHITE, FLM_GREEN); f_conStrOutXY( szTempBuf, 0, uiRow); } // If the item is the current item, display it using the // select colors. Otherwise, display using the unselect colors if (pMenuItem->uiItemNum == gv_uiViewMenuCurrItemNum) { if (!gv_pViewMenuCurrItem) { gv_pViewMenuCurrItem = pMenuItem; } if (pMenuItem->uiOption) { f_conSetBackFore( pMenuItem->uiSelectBackColor, pMenuItem->uiSelectForeColor); } else { f_conSetBackFore( pMenuItem->uiUnselectForeColor, pMenuItem->uiUnselectBackColor); } } else { f_conSetBackFore( pMenuItem->uiUnselectBackColor, pMenuItem->uiUnselectForeColor); } if (pMenuItem->iLabelIndex < 0) { uiCol += (pMenuItem->uiLabelWidth + 1); szTempBuf[ 0] = 0; } else if (!pMenuItem->uiLabelWidth) { f_strcpy( szTempBuf, Labels[ pMenuItem->iLabelIndex]); f_strcpy( &szTempBuf[ f_strlen( szTempBuf)], " "); } else { for( uiLoop = 0; uiLoop < pMenuItem->uiLabelWidth; uiLoop++) { szTempBuf[ uiLoop] = '.'; } szTempBuf[ pMenuItem->uiLabelWidth] = ' '; szTempBuf[ pMenuItem->uiLabelWidth + 1] = 0; f_memcpy( szTempBuf, Labels[ pMenuItem->iLabelIndex], (FLMSIZET)f_strlen( Labels[ pMenuItem->iLabelIndex])); } if (pMenuItem->uiOption) { if (pMenuItem->uiItemNum == gv_uiViewMenuCurrItemNum) { f_conStrOutXY( "*>", (uiCol - 2), uiRow); } else { f_conStrOutXY( "* ", (uiCol - 2), uiRow); } } else { if (pMenuItem->uiItemNum == gv_uiViewMenuCurrItemNum) { f_conStrOutXY( " >", (uiCol - 2), uiRow); } else { f_conStrOutXY( " ", (uiCol - 2), uiRow); } } if (szTempBuf[ 0]) { f_conStrOutXY( szTempBuf, uiCol, uiRow); } // Now output the value uiCol += f_strlen( szTempBuf); switch (pMenuItem->uiValueType & 0x0F) { case VAL_IS_LABEL_INDEX: f_conStrOutXY( (Labels[ pMenuItem->ui64Value]), uiCol, uiRow); break; case VAL_IS_ERR_INDEX: { FLMUINT ValIndex = (FLMUINT)pMenuItem->ui64Value; f_conStrOutXY( gv_pDbSystem->checkErrorToStr( ValIndex), uiCol, uiRow); break; } case VAL_IS_TEXT_PTR: f_conStrOutXY( ((char *)(FLMUINT)pMenuItem->ui64Value), uiCol, uiRow); break; case VAL_IS_BINARY_HEX: case VAL_IS_BINARY_PTR: { FLMUINT uiBytesPerLine = MAX_HORIZ_SIZE( uiCol); FLMUINT uiBytesProcessed = 0; FLMUINT uiLoopJ; FLMUINT uiLoopK; FLMUINT uiNumBytes; FLMBYTE * pucVal = (FLMBYTE *)(FLMUINT)pMenuItem->ui64Value; FLMUINT uiValLen = pMenuItem->uiValueLen; // Process each character in the value uiLoop = 0; uiLoopJ = 0; uiLoopK = uiBytesPerLine * 3 + 5; uiNumBytes = 0; // Fill up a single line with whatever will fit on the line in // hex format. f_memset( szTempBuf, ' ', 80); szTempBuf[ uiLoopK - 3] = '|'; while (uiBytesProcessed < uiValLen && uiLoop < uiBytesPerLine) { ByteToHex( pucVal[ uiBytesProcessed], &szTempBuf[ uiLoopJ], TRUE); if (pucVal[ uiBytesProcessed] > ' ' && pucVal[ uiBytesProcessed] <= 127) { szTempBuf [uiLoopK] = pucVal [uiBytesProcessed]; } else { szTempBuf [uiLoopK] = '.'; } uiLoopK++; uiNumBytes++; uiBytesProcessed++; uiLoop++; uiLoopJ += 2; szTempBuf [uiLoopJ] = ' '; uiLoopJ++; } szTempBuf [uiLoopK] = 0; // Output the line f_conStrOutXY( szTempBuf, uiCol, uiRow); if (pMenuItem->uiItemNum == gv_uiViewMenuCurrItemNum) { UpdateHorizCursor( TRUE); } break; } case VAL_IS_NUMBER: switch (pMenuItem->uiValueType & 0xF0) { case DISP_DECIMAL: f_sprintf( (char *)szTempBuf, "%I64u", pMenuItem->ui64Value); break; case DISP_HEX: if (pMenuItem->ui64Value == 0xFFFFFFFF) { f_strcpy( szTempBuf, "None"); } else if (pMenuItem->ui64Value == 0) { f_strcpy( szTempBuf, "0"); } else { szTempBuf [0] = '0'; szTempBuf [1] = 'x'; f_sprintf( (char *)&szTempBuf[ 2], "%I64X", pMenuItem->ui64Value); } break; case DISP_DECIMAL_HEX: f_sprintf( (char *)szTempBuf, "%I64u (0x%I64X)", pMenuItem->ui64Value, pMenuItem->ui64Value); break; case DISP_HEX_DECIMAL: default: if (pMenuItem->ui64Value == 0xFFFFFFFF) { f_strcpy( szTempBuf, "None"); } else if (pMenuItem->ui64Value == 0) { f_strcpy( szTempBuf, "0"); } else { f_sprintf( (char *)szTempBuf, "0x%I64X (%I64u)", pMenuItem->ui64Value, pMenuItem->ui64Value); } break; } f_conStrOutXY( szTempBuf, uiCol, uiRow); break; case VAL_IS_EMPTY: default: break; } } /*************************************************************************** Desc: This routine adds a menu item to the item list. *****************************************************************************/ FLMBOOL ViewAddMenuItem( FLMINT iLabelIndex, FLMUINT uiLabelWidth, FLMUINT uiValueType, FLMUINT64 ui64Value, FLMUINT uiValueLen, FLMUINT uiModFileNumber, FLMUINT uiModFileOffset, FLMUINT uiModBufLen, FLMUINT uiModType, FLMUINT uiCol, FLMUINT uiRow, FLMUINT uiOption, eColorType uiUnselectBackColor, eColorType uiUnselectForeColor, eColorType uiSelectBackColor, eColorType uiSelectForeColor) { FLMBOOL bOk = FALSE; VIEW_MENU_ITEM_p pMenuItem; FLMUINT uiSize = sizeof( VIEW_MENU_ITEM); // Allocate the memory for the item if ((uiValueType & 0x0F) == VAL_IS_TEXT_PTR) { uiSize += (f_strlen( (const char *)((FLMUINT)ui64Value)) + 1); } else if ((uiValueType & 0x0F) == VAL_IS_BINARY) { uiSize += uiValueLen; } if (RC_BAD( gv_pViewPool->poolAlloc(uiSize, (void **)&pMenuItem))) { ViewShowError( "Could not allocate memory for menu value"); goto Exit; } // Link the item into the array and set some values in the item pMenuItem->pNextItem = NULL; pMenuItem->pPrevItem = gv_pViewMenuLastItem; if (gv_pViewMenuLastItem) { gv_pViewMenuLastItem->pNextItem = pMenuItem; pMenuItem->uiItemNum = gv_pViewMenuLastItem->uiItemNum + 1; } else { gv_pViewMenuFirstItem = pMenuItem; pMenuItem->uiItemNum = 0; } gv_pViewMenuLastItem = pMenuItem; pMenuItem->iLabelIndex = iLabelIndex; pMenuItem->uiLabelWidth = uiLabelWidth; pMenuItem->uiValueType = uiValueType; if ((uiValueType & 0x0F) == VAL_IS_TEXT_PTR) { pMenuItem->ui64Value = (FLMUINT64)((FLMUINT)&pMenuItem[ 1]); f_strcpy( (char *)(FLMUINT)pMenuItem->ui64Value, (const char *)(FLMUINT)ui64Value); } else if ((uiValueType & 0x0F) == VAL_IS_BINARY) { pMenuItem->uiValueType = VAL_IS_BINARY_PTR; pMenuItem->ui64Value = (FLMUINT64)((FLMUINT)&pMenuItem[ 1]); f_memcpy( (void *)(FLMUINT)pMenuItem->ui64Value, (void *)(FLMUINT)ui64Value, uiValueLen); } else { pMenuItem->ui64Value = ui64Value; } pMenuItem->uiValueLen = uiValueLen; pMenuItem->uiModFileOffset = uiModFileOffset; pMenuItem->uiModFileNumber = uiModFileNumber; pMenuItem->uiModBufLen = uiModBufLen; pMenuItem->uiModType = uiModType; pMenuItem->uiCol = uiCol; pMenuItem->uiRow = uiRow; pMenuItem->uiOption = uiOption; pMenuItem->uiUnselectBackColor = uiUnselectBackColor; pMenuItem->uiUnselectForeColor = uiUnselectForeColor; pMenuItem->uiSelectBackColor = uiSelectBackColor; pMenuItem->uiSelectForeColor = uiSelectForeColor; pMenuItem->uiHorizCurPos = 0; if (pMenuItem->uiRow >= gv_uiViewTopRow && pMenuItem->uiRow <= gv_uiViewBottomRow) { ViewDispMenuItem( pMenuItem); } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine displays the prompt to press ESCAPE. This prompt appears at the bottom of every screen. *****************************************************************************/ void ViewEscPrompt( void) { FLMUINT uiNumCols; FLMUINT uiNumRows; f_conGetScreenSize( &uiNumCols, &uiNumRows); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, uiNumRows - 1); f_conSetBackFore( FLM_RED, FLM_WHITE); f_conStrOutXY( "ESC=Exit, ?=Help", 0, uiNumRows - 1); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conStrOutXY( "File: ", 20, uiNumRows - 1); f_conStrOutXY( gv_szViewFileName, 26, uiNumRows - 1); gv_uiViewLastFileOffset = VIEW_INVALID_FILE_OFFSET; } /*************************************************************************** Desc: This routine refreshes the menu display. If NULL is passed in the entire screen is refreshed. Otherwise, the item passed as well as the current item are refreshed. *****************************************************************************/ FSTATIC void ViewRefreshMenu( VIEW_MENU_ITEM_p pPrevItem ) { VIEW_MENU_ITEM_p pMenuItem; gv_uiViewMenuCurrItemNum = gv_pViewMenuCurrItem->uiItemNum; if (pPrevItem) { ViewDispMenuItem( pPrevItem); ViewDispMenuItem( gv_pViewMenuCurrItem); } else { // Refresh the entire screen f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, 1); pMenuItem = gv_pViewMenuFirstItem; while( pMenuItem) { if (pMenuItem->uiRow >= gv_uiViewTopRow && pMenuItem->uiRow <= gv_uiViewBottomRow) { ViewDispMenuItem( pMenuItem); } pMenuItem = pMenuItem->pNextItem; } ViewEscPrompt(); } } /******************************************************************** Desc: Update the horizontal cursor *********************************************************************/ FSTATIC void UpdateHorizCursor( FLMBOOL bOnFlag ) { unsigned char szTempBuf[ 4]; FLMUINT uiLoop; FLMUINT uiRow; FLMUINT uiCol = gv_pViewMenuCurrItem->uiCol + 1; FLMBYTE * pucVal = ((FLMBYTE *)(FLMUINT)gv_pViewMenuCurrItem->ui64Value); if (gv_pViewMenuCurrItem->uiHorizCurPos > HORIZ_SIZE( gv_pViewMenuCurrItem) - 1) { gv_pViewMenuCurrItem->uiHorizCurPos = HORIZ_SIZE( gv_pViewMenuCurrItem) - 1; } uiLoop = gv_pViewMenuCurrItem->uiHorizCurPos; ByteToHex( *(pucVal + uiLoop), (char *)szTempBuf, TRUE); szTempBuf[ 2] = 0; if (bOnFlag) { f_conSetBackFore( FLM_RED, FLM_WHITE); } else { f_conSetBackFore( gv_pViewMenuCurrItem->uiUnselectForeColor, gv_pViewMenuCurrItem->uiUnselectBackColor); } // Calculate row and column where the item is to be displayed uiRow = gv_uiTopLine + (gv_pViewMenuCurrItem->uiRow - gv_uiViewTopRow); f_conStrOutXY( (char *)szTempBuf, (uiCol + uiLoop * 3), uiRow); if (((szTempBuf[ 0] = pucVal[ uiLoop]) < ' ') || (szTempBuf[ 0] > 127)) { szTempBuf[ 0] = ' '; } #if defined( FLM_WIN) if (bOnFlag) { szTempBuf [0] = 128; } #endif szTempBuf[ 1] = 0; f_conStrOutXY( (char *)szTempBuf, (uiCol + MAX_HORIZ_SIZE( uiCol) * 3 + 5 + uiLoop), uiRow); f_conSetBackFore( gv_pViewMenuCurrItem->uiUnselectForeColor, gv_pViewMenuCurrItem->uiUnselectBackColor); if (bOnFlag) { f_conStrOutXY( ">", (uiCol + uiLoop * 3 - 1), uiRow); } else { f_conStrOutXY( " ", (uiCol + uiLoop * 3 - 1), uiRow); } } /******************************************************************** Desc: Handle up arrow key *********************************************************************/ FSTATIC void DoUpArrow( void) { VIEW_MENU_ITEM_p pPrevItem; if ((pPrevItem = gv_pViewMenuCurrItem->pPrevItem) != NULL) { if (pPrevItem->uiHorizCurPos != gv_pViewMenuCurrItem->uiHorizCurPos) { pPrevItem->uiHorizCurPos = gv_pViewMenuCurrItem->uiHorizCurPos; } gv_pViewMenuCurrItem = pPrevItem; if (gv_pViewMenuCurrItem->uiRow < gv_uiViewTopRow) { gv_uiViewTopRow--; gv_uiViewBottomRow--; if (gv_pViewMenuCurrItem->uiRow < gv_uiViewTopRow) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pNextItem; } ViewRefreshMenu( NULL); } else { ViewRefreshMenu( gv_pViewMenuCurrItem->pNextItem); } } } /******************************************************************** Desc: Handle down arrow key *********************************************************************/ FSTATIC void DoDownArrow( void) { VIEW_MENU_ITEM_p pNextItem; if ((pNextItem = gv_pViewMenuCurrItem->pNextItem) != NULL) { if (pNextItem->uiHorizCurPos != gv_pViewMenuCurrItem->uiHorizCurPos) { pNextItem->uiHorizCurPos = gv_pViewMenuCurrItem->uiHorizCurPos; } gv_pViewMenuCurrItem = pNextItem; if (gv_pViewMenuCurrItem->uiRow > gv_uiViewBottomRow) { gv_uiViewTopRow++; gv_uiViewBottomRow++; if (gv_pViewMenuCurrItem->uiRow > gv_uiViewBottomRow) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pPrevItem; } ViewRefreshMenu( NULL); } else { ViewRefreshMenu( gv_pViewMenuCurrItem->pPrevItem); } } } /******************************************************************** Desc: Handle page down key *********************************************************************/ FSTATIC void DoPageDown( void) { FLMUINT uiTargetRow; VIEW_MENU_ITEM_p pSaveItem; if (gv_uiViewBottomRow < gv_pViewMenuLastItem->uiRow) { gv_uiViewBottomRow += LINES_PER_PAGE; if (gv_uiViewBottomRow > gv_pViewMenuLastItem->uiRow) { gv_uiViewBottomRow = gv_pViewMenuLastItem->uiRow; } gv_uiViewTopRow = gv_uiViewBottomRow - LINES_PER_PAGE + 1; uiTargetRow = gv_pViewMenuCurrItem->uiRow + LINES_PER_PAGE; if (uiTargetRow > gv_uiViewBottomRow) { uiTargetRow = gv_uiViewBottomRow; } while (gv_pViewMenuCurrItem->pNextItem && gv_pViewMenuCurrItem->uiRow < uiTargetRow) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pNextItem; } if (gv_pViewMenuCurrItem->uiRow > gv_uiViewBottomRow) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pPrevItem; } ViewRefreshMenu( NULL); } else if (gv_pViewMenuCurrItem->pNextItem) { pSaveItem = gv_pViewMenuCurrItem; gv_pViewMenuCurrItem = gv_pViewMenuLastItem; ViewRefreshMenu( pSaveItem); } } /******************************************************************** Desc: Handle the page up key *********************************************************************/ FSTATIC void DoPageUp( void) { FLMUINT uiTargetRow; VIEW_MENU_ITEM_p pSaveItem; if (gv_uiViewTopRow > 0) { if (gv_uiViewTopRow < LINES_PER_PAGE) { gv_uiViewTopRow = 0; } else { gv_uiViewTopRow -= LINES_PER_PAGE; } gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; uiTargetRow = gv_pViewMenuCurrItem->uiRow - LINES_PER_PAGE; if (uiTargetRow < gv_uiViewTopRow) { uiTargetRow = gv_uiViewTopRow; } while (gv_pViewMenuCurrItem->pPrevItem && gv_pViewMenuCurrItem->uiRow > uiTargetRow) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pPrevItem; } if (gv_pViewMenuCurrItem->uiRow < gv_uiViewTopRow) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pNextItem; } ViewRefreshMenu( NULL); } else if (gv_pViewMenuCurrItem->pPrevItem != NULL) { pSaveItem = gv_pViewMenuCurrItem; gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; ViewRefreshMenu( pSaveItem); } } /******************************************************************** Desc: Handle home key *********************************************************************/ FSTATIC void DoHome( void) { VIEW_MENU_ITEM_p pSaveItem; if (gv_uiViewTopRow != 0) { gv_uiViewTopRow = 0; gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; ViewRefreshMenu( NULL); } else if (gv_pViewMenuCurrItem->pPrevItem) { pSaveItem = gv_pViewMenuCurrItem; gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; ViewRefreshMenu( pSaveItem); } } /******************************************************************** Desc: Handle end key *********************************************************************/ FSTATIC void DoEnd( void) { VIEW_MENU_ITEM_p pSaveItem; if (gv_uiViewBottomRow < gv_pViewMenuLastItem->uiRow) { gv_uiViewBottomRow = gv_pViewMenuLastItem->uiRow; gv_uiViewTopRow = gv_uiViewBottomRow - LINES_PER_PAGE + 1; gv_pViewMenuCurrItem = gv_pViewMenuLastItem; ViewRefreshMenu( NULL); } else if (gv_pViewMenuCurrItem->pNextItem) { pSaveItem = gv_pViewMenuCurrItem; gv_pViewMenuCurrItem = gv_pViewMenuLastItem; ViewRefreshMenu( pSaveItem); } } /******************************************************************** Desc: Handle right arrow key *********************************************************************/ FSTATIC void DoRightArrow( void) { if (!HAVE_HORIZ_CUR( gv_pViewMenuCurrItem) || gv_pViewMenuCurrItem->uiHorizCurPos == HORIZ_SIZE( gv_pViewMenuCurrItem) - 1) { if (gv_pViewMenuCurrItem->uiItemNum != gv_pViewMenuLastItem->uiItemNum) { gv_pViewMenuCurrItem->uiHorizCurPos = 0; DoDownArrow(); } } else if (gv_pViewMenuCurrItem->uiHorizCurPos < HORIZ_SIZE( gv_pViewMenuCurrItem) - 1) { UpdateHorizCursor( FALSE); gv_pViewMenuCurrItem->uiHorizCurPos++; UpdateHorizCursor( TRUE); } } /******************************************************************** Desc: Handle left arrow key *********************************************************************/ FSTATIC void DoLeftArrow( void) { if (!HAVE_HORIZ_CUR( gv_pViewMenuCurrItem) || gv_pViewMenuCurrItem->uiHorizCurPos == 0) { if (gv_pViewMenuCurrItem->pPrevItem) { gv_pViewMenuCurrItem->uiHorizCurPos = HORIZ_SIZE( gv_pViewMenuCurrItem->pPrevItem) - 1; } DoUpArrow(); } else if (gv_pViewMenuCurrItem->uiHorizCurPos > 0) { UpdateHorizCursor( FALSE); gv_pViewMenuCurrItem->uiHorizCurPos--; UpdateHorizCursor( TRUE); } } /*************************************************************************** Desc: This routine displays a help screen showing available commands. *****************************************************************************/ FSTATIC void ViewHelpScreen( void) { FLMUINT uiChar; // Clear the screen and display the menu title f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, 1); f_conSetCursorPos( 0, 3); f_conStrOut( " RECOGNIZED KEYBOARD CHARACTERS\n"); f_conStrOut( "\n"); f_conStrOut( " ESCAPE - Exit Screen\n"); f_conStrOut( " U,u,8 - Up Arrow\n"); f_conStrOut( " D,d,2 - Down Arrow\n"); f_conStrOut( " +,3 - Page Down\n"); f_conStrOut( " R,r,6 - Right Arrow\n"); f_conStrOut( " L,l,5 - Left Arrow\n"); f_conStrOut( " -,9 - Page Up\n"); f_conStrOut( " H,h,7 - Home\n"); f_conStrOut( " Z,z,1 - End\n"); f_conStrOut( " E,e - Edit Data\n"); f_conStrOut( " A,a - Edit Data in RAW Mode (no checksum)\n"); f_conStrOut( " G,g - Goto Block\n"); f_conStrOut( " X,x - Display Hex\n"); f_conStrOut( " Y,y - Display Decrypted\n"); f_conStrOut( " S,s - Search\n"); f_conStrOut( " ? - Show this help screen\n"); f_conStrOut( "\n"); f_conStrOut( " PRESS ANY CHARACTER TO EXIT HELP SCREEN\n"); for (;;) { // Update date and time ViewUpdateDate( FALSE, &gv_ViewLastTime); // See what character was pressed uiChar = (!f_conHaveKey()) ? 0 : (f_conGetKey()); if (gv_bShutdown) { return; } if (uiChar) { break; } viewGiveUpCPU(); } ViewRefreshMenu( NULL); } /*************************************************************************** Desc: This routine allows the user to press keys while in a menu. Keys for navigating through the menu are handled inside this routine. Other keys are passed to the calling routine or are ignored altogether. *****************************************************************************/ FLMUINT ViewGetMenuOption( void) { FLMUINT uiChar; // Make sure we have a pointer to the current item if (!gv_pViewMenuCurrItem) { gv_pViewMenuCurrItem = gv_pViewMenuFirstItem; while (gv_pViewMenuCurrItem->pNextItem && gv_pViewMenuCurrItem->uiItemNum < gv_uiViewMenuCurrItemNum) { gv_pViewMenuCurrItem = gv_pViewMenuCurrItem->pNextItem; } if (gv_pViewMenuCurrItem->uiItemNum != gv_uiViewMenuCurrItemNum) { gv_uiViewMenuCurrItemNum = gv_pViewMenuCurrItem->uiItemNum; ViewDispMenuItem( gv_pViewMenuCurrItem); } } // Loop getting user input ViewEscPrompt(); for (;;) { // Set the file position of the current object if (gv_pViewMenuCurrItem->uiModFileOffset != VIEW_INVALID_FILE_OFFSET) { gv_uiViewCurrFileOffset = gv_pViewMenuCurrItem->uiModFileOffset; gv_uiViewCurrFileNumber = gv_pViewMenuCurrItem->uiModFileNumber; if (HAVE_HORIZ_CUR( gv_pViewMenuCurrItem)) { gv_uiViewCurrFileOffset += gv_pViewMenuCurrItem->uiHorizCurPos; } } // Update date and time ViewUpdateDate( FALSE, &gv_ViewLastTime); // See what character was pressed viewGiveUpCPU(); uiChar = (FLMUINT)(!f_conHaveKey() ? (FLMUINT)0 : (FLMUINT)f_conGetKey()); if (gv_bShutdown) { return( ESCAPE_OPTION); } switch( uiChar) { case FKB_ESCAPE: return( ESCAPE_OPTION); case FKB_UP: case 'U': case 'u': case '8': DoUpArrow(); break; case FKB_DOWN: case 'D': case 'd': case '2': DoDownArrow(); break; case FKB_PGDN: case '+': case '3': DoPageDown(); break; case FKB_PGUP: case '-': case '9': DoPageUp(); break; case FKB_HOME: case 'H': case 'h': case '7': DoHome(); break; case FKB_END: case 'Z': case 'z': case '1': DoEnd(); break; case '\n': case '\r': case FKB_ENTER: if (gv_pViewMenuCurrItem->uiOption) { return( gv_pViewMenuCurrItem->uiOption); } break; case 'G': case 'g': case 7: /* Control-G */ return( GOTO_BLOCK_OPTION); case FKB_RIGHT: case 'R': case 'r': case '6': DoRightArrow(); break; case FKB_LEFT: case 'L': case 'l': case '4': DoLeftArrow(); break; case 'E': case 'e': return( EDIT_OPTION); case 'A': case 'a': return( EDIT_RAW_OPTION); case 'x': case 'X': return( HEX_OPTION); case 'S': case 's': return( SEARCH_OPTION); case '?': ViewHelpScreen(); break; default: break; } } } /*************************************************************************** Desc: This routine updates the date and time on the screen. *****************************************************************************/ void ViewUpdateDate( FLMBOOL bUpdateFlag, F_TMSTAMP * pLastTime ) { F_TMSTAMP CurrTime; char szTempBuf [64]; FLMUINT uiHour; char szAmPm [4]; FLMUINT uiNumCols; FLMUINT uiNumRows; f_conGetScreenSize( &uiNumCols, &uiNumRows); f_timeGetTimeStamp( &CurrTime); // Update the date, if it has changed or the bUpdateFlag is set if (bUpdateFlag || pLastTime->year != CurrTime.year || pLastTime->month != CurrTime.month || pLastTime->day != CurrTime.day) { f_sprintf( (char *)szTempBuf, "%s %u, %u", Months [CurrTime.month], (unsigned)CurrTime.day, (unsigned)CurrTime.year); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conStrOutXY( szTempBuf, 0, 0); } // Update the time, if it has changed or the bUpdateFlag is set if (bUpdateFlag || pLastTime->hour != CurrTime.hour || pLastTime->minute != CurrTime.minute || pLastTime->second != CurrTime.second) { if (CurrTime.hour == 0) { uiHour = 12; } else if (CurrTime.hour > 12) { uiHour = (FLMUINT)CurrTime.hour - 12; } else { uiHour = (FLMUINT)CurrTime.hour; } if (CurrTime.hour >= 12) { f_strcpy( szAmPm, "pm"); } else { f_strcpy( szAmPm, "am"); } f_sprintf( (char *)szTempBuf, "%2u:%02u:%02u %s", (unsigned)uiHour, (unsigned)CurrTime.minute, (unsigned)CurrTime.second, szAmPm); f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conStrOutXY( szTempBuf, 66, 0); } if (bUpdateFlag || gv_uiViewLastFileOffset != gv_uiViewCurrFileOffset || (gv_pViewMenuCurrItem->uiModFileOffset == VIEW_INVALID_FILE_OFFSET && gv_uiViewLastFileOffset != VIEW_INVALID_FILE_OFFSET)) { if (!gv_pViewMenuCurrItem) { gv_uiViewLastFileOffset = VIEW_INVALID_FILE_OFFSET; } else if (gv_pViewMenuCurrItem->uiModFileOffset == VIEW_INVALID_FILE_OFFSET) { gv_uiViewLastFileNumber = gv_pViewMenuCurrItem->uiModFileNumber; gv_uiViewLastFileOffset = gv_pViewMenuCurrItem->uiModFileOffset; } else { gv_uiViewLastFileNumber = gv_uiViewCurrFileNumber; gv_uiViewLastFileOffset = gv_uiViewCurrFileOffset; } if (gv_uiViewLastFileOffset == VIEW_INVALID_FILE_OFFSET) { f_strcpy( szTempBuf, "File: N/A File Pos: N/A "); } else { f_sprintf( (char *)szTempBuf, "File: %03u File Pos: 0x%08X", (unsigned)gv_uiViewLastFileNumber, (unsigned)gv_uiViewLastFileOffset); } f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conStrOutXY( szTempBuf, 47, uiNumRows - 1); } // Save the date and time f_memcpy( pLastTime, &CurrTime, sizeof( F_TMSTAMP)); } /*************************************************************************** Desc: This routine resets the view parameters for a menu and saves the parameters for the current menu. This is done whenever a new menu is being entered. It allows the previous menu to be restored to its original state upon returning. *****************************************************************************/ void ViewReset( VIEW_INFO_p pSaveView ) { pSaveView->uiCurrItem = gv_uiViewMenuCurrItemNum; pSaveView->uiTopRow = gv_uiViewTopRow; pSaveView->uiBottomRow = gv_uiViewBottomRow; pSaveView->uiCurrFileOffset = gv_uiViewCurrFileOffset; pSaveView->uiCurrFileNumber = gv_uiViewCurrFileNumber; gv_pViewMenuCurrItem = NULL; gv_uiViewMenuCurrItemNum = 0; gv_uiViewTopRow = 0; gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; gv_uiViewCurrFileOffset = 0; gv_uiViewCurrFileNumber = 0; } /******************************************************************** Desc: ? *********************************************************************/ void ViewDisable( void) { gv_bViewEnabled = FALSE; } /******************************************************************** Desc: ? *********************************************************************/ void ViewEnable( void) { VIEW_MENU_ITEM_p pMenuItem; FLMUINT uiDistance = 0xFFFFFFFF; VIEW_MENU_ITEM_p pClosestMenuItem = NULL; FLMUINT uiStartOffset; FLMUINT uiEndOffset; if (!gv_bViewEnabled) { if (gv_uiViewCurrFileOffset != VIEW_INVALID_FILE_OFFSET) { pMenuItem = gv_pViewMenuFirstItem; while (pMenuItem) { if (pMenuItem->uiModFileOffset != VIEW_INVALID_FILE_OFFSET) { uiStartOffset = pMenuItem->uiModFileOffset; switch (pMenuItem->uiModType & 0x0F) { case MOD_FLMUINT32: uiEndOffset = uiStartOffset + 3; break; case MOD_FLMUINT16: uiEndOffset = uiStartOffset + 1; break; case MOD_BINARY: case MOD_TEXT: uiEndOffset = uiStartOffset + pMenuItem->uiModBufLen - 1; break; case MOD_CHILD_BLK: uiEndOffset = uiStartOffset + 2; break; case MOD_FLMUINT64: uiEndOffset = uiStartOffset + 7; break; default: uiEndOffset = uiStartOffset; break; } if (gv_uiViewCurrFileOffset >= uiStartOffset && gv_uiViewCurrFileOffset <= uiEndOffset) { if ((pMenuItem->uiModType & 0xF0) == MOD_DISABLED) { pClosestMenuItem = pMenuItem; uiDistance = 0; } else { pClosestMenuItem = pMenuItem; break; } } else if (gv_uiViewCurrFileOffset < uiStartOffset) { if (uiStartOffset - gv_uiViewCurrFileOffset < uiDistance) { pClosestMenuItem = pMenuItem; uiDistance = uiStartOffset - gv_uiViewCurrFileOffset; } } else { if (gv_uiViewCurrFileOffset - uiStartOffset < uiDistance) { pClosestMenuItem = pMenuItem; uiDistance = gv_uiViewCurrFileOffset - uiStartOffset; } } } pMenuItem = pMenuItem->pNextItem; } } if (pClosestMenuItem) { gv_pViewMenuCurrItem = pMenuItem = pClosestMenuItem; gv_uiViewMenuCurrItemNum = pMenuItem->uiItemNum; if (pMenuItem->uiRow < LINES_PER_PAGE) { gv_uiViewTopRow = 0; } else { gv_uiViewTopRow = pMenuItem->uiRow - LINES_PER_PAGE / 2 + 1; } gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; if (gv_uiViewBottomRow > gv_pViewMenuLastItem->uiRow) { gv_uiViewBottomRow = gv_pViewMenuLastItem->uiRow; } if (gv_uiViewBottomRow - gv_uiViewTopRow + 1 < LINES_PER_PAGE) { if (gv_uiViewBottomRow < LINES_PER_PAGE + 1) { gv_uiViewTopRow = 0; } else { gv_uiViewTopRow = gv_uiViewBottomRow + 1 - LINES_PER_PAGE; } } if (HAVE_HORIZ_CUR( pMenuItem) && gv_uiViewCurrFileOffset - pMenuItem->uiModFileOffset <= (FLMUINT)pMenuItem->uiModBufLen) { pMenuItem->uiHorizCurPos = (FLMUINT)(gv_uiViewCurrFileOffset - pMenuItem->uiModFileOffset); } } gv_bViewEnabled = TRUE; ViewRefreshMenu( NULL); } } /*************************************************************************** Desc: This routine restores the view parameters for a menu which were previously saved by the ViewReset routine. *****************************************************************************/ void ViewRestore( VIEW_INFO_p pSaveView ) { gv_uiViewMenuCurrItemNum = pSaveView->uiCurrItem; gv_pViewMenuCurrItem = NULL; gv_uiViewTopRow = pSaveView->uiTopRow; gv_uiViewBottomRow = pSaveView->uiBottomRow; gv_uiViewCurrFileOffset = pSaveView->uiCurrFileOffset; gv_uiViewCurrFileNumber = pSaveView->uiCurrFileNumber; } libxflaim-5.1.969/util/enctestsrv.cpp0000644000175000017500000011626210511001742021126 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Encryption tests // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: enctestsrv.cpp 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\ENC.DB" #define BACKUP_NAME_STR "SYS:\\ENC.BAK" #else #define DB_NAME_STR "enc.db" #define BACKUP_NAME_STR "enc.bak" #endif #define PASSWORD "password" #define BAD_PASSWORD "bad_password" #define BACKUP_PASSWORD "backup_password" #define UINT_VAL 1234 #define INT_VAL -1234 #define UINT64_VAL (FLMUINT64)5000000 #define INT64_VAL (FLMINT64)-5000000 #define NATIVE_VAL "native_val" #define BIN_VAL ((FLMBYTE *)"\x01\x02\x03") #define BIN_VAL_LEN 3 /**************************************************************************** Desc: ****************************************************************************/ class IEncTestImpl : public TestBase { public: IEncTestImpl(); ~IEncTestImpl(); const char * getName( void); RCODE suite1( void); RCODE suite2( void); RCODE execute( void); private: FLMUINT m_uiNumberElm; FLMUINT m_uiTextElm; FLMUINT m_uiBinaryElm; FLMUINT m_uiNoDataElm; FLMUINT m_uiNumberAttr; FLMUINT m_uiTextAttr; FLMUINT m_uiBinaryAttr; IF_DOMNode * m_pRootNode; IF_DOMNode * m_pNumberElmNode; IF_DOMNode * m_pTextElmNode; IF_DOMNode * m_pBinElmNode; IF_DOMNode * m_pAttributedNode; IF_DOMNode * m_pNumberAttrNode; IF_DOMNode * m_pTextAttrNode; IF_DOMNode * m_pBinAttrNode; IF_Backup * m_pBackup; FLMUINT m_uiAESDef; FLMUINT m_uiDES3Def; RCODE importEncDefs( void); RCODE setupElemAndAttrNodes( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IEncTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IEncTestImpl::getName( void) { return( "Encryption Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IEncTestImpl::setupElemAndAttrNodes( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_nodata_element", XFLM_NODATA_TYPE, &m_uiNoDataElm))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, m_uiNoDataElm, &m_pRootNode))) { MAKE_ERROR_STRING( "createRootNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_number_element", XFLM_NUMBER_TYPE, &m_uiNumberElm))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, m_uiNumberElm, XFLM_LAST_CHILD, &m_pNumberElmNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_text_element", XFLM_TEXT_TYPE, &m_uiTextElm))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, m_uiTextElm, XFLM_LAST_CHILD, &m_pTextElmNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_binary_element", XFLM_BINARY_TYPE, &m_uiBinaryElm))) { MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, m_uiBinaryElm, XFLM_LAST_CHILD, &m_pBinElmNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } // Create the element node to hang all the attribute nodes off of if( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, m_uiNoDataElm, XFLM_LAST_CHILD, &m_pAttributedNode))) { MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "generic_number_attr", XFLM_NUMBER_TYPE, &m_uiNumberAttr))) { MAKE_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pAttributedNode->createAttribute( m_pDb, m_uiNumberAttr, &m_pNumberAttrNode))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "generic_text_attr", XFLM_TEXT_TYPE, &m_uiTextAttr))) { MAKE_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pAttributedNode->createAttribute( m_pDb, m_uiTextAttr, &m_pTextAttrNode))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "generic_binary_attr", XFLM_BINARY_TYPE, &m_uiBinaryAttr))) { MAKE_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pAttributedNode->createAttribute( m_pDb, m_uiBinaryAttr, &m_pBinAttrNode))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ IEncTestImpl::IEncTestImpl( void) { m_uiNumberElm = 0; m_uiTextElm = 0; m_uiBinaryElm = 0; m_uiNoDataElm = 0; m_uiNumberAttr = 0; m_uiTextAttr = 0; m_uiBinaryAttr = 0; m_pRootNode = NULL; m_pNumberElmNode = NULL; m_pTextElmNode = NULL; m_pBinElmNode = NULL; m_pAttributedNode = NULL; m_pNumberAttrNode = NULL; m_pTextAttrNode = NULL; m_pBinAttrNode = NULL; m_pBackup = NULL; m_uiAESDef = 0; m_uiDES3Def = 0; } /**************************************************************************** Desc: ****************************************************************************/ IEncTestImpl::~IEncTestImpl( void) { if( m_pRootNode) { m_pRootNode->Release(); } if( m_pNumberElmNode) { m_pNumberElmNode->Release(); } if( m_pTextElmNode) { m_pTextElmNode->Release(); } if( m_pBinElmNode) { m_pBinElmNode->Release(); } if( m_pAttributedNode) { m_pAttributedNode->Release(); } if( m_pNumberAttrNode) { m_pNumberAttrNode->Release(); } if( m_pTextAttrNode) { m_pTextAttrNode->Release(); } if( m_pBinAttrNode) { m_pBinAttrNode->Release(); } if( m_pBackup) { m_pBackup->Release(); } } /**************************************************************************** Desc: ****************************************************************************/ RCODE IEncTestImpl::importEncDefs( void) { RCODE rc = NE_XFLM_OK; char * ppszEncDefs[] = {XFLM_ENC_AES_OPTION_STR, XFLM_ENC_DES3_OPTION_STR}; FLMUINT puiEncDef[ 2]; char szEncDef[ 200]; FLMUINT uiLoop = 0; for( uiLoop = 0; uiLoop < sizeof(ppszEncDefs)/sizeof(ppszEncDefs[0]); uiLoop++) { f_sprintf( szEncDef, "", ppszEncDefs[uiLoop], ppszEncDefs[uiLoop]); if ( RC_BAD( rc = importBuffer( szEncDef, XFLM_DICT_COLLECTION))) { MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); goto Exit; } f_sprintf( szEncDef, "%s definition", ppszEncDefs[uiLoop]); if (RC_BAD( rc = m_pDb->getEncDefId( (char *)szEncDef, (FLMUINT *)&puiEncDef[ uiLoop]))) { goto Exit; } } m_uiAESDef = puiEncDef[ 0]; m_uiDES3Def = puiEncDef[ 1]; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IEncTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; if( RC_BAD( rc = suite1())) { goto Exit; } if( RC_BAD( rc = suite2())) { goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IEncTestImpl::suite1( void) { RCODE rc = NE_XFLM_OK; #ifdef FLM_USE_NICI FLMBOOL bDibCreated = FALSE; FLMBOOL bTransBegun = FALSE; FLMUINT puiEncDefs[ 4]; char * ppszEncDefs[ 4]; FLMUINT uiResult = 0; FLMUINT64 ui64Result = 0; FLMINT iResult = 0; FLMINT64 i64Result = 0; char szTemp[ 128]; FLMUINT uiLoop; FLMUINT uiLoop2; char * ppszTextSizes[] = {"Medium","Large","Small"}; char * ppszNativeValues[] = {"MediumMedium","LargeLargeLargeLargeLargeLargeLarge","Small"}; FLMUNICODE puzMedium[] = {'M','e','d','i','u','m','M','e','d','i','u','m','\0'}; FLMUNICODE puzLarge[] = {'L','a','r','g','e','L','a','r','g','e','L','a','r','g','e', 'L','a','r','g','e','\0'}; FLMUNICODE puzSmall[] = {'S','m','a','l','l','\0'}; FLMUNICODE * ppuzUnicodeValues[3]; FLMBYTE * ppucBinaryValues[3]; FLMBYTE pucMedium[] = {0x01,0x02,0x03}; FLMBYTE pucLarge[] = {0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C}; FLMBYTE pucSmall[] = {0x0D,0x0E,0x0F,0x10,0x11}; FLMUINT uiBinaryLen; FLMUNICODE * puzResult = NULL; FLMBYTE pucResult[100]; char * pszResult = NULL; FLMBYTE pucUTF8Result[100]; FLMUINT uiBufSize; ppuzUnicodeValues[ 0] = puzMedium; ppuzUnicodeValues[ 1] = puzLarge; ppuzUnicodeValues[ 2] = puzSmall; ppucBinaryValues[ 0] = pucMedium; ppucBinaryValues[ 1] = pucLarge; ppucBinaryValues[ 2] = pucSmall; beginTest( "Encryption test setup", "Creates all the necessary database objects to run the encryption tests", "Create the database/create necessary element and attribute definitions/" "create encryption definitions", "none"); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; // Set up element and attribute definitions of all data types if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = setupElemAndAttrNodes())) { goto Exit; } if ( RC_BAD( rc = importEncDefs())) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); puiEncDefs[0] = m_uiAESDef; puiEncDefs[1] = m_uiDES3Def; puiEncDefs[2] = 0; // No encryption; ppszEncDefs[0] = XFLM_ENC_AES_OPTION_STR; ppszEncDefs[1] = XFLM_ENC_DES3_OPTION_STR; ppszEncDefs[2] = "none"; for ( uiLoop = 0; uiLoop < 3; uiLoop++) { /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueUINT Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setAttributeValueUINT API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUINT( m_pDb, m_uiNumberAttr, UINT_VAL, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUINT( m_pDb, m_uiNumberAttr, &uiResult))) { MAKE_ERROR_STRING( "getAttributeValueUINT failed.", m_szDetails, rc); goto Exit; } if ( uiResult != UINT_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "UINT value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueUINT64 Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setAttributeValueUINT64 API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUINT64( m_pDb, m_uiNumberAttr, UINT64_VAL, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueUINT64 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUINT64( m_pDb, m_uiNumberAttr, &ui64Result))) { MAKE_ERROR_STRING( "getAttributeValueUINT64 failed.", m_szDetails, rc); goto Exit; } if ( ui64Result != UINT64_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "UINT64 value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueINT Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setAttributeValueINT API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueINT( m_pDb, m_uiNumberAttr, INT_VAL, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueINT( m_pDb, m_uiNumberAttr, &iResult))) { MAKE_ERROR_STRING( "getAttributeValueINT failed.", m_szDetails, rc); goto Exit; } if ( iResult != INT_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "INT value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueINT64 Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setAttributeValueINT64 API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueINT64( m_pDb, m_uiNumberAttr, INT64_VAL, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueINT64 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueINT64( m_pDb, m_uiNumberAttr, &i64Result))) { MAKE_ERROR_STRING( "getAttributeValueINT64 failed.", m_szDetails, rc); goto Exit; } if ( i64Result != INT64_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "INT64 value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setUINT Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setUINT API using encryption definitions", "Self-explanatory", ""); // "Direct" setter tests if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pNumberElmNode->setUINT( m_pDb, UINT_VAL))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pNumberElmNode->getUINT( m_pDb, &uiResult))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( uiResult != UINT_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "UINT value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setUINT64 Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setUINT64 API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pNumberElmNode->setUINT64( m_pDb, UINT64_VAL))) { MAKE_ERROR_STRING( "setUINT64 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pNumberElmNode->getUINT64( m_pDb, &ui64Result))) { MAKE_ERROR_STRING( "getUINT64 failed.", m_szDetails, rc); goto Exit; } if ( ui64Result != UINT64_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "UINT64 value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setINT Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setINT API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pNumberElmNode->setINT( m_pDb, INT_VAL))) { MAKE_ERROR_STRING( "setINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pNumberElmNode->getINT( m_pDb, &iResult))) { MAKE_ERROR_STRING( "getINT failed.", m_szDetails, rc); goto Exit; } if ( iResult != INT_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "INT value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setINT64 Encrypted Value Test (encryption=%s)", ppszEncDefs[uiLoop]); beginTest( szTemp, "Tests setINT64 API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pNumberElmNode->setINT64( m_pDb, INT64_VAL))) { MAKE_ERROR_STRING( "setINT64 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pNumberElmNode->getINT64( m_pDb, &i64Result))) { MAKE_ERROR_STRING( "getINT64 failed.", m_szDetails, rc); goto Exit; } if ( i64Result != INT64_VAL) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "INT64 value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /******************************text value tests*************************/ for ( uiLoop2 = 0; uiLoop2 < sizeof(ppszNativeValues)/sizeof(ppszNativeValues[0]); uiLoop2++) { uiBinaryLen = (uiLoop2 == 0) ? sizeof(pucMedium) : (uiLoop2 == 1) ? sizeof(pucLarge) : sizeof(pucSmall); /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueUnicode Encrypted Value Test (encryption=%s/value size=%s)", ppszEncDefs[uiLoop], ppszTextSizes[uiLoop2]); beginTest( szTemp, "Tests setAttributeValueUnicode API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUnicode( m_pDb, m_uiTextAttr, ppuzUnicodeValues[uiLoop2], puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueUnicode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( puzResult) { f_free( &puzResult); } if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUnicode( m_pDb, m_uiTextAttr, &puzResult))) { MAKE_ERROR_STRING( "getAttributeValueUnicode failed.", m_szDetails, rc); goto Exit; } if ( f_unicmp( puzResult, ppuzUnicodeValues[uiLoop2]) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "unicode value corruption detection.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueBinary Encrypted Value Test (encryption=%s/value size=%s)", ppszEncDefs[uiLoop], ppszTextSizes[uiLoop2]); beginTest( szTemp, "Tests setAttributeValueBinary API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueBinary( m_pDb, m_uiBinaryAttr, ppucBinaryValues[uiLoop2], uiBinaryLen, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueBinary failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueBinary( m_pDb,m_uiBinaryAttr, pucResult, uiBinaryLen, &uiBufSize))) { MAKE_ERROR_STRING( "getAttributeValueBinary failed.", m_szDetails, rc); goto Exit; } flmAssert( uiBufSize == uiBinaryLen); if ( f_memcmp( pucResult, ppucBinaryValues[uiLoop2], uiBinaryLen) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "Binary value corruption detected.", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setAttributeValueUTF8 Encrypted Value Test (encryption=%s/value size=%s)", ppszEncDefs[uiLoop], ppszTextSizes[uiLoop2]); beginTest( szTemp, "Tests setAttributeValueUTF8 API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUTF8( m_pDb, m_uiTextAttr, (FLMBYTE *)ppszNativeValues[uiLoop2], strlen(ppszNativeValues[uiLoop2]), puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setAttributeValueUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; uiBufSize = sizeof(pucUTF8Result); if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUTF8( m_pDb, m_uiTextAttr, pucUTF8Result, uiBufSize, &uiBufSize))) { MAKE_ERROR_STRING( "getAttributeValueUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( (char *)pucUTF8Result, ppszNativeValues[uiLoop2]) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "UTF8 value corruption detected", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setUnicode Encrypted Value Test (encryption=%s/value size=%s)", ppszEncDefs[uiLoop], ppszTextSizes[uiLoop2]); beginTest( szTemp, "Tests setUnicode API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pTextElmNode->setUnicode( m_pDb, ppuzUnicodeValues[uiLoop2], 0, TRUE, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setUnicode failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; if ( puzResult) { f_free( &puzResult); } if ( RC_BAD( rc = m_pTextElmNode->getUnicode( m_pDb, &puzResult))) { MAKE_ERROR_STRING( "getUnicode failed.", m_szDetails, rc); goto Exit; } if ( f_unicmp( puzResult, ppuzUnicodeValues[uiLoop2]) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "Unicode value corruption detected", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setUTF8 Encrypted Value Test (encryption=%s/value size=%s)", ppszEncDefs[uiLoop], ppszTextSizes[uiLoop2]); beginTest( szTemp, "Tests setUTF8 API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pTextElmNode->setUTF8( m_pDb, (FLMBYTE *)ppszNativeValues[uiLoop2], 0, TRUE, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; uiBufSize = sizeof(pucUTF8Result); if ( RC_BAD( rc = m_pTextElmNode->getUTF8( m_pDb, pucUTF8Result, uiBufSize, 0, uiBufSize))) { MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( (char *)pucUTF8Result, ppszNativeValues[uiLoop2]) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "UTF8 value corruption detected", m_szDetails, rc); goto Exit; } endTest("PASS"); /***********************************************************************/ f_sprintf( szTemp, "setBinary Encrypted Value Test (encryption=%s/value size=%s)", ppszEncDefs[uiLoop], ppszTextSizes[uiLoop2]); beginTest( szTemp, "Tests setBinary API using encryption definitions", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = m_pBinElmNode->setBinary( m_pDb, ppucBinaryValues[uiLoop2], uiBinaryLen, TRUE, puiEncDefs[uiLoop]))) { MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; uiBufSize = sizeof(pucResult); if ( RC_BAD( rc = m_pBinElmNode->getBinary( m_pDb, pucResult, 0, uiBufSize, &uiBufSize))) { if (rc != NE_XFLM_EOF_HIT || uiBufSize != uiBinaryLen) { MAKE_ERROR_STRING( "getBinary failed.", m_szDetails, rc); goto Exit; } } if ( memcmp( pucResult, ppucBinaryValues[uiLoop2], uiBinaryLen) != 0) { rc = NE_XFLM_DATA_ERROR; MAKE_ERROR_STRING( "binary value corruption detected", m_szDetails, rc); goto Exit; } endTest("PASS"); } } // DB Key Rollover tests // // Create a new database key // Close the database // Verify we can open the database and read encrypted data. beginTest( "Database Key Rollover Test", "Replace the DB key with a new one", "Self-explanatory", ""); if (RC_BAD( rc = m_pDb->rollOverDbKey())) { MAKE_ERROR_STRING( "rollOverDbKey function failed", m_szDetails, rc); goto Exit; } m_pDb->Release(); m_pDb = NULL; if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) { MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, &m_pDb))) { MAKE_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } // VISIT: Add code to read an encrypted value from the database endTest("PASS"); // WRAP KEY TESTS // Wrap the database key in a password // Close the database // Try reopening the database with the incorrect password // Open the database with the correct password beginTest( "Wrap Key Test", "Wraps the database key in a password", "Self-explanatory", ""); if ( RC_BAD( rc = m_pDb->wrapKey( PASSWORD))) { MAKE_ERROR_STRING( "binary value corruption detected", m_szDetails, rc); goto Exit; } endTest("PASS"); beginTest( "Open Database - Incorrect Password Test", "Ensure the dbOpen fails if the password is incorrect", "Close the database/call dbOpen with the incorrect password", ""); if ( m_pDb->Release()) { display("Warning, database did not close!"); } m_pDb = NULL; rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, &m_pDb, BAD_PASSWORD); if ( rc != NE_XFLM_PBE_DECRYPT_FAILED) { MAKE_ERROR_STRING( "Unexpected rc from dbOpen with incorrect password", m_szDetails, rc); rc = NE_XFLM_FAILURE; goto Exit; } endTest("PASS"); beginTest( "Open Database - Correct Password Test", "Ensure the dbOpen succeeds if the password is correct", "call dbOpen with the correct password", ""); if (m_pDb) { m_pDb->Release(); m_pDb = NULL; } if ( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, &m_pDb, PASSWORD))) { MAKE_ERROR_STRING( "dbOpen Failed", m_szDetails, rc); goto Exit; } endTest("PASS"); beginTest( "backup (w/ password)", "backup the database using a password", "Self-explanatory", "No Additional Details."); // Backup the database if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, XFLM_READ_TRANS, 0, &m_pBackup))) { MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); goto Exit; } rc = m_pBackup->backup( BACKUP_NAME_STR, BACKUP_PASSWORD, NULL, NULL, NULL); if( RC_BAD( rc)) { MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); goto Exit; } m_pBackup->Release(); m_pBackup = NULL; endTest("PASS"); beginTest( "dbRestore (w/ password)", "restore the database from the backup we just made", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) { MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); goto Exit; } m_pDb->Release(); m_pDb = NULL; // Remove the database if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) { MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, NULL, NULL, BACKUP_NAME_STR, BACKUP_PASSWORD, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if (puzResult) { f_free( &puzResult); } if (pszResult) { f_free( &pszResult); } if ( RC_BAD( rc)) { endTest("FAIL"); } if ( bTransBegun) { if ( RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); #endif return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE IEncTestImpl::suite2( void) { RCODE rc = NE_XFLM_OK; #ifdef FLM_USE_NICI FLMBOOL bDibCreated = FALSE; FLMBOOL bTransBegun = FALSE; FLMUINT * puiEncDefs[] = {&m_uiAESDef,&m_uiDES3Def}; FLMUINT64 uiLoop; FLMUINT64 ui64TextNodeId; FLMUINT64 ui64BinNodeId; FLMUINT64 ui64NumNodeId; char * pszRetVal = NULL; FLMBYTE pucRetVal[100]; FLMUINT64 ui64RetVal; FLMUINT uiBinValRetLen; FLMUINT uiRefCount; beginTest( "Encryption Force-To-Disk Test Setup", "Creates all the necessary database objects to run the encryption tests", "Create the database/create necessary element and attribute definitions/" "create encryption definitions", "none"); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; // ENCRYPTED VALUE TESTS // Set up element and attribute definitions of all data types if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = setupElemAndAttrNodes())) { goto Exit; } if ( RC_BAD( rc = importEncDefs())) { goto Exit; } if( RC_BAD( rc = m_pNumberElmNode->getNodeId( m_pDb, &ui64NumNodeId))) { goto Exit; } if( RC_BAD( rc = m_pBinElmNode->getNodeId( m_pDb, &ui64BinNodeId))) { goto Exit; } if( RC_BAD( rc = m_pTextElmNode->getNodeId( m_pDb, &ui64TextNodeId))) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); if( m_pAttributedNode) { m_pAttributedNode->Release(); m_pAttributedNode = NULL; } if( m_pNumberAttrNode) { m_pNumberAttrNode->Release(); m_pNumberAttrNode = NULL; } if( m_pTextAttrNode) { m_pTextAttrNode->Release(); m_pTextAttrNode = NULL; } if( m_pBinAttrNode) { m_pBinAttrNode->Release(); m_pBinAttrNode = NULL; } beginTest( "Encryption Force-To-Disk test", "Force element values to disk to ensure encryption/decryption is working", "Create the database/set node values/close database/open database/retrieve and " "verify database values", "none"); for ( uiLoop = 0; uiLoop < 2; uiLoop++) { if ( RC_BAD( rc = m_pNumberElmNode->setUINT64( m_pDb, UINT64_VAL, *puiEncDefs[uiLoop]))) { MAKE_FLM_ERROR_STRING( "setUINT64 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pBinElmNode->setBinary( m_pDb, BIN_VAL, BIN_VAL_LEN, TRUE, *puiEncDefs[uiLoop]))) { MAKE_FLM_ERROR_STRING( "setBinary failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pTextElmNode->setUTF8( m_pDb, (FLMBYTE *)NATIVE_VAL, 0, TRUE, *puiEncDefs[uiLoop]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if (m_pRootNode) { m_pRootNode->Release(); m_pRootNode = NULL; } if (m_pNumberElmNode) { m_pNumberElmNode->Release(); m_pNumberElmNode = NULL; } if( m_pTextElmNode) { m_pTextElmNode->Release(); m_pTextElmNode = NULL; } if( m_pBinElmNode) { m_pBinElmNode->Release(); m_pBinElmNode = NULL; } // close the database -- force values to disk uiRefCount = m_pDb->Release(); flmAssert( !uiRefCount); if ( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { MAKE_FLM_ERROR_STRING( "dbOpen failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NumNodeId, &m_pNumberElmNode))) { MAKE_FLM_ERROR_STRING( "getNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64BinNodeId, &m_pBinElmNode))) { MAKE_FLM_ERROR_STRING( "getNode failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64TextNodeId, &m_pTextElmNode))) { MAKE_FLM_ERROR_STRING( "getNode failed.", m_szDetails, rc); goto Exit; } // Retrieve and check values if( RC_BAD( rc = m_pNumberElmNode->getUINT64( m_pDb, &ui64RetVal))) { MAKE_FLM_ERROR_STRING( "getUINT64 failed.", m_szDetails, rc); goto Exit; } if( ui64RetVal != UINT64_VAL) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pBinElmNode->getBinary( m_pDb, pucRetVal, 0, BIN_VAL_LEN, &uiBinValRetLen))) { MAKE_FLM_ERROR_STRING( "getBinary failed.", m_szDetails, rc); goto Exit; } if( uiBinValRetLen != BIN_VAL_LEN || f_memcmp( pucRetVal, BIN_VAL, BIN_VAL_LEN) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if( pszRetVal) { f_free( &pszRetVal); } if( RC_BAD( rc = m_pTextElmNode->getUTF8( m_pDb, (FLMBYTE **)&pszRetVal))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if( f_strcmp( pszRetVal, NATIVE_VAL) != 0) { rc = RC_SET( NE_XFLM_DATA_ERROR); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } } endTest("PASS"); Exit: if( bTransBegun) { if( RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } if( pszRetVal) { f_free( &pszRetVal); } shutdownTestState( DB_NAME_STR, bDibCreated); #endif return( rc); } libxflaim-5.1.969/util/sortkeytest.cpp0000644000175000017500000006202410511001742021322 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Sort key unit test // // Tabs: 3 // // Copyright (c) 2005-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: sortkeytest.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\SKEY.DB" #else #define DB_NAME_STR "skey.db" #endif #define FIRST_NAME_ID 123 #define LAST_NAME_ID 456 #define IX_NUM 789 /**************************************************************************** Desc: ****************************************************************************/ class SortKeyTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); private: RCODE verifyQuery( const char * pszQuery, char * ppszNames[][2], IF_Query * pQuery, FLMUINT uiNumNames, FLMBOOL bFirstAscending, FLMBOOL bLastAscending, FLMBOOL bDocsIndexed, FLMBOOL bIxFirstAscending, FLMBOOL bIxLastAscending); RCODE createNameDoc( char * pszNames[ 2]); RCODE createOrModifyIndex( FLMUINT uiIndex, FLMBOOL bComp1SortDescending, FLMBOOL bComp1CaseSensitive, FLMBOOL bComp1SortMissingHigh, FLMBOOL bComp2SortDescending, FLMBOOL bComp2SortMissingHigh); RCODE createSortKey( IF_Query * pQuery, FLMBOOL bFirstAscending, FLMBOOL bLastAscending); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new SortKeyTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * SortKeyTestImpl::getName( void) { return( "SortKey Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE SortKeyTestImpl::verifyQuery( const char * pszQuery, char* ppszNames[][ 2], IF_Query * pQuery, FLMUINT uiNumNames, FLMBOOL bFirstAscending, FLMBOOL bLastAscending, FLMBOOL bDocsIndexed, FLMBOOL bIxFirstAscending, FLMBOOL bIxLastAscending) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pResultNode = NULL; IF_DOMNode * pFirstNameNode = NULL; IF_DOMNode * pLastNameNode = NULL; FLMUINT uiLoop; FLMUINT uiPos; char szBuf[ 100]; IF_DataVector * pSearchKey = NULL; char szTestName[ 200]; static FLMUINT uiCallCount = 0; const char * pszTestNameFormat = "Verify Query #%u " "(%s/First Name Asc == %u/Last Name Asc ==%u/%s)"; uiCallCount++; f_sprintf( szTestName, pszTestNameFormat, uiCallCount, pszQuery, (unsigned)bFirstAscending, (unsigned)bLastAscending, "positioning"); beginTest( szTestName, "Ensure queries return proper results in proper order", "No Additional Info", ""); // positioning tests if ( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, pszQuery))) { MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", m_szDetails, rc); goto Exit; } if ( bDocsIndexed && (bFirstAscending == bIxFirstAscending && bLastAscending == bIxLastAscending)) { pQuery->setIndex( IX_NUM); } if ( RC_BAD( rc = pQuery->enablePositioning())) { MAKE_FLM_ERROR_STRING( "enablePositioning failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = createSortKey( pQuery, bFirstAscending, bLastAscending))) { goto Exit; } for( uiLoop = 0; uiLoop < uiNumNames; uiLoop++) { if ( RC_BAD( rc = pQuery->positionTo( m_pDb, &pResultNode, 0, uiLoop))) { MAKE_FLM_ERROR_STRING( "positionTo failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pResultNode->getFirstChild( m_pDb, &pFirstNameNode))) { MAKE_FLM_ERROR_STRING( "getFirstChild failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getNextSibling( m_pDb, &pLastNameNode))) { MAKE_FLM_ERROR_STRING( "getNextSibling failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][0]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pLastNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][1]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getPosition( m_pDb, &uiPos))) { MAKE_FLM_ERROR_STRING( "getPosition failed.", m_szDetails, rc); goto Exit; } if ( uiPos != uiLoop) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected position", m_szDetails, rc); goto Exit; } } endTest("PASS"); f_sprintf( szTestName, pszTestNameFormat, uiCallCount, pszQuery, (unsigned)bFirstAscending, (unsigned)bLastAscending, "search key positioning"); beginTest( szTestName, "Ensure queries return proper results in proper order", "No Additional Info", ""); if( RC_BAD( rc = m_pDbSystem->createIFDataVector( &pSearchKey))) { goto Exit; } // positioning tests 2 for( uiLoop = 0; uiLoop < uiNumNames; uiLoop++) { if ( uiLoop == 0) { if ( RC_BAD( rc = pQuery->positionTo( m_pDb, &pResultNode, 0, pSearchKey, XFLM_FIRST))) { MAKE_FLM_ERROR_STRING( "positionTo failed.", m_szDetails, rc); goto Exit; } } else { if ( RC_BAD( rc = pSearchKey->setUTF8( 0, (FLMBYTE *)ppszNames[uiLoop][0]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pSearchKey->setUTF8( 1, (FLMBYTE *)ppszNames[uiLoop][1]))) { MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->positionTo( m_pDb, &pResultNode, 0, pSearchKey, XFLM_EXACT))) { MAKE_FLM_ERROR_STRING( "positionTo failed.", m_szDetails, rc); goto Exit; } } if ( RC_BAD( rc = pResultNode->getFirstChild( m_pDb, &pFirstNameNode))) { MAKE_FLM_ERROR_STRING( "getFirstChild failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getNextSibling( m_pDb, &pLastNameNode))) { MAKE_FLM_ERROR_STRING( "getNextSibling failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pFirstNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][0]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pLastNameNode->getUTF8( m_pDb, (FLMBYTE *)szBuf, sizeof( szBuf), 0, sizeof( szBuf) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if ( f_strcmp( szBuf, ppszNames[uiLoop][1]) != 0) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pQuery->getPosition( m_pDb, &uiPos))) { MAKE_FLM_ERROR_STRING( "getPosition failed.", m_szDetails, rc); goto Exit; } if ( uiPos != uiLoop) { rc = RC_SET( NE_XFLM_FAILURE); MAKE_FLM_ERROR_STRING( "Unexpected position", m_szDetails, rc); goto Exit; } } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } if( pResultNode) { pResultNode->Release(); } if( pFirstNameNode) { pFirstNameNode->Release(); } if( pLastNameNode) { pLastNameNode->Release(); } if( pSearchKey) { pSearchKey->Release(); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE SortKeyTestImpl::createOrModifyIndex( FLMUINT uiIndex, FLMBOOL bComp1SortDescending, FLMBOOL bComp1CaseSensitive, FLMBOOL bComp1SortMissingHigh, FLMBOOL bComp2SortDescending, FLMBOOL bComp2SortMissingHigh) { RCODE rc = NE_XFLM_OK; char szCompareRules1[100]; char szCompareRules2[100]; char szName[100]; char * pszIndex = NULL; IF_DOMNode * pNode = NULL; const char * pszIndexFormat = "" "" "" ""; szCompareRules1[0] = '\0'; szCompareRules2[0] = '\0'; if( bComp1SortDescending) { f_strcpy( szCompareRules1, XFLM_DESCENDING_OPTION_STR); } if ( !bComp1CaseSensitive) { f_strcat( szCompareRules1, " "XFLM_CASE_INSENSITIVE_OPTION_STR); } if ( bComp1SortMissingHigh) { f_strcat( szCompareRules1, " "XFLM_MISSING_HIGH_OPTION_STR); } if ( bComp2SortDescending) { f_strcpy( szCompareRules2, XFLM_DESCENDING_OPTION_STR); } if ( bComp2SortMissingHigh) { f_strcat( szCompareRules2, " "XFLM_MISSING_HIGH_OPTION_STR); } f_sprintf( szName, "(%s/%s)", szCompareRules1, szCompareRules2); if ( RC_BAD( rc = f_alloc( f_strlen( pszIndexFormat) + (f_strlen( szCompareRules1) + f_strlen(szCompareRules2)) * 2 + 16, &pszIndex))) { MAKE_FLM_ERROR_STRING( "f_alloc failed", m_szDetails, rc); goto Exit; } f_sprintf( pszIndex, pszIndexFormat, szName, uiIndex, szCompareRules1, FIRST_NAME_ID, szCompareRules2, LAST_NAME_ID); // remove the index if it already exists if ( RC_OK( rc = m_pDb->getDictionaryDef( ELM_INDEX_TAG, uiIndex, &pNode))) { if ( RC_BAD( rc = pNode->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); goto Exit; } } if ( RC_BAD( rc = importBuffer( pszIndex, XFLM_DICT_COLLECTION))) { goto Exit; } Exit: if ( pNode) { pNode->Release(); } if ( pszIndex) { f_free( &pszIndex); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE SortKeyTestImpl::createNameDoc( char * pszNames[ 2]) { RCODE rc = NE_XFLM_OK; ELEMENT_NODE_INFO pNameNodes[2]; f_memset( pNameNodes, 0, sizeof( pNameNodes)); if ( RC_BAD( rc = f_alloc( f_strlen( pszNames[0]) + 1, &pNameNodes[0].pvData))) { MAKE_FLM_ERROR_STRING( "f_alloc failed.", m_szDetails, rc); goto Exit; } f_strcpy( (char*)pNameNodes[0].pvData, pszNames[0]); pNameNodes[0].uiDataType = XFLM_TEXT_TYPE; pNameNodes[0].uiDataSize = f_strlen( pszNames[0]); pNameNodes[0].uiDictNum = FIRST_NAME_ID; if ( RC_BAD( rc = f_alloc( f_strlen( pszNames[1]) + 1, &pNameNodes[1].pvData))) { MAKE_FLM_ERROR_STRING( "f_alloc failed.", m_szDetails, rc); goto Exit; } f_strcpy( (char*)pNameNodes[1].pvData, pszNames[1]); pNameNodes[1].uiDataType = XFLM_TEXT_TYPE; pNameNodes[1].uiDataSize = f_strlen( pszNames[0]); pNameNodes[1].uiDictNum = LAST_NAME_ID; if ( RC_BAD( rc = createCompoundDoc( pNameNodes, 2, NULL))) { goto Exit; } Exit: if ( pNameNodes[0].pvData) { f_free( &pNameNodes[0].pvData); } if ( pNameNodes[1].pvData) { f_free( &pNameNodes[1].pvData); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE SortKeyTestImpl::createSortKey( IF_Query * pQuery, FLMBOOL bFirstAscending, FLMBOOL bLastAscending) { RCODE rc = NE_XFLM_OK; void * pvKeyContext = NULL; // First Name Sort Key Component if ( RC_BAD( rc = pQuery->addSortKey( NULL, TRUE, TRUE, FIRST_NAME_ID, 0, // VISIT - allow compare rules to be specified 0, // VISIT - allow limit to be specified 1, !bFirstAscending, FALSE, // VISIT - allow sort missing high to be specified &pvKeyContext))) { MAKE_FLM_ERROR_STRING( "addSortKey failed.", m_szDetails, rc); goto Exit; } // LAST Name Sort Key Component if ( RC_BAD( rc = pQuery->addSortKey( pvKeyContext, FALSE, //sibling TRUE, LAST_NAME_ID, 0, // VISIT - allow compare rules to be specified 0, // VISIT - allow limit to be specified 2, !bLastAscending, FALSE, // VISIT - allow sort missing high to be specified &pvKeyContext))) { MAKE_FLM_ERROR_STRING( "addSortKey failed.", m_szDetails, rc); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE SortKeyTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMBOOL bTransActive = FALSE; FLMUINT uiFirstNameId = FIRST_NAME_ID; FLMUINT uiLastNameId = LAST_NAME_ID; IF_Query * pQuery = NULL; FLMUINT uiLoop; FLMBOOL bIxFirstAsc = FALSE; FLMBOOL bIxLastAsc = FALSE; FLMBOOL bIndexCreated = FALSE; char* ppszNames[][2] = { {"Rebecca","Betz"}, {"Russell","Bakker"}, {"April","Sansone"}, {"Julie","Betz"}, {"Jason","Betz"}, {"Ronald","Betz"}, {"Shawn","Hafner"}, {"Karen","Bradley"}, {"Mabel","Zepeda"}, {"Kathleen","Ellinger"}, {"Victor","Tankersley"}, {"Leonard","Kuehn"}, {"Danny","Decastro"}, {"Harold","Bergeron"}, {"Annette","Sartin"}, {"Anthony","Glasser"}, {"Albert","Glasser"}, {"Yvette","Patch"}, {"Joyce","Lundberg"}, {"John","Reinhold"}, {"Kristen","Hansel"}, {"Victor","Schell"}, {"Patrick","Belt"}, {"Gina","Belt"}, {"Brenda","Ellis"}, {"Maryann","Sumpter"}, {"Samantha","Beckford"}, {"Carl","Collette"}, {"Carl","Davis"}, {"Kristina","Tso"}, {"Jeanne","Leonard"}, {"Patty","Ogletree"}, {"Alan","Villa"}, {"Carl","Wacker"}, {"Lawrence","Kent"}, }; beginTest( "Sorting/Positioning Query Test Setup", "Prepare database to test sorted and positionable queries", "", ""); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { goto Exit; } bDibCreated = TRUE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransActive = TRUE; if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "first_name", XFLM_TEXT_TYPE, &uiFirstNameId, NULL))) { MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->createElementDef( NULL, "last_name", XFLM_TEXT_TYPE, &uiLastNameId, NULL))) { MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); goto Exit; } // create all of our documents for ( uiLoop = 0; uiLoop < ELEMCOUNT(ppszNames); uiLoop++) { if ( RC_BAD( rc = createNameDoc( ppszNames[uiLoop]))) { goto Exit; } } if ( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "createIFQuery failed.", m_szDetails, rc); goto Exit; } endTest("PASS"); for( uiLoop = 0; uiLoop < 5; uiLoop++) { // We have to commit all our changes here because when result sets are // built in the background, the F_Db the background thread receives to // work with is created via a call to dbDup() and will not have knowledge // of any uncommitted changes made to the database. If this issue is ever // fixed, remove these calls to transCommit and transBegin. if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "createIFQuery failed.", m_szDetails, rc); goto Exit; } bTransActive = FALSE; if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "createIFQuery failed.", m_szDetails, rc); goto Exit; } bTransActive = TRUE; // no index the first time throug... { const char * pszQuery = "//first_name[.==\"Carl\"]"; char * pszResults[][3][2] = { {{"Carl","Wacker"},{"Carl","Davis"},{"Carl","Collette"}}, // desc,desc {{"Carl","Collette"},{"Carl","Davis"},{"Carl","Wacker"}}, // desc,asc {{"Carl","Wacker"},{"Carl","Davis"},{"Carl","Collette"}}, // asc,desc {{"Carl","Collette"},{"Carl","Davis"},{"Carl","Wacker"}}, // asc,asc }; if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[0], pQuery, ELEMCOUNT(pszResults[0]), FALSE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[1], pQuery, ELEMCOUNT(pszResults[1]), FALSE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[2], pQuery, ELEMCOUNT(pszResults[2]), TRUE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[3], pQuery, ELEMCOUNT(pszResults[3]), TRUE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } } { const char * pszQuery = "//last_name[.==\"Glasser\"]"; char * pszResults[][2][2] = { {{"Anthony","Glasser"},{"Albert","Glasser"}}, // desc,desc {{"Anthony","Glasser"},{"Albert","Glasser"}}, // desc,asc {{"Albert","Glasser"},{"Anthony","Glasser"}}, // asc,desc {{"Albert","Glasser"},{"Anthony","Glasser"}}, // asc,asc }; if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[0], pQuery, ELEMCOUNT(pszResults[0]), FALSE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[1], pQuery, ELEMCOUNT(pszResults[1]), FALSE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[2], pQuery, ELEMCOUNT(pszResults[2]), TRUE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[3], pQuery, ELEMCOUNT(pszResults[3]), TRUE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } } { const char * pszQuery = "//first_name[. >= \"C\" && . < \"J\"]"; char * pszResults[][6][2] = { { // Desc, Desc {"Harold","Bergeron"}, {"Gina","Belt"}, {"Danny","Decastro"}, {"Carl","Wacker"}, {"Carl","Davis"}, {"Carl","Collette"}, }, { // Desc, Asc {"Harold","Bergeron"}, {"Gina","Belt"}, {"Danny","Decastro"}, {"Carl","Collette"}, {"Carl","Davis"}, {"Carl","Wacker"}, }, { // Asc, Desc {"Carl","Wacker"}, {"Carl","Davis"}, {"Carl","Collette"}, {"Danny","Decastro"}, {"Gina","Belt"}, {"Harold","Bergeron"}, }, { // Asc, Asc {"Carl","Collette"}, {"Carl","Davis"}, {"Carl","Wacker"}, {"Danny","Decastro"}, {"Gina","Belt"}, {"Harold","Bergeron"}, }, }; if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[0], pQuery, ELEMCOUNT(pszResults[0]), FALSE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[1], pQuery, ELEMCOUNT(pszResults[1]), FALSE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[2], pQuery, ELEMCOUNT(pszResults[2]), TRUE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[3], pQuery, ELEMCOUNT(pszResults[3]), TRUE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } } { const char * pszQuery = "//last_name[. >= \"Betz\" && . <= \"Davis\"]"; char * pszResults[][7][2] = { {// Desc, Desc {"Ronald","Betz"}, {"Rebecca","Betz"}, {"Karen","Bradley"}, {"Julie","Betz"}, {"Jason","Betz"}, {"Carl","Davis"}, {"Carl","Collette"}, }, {// Desc, Asc {"Ronald","Betz"}, {"Rebecca","Betz"}, {"Karen","Bradley"}, {"Julie","Betz"}, {"Jason","Betz"}, {"Carl","Collette"}, {"Carl","Davis"}, }, {// Asc, Desc {"Carl","Davis"}, {"Carl","Collette"}, {"Jason","Betz"}, {"Julie","Betz"}, {"Karen","Bradley"}, {"Rebecca","Betz"}, {"Ronald","Betz"}, }, {// Asc, Asc {"Carl","Collette"}, {"Carl","Davis"}, {"Jason","Betz"}, {"Julie","Betz"}, {"Karen","Bradley"}, {"Rebecca","Betz"}, {"Ronald","Betz"}, }, }; if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[0], pQuery, ELEMCOUNT(pszResults[0]), FALSE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[1], pQuery, ELEMCOUNT(pszResults[1]), FALSE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[2], pQuery, ELEMCOUNT(pszResults[2]), TRUE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[3], pQuery, ELEMCOUNT(pszResults[3]), TRUE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } } { const char * pszQuery = "//first_name[.==\"A*\" or .==\"J*\"]"; char * pszResults[][10][2] = { { //Desc, Desc {"Julie","Betz"}, {"Joyce","Lundberg"}, {"John","Reinhold"}, {"Jeanne","Leonard"}, {"Jason","Betz"}, {"April","Sansone"}, {"Anthony","Glasser"}, {"Annette","Sartin"}, {"Albert","Glasser"}, {"Alan","Villa"}, }, { //Desc, Asc {"Julie","Betz"}, {"Joyce","Lundberg"}, {"John","Reinhold"}, {"Jeanne","Leonard"}, {"Jason","Betz"}, {"April","Sansone"}, {"Anthony","Glasser"}, {"Annette","Sartin"}, {"Albert","Glasser"}, {"Alan","Villa"}, }, { //Asc, Desc {"Alan","Villa"}, {"Albert","Glasser"}, {"Annette","Sartin"}, {"Anthony","Glasser"}, {"April","Sansone"}, {"Jason","Betz"}, {"Jeanne","Leonard"}, {"John","Reinhold"}, {"Joyce","Lundberg"}, {"Julie","Betz"}, }, { //Asc, Asc {"Alan","Villa"}, {"Albert","Glasser"}, {"Annette","Sartin"}, {"Anthony","Glasser"}, {"April","Sansone"}, {"Jason","Betz"}, {"Jeanne","Leonard"}, {"John","Reinhold"}, {"Joyce","Lundberg"}, {"Julie","Betz"}, }, }; if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[0], pQuery, ELEMCOUNT(pszResults[0]), FALSE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[1], pQuery, ELEMCOUNT(pszResults[1]), FALSE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[2], pQuery, ELEMCOUNT(pszResults[2]), TRUE, FALSE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } if ( RC_BAD( rc = verifyQuery( pszQuery, pszResults[3], pQuery, ELEMCOUNT(pszResults[3]), TRUE, TRUE, bIndexCreated, bIxFirstAsc, bIxLastAsc))) { goto Exit; } } switch( uiLoop) { case 0: bIxFirstAsc = FALSE; bIxLastAsc = FALSE; break; case 1: bIxFirstAsc = FALSE; bIxLastAsc = TRUE; break; case 2: bIxFirstAsc = TRUE; bIxLastAsc = FALSE; break; case 3: bIxFirstAsc = TRUE; bIxLastAsc = TRUE; break; default: break; } if ( RC_BAD( rc = createOrModifyIndex( IX_NUM, !bIxFirstAsc, TRUE, FALSE, !bIxLastAsc, FALSE))) { goto Exit; } bIndexCreated = TRUE; } Exit: if ( pQuery) { pQuery->Release(); } if( bTransActive) { if ( RC_BAD( rc)) { m_pDb->transAbort(); } else { rc = m_pDb->transCommit(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/xpathtest1srv.cpp0000644000175000017500000004154510511001742021567 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: XPATH unit test 1 // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: xpathtest1srv.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined(NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif #define CD_NUM 1 #define TITLE_NUM 2 #define ARTIST_NUM 3 #define LOCATION_NUM 4 #define COMPANY_NUM 5 #define PRICE_NUM 6 #define YEAR_NUM 7 #define CATALOG_NUM 8 #define COUNTRY_NUM 9 #define CITY_NUM 10 /**************************************************************************** Desc: ****************************************************************************/ class IXPathTest1Impl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IXPathTest1Impl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IXPathTest1Impl::getName( void) { return( "XPath Test 1"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IXPathTest1Impl::execute( void) { RCODE rc = NE_XFLM_OK; FlagSet flagSet; FLMBOOL bTransStarted = FALSE; FLMBOOL bDibCreated = FALSE; IF_PosIStream * pPosIStream = NULL; IF_Query * pQuery = NULL; FLMUINT uiDictNum = 0; IF_DOMNode * pReturn = NULL; char szBuffer[ 128]; const char * ppszQueryFourResults[] = {"Hide your heart", "Still got the blues", "One night only", "Sylvias Mother", "Maggie May"}; const char * ppszQueryFiveResults[] = {"Empire Burlesque", "Greatest Hits", "Unchain my heart"}; const char * ppszQuerySixResults[] = {"Gary Moore", "Rod Stewart"}; const char * ppszQuerySevenResults[] = {"Bob Dylan", "Bonnie Tyler", "Dolly Parton", "Eros Ramazzotti", "Bee Gees", "Dr.Hook", "Andrea Bocelli", "Joe Cocker"}; const char * ppszQueryEightResults[] = {"CBS Records", "RCA", "Virgin records", "BMG", "CBS", "Pickwick", "EMI"}; const char * ppszQueryNineResults[] = {"Still got the blues", "Eros", "One night only", "Maggie May", "Romanza"}; const char * pszDoc1 = "" "" "Empire Burlesque " "Bob Dylan " " " "Columbia " "1090 " "1985 " "" "" "Hide your heart " "Bonnie Tyler" " " "CBS Records " "990 " "1988 " "" "" "Greatest Hits " "Dolly Parton" "" "RCA " "990 " "1982 " "" "" "Still got the blues " "Gary Moore " " " "Virgin records " "1020 " "1990 " "" "" "Eros " "Eros Ramazzotti" " " "BMG" "990 " "1997" "" "" "One night only" "Bee Gees" " " "Polydor" "1090" "1998" "" "" "Sylvias Mother" "Dr.Hook" " " "CBS" "810" "1973" "" "" "Maggie May" "Rod Stewart" " " "Pickwick" "850" "1990" "" "" "Romanza" "Andrea Bocelli" " " "Polydor" "1080" "1996" "" "" "Unchain my heart" "Joe Cocker" " " "EMI" "820" "1987" "" ""; beginTest( "Database Setup", "Prep the database for the query tests", "1) get the class factory for the dbSystem " "2) Create the database 3) Setup element and attribute definitions " "4) Import a document 5) get a IF_Query object", "No Additional Details."); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to init test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bTransStarted = TRUE; // Create element defs for easy lookups uiDictNum = CD_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "cd", XFLM_NODATA_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != CD_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = TITLE_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "title", XFLM_TEXT_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != TITLE_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = ARTIST_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "artist", XFLM_TEXT_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != ARTIST_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = LOCATION_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "location", XFLM_TEXT_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != LOCATION_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = COMPANY_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "company", XFLM_TEXT_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != COMPANY_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = PRICE_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "price", XFLM_NUMBER_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != PRICE_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = YEAR_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "year", XFLM_NUMBER_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != YEAR_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = CATALOG_NUM; if( RC_BAD( rc = m_pDb->createElementDef(NULL, "catalog", XFLM_NODATA_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create elementDef.", m_szDetails, rc); goto Exit; } if( uiDictNum != CATALOG_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = COUNTRY_NUM; if( RC_BAD( rc = m_pDb->createAttributeDef(NULL, "country", XFLM_TEXT_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create attribute Def.", m_szDetails, rc); goto Exit; } if( uiDictNum != COUNTRY_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } uiDictNum = CITY_NUM; if( RC_BAD( rc = m_pDb->createAttributeDef(NULL, "city", XFLM_TEXT_TYPE, &uiDictNum, NULL))) { MAKE_FLM_ERROR_STRING( "Failed to create attribute Def.", m_szDetails, rc); goto Exit; } if( uiDictNum != CITY_NUM) { MAKE_FLM_ERROR_STRING( "Dict num already in use.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDbSystem->createIFQuery( &pQuery))) { MAKE_FLM_ERROR_STRING( "Failed to create query object.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = importBuffer( pszDoc1, XFLM_DATA_COLLECTION))) { goto Exit; } endTest("PASS"); /********************* First Query ************************************/ beginTest( "Query Test #1", "/catalog/cd/title[. ~= \"Won Knight Only\"]", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "/catalog/cd/title[. ~= \"Won Knight Only\"]"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pReturn->getUTF8( m_pDb, (FLMBYTE *)szBuffer, sizeof( szBuffer), 0, sizeof( szBuffer) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if( f_strcmp( szBuffer, "One night only") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } if( (rc = pQuery->getNext( m_pDb, &pReturn)) != NE_XFLM_EOF_HIT) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Second Query ************************************/ beginTest( "Query Test #2", "/catalog/cd/price[. == 1080]", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "/catalog/cd/price[. == 1080]"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pReturn->getUTF8( m_pDb, (FLMBYTE *)szBuffer, sizeof( szBuffer), 0, sizeof( szBuffer) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if( f_strcmp( szBuffer, "1080") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } if( (rc = pQuery->getNext( m_pDb, &pReturn)) != NE_XFLM_EOF_HIT) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Third Query ************************************/ beginTest( "Query Test #3", "/catalog/cd[price == 1080]/company", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "/catalog/cd[price == 1080]/company"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) { MAKE_FLM_ERROR_STRING( "getFirst failed.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pReturn->getUTF8( m_pDb, (FLMBYTE *)szBuffer, sizeof( szBuffer), 0, sizeof( szBuffer) - 1))) { MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); goto Exit; } if( f_strcmp( szBuffer, "Polydor") != 0) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } if( (rc = pQuery->getNext( m_pDb, &pReturn)) != NE_XFLM_EOF_HIT) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "Unexpected query result.", m_szDetails, rc); goto Exit; } endTest("PASS"); /********************* Fourth Query ************************************/ beginTest( "Query Test #4", "/catalog/cd[location[@country == \"UK\"]]/title", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "/catalog/cd[location[@country == \"UK\"]]/title"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = checkQueryResults( ppszQueryFourResults, sizeof( ppszQueryFourResults) / sizeof( ppszQueryFourResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Fifth Query ************************************/ beginTest( "Query Test #5", "//cd[location[@country == \"USA\"]]/title", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "//cd[location[@country == \"USA\"]]/title"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = checkQueryResults( ppszQueryFiveResults, sizeof( ppszQueryFiveResults) / sizeof( ppszQueryFiveResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Sixth Query ************************************/ beginTest( "Query Test #6", "//cd[location[@city == \"London\"]]/artist", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "//cd[location[@city == \"London\"]]/artist"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = checkQueryResults( ppszQuerySixResults, sizeof( ppszQuerySixResults) / sizeof( ppszQuerySixResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Seventh Query ************************************/ beginTest( "Query Test #7", "//cd[location[@city != \"London\"]]/artist", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "//cd[location[@city != \"London\"]]/artist"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = checkQueryResults( ppszQuerySevenResults, sizeof( ppszQuerySevenResults) / sizeof( ppszQuerySevenResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Eighth Query ************************************/ beginTest( "Query Test #8", "//cd[price < 1080]/company", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "//cd[price < 1080]/company"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = checkQueryResults( ppszQueryEightResults, sizeof( ppszQueryEightResults) / sizeof( ppszQueryEightResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); /********************* Ninth Query ************************************/ beginTest( "Query Test #9", "////cd[year >= 1990]/title", "Run the query and validate the results.", ""); if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, "//cd[year >= 1990]/title"))) { MAKE_FLM_ERROR_STRING( "Failed to set up query expression.", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = checkQueryResults( ppszQueryNineResults, sizeof( ppszQueryNineResults) / sizeof( ppszQueryNineResults[0]), pQuery, m_szDetails))) { goto Exit; } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } if( bTransStarted) { m_pDb->transCommit(); } if( pPosIStream) { pPosIStream->Release(); } if( pQuery) { pQuery->Release(); } if( pReturn) { pReturn->Release(); } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } libxflaim-5.1.969/util/colldeftestsrv.cpp0000644000175000017500000004663610511001742022000 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Collection definition unit tests // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: colldeftestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #else #define DB_NAME_STR "tst.db" #endif static FLMBYTE gv_ucLargeBuffer[8192]; static void buildLargeBuffer( void); /**************************************************************************** Desc: ****************************************************************************/ class ICollDefTestImpl : public TestBase { public: const char * getName(); RCODE execute( void); private: RCODE importEncDefs( void); RCODE importToCollection( FLMUINT uiDictNum); RCODE verifyDocument( FLMUINT uiDictNum); RCODE createDODocument( FLMUINT uiCollNum, FLMUINT64 * pui64NodeId); RCODE verifyDODocument( FLMUINT uiCollNum, FLMUINT64 ui64NodeId); FLMUINT m_uiAESDef; FLMUINT m_uiDES3Def; }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new ICollDefTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ const char * ICollDefTestImpl::getName( void) { return( "Collection Definition Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ICollDefTestImpl::importEncDefs( void) { RCODE rc = NE_XFLM_OK; char * ppszEncDefs[] = {XFLM_ENC_AES_OPTION_STR, XFLM_ENC_DES3_OPTION_STR}; FLMUINT puiEncDef[ 2]; char szEncDef[ 200]; FLMUINT uiLoop = 0; for( uiLoop = 0; uiLoop < sizeof(ppszEncDefs)/sizeof(ppszEncDefs[0]); uiLoop++) { f_sprintf( szEncDef, "", ppszEncDefs[uiLoop], ppszEncDefs[uiLoop]); if ( RC_BAD( rc = importBuffer( szEncDef, XFLM_DICT_COLLECTION))) { MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); goto Exit; } f_sprintf( szEncDef, "%s definition", ppszEncDefs[uiLoop]); if (RC_BAD( rc = m_pDb->getEncDefId( (char *)szEncDef, (FLMUINT *)&puiEncDef[ uiLoop]))) { goto Exit; } } m_uiAESDef = puiEncDef[ 0]; m_uiDES3Def = puiEncDef[ 1]; Exit: return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE ICollDefTestImpl::importToCollection( FLMUINT uiDictNum) { RCODE rc = NE_XFLM_OK; char szBuffer[ 200]; FLMBOOL bTransBegun = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; f_sprintf( szBuffer, " " "
" "\"2999 Birches Lane\"" "\"Anytown\"" "\"MyState\"" "\"86507\"" "
" "
"); if ( RC_BAD( rc = importBuffer( szBuffer, uiDictNum))) { MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; Exit: if (bTransBegun) { m_pDb->transAbort(); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE ICollDefTestImpl::verifyDocument( FLMUINT uiDictNum) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; char szValue[ 20]; char szLocalName[ 20]; FLMUINT uiBufSize = sizeof(szValue); FLMUINT uiCharsReturned; // Get the first ( and only) document if (RC_BAD( rc = m_pDb->getFirstDocument( uiDictNum, &pNode))) { goto Exit; } // Verify the root node - s/b if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("sample")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szLocalName, "sample")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the child node - s/b
if (RC_BAD( rc = pNode->getFirstChild( m_pDb, &pNode))) { goto Exit; } if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("address")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szLocalName, "address")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the child node - s/b if (RC_BAD( rc = pNode->getFirstChild( m_pDb, &pNode))) { goto Exit; } if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("street")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szLocalName, "street")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the value. if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, 0, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("\"2999 Birches Lane\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szValue, "\"2999 Birches Lane\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the sibling node - s/b if (RC_BAD( rc = pNode->getNextSibling( m_pDb, &pNode))) { goto Exit; } if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, &uiCharsReturned))) { goto Exit; } if( uiCharsReturned != f_strlen("city")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if( f_strcmp( szLocalName, "city")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the value. if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, 0, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("\"AnyTown\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szValue, "\"Anytown\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the sibling node - s/b if (RC_BAD( rc = pNode->getNextSibling( m_pDb, &pNode))) { goto Exit; } if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("state")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szLocalName, "state")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the value. if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, 0, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("\"MyState\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szValue, "\"MyState\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the sibling node - s/b if (RC_BAD( rc = pNode->getNextSibling( m_pDb, &pNode))) { goto Exit; } if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("zip")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szLocalName, "zip")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } // Verify the value. if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, 0, uiBufSize, &uiCharsReturned))) { goto Exit; } if (uiCharsReturned != f_strlen("\"86507\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (f_strcmp( szValue, "\"86507\"")) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } Exit: if (pNode) { pNode->Release(); } return rc; } /**************************************************************************** Desc: ****************************************************************************/ RCODE ICollDefTestImpl::createDODocument( FLMUINT uiCollNum, FLMUINT64 * pui64NodeId) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; IF_DOMNode * pAttr = NULL; FLMUINT uiBuffSize = sizeof(gv_ucLargeBuffer); FLMBOOL bTransBegun = FALSE; if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; // Create a new document if (RC_BAD( rc = m_pDb->createRootElement( uiCollNum, ELM_ELEMENT_TAG, &pNode, pui64NodeId))) { goto Exit; } if (RC_BAD( rc = pNode->createAttribute( m_pDb, ATTR_ENCRYPTION_KEY_TAG, &pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->setBinary( m_pDb, gv_ucLargeBuffer, uiBuffSize, TRUE, 0))) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit())) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; Exit: if (pAttr) { pAttr->Release(); } if (pNode) { pNode->Release(); } if (bTransBegun) { m_pDb->transAbort(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ICollDefTestImpl::verifyDODocument( FLMUINT uiCollNum, FLMUINT64 ui64NodeId) { RCODE rc = NE_XFLM_OK; IF_DOMNode * pNode = NULL; IF_DOMNode * pAttr = NULL; FLMBYTE * pucBuff = NULL; FLMUINT uiLength; FLMUINT uiLengthRV; FLMUINT uiLoop; // Create a new document if (RC_BAD( rc = m_pDb->getDocument( uiCollNum, XFLM_EXACT, ui64NodeId, &pNode))) { goto Exit; } if (RC_BAD( rc = pNode->getAttribute( m_pDb, ATTR_ENCRYPTION_KEY_TAG, &pAttr))) { goto Exit; } if (RC_BAD( rc = pAttr->getDataLength(m_pDb, &uiLength))) { goto Exit; } if (uiLength != sizeof( gv_ucLargeBuffer)) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } if (RC_BAD( rc = f_alloc( sizeof( gv_ucLargeBuffer), &pucBuff))) { goto Exit; } if (RC_BAD( rc = pAttr->getBinary( m_pDb, pucBuff, 0, uiLength, &uiLengthRV))) { goto Exit; } if (uiLength != uiLengthRV) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } for (uiLoop = 0; uiLoop < sizeof( gv_ucLargeBuffer); uiLoop++) { if (gv_ucLargeBuffer[ uiLoop] != pucBuff[ uiLoop]) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } } Exit: if (pucBuff) { f_free( &pucBuff); } if (pAttr) { pAttr->Release(); } if (pNode) { pNode->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE ICollDefTestImpl::execute( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bDibCreated = FALSE; FLMUINT uiAESDictNum = 0; FLMUINT uiDES3DictNum = 0; FLMUINT64 ui64AESDoc; FLMUINT64 ui64DES3Doc; IF_DOMNode * pNode = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pCollDef = NULL; IF_DOMNode * pTmpNode = NULL; FLMBOOL bTransBegun = FALSE; beginTest( "Encrypted Collection Definition Test", "Define encrypted collections tests.", "1) create a database 2) create required encryption definitions " "3) create some encrypted collection definitions.", "none"); f_strcpy (m_szDetails, "No Additional Info."); if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, FLM_NO_TIMEOUT))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; if ( RC_BAD( rc = importEncDefs())) { goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; #endif // Start an update transaction if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; bDibCreated = TRUE; // Create a collection def /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_COLLECTION_TAG, &pCollDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pCollDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Encrypted Collection AES"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI if ( RC_BAD( rc = pCollDef->createAttribute( m_pDb, ATTR_ENCRYPTION_ID_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, m_uiAESDef))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } #endif if ( RC_BAD( rc = m_pDb->documentDone( pCollDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pCollDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } // Get the dictionary number (used by later tests) if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiAESDictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; // Create an encrypted collection definition for DES3 encryption if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); goto Exit; } bTransBegun = TRUE; /* */ if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DICT_COLLECTION, ELM_COLLECTION_TAG, &pCollDef))) { MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pCollDef->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"Encrypted Collection DES3"))) { MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); goto Exit; } #ifdef FLM_USE_NICI if ( RC_BAD( rc = pCollDef->createAttribute( m_pDb, ATTR_ENCRYPTION_ID_TAG, &pAttr))) { MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pAttr->setUINT( m_pDb, m_uiDES3Def))) { MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); goto Exit; } #endif if ( RC_BAD( rc = m_pDb->documentDone( pCollDef))) { MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = pCollDef->getAttribute( m_pDb, ATTR_DICT_NUMBER_TAG, &pAttr))) { MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); goto Exit; } // Get the dictionary number (used by later tests) if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiDES3DictNum))) { MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); goto Exit; } if ( RC_BAD( rc = m_pDb->transCommit( ))) { MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); goto Exit; } bTransBegun = FALSE; endTest("PASS"); beginTest( "Import to Encrypted Collections Tests", "Verify that we can import data into an encrypted collection", "Import a document into each of the previously defined collections." " Retrieve them and verify them against the original document.", "none"); if (RC_BAD( rc = importToCollection( uiAESDictNum))) { goto Exit; } if (RC_BAD( rc = importToCollection( uiDES3DictNum))) { goto Exit; } // Close the database, reopen it and verify the documents. if( pNode) { pNode->Release(); pNode = NULL; } if( pAttr) { pAttr->Release(); pAttr = NULL; } if( pCollDef) { pCollDef->Release(); pCollDef = NULL; } if( pTmpNode) { pTmpNode->Release(); pTmpNode = NULL; } if( m_pDb->Release()) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } m_pDb = NULL; // Open the database. if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { goto Exit; } // Verify the documents at a time and compare them. if (RC_BAD( rc = verifyDocument( uiAESDictNum))) { goto Exit; } if (RC_BAD( rc = verifyDocument( uiDES3DictNum))) { goto Exit; } endTest( "PASS"); beginTest( "Encrypted Data Only Block Test", "Verify that we can encrypt the data only blocks that are part of an encrypted collection", "Create a document with a very large value, then retrieve and verify it.", "none"); buildLargeBuffer(); if (RC_BAD( rc = createDODocument( uiAESDictNum, &ui64AESDoc))) { goto Exit; } if (RC_BAD( rc = createDODocument( uiDES3DictNum, &ui64DES3Doc))) { goto Exit; } // Close the database, reopen it and verify the documents. if ( m_pDb->Release()) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } m_pDb = NULL; // Open the database. if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { goto Exit; } if( RC_BAD( rc = verifyDODocument( uiAESDictNum, ui64AESDoc))) { goto Exit; } if( RC_BAD( rc = verifyDODocument( uiDES3DictNum, ui64DES3Doc))) { goto Exit; } endTest( "PASS"); Exit: if ( RC_BAD( rc)) { endTest( "FAIL"); } if( pNode) { pNode->Release(); } if( pAttr) { pAttr->Release(); } if( pCollDef) { pCollDef->Release(); } if( pTmpNode) { pTmpNode->Release(); } if( bTransBegun) { if( RC_OK( rc)) { m_pDb->transCommit(); } else { m_pDb->transAbort(); } } shutdownTestState( DB_NAME_STR, bDibCreated); return( rc); } /**************************************************************************** Desc: ****************************************************************************/ static void buildLargeBuffer( void) { FLMUINT uiLoop; for (uiLoop = 0; uiLoop < sizeof(gv_ucLargeBuffer); uiLoop++) { gv_ucLargeBuffer[ uiLoop] = (FLMBYTE)(uiLoop & 0xFF); } } libxflaim-5.1.969/util/viewblk.cpp0000644000175000017500000026212510511001742020371 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains routines for viewing blocks in the database. // // Tabs: 3 // // Copyright (c) 1992-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: viewblk.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "view.h" FSTATIC void InitStatusBits( FLMBYTE * pucStatusBytes); FSTATIC void OrStatusBits( FLMBYTE * pucDestStatusBytes, FLMBYTE * pucSrcStatusBytes); FSTATIC FLMBOOL TestStatusBit( FLMBYTE * pucStatusBytes, FLMINT iErrorCode); FSTATIC FLMBOOL OutBlkHdrExpNum( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, eColorType uiUnselectBackColor, eColorType uiUnselectForeColor, eColorType uiSelectBackColor, eColorType uiSelectForeColor, FLMUINT uiLabelWidth, FLMINT iLabelIndex, FLMUINT uiFileNumber, FLMUINT uiFileOffset, void * pvcValue, FLMUINT uiValueType, FLMUINT uiModType, FLMUINT64 ui64ExpNum, FLMUINT64 ui64IgnoreExpNum, FLMUINT uiOption); FSTATIC void FormatBlkType( char * pszTempBuf, FLMUINT uiBlkType ); FSTATIC FLMBOOL OutputStatus( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, FLMUINT uiLabelWidth, FLMINT iLabelIndex, FLMBYTE * pucStatusFlags ); FSTATIC FLMBOOL OutputLeafElements( FLMUINT uiCol, FLMUINT * puiRow, F_BTREE_BLK_HDR * pBlkHdr, BLK_EXP_p pBlkExp, FLMBYTE * pucBlkStatus, FLMBOOL bStatusOnlyFlag); FSTATIC FLMBOOL OutputDataOnlyElements( FLMUINT uiCol, FLMUINT * puiRow, F_BLK_HDR * pBlkHdr, BLK_EXP_p pBlkExp, FLMBYTE * pucBlkStatus, FLMBOOL bStatusOnlyFlag); FSTATIC FLMBOOL OutputNonLeafElements( FLMUINT uiCol, FLMUINT * puiRow, F_BTREE_BLK_HDR * pBlkHdr, BLK_EXP_p pBlkExp, FLMBYTE * pucBlkStatus, FLMBOOL bStatusOnlyFlag); FSTATIC FLMBOOL OutputDomNode( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, FLMBYTE * pucVal, STATE_INFO * pStateInfo); FSTATIC void SetSearchTopBottom( void); FSTATIC void GetElmInfo( FLMBYTE * pucEntry, F_BTREE_BLK_HDR * pBlkHdr, STATE_INFO * pStateInfo); extern FLMUINT gv_uiTopLine; extern FLMUINT gv_uiBottomLine; /******************************************************************** Desc: Initialize status bits *********************************************************************/ FSTATIC void InitStatusBits( FLMBYTE * pucStatusBytes ) { if (pucStatusBytes) { f_memset( pucStatusBytes, 0, NUM_STATUS_BYTES); } } /******************************************************************** Desc: OR together two sets of status bytes *********************************************************************/ FSTATIC void OrStatusBits( FLMBYTE * pucDestStatusBytes, FLMBYTE * pucSrcStatusBytes ) { FLMUINT uiLoop; if (pucDestStatusBytes && pucSrcStatusBytes) { for (uiLoop = 0; uiLoop < NUM_STATUS_BYTES; uiLoop++) { pucDestStatusBytes [uiLoop] |= pucSrcStatusBytes [uiLoop]; } } } /******************************************************************** Desc: TestStatusBit *********************************************************************/ FSTATIC FLMBOOL TestStatusBit( FLMBYTE * pucStatusBytes, FLMINT iErrorCode ) { if (!pucStatusBytes) { return( FALSE); } else { return( (FLMBOOL)(pucStatusBytes [(iErrorCode - 1) / 8] & (FLMBYTE)((FLMBYTE)0x80 >> (FLMBYTE)((iErrorCode - 1) % 8)) ? TRUE : FALSE)); } } /*************************************************************************** Desc: This routine outputs all of the status bits which were set for a block. Each error discovered in a block will be displayed on a separate line. *****************************************************************************/ FSTATIC FLMBOOL OutputStatus( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, FLMUINT uiLabelWidth, FLMINT iLabelIndex, FLMBYTE * pucStatusFlags ) { FLMBOOL bOk = FALSE; FLMUINT uiRow = *puiRow; FLMINT iError; FLMBOOL bHadError = FALSE; // Output each error on a separate line if (pucStatusFlags) { for (iError = 0; iError < FLM_NUM_CORRUPT_ERRORS; iError++) { if (TestStatusBit( pucStatusFlags, iError)) { bHadError = TRUE; if (!ViewAddMenuItem( iLabelIndex, uiLabelWidth, VAL_IS_ERR_INDEX, (FLMUINT64)iError, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, FLM_RED, FLM_LIGHTGRAY, FLM_RED, FLM_LIGHTGRAY)) { goto Exit; } // Set iLabelIndex to -1 so that it will not be displayed after // the first one. iLabelIndex = -1; } } } // If there were no errors in the block, just output an OK status if (!bHadError) { if (!ViewAddMenuItem( iLabelIndex, uiLabelWidth, VAL_IS_LABEL_INDEX, (FLMUINT64)LBL_OK, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } *puiRow = uiRow; bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine reads into memory a database block. It will also allocate memory to hold the block if necessary. The block will also be decrypted if necessary. *****************************************************************************/ FLMBOOL ViewBlkRead( FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, FLMBOOL bOkToConvert, FLMUINT uiReadLen, FLMUINT32 * pui32CalcCRC, FLMUINT32 * pui32BlkCRC, FLMUINT * puiBytesRead, FLMBOOL bShowPartialReadError ) { FLMBOOL bOk = FALSE; RCODE rc; F_BLK_HDR * pBlkHdr; char szErrMsg [80]; FLMUINT uiBlkEnd; FLMUINT16 ui16BlkBytesAvail; // First allocate memory to read the block into // if not already allocated if (!(*ppBlkHdr)) { if (RC_BAD( rc = f_alloc( uiReadLen, ppBlkHdr))) { ViewShowRCError( "allocating memory to read block", rc); goto Exit; } } pBlkHdr = *ppBlkHdr; // Read the block into memory if (RC_BAD( rc = gv_pSFileHdl->readBlock( uiBlkAddress, uiReadLen, pBlkHdr, puiBytesRead))) { if (rc == NE_XFLM_IO_END_OF_FILE) { rc = NE_XFLM_OK; f_memset( (FLMBYTE *)pBlkHdr + *puiBytesRead, 0xEE, uiReadLen - *puiBytesRead); if (bShowPartialReadError) { if (!(*puiBytesRead)) { ViewShowRCError( "reading block", NE_XFLM_IO_END_OF_FILE); goto Exit; } else if (*puiBytesRead < uiReadLen) { f_sprintf( szErrMsg, "Only %u bytes of data were read (requested %u)", (unsigned)*puiBytesRead, (unsigned)uiReadLen); ViewShowError( szErrMsg); } } } else { ViewShowRCError( "reading block", rc); goto Exit; } } // Calculate the CRC on the block. if (bOkToConvert) { if (pui32CalcCRC) { ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; if (blkIsNonNativeFormat( pBlkHdr)) { convert16( &ui16BlkBytesAvail); } if( ui16BlkBytesAvail > gv_ViewDbHdr.ui16BlockSize) { *pui32CalcCRC = 0; } else { uiBlkEnd = (blkIsNewBTree( pBlkHdr) ? gv_ViewDbHdr.ui16BlockSize : gv_ViewDbHdr.ui16BlockSize - (FLMUINT)ui16BlkBytesAvail); *pui32CalcCRC = calcBlkCRC( pBlkHdr, uiBlkEnd); } } if (blkIsNonNativeFormat( pBlkHdr)) { convertBlk( (FLMUINT)gv_ViewDbHdr.ui16BlockSize, pBlkHdr); } if (pui32BlkCRC) { *pui32BlkCRC = pBlkHdr->ui32BlkCRC; } } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine searches through the LFH blocks searching for the LFH of a particular logical file. *****************************************************************************/ FLMBOOL ViewGetLFH( FLMUINT uiLfNum, eLFileType eLfType, F_LF_HDR * pLfHdr, FLMUINT * puiFileOffset) { FLMUINT uiBlkAddress; FLMUINT uiBlkCount = 0; F_BLK_HDR * pBlkHdr = NULL; F_LF_HDR * pTmpLfHdr; FLMUINT uiEndOfBlock; FLMUINT uiPos; FLMUINT uiBytesRead; FLMBOOL bGotLFH = FALSE; // Read the LFH blocks and get the information needed // If we read too many, the file is probably corrupt. *puiFileOffset = 0; uiBlkAddress = (FLMUINT)gv_ViewDbHdr.ui32FirstLFBlkAddr; while (uiBlkAddress) { if (!ViewBlkRead( uiBlkAddress, &pBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, NULL, NULL, &uiBytesRead, FALSE) || !uiBytesRead) { break; } // Count the blocks read to prevent too many from being read. // We don't want to get into an infinite loop if the database // is corrupted. uiBlkCount++; // Search through the block for the particular LFH which matches // the one we are looking for. if (uiBytesRead <= SIZEOF_STD_BLK_HDR) { uiEndOfBlock = SIZEOF_STD_BLK_HDR; } else { uiEndOfBlock = blkGetEnd( (FLMUINT)gv_ViewDbHdr.ui16BlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); if (uiEndOfBlock > uiBytesRead) { uiEndOfBlock = uiBytesRead; } } uiPos = SIZEOF_STD_BLK_HDR; pTmpLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR); while (uiPos < uiEndOfBlock) { // See if we got the one we wanted if (pTmpLfHdr->ui32LfType != XFLM_LF_INVALID && (FLMUINT)pTmpLfHdr->ui32LfNumber == uiLfNum && (FLMUINT)pTmpLfHdr->ui32LfType == (FLMUINT)eLfType) { f_memcpy( pLfHdr, pTmpLfHdr, sizeof( F_LF_HDR)); bGotLFH = TRUE; *puiFileOffset = uiBlkAddress + uiPos; break; } uiPos += sizeof( F_LF_HDR); pTmpLfHdr++; } // If we didn't end right on end of block, return if (uiPos != uiEndOfBlock || bGotLFH) { break; } // If we have traversed too many blocks, things are probably corrupt if (uiBlkCount > 1000) { break; } if (F_BLK_HDR_ui32NextBlkInChain_OFFSET + 4 <= uiBytesRead) { uiBlkAddress = pBlkHdr->ui32NextBlkInChain; } else { uiBlkAddress = 0; } } // Be sure to free the block -- if one was allocated f_free( &pBlkHdr); return( bGotLFH); } /*************************************************************************** Desc: This routine attempts to find an LFH for a particular logical file. It then extracts the name from the LFH. *****************************************************************************/ FLMBOOL ViewGetLFName( char * pszName, FLMUINT uiLfNum, eLFileType eLfType, F_LF_HDR * pLfHdr, FLMUINT * puiFileOffset) { f_sprintf( pszName, "lfNum=%u", (unsigned)uiLfNum); if (!uiLfNum || !ViewGetLFH( uiLfNum, eLfType, pLfHdr, puiFileOffset)) { *puiFileOffset = 0; f_memset( pLfHdr, 0, sizeof( F_LF_HDR)); return( FALSE); } return( TRUE); } /*************************************************************************** Desc: This routine outputs one of the number fields in the block header. It checks the number against an expected value, and if the number does not match the expected value also outputs the value which was expected. This routine is used to output values in the block header where we expect certain values. *****************************************************************************/ FSTATIC FLMBOOL OutBlkHdrExpNum( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, eColorType uiUnselectBackColor, eColorType uiUnselectForeColor, eColorType uiSelectBackColor, eColorType uiSelectForeColor, FLMUINT uiLabelWidth, FLMINT iLabelIndex, FLMUINT uiFileNumber, FLMUINT uiFileOffset, void * pvValue, FLMUINT uiValueType, FLMUINT uiModType, FLMUINT64 ui64ExpNum, FLMUINT64 ui64IgnoreExpNum, FLMUINT uiOption) { FLMBOOL bOk = FALSE; FLMUINT uiRow = *puiRow; FLMUINT64 ui64Num = 0; if (!uiOption) { uiUnselectBackColor = uiSelectBackColor = uiBackColor; uiUnselectForeColor = uiSelectForeColor = uiForeColor; } switch (uiModType & 0x0F) { case MOD_FLMUINT64: ui64Num = *((FLMUINT64 *)pvValue); break; case MOD_FLMUINT32: ui64Num = (FLMUINT64)(*((FLMUINT32 *)pvValue)); break; case MOD_FLMUINT16: ui64Num = (FLMUINT64)(*((FLMUINT16 *)pvValue)); break; case MOD_FLMBYTE: ui64Num = (FLMUINT64)(*((FLMUINT8 *)pvValue)); break; } if (!ViewAddMenuItem( iLabelIndex, uiLabelWidth, uiValueType, ui64Num, 0, uiFileNumber, uiFileOffset, 0, uiModType | MOD_NATIVE, uiCol, uiRow++, uiOption, uiUnselectBackColor, uiUnselectForeColor, uiSelectBackColor, uiSelectForeColor)) { goto Exit; } if (ui64ExpNum != ui64IgnoreExpNum && ui64Num != ui64ExpNum) { if (!ViewAddMenuItem( LBL_EXPECTED, 0, uiValueType, ui64ExpNum, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol + uiLabelWidth + 1, uiRow++, 0, FLM_RED, FLM_LIGHTGRAY, FLM_RED, FLM_LIGHTGRAY)) { goto Exit; } } *puiRow = uiRow; bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine formats a block's type into ASCII. *****************************************************************************/ FSTATIC void FormatBlkType( char * pszTempBuf, FLMUINT uiBlkType ) { switch (uiBlkType) { case BT_FREE: f_strcpy( pszTempBuf, "Free"); break; case BT_LEAF: f_strcpy( pszTempBuf, "Leaf"); break; case BT_NON_LEAF: f_strcpy( pszTempBuf, "Non-Leaf"); break; case BT_NON_LEAF_COUNTS: f_strcpy( pszTempBuf, "Non-Leaf /w Counts"); break; case BT_LEAF_DATA: f_strcpy( pszTempBuf, "Leaf /w Data"); break; case BT_DATA_ONLY: f_strcpy( pszTempBuf, "Data Only"); break; case BT_LFH_BLK: f_strcpy( pszTempBuf, "LFH"); break; default: f_sprintf( pszTempBuf, "Unknown Type: %u", (unsigned)uiBlkType); break; } } /*************************************************************************** Desc: This routine outputs a block's header. *****************************************************************************/ FLMBOOL ViewOutBlkHdr( FLMUINT uiCol, FLMUINT * puiRow, F_BLK_HDR * pBlkHdr, BLK_EXP_p pBlkExp, FLMBYTE * pucBlkStatus, FLMUINT32 ui32CalcCRC, FLMUINT32 ui32BlkCRC ) { FLMBOOL bOk = FALSE; FLMUINT uiLabelWidth = 35; FLMUINT uiRow = *puiRow; char szTempBuf [80]; FLMUINT uiBlkAddress; FLMUINT uiEndOfBlock; FLMUINT uiBytesUsed; FLMUINT uiPercentFull; FLMUINT uiOption; eColorType uiBackColor = FLM_BLACK; eColorType uiForeColor = FLM_LIGHTGRAY; eColorType uiUnselectBackColor = FLM_BLACK; eColorType uiUnselectForeColor = FLM_WHITE; eColorType uiSelectBackColor = FLM_BLUE; eColorType uiSelectForeColor = FLM_WHITE; F_LF_HDR lfHdr; char szLfName [80]; FLMUINT uiLfNum; eLFileType eLfType; FLMUINT uiTempFileOffset; // Output the block Header address if (!OutBlkHdrExpNum( uiCol, &uiRow, FLM_RED, FLM_LIGHTGRAY, FLM_RED, FLM_WHITE, uiSelectBackColor, uiSelectForeColor, uiLabelWidth, LBL_BLOCK_ADDRESS_BLOCK_HEADER, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr), &pBlkHdr->ui32BlkAddr, VAL_IS_NUMBER | DISP_HEX_DECIMAL, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, (FLMUINT64)pBlkExp->uiBlkAddr, 0, 0)) { goto Exit; } // Adjust column so rest of header is indented uiCol += 2; uiLabelWidth -= 2; // Output the previous block address if (!pBlkHdr->ui32PrevBlkInChain) { uiOption = 0; } else { uiOption = PREV_BLOCK_OPTION; } if (!OutBlkHdrExpNum( uiCol, &uiRow, uiBackColor, uiForeColor, uiUnselectBackColor, uiUnselectForeColor, uiSelectBackColor, uiSelectForeColor, uiLabelWidth, LBL_PREVIOUS_BLOCK_ADDRESS, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui32PrevBlkInChain_OFFSET, &pBlkHdr->ui32PrevBlkInChain, VAL_IS_NUMBER | DISP_HEX_DECIMAL, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, (FLMUINT64)pBlkExp->uiPrevAddr, 0xFFFFFFFF, uiOption)) { goto Exit; } // Output the next block address if (!pBlkHdr->ui32NextBlkInChain) { uiOption = 0; } else { uiOption = NEXT_BLOCK_OPTION; } if (!OutBlkHdrExpNum( uiCol, &uiRow, uiBackColor, uiForeColor, uiUnselectBackColor, uiUnselectForeColor, uiSelectBackColor, uiSelectForeColor, uiLabelWidth, LBL_NEXT_BLOCK_ADDRESS, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui32NextBlkInChain_OFFSET, &pBlkHdr->ui32NextBlkInChain, VAL_IS_NUMBER | DISP_HEX_DECIMAL, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, (FLMUINT64)pBlkExp->uiNextAddr, 0xFFFFFFFF, uiOption)) { goto Exit; } // Output the prior block image address uiBlkAddress = (FLMUINT)pBlkHdr->ui32PriorBlkImgAddr; if (uiBlkAddress == 0 || pBlkHdr->ui64TransID <= gv_ViewDbHdr.ui64CurrTransID) { uiOption = 0; } else { uiOption = PREV_BLOCK_IMAGE_OPTION; } if (!ViewAddMenuItem( LBL_OLD_BLOCK_IMAGE_ADDRESS, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)uiBlkAddress, 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui32PriorBlkImgAddr_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } // Output the block transaction ID if (!ViewAddMenuItem( LBL_BLOCK_TRANS_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, pBlkHdr->ui64TransID, 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui64TransID_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the block CRC if (!OutBlkHdrExpNum( uiCol, &uiRow, uiBackColor, uiForeColor, uiUnselectBackColor, uiUnselectForeColor, uiSelectBackColor, uiSelectForeColor, uiLabelWidth, LBL_BLOCK_CRC, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui32BlkCRC_OFFSET, &ui32BlkCRC, VAL_IS_NUMBER | DISP_DECIMAL, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, (FLMUINT64)ui32CalcCRC, 0, 0)) { return( 0); } // Output the available bytes in the block if (!ViewAddMenuItem( LBL_BLOCK_BYTES_AVAIL, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)pBlkHdr->ui16BlkBytesAvail, 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui16BlkBytesAvail_OFFSET, 0, MOD_FLMUINT16 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the heap bytes in the block if ((pBlkHdr->ui8BlkType == BT_LEAF) || (pBlkHdr->ui8BlkType == BT_LEAF_DATA) || (pBlkHdr->ui8BlkType == BT_NON_LEAF) || (pBlkHdr->ui8BlkType == BT_NON_LEAF_COUNTS)) { if (!ViewAddMenuItem( LBL_BLOCK_HEAP_SIZE, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16HeapSize, 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui16HeapSize_OFFSET, 0, MOD_FLMUINT16 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } uiEndOfBlock = blkGetEnd( (FLMUINT)gv_ViewDbHdr.ui16BlockSize, blkHdrSize( pBlkHdr), pBlkHdr); // Output the percent full if (blkIsNewBTree( pBlkHdr)) { uiBytesUsed = gv_ViewDbHdr.ui16BlockSize - pBlkHdr->ui16BlkBytesAvail - blkHdrSize( pBlkHdr); } else { uiBytesUsed = uiEndOfBlock - blkHdrSize( pBlkHdr); } if (!uiBytesUsed || uiEndOfBlock < blkHdrSize( pBlkHdr)) { uiPercentFull = 0; } else if (uiEndOfBlock > (FLMUINT)gv_ViewDbHdr.ui16BlockSize) { uiPercentFull = 100; } else { uiPercentFull = ((FLMUINT)(uiBytesUsed) * (FLMUINT)(100)) / ((FLMUINT)(gv_ViewDbHdr.ui16BlockSize) - blkHdrSize( pBlkHdr)); } if (!ViewAddMenuItem( LBL_PERCENT_FULL, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)uiPercentFull, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } if (!ViewAddMenuItem( LBL_BLOCK_END, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)(FSGetFileOffset( pBlkExp->uiBlkAddr) + uiEndOfBlock), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + uiEndOfBlock, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output block flags here if (pBlkHdr->ui8BlkFlags & BLK_FORMAT_IS_LITTLE_ENDIAN) { f_strcpy( szTempBuf, "Little-Endian"); } else { f_strcpy( szTempBuf, "Big-Endian"); } if (pBlkHdr->ui8BlkFlags & BLK_IS_BEFORE_IMAGE) { f_strcpy( &szTempBuf [f_strlen( szTempBuf)], ", Before-Image"); } if (!ViewAddMenuItem( LBL_BLOCK_FLAGS, uiLabelWidth, VAL_IS_TEXT_PTR, (FLMUINT)&szTempBuf[0], 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui8BlkFlags_OFFSET, 0, MOD_FLMBYTE | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the block type FormatBlkType( szTempBuf, (FLMUINT)pBlkHdr->ui8BlkType); if (pBlkHdr->ui8BlkType != (FLMUINT8)pBlkExp->uiType) { f_strcpy( &szTempBuf [f_strlen( szTempBuf)], ", Expecting "); FormatBlkType( &szTempBuf [f_strlen( szTempBuf)], pBlkExp->uiType); } if (!ViewAddMenuItem( LBL_BLOCK_TYPE, uiLabelWidth, VAL_IS_TEXT_PTR, (FLMUINT)(&szTempBuf [0]), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BLK_HDR_ui8BlkType_OFFSET, 0, MOD_FLMBYTE | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } if (blkIsBTree( pBlkHdr) && (pBlkHdr->ui8BlkType != BT_DATA_ONLY)) { // Output the logical file this block belongs to - if any uiLfNum = (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile); eLfType = getBlkLfType( (F_BTREE_BLK_HDR *)pBlkHdr); if (!ViewGetLFName( szLfName, uiLfNum, eLfType, &lfHdr, &uiTempFileOffset)) { eLfType = XFLM_LF_INVALID; uiOption = 0; } else { if (eLfType == XFLM_LF_INDEX) { uiOption = LOGICAL_INDEX_OPTION | uiLfNum; } else { uiOption = LOGICAL_CONTAINER_OPTION | uiLfNum; } } if (pBlkExp->uiLfNum && uiLfNum != pBlkExp->uiLfNum) { f_sprintf( (char *)szTempBuf, "%s (Expected %u)", szLfName, (unsigned)pBlkExp->uiLfNum); } else { f_strcpy( szTempBuf, szLfName); } if (!ViewAddMenuItem( LBL_BLOCK_LOGICAL_FILE_NAME, uiLabelWidth, VAL_IS_TEXT_PTR, (FLMUINT)((FLMBYTE *)(&szTempBuf [0])), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui16LogicalFile_OFFSET, 0, MOD_FLMUINT16 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } // Output the logical file type FormatLFType( szTempBuf, eLfType); if (!ViewAddMenuItem( LBL_BLOCK_LOGICAL_FILE_TYPE, uiLabelWidth, VAL_IS_TEXT_PTR, (FLMUINT)((FLMBYTE *)(&szTempBuf [0])), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET, 0, MOD_FLMBYTE | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output number of keys if (!ViewAddMenuItem( LBL_BLOCK_NUM_KEYS, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui16NumKeys_OFFSET, 0, MOD_FLMUINT16 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output block level if (!OutBlkHdrExpNum( uiCol, &uiRow, uiBackColor, uiForeColor, uiUnselectBackColor, uiUnselectForeColor, uiSelectBackColor, uiSelectForeColor, uiLabelWidth, LBL_B_TREE_LEVEL, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui8BlkLevel_OFFSET, &(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel), VAL_IS_NUMBER | DISP_DECIMAL, MOD_FLMBYTE | MOD_DECIMAL | MOD_NATIVE, (FLMUINT64)pBlkExp->uiLevel, (FLMUINT64)0xFF, 0)) { goto Exit; } // Output if block is a root block if (!ViewAddMenuItem( LBL_BLOCK_ROOT, uiLabelWidth, VAL_IS_LABEL_INDEX, (FLMUINT64)(isRootBlk( (F_BTREE_BLK_HDR *)pBlkHdr) ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET, 0, MOD_FLMBYTE | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output if block is encrypted if (!ViewAddMenuItem( LBL_BLOCK_ENCRYPTED, uiLabelWidth, VAL_IS_LABEL_INDEX, (FLMUINT64)(isEncryptedBlk( pBlkHdr) ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO), 0, FSGetFileNumber( pBlkExp->uiBlkAddr), FSGetFileOffset( pBlkExp->uiBlkAddr) + F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET, 0, MOD_FLMBYTE | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } // Output the flags which indicate the state of the block if (!OutputStatus( uiCol, &uiRow, uiBackColor, uiForeColor, uiLabelWidth, LBL_BLOCK_STATUS, pucBlkStatus)) { goto Exit; } *puiRow = uiRow + 1; bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine displays a block in the AVAIL list. *****************************************************************************/ FLMBOOL ViewAvailBlk( FLMUINT uiReadAddress, FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, BLK_EXP_p pBlkExp ) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; F_BLK_HDR * pBlkHdr; FLMBYTE ucBlkStatus [NUM_STATUS_BYTES]; STATE_INFO StateInfo; // FLMBOOL bStateInitialized = FALSE; // BLOCK_INFO BlockInfo; // FLMINT iErrorCode; FLMUINT32 ui32CalcCRC; FLMUINT32 ui32BlkCRC; FLMUINT uiBytesRead; // Read the block into memory if (!ViewBlkRead( uiReadAddress, ppBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, &ui32CalcCRC, &ui32BlkCRC, &uiBytesRead, TRUE)) { goto Exit; } pBlkHdr = *ppBlkHdr; ViewMenuInit( "AVAIL Block"); // Output the block header first uiRow = 0; uiCol = 5; pBlkExp->uiType = BT_FREE; pBlkExp->uiLfNum = 0; pBlkExp->uiBlkAddr = uiBlkAddress; pBlkExp->uiLevel = 0xFF; // Setup the STATE variable for processing through the block InitStatusBits( ucBlkStatus); #if 0 flmInitReadState( &StateInfo, &bStateInitialized, (FLMUINT)gv_ViewDbHdr.ui32DbVersion, (gv_bViewDbInitialized) ? gv_hViewDb : NULL, NULL, 0, BT_FREE, NULL); #endif StateInfo.ui32BlkAddress = (FLMUINT32)uiBlkAddress; StateInfo.pBlkHdr = pBlkHdr; #if 0 if ((iErrorCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, pBlkExp->uiNextAddr, 0xFFFFFFFF, (FLMBOOL)(StateInfo.pDb != NULL ? TRUE : FALSE))) != 0) { SetStatusBit( ucBlkStatus, iErrorCode); } #endif if (!ViewOutBlkHdr( uiCol, &uiRow, pBlkHdr, pBlkExp, ucBlkStatus, ui32CalcCRC, ui32BlkCRC)) { goto Exit; } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine outputs a stream of FLMBYTEs in hex format. This routine is used to output key values and records within an element. *****************************************************************************/ FLMBOOL OutputHexValue( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, FLMINT iLabelIndex, FLMUINT uiFileNumber, FLMUINT uiFileOffset, FLMBYTE * pucVal, FLMUINT uiValLen, FLMBOOL bCopyVal) { FLMBOOL bOk = FALSE; FLMUINT uiRow = *puiRow; FLMUINT uiBytesPerLine = MAX_HORIZ_SIZE( uiCol + 3); FLMBOOL bFirstTime = TRUE; FLMUINT uiBytesProcessed = 0; FLMUINT uiNumBytes; while (uiBytesProcessed < uiValLen) { if ((uiNumBytes = uiValLen - uiBytesProcessed) > uiBytesPerLine) { uiNumBytes = uiBytesPerLine; } // Output the line if (bFirstTime) { if (!ViewAddMenuItem( iLabelIndex, 0, VAL_IS_EMPTY, 0, 0, uiFileNumber, uiFileOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } bFirstTime = FALSE; uiCol += 2; } if (!ViewAddMenuItem( -1, 0, (FLMBYTE)((bCopyVal) ? (FLMBYTE)VAL_IS_BINARY : (FLMBYTE)VAL_IS_BINARY_PTR), (FLMUINT)pucVal, uiNumBytes, uiFileNumber, uiFileOffset, uiNumBytes, MOD_BINARY, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } uiFileOffset += uiNumBytes; uiBytesProcessed += uiNumBytes; pucVal += uiNumBytes; } *puiRow = uiRow; bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine outputs the content of a DOM node in the Element *****************************************************************************/ FSTATIC FLMBOOL OutputDomNode( FLMUINT uiCol, FLMUINT * puiRow, eColorType uiBackColor, eColorType uiForeColor, FLMBYTE * pucVal, STATE_INFO * pStateInfo) { RCODE rc = NE_XFLM_OK; FLMBOOL bOk = FALSE; FLMUINT uiLabelWidth = 30; FLMUINT uiRow = *puiRow; FLMUINT uiNumBytes; FLMBYTE * pucData = pucVal; FLMUINT uiDataOffset = pStateInfo->uiElmDataOffset; FLMBYTE ucFlags = 0; FLMBYTE ucExtFlags = 0; FLMUINT uiSENLength = 0; const FLMBYTE * pucTmp; FLMUINT64 ui64Value; FLMUINT uiValue; // VISIT: change to use flmReadNodeInfo if ((uiNumBytes = pStateInfo->uiElmDataLen) == 0) { bOk = TRUE; goto Exit; } // Display the Node Type. The first byte is encoded as RDDDNNNN // where R = Reserved, DDD = Data Type, NNNN = Node Type // Display the Data Type if (!ViewAddMenuItem( LBL_DOM_DATA_TYPE, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (*pucData & 0x70) >> 4, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the Node Type if (!ViewAddMenuItem( LBL_DOM_NODE_TYPE, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, *pucData & 0x0F, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } pucData++; uiDataOffset++; uiNumBytes--; if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_FLAGS, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { ucFlags = *pucData; // Display the flags if (!ViewAddMenuItem( LBL_DOM_FLAGS, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, *pucData, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } pucData++; uiDataOffset++; uiNumBytes--; } // Name ID if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_NAME_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &uiValue))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_NAME_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, uiValue, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN5 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_NAME_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiNumBytes -= uiSENLength; uiDataOffset += uiSENLength; } // Prefix ID if (ucFlags & NSF_EXT_HAVE_PREFIX_BIT) { if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_PREFIX_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &uiValue))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_PREFIX_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, uiValue, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN5 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_PREFIX_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiNumBytes -= uiSENLength; uiDataOffset += uiSENLength; } } if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_BASE_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // Base ID (required) pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_BASE_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_BASE_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_ROOT_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_ROOT_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_ROOT_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_PARENT_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_PARENT_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_PARENT_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } // Prev+Next Siblings if (ucFlags & NSF_HAVE_SIBLINGS_BIT) { if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_PREV_SIB_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // Previous pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_PREV_SIB_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_PREV_SIB_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_NEXT_SIB_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // Next pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_NEXT_SIB_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_NEXT_SIB_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } } // First+Last Child ID if (ucFlags & NSF_HAVE_CHILDREN_BIT) { // First if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_FIRST_CHILD_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_FIRST_CHILD_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_FIRST_CHILD_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } // Last if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_LAST_CHILD_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_LAST_CHILD_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_LAST_CHILD_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } } // Annotation if (ucExtFlags & NSF_EXT_ANNOTATION_BIT) { if (!uiNumBytes) { if (!ViewAddMenuItem( LBL_DOM_ANNOTATION_ID, uiLabelWidth, VAL_IS_LABEL_INDEX, LBL_NO_VALUE, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { pucTmp = pucData; uiSENLength = f_getSENLength( *pucTmp); if (uiNumBytes >= uiSENLength) { if( RC_BAD( rc = f_decodeSEN64( &pucTmp, &pucTmp[ FLM_MAX_NUM_BUF_SIZE], &ui64Value))) { goto Exit; } if (!ViewAddMenuItem( LBL_DOM_ANNOTATION_ID, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, ui64Value, 0, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, 0, MOD_SEN9 | MOD_HEX, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } else { // If we can't display the number, at least display what we have left. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_ANNOTATION_ID, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } uiSENLength = uiNumBytes; } pucData += uiSENLength; uiDataOffset += uiSENLength; uiNumBytes -= uiSENLength; } } if (uiNumBytes) { // Output whatever is left as a HEX dump. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DOM_VALUE, FSGetFileNumber( pStateInfo->ui32BlkAddress), FSGetFileOffset( pStateInfo->ui32BlkAddress) + uiDataOffset, pucData, uiNumBytes, FALSE)) { goto Exit; } } bOk = TRUE; Exit: *puiRow = uiRow; return( bOk); } /*************************************************************************** Desc: This routine outputs the elements in a LEAF block. *****************************************************************************/ FSTATIC FLMBOOL OutputLeafElements( FLMUINT uiCol, FLMUINT * puiRow, F_BTREE_BLK_HDR * pBlkHdr, BLK_EXP_p pBlkExp, FLMBYTE * pucBlkStatus, FLMBOOL bStatusOnlyFlag ) { // VISIT: There are a number of places throughout that have been commentd out // that have to do with the block status. Currently, the pucBlkStatus // flag is not being updated in a meaningful way. Need to investigate // what this is used for and find a way to make it meaningful. FLMBOOL bOk = FALSE; FLMUINT uiLabelWidth = 30; eColorType uiBackColor = FLM_BLACK; eColorType uiForeColor = FLM_LIGHTGRAY; eColorType uiUnselectBackColor = FLM_BLACK; eColorType uiUnselectForeColor = FLM_WHITE; eColorType uiSelectBackColor = FLM_BLUE; eColorType uiSelectForeColor = FLM_WHITE; FLMUINT uiRow = *puiRow; FLMUINT uiElementCount = 0; FLMINT iErrorCode; // LFileType eLfType = LF_INVALID; FLMBYTE ucElmStatus [NUM_STATUS_BYTES]; STATE_INFO StateInfo; // LF_HDR_p pLogicalFile = NULL; FLMUINT uiCurOffset; FLMUINT16 * pui16OffsetArray; FLMUINT uiOption; FLMBOOL bHasData = (pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA); if (pBlkExp->uiLfNum == 0) { pBlkExp->uiLfNum = (FLMUINT)pBlkHdr->ui16LogicalFile; } // Setup the STATE variable for processing through the block (void)ViewGetDictInfo(); #if 0 if ((iErrorCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, pBlkExp->uiNextAddr, pBlkExp->uiPrevAddr, (FLMBOOL)(StateInfo.pDb != NULL ? TRUE : FALSE))) != 0) { SetStatusBit( pucBlkStatus, iErrorCode); } #endif uiCurOffset = 0; pui16OffsetArray = (FLMUINT16 *)((FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr)); // Read through the elements in the block while( uiCurOffset <= (FLMUINT)(pBlkHdr->ui16NumKeys - 1)) { InitStatusBits( ucElmStatus); uiElementCount++; GetElmInfo( (FLMBYTE *)pBlkHdr + pui16OffsetArray[ uiCurOffset], pBlkHdr, &StateInfo); iErrorCode = 0; // ?? #if 0 if ((iErrorCode = flmVerifyElement( &StateInfo, FO_DO_EXTENDED_DATA_CHECK)) != 0) { SetStatusBit( ucElmStatus, iErrorCode); } else if (eLfType == XFLM_LF_INDEX) { if (StateInfo.uiCurKeyLen) { if (RC_BAD( flmVerifyIXRefs( &StateInfo, NULL, 0, &iErrorCode)) || iErrorCode != 0) { SetStatusBit( ucElmStatus, iErrorCode); } } } #endif // Output the element if (!bStatusOnlyFlag) { uiRow++; // Output the element number if (!ViewAddMenuItem( LBL_ELEMENT_NUMBER, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)uiElementCount, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, FLM_GREEN, FLM_WHITE, FLM_GREEN, FLM_WHITE)) { goto Exit; } // Remember this item if we are searching #if 0 if ((gv_bViewSearching) && (flmCompareKeys( StateInfo.pucCurKey, StateInfo.uiCurKeyLen, gv_ucViewSearchKey, gv_uiViewSearchKeyLen) >= 0) && (gv_pViewSearchItem == NULL)) { gv_pViewSearchItem = gv_pViewMenuLastItem; } #endif // Output the element offset within the block if (!ViewAddMenuItem( LBL_ELEMENT_OFFSET, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmOffset, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the element length if (!ViewAddMenuItem( LBL_ELEMENT_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the first element flag if present if (bHasData) { if (!ViewAddMenuItem( LBL_FIRST_ELEMENT_FLAG, uiLabelWidth, VAL_IS_LABEL_INDEX, (bteFirstElementFlag( StateInfo.pucElm) ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO), 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0x80, MOD_BITS | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last element flag if (!ViewAddMenuItem( LBL_LAST_ELEMENT_FLAG, uiLabelWidth, VAL_IS_LABEL_INDEX, (bteLastElementFlag( StateInfo.pucElm) ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO), 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0x40, MOD_BITS | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the Data Only flag if (!ViewAddMenuItem( LBL_DATA_BLOCK_FLAG, uiLabelWidth, VAL_IS_LABEL_INDEX, (bteDataBlockFlag( StateInfo.pucElm) ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO), 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0x40, MOD_BITS | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } // Display the key length if (!ViewAddMenuItem( LBL_KEY_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmKeyLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmKeyLenOffset, 0, MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the current key portion, if any if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_ELEMENT_KEY, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmKeyOffset, StateInfo.pucElmKey, StateInfo.uiElmKeyLen, FALSE)) { goto Exit; } // Display the data length if (bHasData) { // Output the overall data field (if present) if (bteOADataLenFlag( StateInfo.pucElm)) { if (!ViewAddMenuItem( LBL_OA_DATA_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmOADataLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOADataLenOffset, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } if (!ViewAddMenuItem( LBL_DATA_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmDataLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmDataLenOffset, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the data portion // Check for a data only block. if ( bteDataBlockFlag( StateInfo.pucElm)) { FLMUINT uiTempAddress = FB2UD( StateInfo.pucElmData); if (uiTempAddress == 0) { uiOption = 0; } else { uiOption = BLK_OPTION_DATA_BLOCK | StateInfo.uiElmOffset; } if (!ViewAddMenuItem( LBL_DATA_BLOCK_ADDRESS, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)uiTempAddress, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmDataOffset, 0, MOD_DISABLED, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } } else { if ( bteFirstElementFlag( StateInfo.pucElm) && !isIndexBlk((F_BTREE_BLK_HDR *)StateInfo.pBlkHdr)) { if (!OutputDomNode( uiCol, &uiRow, uiBackColor, uiForeColor, StateInfo.pucElmData + 1, &StateInfo)) { goto Exit; } } else { if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DATA, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmDataOffset, StateInfo.pucElmData, StateInfo.uiElmDataLen, FALSE)) { goto Exit; } } } } } // Go to the next element uiCurOffset++; OrStatusBits( pucBlkStatus, ucElmStatus); } if (!bStatusOnlyFlag) { *puiRow = uiRow; // If we were searching and did not find a key, set it on the // last key found if (gv_bViewSearching && !gv_pViewSearchItem) { gv_pViewSearchItem = gv_pViewMenuLastItem; while (gv_pViewSearchItem && gv_pViewSearchItem->iLabelIndex != LBL_ELEMENT_NUMBER) { gv_pViewSearchItem = gv_pViewSearchItem->pPrevItem; } } } bOk = TRUE; Exit: *puiRow = uiRow; #if 0 if (bStateInitialized && StateInfo.pRecord) { StateInfo.pRecord->release( &StateInfo.pRecord); } #endif return( bOk); } /*************************************************************************** Desc: This routine outputs the elements in a DATA-ONLY block. *****************************************************************************/ FSTATIC FLMBOOL OutputDataOnlyElements( FLMUINT uiCol, FLMUINT * puiRow, F_BLK_HDR * pBlkHdr, BLK_EXP_p, //pBlkExp, FLMBYTE *, //pucBlkStatus, FLMBOOL bStatusOnlyFlag ) { FLMBOOL bOk = FALSE; FLMUINT uiLabelWidth = 30; eColorType uiBackColor = FLM_BLACK; eColorType uiForeColor = FLM_LIGHTGRAY; FLMUINT uiRow = *puiRow; FLMINT iErrorCode; // LFileType eLfType = LF_INVALID; FLMBYTE ucElmStatus [NUM_STATUS_BYTES]; STATE_INFO StateInfo; // Setup the STATE variable for processing through the block (void)ViewGetDictInfo(); #if 0 if ((iErrorCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, pBlkExp->uiNextAddr, pBlkExp->uiPrevAddr, (FLMBOOL)(StateInfo.pDb != NULL ? TRUE : FALSE))) != 0) { SetStatusBit( pucBlkStatus, iErrorCode); } #endif // Read through the block InitStatusBits( ucElmStatus); GetElmInfo( (FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR, (F_BTREE_BLK_HDR *)pBlkHdr, &StateInfo); iErrorCode = 0; // ?? #if 0 if ((iErrorCode = flmVerifyElement( &StateInfo, FO_DO_EXTENDED_DATA_CHECK)) != 0) { SetStatusBit( ucElmStatus, iErrorCode); } else if (eLfType == XFLM_LF_INDEX) { if (StateInfo.uiCurKeyLen) { if (RC_BAD( flmVerifyIXRefs( &StateInfo, NULL, 0, &iErrorCode)) || iErrorCode != 0) { SetStatusBit( ucElmStatus, iErrorCode); } } } #endif // Output the element if (!bStatusOnlyFlag) { uiRow++; // Remember this item if we are searching #if 0 if ((gv_bViewSearching) && (flmCompareKeys( StateInfo.pucCurKey, StateInfo.uiCurKeyLen, gv_ucViewSearchKey, gv_uiViewSearchKeyLen) >= 0) && (gv_pViewSearchItem == NULL)) { gv_pViewSearchItem = gv_pViewMenuLastItem; } #endif // Output the element length if (!ViewAddMenuItem( LBL_ELEMENT_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the key length if there is a key... if ( StateInfo.uiElmKeyLen) { if (!ViewAddMenuItem( LBL_KEY_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmKeyLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmKeyOffset, 0, MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the key portion if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_ELEMENT_KEY, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmKeyOffset, StateInfo.pucElmKey, StateInfo.uiElmKeyLen, FALSE)) { goto Exit; } } // Display the data length if (!ViewAddMenuItem( LBL_DATA_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmDataLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmDataLenOffset, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the data portion. if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_DATA, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmDataOffset, StateInfo.pucElmData, StateInfo.uiElmDataLen, FALSE)) { goto Exit; } } if (!bStatusOnlyFlag) { *puiRow = uiRow; // If we were searching and did not find a key, set it on the // last key found if (gv_bViewSearching && !gv_pViewSearchItem) { gv_pViewSearchItem = gv_pViewMenuLastItem; while (gv_pViewSearchItem && gv_pViewSearchItem->iLabelIndex != LBL_ELEMENT_NUMBER) { gv_pViewSearchItem = gv_pViewSearchItem->pPrevItem; } } } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine outputs a LEAF block, including the block header. *****************************************************************************/ FLMBOOL ViewLeafBlk( FLMUINT uiReadAddress, FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, BLK_EXP_p pBlkExp ) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; F_BLK_HDR * pBlkHdr; FLMBYTE ucBlkStatus [NUM_STATUS_BYTES]; FLMUINT32 ui32CalcCRC; FLMUINT32 ui32BlkCRC; FLMUINT uiBytesRead; InitStatusBits( ucBlkStatus); // Read the block into memory if (!ViewBlkRead( uiReadAddress, ppBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, &ui32CalcCRC, &ui32BlkCRC, &uiBytesRead, TRUE)) { goto Exit; } pBlkHdr = *ppBlkHdr; ViewMenuInit( "LEAF Block"); // Output the block header first uiRow = 0; uiCol = 5; pBlkExp->uiType = pBlkHdr->ui8BlkType; pBlkExp->uiBlkAddr = uiBlkAddress; pBlkExp->uiLevel = 0; OutputLeafElements( uiCol, &uiRow, (F_BTREE_BLK_HDR *)pBlkHdr, pBlkExp, ucBlkStatus, TRUE); if (!ViewOutBlkHdr( uiCol, &uiRow, pBlkHdr, pBlkExp, ucBlkStatus, ui32CalcCRC, ui32BlkCRC)) { goto Exit; } // Now output the leaf data if (!OutputLeafElements( uiCol, &uiRow, (F_BTREE_BLK_HDR *)pBlkHdr, pBlkExp, ucBlkStatus, FALSE)) { goto Exit; } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine outputs a Data Only block, including the block header. *****************************************************************************/ FLMBOOL ViewDataBlk( FLMUINT uiReadAddress, FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, BLK_EXP_p pBlkExp ) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; F_BLK_HDR * pBlkHdr; FLMBYTE ucBlkStatus [NUM_STATUS_BYTES]; FLMUINT32 ui32CalcCRC; FLMUINT32 ui32BlkCRC; FLMUINT uiBytesRead; InitStatusBits( ucBlkStatus); // Read the block into memory if (!ViewBlkRead( uiReadAddress, ppBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, &ui32CalcCRC, &ui32BlkCRC, &uiBytesRead, TRUE)) { goto Exit; } pBlkHdr = *ppBlkHdr; ViewMenuInit( "DATA Block"); // Output the block header first uiRow = 0; uiCol = 5; pBlkExp->uiType = pBlkHdr->ui8BlkType; pBlkExp->uiBlkAddr = uiBlkAddress; pBlkExp->uiLevel = 0; OutputDataOnlyElements( uiCol, &uiRow, pBlkHdr, pBlkExp, ucBlkStatus, TRUE); if (!ViewOutBlkHdr( uiCol, &uiRow, pBlkHdr, pBlkExp, ucBlkStatus, ui32CalcCRC, ui32BlkCRC)) { goto Exit; } // Now output the leaf data if (!OutputDataOnlyElements( uiCol, &uiRow, pBlkHdr, pBlkExp, ucBlkStatus, FALSE)) { goto Exit; } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine outputs the elements of a NON-LEAF block. *****************************************************************************/ FSTATIC FLMBOOL OutputNonLeafElements( FLMUINT uiCol, FLMUINT * puiRow, F_BTREE_BLK_HDR * pBlkHdr, BLK_EXP_p pBlkExp, FLMBYTE * pucBlkStatus, FLMBOOL bStatusOnlyFlag ) { FLMBOOL bOk = FALSE; FLMUINT uiLabelWidth = 30; eColorType uiBackColor = FLM_BLACK; eColorType uiForeColor = FLM_LIGHTGRAY; eColorType uiUnselectBackColor = FLM_BLACK; eColorType uiUnselectForeColor = FLM_WHITE; eColorType uiSelectBackColor = FLM_BLUE; eColorType uiSelectForeColor = FLM_WHITE; FLMUINT uiRow = *puiRow; FLMUINT uiElementCount = 0; // FLMBYTE * pucTmpAddr; FLMUINT uiTempAddress; FLMUINT iErrorCode=0; FLMUINT uiOption; eLFileType eLfType; FLMUINT uiBlkType = (FLMUINT)pBlkHdr->stdBlkHdr.ui8BlkType; FLMBYTE ucElmStatus [NUM_STATUS_BYTES]; STATE_INFO StateInfo; // FLMBOOL bStateInitialized = FALSE; // BLOCK_INFO BlockInfo; // FLMBYTE ucKeyBuffer [MAX_KEY_SIZ]; // LF_HDR LogicalFile; LF_HDR * pLogicalFile = NULL; // LFILE_p pLFile = NULL; // FLMUINT uiFixedDrn; FLMUINT16 * pui16OffsetArray; FLMUINT uiCurOffset; if (pBlkExp->uiLfNum == 0) { pBlkExp->uiLfNum = (FLMUINT)pBlkHdr->ui16LogicalFile; } pui16OffsetArray = (FLMUINT16 *)((FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr)); uiCurOffset = 0; // Setup the STATE variable for processing through the block (void)ViewGetDictInfo(); #if 0 if (gv_bViewHaveDictInfo) { F_Dict * pDict = gv_hViewDb->pDict; if (isContainerBlk( pBlkHdr)) { if (RC_OK( pDict->getContainer( pBlkExp->uiLfNum, &pLFile))) { f_memset( &LogicalFile, 0, sizeof( LF_HDR)); pLogicalFile = &LogicalFile; LogicalFile.pLFile = pLFile; } } else { IXD * pIxd; if (RC_OK( pDict->getIndex( pBlkExp->uiLfNum, &pLFile, &pIxd))) { f_memset( &LogicalFile, 0, sizeof( LF_HDR)); pLogicalFile = &LogicalFile; LogicalFile.pLFile = pLFile; LogicalFile.pIxd = pIxd; LogicalFile.pIfd = pIxd->pFirstIfd; } } } #endif eLfType = (pLogicalFile) ? pLogicalFile->eLfType : XFLM_LF_INVALID; #if 0 flmInitReadState( &StateInfo, &bStateInitialized, (FLMUINT)gv_ViewDbHdr.ui32DbVersion, (gv_bViewDbInitialized) ? gv_hViewDb : NULL, pLogicalFile, pBlkExp->uiLevel, uiBlkType, ucKeyBuffer); if ((iErrorCode = flmVerifyBlockHeader( &StateInfo, &BlockInfo, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, pBlkExp->uiNextAddr, pBlkExp->uiPrevAddr, (FLMBOOL)(StateInfo.pDb != NULL ? TRUE : FALSE))) != 0) { SetStatusBit( pucBlkStatus, iErrorCode); } #endif // Output each element in the block while (uiCurOffset <= (FLMUINT)(pBlkHdr->ui16NumKeys - 1)) { InitStatusBits( ucElmStatus); uiElementCount++; GetElmInfo( (FLMBYTE *)pBlkHdr + pui16OffsetArray[ uiCurOffset], pBlkHdr, &StateInfo); /* if ((iErrorCode = flmVerifyElement( &StateInfo, FO_DO_EXTENDED_DATA_CHECK)) != 0) { SetStatusBit( ucElmStatus, iErrorCode); } */ if (!bStatusOnlyFlag) { uiRow++; // Output the element number if (!ViewAddMenuItem( LBL_ELEMENT_NUMBER, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)uiElementCount, 0, 0, VIEW_INVALID_FILE_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, FLM_GREEN, FLM_WHITE, FLM_GREEN, FLM_WHITE)) { goto Exit; } // Output the element offset within the block if (!ViewAddMenuItem( LBL_ELEMENT_OFFSET, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmOffset, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the element length if (!ViewAddMenuItem( LBL_ELEMENT_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the child block address uiTempAddress = FB2UD( StateInfo.pucElm); if (uiTempAddress == 0) { uiOption = 0; } else { uiOption = BLK_OPTION_CHILD_BLOCK | StateInfo.uiElmOffset; } if (!ViewAddMenuItem( LBL_CHILD_BLOCK_ADDRESS, uiLabelWidth, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)uiTempAddress, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmOffset, 0, MOD_CHILD_BLK, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } // Display the counts info if any. if (uiBlkType == BT_NON_LEAF_COUNTS) { if (!ViewAddMenuItem( LBL_CHILD_REFERENCE_COUNT, uiLabelWidth, VAL_IS_NUMBER, (FLMUINT64) StateInfo.uiElmCounts, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmCountsOffset, 0, MOD_FLMUINT32 | MOD_DECIMAL, uiCol, uiRow++, uiOption, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } // Remember this item if we are searching #if 0 if (gv_bViewSearching && flmCompareKeys( StateInfo.pucCurKey, StateInfo.uiCurKeyLen, gv_ucViewSearchKey, gv_uiViewSearchKeyLen) >= 0 && !gv_pViewSearchItem) { gv_pViewSearchItem = gv_pViewMenuLastItem; } #endif if (iErrorCode != 0) { if (!OutputStatus( uiCol, &uiRow, uiBackColor, uiForeColor, uiLabelWidth, LBL_ELEMENT_STATUS, ucElmStatus)) { goto Exit; } } // Display the key length if (!ViewAddMenuItem( LBL_KEY_LENGTH, uiLabelWidth, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)StateInfo.uiElmKeyLen, 0, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmKeyLenOffset, 0, MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Output the key if (!OutputHexValue( uiCol, &uiRow, uiBackColor, uiForeColor, LBL_ELEMENT_KEY, FSGetFileNumber( StateInfo.ui32BlkAddress), FSGetFileOffset( StateInfo.ui32BlkAddress) + StateInfo.uiElmKeyOffset, StateInfo.pucElmKey, StateInfo.uiElmKeyLen, FALSE)) { goto Exit; } } // Go to the next element uiCurOffset++; OrStatusBits( pucBlkStatus, ucElmStatus); } if (!bStatusOnlyFlag) { *puiRow = uiRow; // If we were searching and did not find a key, set it on the // last key found if (gv_bViewSearching && !gv_pViewSearchItem) { gv_pViewSearchItem = gv_pViewMenuLastItem; while (gv_pViewSearchItem && gv_pViewSearchItem->iLabelIndex != LBL_CHILD_BLOCK_ADDRESS) { gv_pViewSearchItem = gv_pViewSearchItem->pPrevItem; } } } bOk = TRUE; Exit: #if 0 if (bStateInitialized && StateInfo.pRecord) { StateInfo.pRecord->release( &StateInfo.pRecord); } #endif return( bOk); } /*************************************************************************** Desc: This routine outputs a NON-LEAF block, including the block header. *****************************************************************************/ FLMBOOL ViewNonLeafBlk( FLMUINT uiReadAddress, FLMUINT uiBlkAddress, F_BLK_HDR ** ppBlkHdr, BLK_EXP_p pBlkExp ) { FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; F_BLK_HDR * pBlkHdr; FLMBYTE ucBlkStatus [NUM_STATUS_BYTES]; FLMUINT32 ui32CalcCRC; FLMUINT32 ui32BlkCRC; FLMUINT uiBytesRead; InitStatusBits( ucBlkStatus); // Read the block into memory if (!ViewBlkRead( uiReadAddress, ppBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, &ui32CalcCRC, &ui32BlkCRC, &uiBytesRead, TRUE)) { goto Exit; } pBlkHdr = *ppBlkHdr; ViewMenuInit( "NON-LEAF Block"); // Output the block header first uiRow = 0; uiCol = 5; pBlkExp->uiType = (FLMUINT)pBlkHdr->ui8BlkType; pBlkExp->uiBlkAddr = uiBlkAddress; OutputNonLeafElements( uiCol, &uiRow, (F_BTREE_BLK_HDR *)pBlkHdr, pBlkExp, ucBlkStatus, TRUE); if (!ViewOutBlkHdr( uiCol, &uiRow, pBlkHdr, pBlkExp, ucBlkStatus, ui32CalcCRC, ui32BlkCRC)) { goto Exit; } // Now output the non-leaf data if (!OutputNonLeafElements( uiCol, &uiRow, (F_BTREE_BLK_HDR *)pBlkHdr, pBlkExp, ucBlkStatus, FALSE)) { goto Exit; } bOk = TRUE; Exit: return( bOk); } /******************************************************************** Desc: View a block in HEX *********************************************************************/ void ViewHexBlock( FLMUINT uiReadAddress, F_BLK_HDR ** ppBlkHdr, FLMUINT uiViewLen ) { FLMBYTE * pucBlk; FLMUINT uiRow = 0; FLMUINT uiCol = 13; FLMUINT uiBytesPerLine = MAX_HORIZ_SIZE( uiCol); FLMUINT uiBytesProcessed = 0; FLMUINT uiNumBytes; FLMUINT uiFileOffset; FLMUINT uiFileNumber; char szTitle [80]; FLMUINT uiBytesRead; uiFileOffset = FSGetFileOffset( uiReadAddress); uiFileNumber = FSGetFileNumber( uiReadAddress); if (!ViewBlkRead( uiReadAddress, ppBlkHdr, uiViewLen, FALSE, NULL, NULL, &uiBytesRead, TRUE)) { return; } pucBlk = (FLMBYTE *)(*ppBlkHdr); f_sprintf( szTitle, "HEX DISPLAY OF BLOCK %08X", (unsigned)uiReadAddress); ViewMenuInit( szTitle); while (uiBytesProcessed < uiViewLen) { if ((uiNumBytes = uiViewLen - uiBytesProcessed) > uiBytesPerLine) { uiNumBytes = uiBytesPerLine; } // Output the line if (!ViewAddMenuItem( -1, 0, VAL_IS_BINARY_HEX, (FLMUINT)pucBlk, uiNumBytes, uiFileNumber, uiFileOffset, uiNumBytes, MOD_BINARY, uiCol, uiRow++, 0, FLM_BLACK, FLM_LIGHTGRAY, FLM_BLACK, FLM_LIGHTGRAY)) { return; } uiFileOffset += uiNumBytes; uiBytesProcessed += uiNumBytes; pucBlk += uiNumBytes; } } /*************************************************************************** Desc: This routine outputs a block in the database. Depending on the type of block, it will call a different routine to display the block. The routine then allows the user to press keys to navigate to other blocks in the database if desired. *****************************************************************************/ void ViewBlocks( FLMUINT uiReadAddress, FLMUINT uiBlkAddress, BLK_EXP_p pBlkExp ) { FLMUINT uiOption; VIEW_INFO SaveView; VIEW_INFO DummySave; FLMBOOL bRepaint = TRUE; F_BLK_HDR * pBlkHdr = NULL; FLMUINT uiBlkAddress2; BLK_EXP BlkExp2; FLMBOOL bSetExp = FALSE; FLMBOOL bViewHexFlag = FALSE; FLMUINT uiBytesRead; // Loop getting commands until hit the exit key if (!gv_bViewHdrRead) { ViewReadHdr(); } gv_pViewSearchItem = NULL; ViewReset( &SaveView); while (!gv_bViewPoppingStack) { // Display the type of block expected if (bRepaint) { if (bViewHexFlag) { ViewHexBlock( uiReadAddress, &pBlkHdr, (FLMUINT)gv_ViewDbHdr.ui16BlockSize); } else { Switch_Statement: switch (pBlkExp->uiType) { case BT_NON_LEAF_COUNTS: case BT_NON_LEAF: if (!ViewNonLeafBlk( uiReadAddress, uiBlkAddress, &pBlkHdr, pBlkExp)) { goto Exit; } break; case BT_LEAF: case BT_LEAF_DATA: if (!ViewLeafBlk( uiReadAddress, uiBlkAddress, &pBlkHdr, pBlkExp)) { goto Exit; } break; case BT_DATA_ONLY: if (!ViewDataBlk( uiReadAddress, uiBlkAddress, &pBlkHdr, pBlkExp)) { goto Exit; } break; case BT_FREE: if (!ViewAvailBlk( uiReadAddress, uiBlkAddress, &pBlkHdr, pBlkExp)) { goto Exit; } break; case BT_LFH_BLK: if (!ViewLFHBlk( uiReadAddress, uiBlkAddress, &pBlkHdr, pBlkExp)) { goto Exit; } break; case 0xFF: if (!ViewBlkRead( uiReadAddress, &pBlkHdr, TRUE, (FLMUINT)gv_ViewDbHdr.ui16BlockSize, NULL, NULL, &uiBytesRead, FALSE)) { goto Exit; } else { pBlkExp->uiType = (FLMUINT)pBlkHdr->ui8BlkType; goto Switch_Statement; } } } } // See what the user wants to do next. if (bSetExp && (pBlkExp->uiType == BT_LEAF || pBlkExp->uiType == BT_NON_LEAF_COUNTS || pBlkExp->uiType == BT_NON_LEAF || pBlkExp->uiType == BT_LEAF_DATA)) { pBlkExp->uiLfNum = (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile); pBlkExp->uiLevel = (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel); } bSetExp = FALSE; bRepaint = TRUE; if (gv_bViewSearching) { SetSearchTopBottom(); if (pBlkExp->uiType == BT_LEAF || pBlkExp->uiType == BT_LEAF_DATA || !gv_pViewSearchItem) { gv_bViewSearching = FALSE; ViewEnable(); uiOption = ViewGetMenuOption(); } else { uiOption = gv_pViewSearchItem->uiOption; } } else { ViewEnable(); uiOption = ViewGetMenuOption(); } switch (uiOption) { case ESCAPE_OPTION: goto Exit; case PREV_BLOCK_OPTION: ViewReset( &DummySave); pBlkExp->uiNextAddr = uiBlkAddress; pBlkExp->uiPrevAddr = 0xFFFFFFFF; uiReadAddress = uiBlkAddress = (FLMUINT)pBlkHdr->ui32PrevBlkInChain; break; case NEXT_BLOCK_OPTION: ViewReset( &DummySave); pBlkExp->uiNextAddr = 0xFFFFFFFF; pBlkExp->uiPrevAddr = uiBlkAddress; uiReadAddress = uiBlkAddress = (FLMUINT)pBlkHdr->ui32NextBlkInChain; break; case PREV_BLOCK_IMAGE_OPTION: f_memcpy( &BlkExp2, pBlkExp, sizeof( BLK_EXP)); BlkExp2.uiNextAddr = 0xFFFFFFFF; BlkExp2.uiPrevAddr = 0xFFFFFFFF; ViewBlocks( (FLMUINT)pBlkHdr->ui32PriorBlkImgAddr, uiBlkAddress, &BlkExp2); break; case GOTO_BLOCK_OPTION: if (GetBlockAddrType( &uiBlkAddress2)) { ViewReset( &DummySave); uiReadAddress = uiBlkAddress = uiBlkAddress2; pBlkExp->uiType = 0xFF; pBlkExp->uiLevel = 0xFF; pBlkExp->uiNextAddr = 0xFFFFFFFF; pBlkExp->uiPrevAddr = 0xFFFFFFFF; pBlkExp->uiLfNum = 0; bSetExp = TRUE; if (uiBlkAddress < 2048) { bViewHexFlag = TRUE; } } else { bRepaint = FALSE; } break; case EDIT_OPTION: case EDIT_RAW_OPTION: if (!ViewEdit( (uiOption == EDIT_OPTION) ? TRUE : FALSE)) { bRepaint = FALSE; } break; case HEX_OPTION: ViewDisable(); bViewHexFlag = !bViewHexFlag; break; case SEARCH_OPTION: switch (pBlkHdr->ui8BlkType) { case BT_NON_LEAF_COUNTS: case BT_NON_LEAF: case BT_LEAF_DATA: case BT_LEAF: gv_uiViewSearchLfNum = (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile); gv_uiViewSearchLfType = getBlkLfType( (F_BTREE_BLK_HDR *)pBlkHdr); if (ViewGetKey()) { gv_bViewPoppingStack = TRUE; } break; case BT_LFH_BLK: { VIEW_MENU_ITEM_p pMenuItem = gv_pViewMenuCurrItem; // Determine which logical file, if any we are pointing at while (pMenuItem && pMenuItem->iLabelIndex != LBL_LOGICAL_FILE_NAME) { pMenuItem = pMenuItem->pPrevItem; } if (pMenuItem) { while (pMenuItem && pMenuItem->iLabelIndex != LBL_LOGICAL_FILE_NUMBER) { pMenuItem = pMenuItem->pNextItem; } } if (pMenuItem) { FLMBYTE * pszTmp; gv_uiViewSearchLfNum = (FLMUINT)pMenuItem->ui64Value; while (pMenuItem && pMenuItem->iLabelIndex != LBL_LOGICAL_FILE_TYPE) { pMenuItem = pMenuItem->pNextItem; } pszTmp = (FLMBYTE *)((FLMUINT)pMenuItem->ui64Value); if (f_stricmp( (const char *)pszTmp, COLLECTION_STRING) == 0) { gv_uiViewSearchLfType = XFLM_LF_COLLECTION; } else { gv_uiViewSearchLfType = XFLM_LF_INDEX; } if (ViewGetKey()) { gv_bViewPoppingStack = TRUE; } } else { ViewShowError( "Position cursor to a logical file before searching"); } } break; default: ViewShowError( "This block does not belong to a logical file - cannot search"); break; } break; default: if (uiOption & LOGICAL_INDEX_OPTION) { ViewLogicalFile( (FLMUINT)(uiOption & (~(LOGICAL_INDEX_OPTION))), XFLM_LF_INDEX); } else if (uiOption & LOGICAL_CONTAINER_OPTION) { ViewLogicalFile( (FLMUINT)(uiOption & (~(LOGICAL_CONTAINER_OPTION))), XFLM_LF_COLLECTION); } else if (uiOption & LFH_OPTION_ROOT_BLOCK) { F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR); pLfHdr += (uiOption & 0xFFFF); BlkExp2.uiLevel = 0xFF; BlkExp2.uiType = 0xFF; uiBlkAddress2 = (FLMUINT)pLfHdr->ui32RootBlkAddr; BlkExp2.uiLfNum = (FLMUINT)pLfHdr->ui32LfNumber; BlkExp2.uiNextAddr = BlkExp2.uiPrevAddr = 0; ViewBlocks( uiBlkAddress2, uiBlkAddress2, &BlkExp2); } else if (uiOption & BLK_OPTION_CHILD_BLOCK) { uiBlkAddress2 = (FLMUINT)gv_pViewMenuCurrItem->ui64Value; f_memcpy( &BlkExp2, pBlkExp, sizeof( BLK_EXP)); BlkExp2.uiNextAddr = 0xFFFFFFFF; BlkExp2.uiPrevAddr = 0xFFFFFFFF; BlkExp2.uiLevel = 0xFF; BlkExp2.uiType = 0xFF; ViewBlocks( uiBlkAddress2, uiBlkAddress2, &BlkExp2); } else if (uiOption & BLK_OPTION_DATA_BLOCK) { uiBlkAddress2 = (FLMUINT)gv_pViewMenuCurrItem->ui64Value; f_memcpy( &BlkExp2, pBlkExp, sizeof( BLK_EXP)); BlkExp2.uiNextAddr = 0xFFFFFFFF; BlkExp2.uiPrevAddr = 0xFFFFFFFF; BlkExp2.uiLevel = 0xFF; BlkExp2.uiType = 0xFF; ViewBlocks( uiBlkAddress2, uiBlkAddress2, &BlkExp2); } else { bRepaint = FALSE; } break; } } Exit: f_free( &pBlkHdr); ViewRestore( &SaveView); } /******************************************************************** Desc: Have user enter a block address. *********************************************************************/ FLMBOOL GetBlockAddrType( FLMUINT * puiBlkAddress ) { FLMBOOL bGotAddress = FALSE; FLMBOOL bBadDigit; char szTempBuf [20]; FLMUINT uiLoop; FLMUINT uiChar; FLMUINT uiNumCols; FLMUINT uiNumRows; f_conGetScreenSize( &uiNumCols, &uiNumRows); // Get the block address for (;;) { bBadDigit = FALSE; f_conSetBackFore( FLM_BLACK, FLM_WHITE); f_conClearScreen( 0, uiNumRows - 2); ViewAskInput( "Enter Block Address (in hex): ", szTempBuf, sizeof( szTempBuf)); if (f_stricmp( szTempBuf, "\\") == 0 || !szTempBuf [0]) { break; } uiLoop = 0; *puiBlkAddress = 0; while (szTempBuf [uiLoop] && uiLoop < 8) { (*puiBlkAddress) <<= 4; uiChar = (FLMUINT)szTempBuf [uiLoop]; if (uiChar >= '0' && uiChar <= '9') { (*puiBlkAddress) += (FLMUINT)(uiChar - '0'); } else if (uiChar >= 'a' && uiChar <= 'f') { (*puiBlkAddress) += (FLMUINT)(uiChar - 'a' + 10); } else if (uiChar >= 'A' && uiChar<= 'F') { (*puiBlkAddress) += (FLMUINT)(uiChar - 'A' + 10); } else { bBadDigit = TRUE; break; } uiLoop++; } if (bBadDigit) { ViewShowError( "Illegal digit in number - must be hex digits"); } else if (szTempBuf [uiLoop]) { ViewShowError( "Too many characters in number"); } else { bGotAddress = TRUE; break; } } f_conClearScreen( 0, uiNumRows - 2); ViewEscPrompt(); return( bGotAddress); } /******************************************************************** Desc: ? *********************************************************************/ FSTATIC void SetSearchTopBottom( void) { if (!gv_pViewSearchItem) { gv_pViewMenuCurrItem = NULL; gv_uiViewMenuCurrItemNum = 0; gv_uiViewCurrFileOffset = 0; gv_uiViewCurrFileNumber = 0; gv_uiViewTopRow = 0; } else { gv_pViewMenuCurrItem = gv_pViewSearchItem; gv_uiViewMenuCurrItemNum = gv_pViewSearchItem->uiItemNum; gv_uiViewCurrFileOffset = gv_pViewSearchItem->uiModFileOffset; gv_uiViewCurrFileNumber = gv_pViewSearchItem->uiModFileNumber; gv_uiViewTopRow = gv_pViewSearchItem->uiRow; if (gv_uiViewTopRow < LINES_PER_PAGE / 2) { gv_uiViewTopRow = 0; } else { gv_uiViewTopRow -= (LINES_PER_PAGE / 2); } } gv_uiViewBottomRow = gv_uiViewTopRow + LINES_PER_PAGE - 1; if (gv_uiViewBottomRow > gv_pViewMenuLastItem->uiRow) { gv_uiViewBottomRow = gv_pViewMenuLastItem->uiRow; if (gv_uiViewBottomRow < LINES_PER_PAGE) { gv_uiViewTopRow = 0; } else { gv_uiViewTopRow = gv_uiViewBottomRow - LINES_PER_PAGE + 1; } } } /*============================================================================= Desc: =============================================================================*/ FSTATIC void GetElmInfo( FLMBYTE * pucEntry, F_BTREE_BLK_HDR * pBlkHdr, STATE_INFO * pStateInfo ) { F_NodeVerifier * pNodeVerifier = pStateInfo->pNodeVerifier; f_memset( pStateInfo, 0, sizeof(STATE_INFO)); pStateInfo->pNodeVerifier = pNodeVerifier; pStateInfo->pBlkHdr = (F_BLK_HDR *)pBlkHdr; pStateInfo->ui32BlkAddress = pBlkHdr->stdBlkHdr.ui32BlkAddr; pStateInfo->pucElm = pucEntry; pStateInfo->uiElmOffset = (FLMUINT)pucEntry - (FLMUINT)pBlkHdr; switch ( pBlkHdr->stdBlkHdr.ui8BlkType) { // Leaf node - no data. Only element and key. case BT_LEAF: { pStateInfo->uiElmKeyLenOffset = pStateInfo->uiElmOffset; // No flags. pStateInfo->uiElmKeyLen = FB2UW( pucEntry); pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 2; // 2 byte key len pStateInfo->uiElmKeyOffset = pStateInfo->uiElmOffset + 2; // ditto pStateInfo->pucElmKey = &pucEntry[ 2]; // Key break; } // Leaf node with data case BT_LEAF_DATA: { FLMBYTE ucFlag; FLMBYTE * pucTmp; FLMUINT uiElmLen; pucTmp = &pucEntry[ 1]; ucFlag = pucEntry[ 0]; uiElmLen = 1; // Flag pStateInfo->uiElmKeyLenOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; if (bteKeyLenFlag( pucEntry)) { uiElmLen += 2; pStateInfo->uiElmKeyLen = FB2UW( pucTmp); uiElmLen += pStateInfo->uiElmKeyLen; pucTmp += 2; } else { uiElmLen++; pStateInfo->uiElmKeyLen = *pucTmp; uiElmLen += pStateInfo->uiElmKeyLen; pucTmp++; } pStateInfo->uiElmDataLenOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; if (bteDataLenFlag( pucEntry)) { uiElmLen += 2; pStateInfo->uiElmDataLen = FB2UW( pucTmp); uiElmLen += pStateInfo->uiElmDataLen; pucTmp += 2; } else { uiElmLen++; pStateInfo->uiElmDataLen = *pucTmp; uiElmLen += pStateInfo->uiElmDataLen; pucTmp++; } if (bteOADataLenFlag( pucEntry)) { pStateInfo->uiElmOADataLen = FB2UD( pucTmp); pStateInfo->uiElmOADataLenOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; uiElmLen += 4; // Add the OA Data Length pucTmp += 4; // Skip over the OA Data Length } // Save the key pointer... pStateInfo->pucElmKey = pucTmp; pStateInfo->uiElmKeyOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // Now the data pucTmp += pStateInfo->uiElmKeyLen; pStateInfo->pucElmData = pucTmp; pStateInfo->uiElmDataOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // The element length pStateInfo->uiElmLen = uiElmLen; break; } case BT_NON_LEAF: { FLMBYTE * pucTmp = pucEntry; // Skip over the child block address for now. pucTmp += 4; // Get the key length & offset pStateInfo->uiElmKeyLen = FB2UW( pucTmp); pStateInfo->uiElmKeyLenOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // Get the key and offset pucTmp += 2; pStateInfo->pucElmKey = pucTmp; pStateInfo->uiElmKeyOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // The element length pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 6; break; } case BT_NON_LEAF_COUNTS: { FLMBYTE * pucTmp = pucEntry; // Skip over the child block address for now. pucTmp += 4; // Get the counts and offset pStateInfo->uiElmCounts = FB2UD( pucTmp); pStateInfo->uiElmCountsOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // Get the key length & offset pucTmp += 4; pStateInfo->uiElmKeyLen = FB2UW( pucTmp); pStateInfo->uiElmKeyLenOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // Get the key and offset pucTmp += 2; pStateInfo->pucElmKey = pucTmp; pStateInfo->uiElmKeyOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; // The element length pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 10; break; } case BT_DATA_ONLY: { FLMBYTE * pucTmp = pucEntry; if (!pBlkHdr->stdBlkHdr.ui32PrevBlkInChain) { pStateInfo->uiElmKeyLen = (FLMUINT)FB2UW( pucTmp); pStateInfo->uiElmKeyLenOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; pucTmp += 2; pStateInfo->pucElmKey = pucTmp; pStateInfo->uiElmKeyOffset = (FLMUINT)pucTmp - (FLMUINT)pBlkHdr; pucTmp += pStateInfo->uiElmKeyLen; } else { pStateInfo->uiElmKeyLen = 0; } pStateInfo->uiElmLen = gv_ViewDbHdr.ui16BlockSize - pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; pStateInfo->uiElmDataLen = pStateInfo->uiElmLen - pStateInfo->uiElmKeyLen; pStateInfo->pucElmData = pucTmp + (pStateInfo->uiElmKeyLen ? pStateInfo->uiElmKeyLen + 2 : 0); } } } libxflaim-5.1.969/util/viewhdr.cpp0000644000175000017500000005116210511001742020373 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This file contains the routines which display database header // information. // // Tabs: 3 // // Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: viewhdr.cpp 3119 2006-01-19 13:39:12 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "view.h" // Menu items #define DB_HDR_MENU_AVAIL_BLOCK 1 #define DB_HDR_MENU_LFH_BLOCK 2 // Local Function Prototypes FSTATIC void viewFormatSerialNum( char * pszBuf, FLMBYTE * pucSerialNum); FSTATIC void viewFormatDBKey( char * pszBuf, FLMBYTE * pucDBKey); FSTATIC FLMBOOL ViewSetupDbHdrMenu( void); /*************************************************************************** Desc: Format a serial number for display. *****************************************************************************/ FSTATIC void viewFormatSerialNum( char * pszBuf, FLMBYTE * pucSerialNum ) { f_sprintf( pszBuf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", (unsigned)pucSerialNum[ 0], (unsigned)pucSerialNum[ 1], (unsigned)pucSerialNum[ 2], (unsigned)pucSerialNum[ 3], (unsigned)pucSerialNum[ 4], (unsigned)pucSerialNum[ 5], (unsigned)pucSerialNum[ 6], (unsigned)pucSerialNum[ 7], (unsigned)pucSerialNum[ 8], (unsigned)pucSerialNum[ 9], (unsigned)pucSerialNum[ 10], (unsigned)pucSerialNum[ 11], (unsigned)pucSerialNum[ 12], (unsigned)pucSerialNum[ 13], (unsigned)pucSerialNum[ 14], (unsigned)pucSerialNum[ 15]); } /*************************************************************************** Desc: Format a DB Key. *****************************************************************************/ FSTATIC void viewFormatDBKey( char * pszBuf, FLMBYTE * pucDBKey ) { f_sprintf( pszBuf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x...", (unsigned)pucDBKey[ 0], (unsigned)pucDBKey[ 1], (unsigned)pucDBKey[ 2], (unsigned)pucDBKey[ 3], (unsigned)pucDBKey[ 4], (unsigned)pucDBKey[ 5], (unsigned)pucDBKey[ 6], (unsigned)pucDBKey[ 7], (unsigned)pucDBKey[ 8], (unsigned)pucDBKey[ 9], (unsigned)pucDBKey[ 10], (unsigned)pucDBKey[ 11], (unsigned)pucDBKey[ 12], (unsigned)pucDBKey[ 13], (unsigned)pucDBKey[ 14], (unsigned)pucDBKey[ 15]); } /*************************************************************************** Desc: This routine displays the information found in a log header and sets up the menu for the log header display. *****************************************************************************/ FSTATIC FLMBOOL ViewSetupDbHdrMenu( void) { #define LABEL_WIDTH 35 FLMBOOL bOk = FALSE; FLMUINT uiRow; FLMUINT uiCol; eColorType uiBackColor = FLM_BLACK; eColorType uiForeColor = FLM_LIGHTGRAY; eColorType uiUnselectBackColor = FLM_BLACK; eColorType uiUnselectForeColor = FLM_WHITE; eColorType uiSelectBackColor = FLM_BLUE; eColorType uiSelectForeColor = FLM_WHITE; FLMUINT uiOption; char szBuf [64]; FLMUINT32 ui32CalcCRC; // Re-read the header information in case it has changed. ViewReadHdr( &ui32CalcCRC); ViewMenuInit( "DB Header"); uiRow = 0; uiCol = 5; // Display signature if (!ViewAddMenuItem( LBL_SIGNATURE, LABEL_WIDTH, VAL_IS_TEXT_PTR, (FLMUINT64)((FLMUINT)(&gv_ViewDbHdr.szSignature[ 0])), 0, 0, XFLM_DB_HDR_szSignature_OFFSET, sizeof( gv_ViewDbHdr.szSignature), MOD_TEXT, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display whether this is little-endian or not. if (!ViewAddMenuItem( LBL_LITTLE_ENDIAN, LABEL_WIDTH, VAL_IS_LABEL_INDEX, gv_ViewDbHdr.ui8IsLittleEndian ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO, 0, 0, XFLM_DB_HDR_ui8IsLittleEndian_OFFSET, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the default language f_languageToStr( (FLMUINT)gv_ViewDbHdr.ui8DefaultLanguage, szBuf); if (!ViewAddMenuItem( LBL_DEFAULT_LANGUAGE, LABEL_WIDTH, VAL_IS_TEXT_PTR, (FLMUINT64)((FLMUINT)(&szBuf[ 0])), 0, 0, XFLM_DB_HDR_ui8DefaultLanguage_OFFSET, 0, MOD_LANGUAGE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the database block size if (!ViewAddMenuItem( LBL_BLOCK_SIZE, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)(gv_ViewDbHdr.ui16BlockSize), 0, 0, XFLM_DB_HDR_ui16BlockSize_OFFSET, 0, MOD_FLMUINT16 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the version number if (!ViewAddMenuItem( LBL_FLAIM_VERSION, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32DbVersion, 0, 0, XFLM_DB_HDR_ui32DbVersion_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display whether block checksumming is enabled. if (!ViewAddMenuItem( LBL_BLK_CRC_ENABLED, LABEL_WIDTH, VAL_IS_LABEL_INDEX, gv_ViewDbHdr.ui8BlkChkSummingEnabled ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO, 0, 0, XFLM_DB_HDR_ui8BlkChkSummingEnabled_OFFSET, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the keep-RFL-files flag. if (!ViewAddMenuItem( LBL_KEEP_RFL_FILES, LABEL_WIDTH, VAL_IS_LABEL_INDEX, gv_ViewDbHdr.ui8RflKeepFiles ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO, 0, 0, XFLM_DB_HDR_ui8RflKeepFiles_OFFSET, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the auto turn off aborted transactions flag. if (!ViewAddMenuItem( LBL_AUTO_TURN_OFF_KEEP_RFL, LABEL_WIDTH, VAL_IS_LABEL_INDEX, gv_ViewDbHdr.ui8RflAutoTurnOffKeep ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO, 0, 0, XFLM_DB_HDR_ui8RflAutoTurnOffKeep_OFFSET, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the keep aborted transactions flag. if (!ViewAddMenuItem( LBL_KEEP_ABORTED_TRANS_IN_RFL_FILES, LABEL_WIDTH, VAL_IS_LABEL_INDEX, gv_ViewDbHdr.ui8RflKeepAbortedTrans ? (FLMUINT64)LBL_YES : (FLMUINT64)LBL_NO, 0, 0, XFLM_DB_HDR_ui8RflKeepAbortedTrans_OFFSET, 0, MOD_FLMBYTE | MOD_DECIMAL, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the current roll-forward log file number if (!ViewAddMenuItem( LBL_RFL_FILE_NUM, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RflCurrFileNum, 0, 0, XFLM_DB_HDR_ui32RflCurrFileNum_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last committed transaction ID if (!ViewAddMenuItem( LBL_LAST_RFL_COMMIT_ID, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, gv_ViewDbHdr.ui64LastRflCommitID, 0, 0, XFLM_DB_HDR_ui64LastRflCommitID_OFFSET, 0, MOD_FLMUINT64 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last RFL file that was deleted. if (!ViewAddMenuItem( LBL_LAST_RFL_FILE_DELETED, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RflLastFileNumDeleted, 0, 0, XFLM_DB_HDR_ui32RflLastFileNumDeleted_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last transaction offset in the roll forward log file. if (!ViewAddMenuItem( LBL_RFL_LAST_TRANS_OFFSET, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RflLastTransOffset, 0, 0, XFLM_DB_HDR_ui32RflLastTransOffset_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last checkpoint roll-forward log file number. if (!ViewAddMenuItem( LBL_RFL_LAST_CP_FILE_NUM, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RflLastCPFileNum, 0, 0, XFLM_DB_HDR_ui32RflLastCPFileNum_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last checkpoint roll-forward log file offset. if (!ViewAddMenuItem( LBL_RFL_LAST_CP_OFFSET, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RflLastCPOffset, 0, 0, XFLM_DB_HDR_ui32RflLastCPOffset_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the transaction ID of the last checkpoint that was done. if (!ViewAddMenuItem( LBL_LAST_CP_ID, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, gv_ViewDbHdr.ui64RflLastCPTransID, 0, 0, XFLM_DB_HDR_ui64RflLastCPTransID_OFFSET, 0, MOD_FLMUINT64 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the minumum roll-forward log file size. if (!ViewAddMenuItem( LBL_RFL_MIN_FILE_SIZE, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL_HEX, (FLMUINT64)gv_ViewDbHdr.ui32RflMinFileSize, 0, 0, XFLM_DB_HDR_ui32RflMinFileSize_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the maximum roll-forward log file size. if (!ViewAddMenuItem( LBL_RFL_MAX_FILE_SIZE, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL_HEX, (FLMUINT64)gv_ViewDbHdr.ui32RflMaxFileSize, 0, 0, XFLM_DB_HDR_ui32RflMaxFileSize_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the current transaction ID if (!ViewAddMenuItem( LBL_CURRENT_TRANS_ID, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, gv_ViewDbHdr.ui64CurrTransID, 0, 0, XFLM_DB_HDR_ui64CurrTransID_OFFSET, 0, MOD_FLMUINT64 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the commit count if (!ViewAddMenuItem( LBL_COMMIT_COUNT, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, gv_ViewDbHdr.ui64TransCommitCnt, 0, 0, XFLM_DB_HDR_ui64TransCommitCnt_OFFSET, 0, MOD_FLMUINT64 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the end of log address if (!ViewAddMenuItem( LBL_END_OF_LOG_ADDRESS, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RblEOF, 0, 0, XFLM_DB_HDR_ui32RblEOF_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the first checkpoint block address. if (!ViewAddMenuItem( LBL_FIRST_CP_BLK_ADDR, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32RblFirstCPBlkAddr, 0, 0, XFLM_DB_HDR_ui32RblFirstCPBlkAddr_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the first avail block address if (gv_ViewDbHdr.ui32FirstAvailBlkAddr == 0) { uiOption = 0; } else { uiOption = DB_HDR_MENU_AVAIL_BLOCK; } if (!ViewAddMenuItem( LBL_FIRST_AVAIL_BLOCK_ADDRESS, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32FirstAvailBlkAddr, 0, 0, XFLM_DB_HDR_ui32FirstAvailBlkAddr_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } // Display the first LFH block address if (gv_ViewDbHdr.ui32FirstLFBlkAddr == 0) { uiOption = 0; } else { uiOption = DB_HDR_MENU_LFH_BLOCK; } if (!ViewAddMenuItem( LBL_FIRST_LFH_BLOCK_ADDRESS, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32FirstLFBlkAddr, 0, 0, XFLM_DB_HDR_ui32FirstLFBlkAddr_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, uiOption, (!uiOption ? uiBackColor : uiUnselectBackColor), (!uiOption ? uiForeColor : uiUnselectForeColor), (!uiOption ? uiBackColor : uiSelectBackColor), (!uiOption ? uiForeColor : uiSelectForeColor))) { goto Exit; } // Display the logical end of file address if (!ViewAddMenuItem( LBL_LOGICAL_END_OF_FILE, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32LogicalEOF, 0, 0, XFLM_DB_HDR_ui32LogicalEOF_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } if (!ViewAddMenuItem( LBL_MAX_FILE_SIZE, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32MaxFileSize, 0, 0, XFLM_DB_HDR_ui32MaxFileSize_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last backup transaction ID if (!ViewAddMenuItem( LBL_LAST_BACKUP_TRANS_ID, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, gv_ViewDbHdr.ui64LastBackupTransID, 0, 0, XFLM_DB_HDR_ui64LastBackupTransID_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the incremental backup sequence number if (!ViewAddMenuItem( LBL_INC_BACKUP_SEQ_NUM, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32IncBackupSeqNum, 0, 0, XFLM_DB_HDR_ui32IncBackupSeqNum_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the number of blocks changed since last backup if (!ViewAddMenuItem( LBL_BLK_CHG_SINCE_BACKUP, LABEL_WIDTH, VAL_IS_NUMBER | DISP_HEX_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32BlksChangedSinceBackup, 0, 0, XFLM_DB_HDR_ui32BlksChangedSinceBackup_OFFSET, 0, MOD_FLMUINT32 | MOD_HEX | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } uiRow++; // Display the database serial number viewFormatSerialNum( szBuf, gv_ViewDbHdr.ucDbSerialNum); if (!ViewAddMenuItem( LBL_DB_SERIAL_NUM, LABEL_WIDTH, VAL_IS_TEXT_PTR, ((FLMUINT64)(FLMUINT)&szBuf[ 0]), f_strlen( szBuf), 0, XFLM_DB_HDR_ucDbSerialNum_OFFSET, 0, 0, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the last RFL transaction serial number viewFormatSerialNum( szBuf, gv_ViewDbHdr.ucLastTransRflSerialNum); if (!ViewAddMenuItem( LBL_LAST_TRANS_RFL_SERIAL_NUM, LABEL_WIDTH, VAL_IS_TEXT_PTR, ((FLMUINT64)(FLMUINT)&szBuf[ 0]), f_strlen( szBuf), 0, XFLM_DB_HDR_ucLastTransRflSerialNum_OFFSET, 0, 0, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the next RFL serial number viewFormatSerialNum( szBuf, gv_ViewDbHdr.ucNextRflSerialNum); if (!ViewAddMenuItem( LBL_RFL_NEXT_SERIAL_NUM, LABEL_WIDTH, VAL_IS_TEXT_PTR, ((FLMUINT64)(FLMUINT)&szBuf[ 0]), f_strlen( szBuf), 0, XFLM_DB_HDR_ucNextRflSerialNum_OFFSET, 0, 0, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } viewFormatSerialNum( szBuf, gv_ViewDbHdr.ucIncBackupSerialNum); if (!ViewAddMenuItem( LBL_INC_BACKUP_SERIAL_NUM, LABEL_WIDTH, VAL_IS_TEXT_PTR, ((FLMUINT64)(FLMUINT)&szBuf[ 0]), f_strlen( szBuf), 0, XFLM_DB_HDR_ucIncBackupSerialNum_OFFSET, 0, 0, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } uiRow++; // Display the datbase key length if (!ViewAddMenuItem( LBL_HDR_KEY_LEN, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32DbKeyLen, 0, 0, XFLM_DB_HDR_ui32DbKeyLen, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } if( gv_ViewDbHdr.ui32DbKeyLen) { // Display the datbase key viewFormatDBKey( szBuf, gv_ViewDbHdr.DbKey); if (!ViewAddMenuItem( LBL_HDR_DB_KEY, LABEL_WIDTH, VAL_IS_TEXT_PTR, ((FLMUINT64)(FLMUINT)&szBuf[ 0]), f_strlen( szBuf), 0, XFLM_DB_HDR_ucDbSerialNum_OFFSET, 0, 0, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } } uiRow++; // Display the database header CRC if (!ViewAddMenuItem( LBL_HDR_CRC, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)gv_ViewDbHdr.ui32HdrCRC, 0, 0, XFLM_DB_HDR_ui32HdrCRC_OFFSET, 0, MOD_FLMUINT32 | MOD_DECIMAL | MOD_NATIVE, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } // Display the database header CALCULATED CRC if (!ViewAddMenuItem( LBL_CALC_HDR_CRC, LABEL_WIDTH, VAL_IS_NUMBER | DISP_DECIMAL, (FLMUINT64)ui32CalcCRC, 0, 0, XFLM_DB_HDR_ui32HdrCRC_OFFSET, 0, MOD_DISABLED, uiCol, uiRow++, 0, uiBackColor, uiForeColor, uiBackColor, uiForeColor)) { goto Exit; } bOk = TRUE; Exit: return( bOk); } /*************************************************************************** Desc: This routine sets up the log header menu and then allows a user to press keys while in the menu. *****************************************************************************/ void ViewDbHeader( void) { FLMUINT uiOption; VIEW_INFO SaveView; FLMBOOL bRepaint = TRUE; BLK_EXP BlkExp; FLMUINT uiBlkAddress; FLMBOOL bViewHexFlag = FALSE; F_BLK_HDR * pBlkHdr = NULL; // Loop getting commands until the ESC key is pressed ViewReset( &SaveView); for (;;) { if (gv_bViewPoppingStack) { ViewSearch(); } if (bRepaint) { if (bViewHexFlag) { ViewHexBlock( 0, &pBlkHdr, sizeof( XFLM_DB_HDR)); } else { if (!ViewSetupDbHdrMenu()) { goto Exit; } } } bRepaint = TRUE; ViewEnable(); uiOption = ViewGetMenuOption(); switch (uiOption) { case ESCAPE_OPTION: goto Exit; case DB_HDR_MENU_AVAIL_BLOCK: BlkExp.uiType = BT_FREE; BlkExp.uiPrevAddr = 0xFFFFFFFF; BlkExp.uiNextAddr = 0xFFFFFFFF; uiBlkAddress = (FLMUINT)gv_ViewDbHdr.ui32FirstAvailBlkAddr; ViewBlocks( uiBlkAddress, uiBlkAddress, &BlkExp); break; case DB_HDR_MENU_LFH_BLOCK: BlkExp.uiType = BT_LFH_BLK; BlkExp.uiPrevAddr = 0xFFFFFFFF; BlkExp.uiNextAddr = 0xFFFFFFFF; uiBlkAddress = (FLMUINT)gv_ViewDbHdr.ui32FirstLFBlkAddr; ViewBlocks( uiBlkAddress, uiBlkAddress, &BlkExp); break; case SEARCH_OPTION: gv_uiViewSearchLfNum = XFLM_DATA_COLLECTION; gv_uiViewSearchLfType = XFLM_LF_COLLECTION; if (ViewGetKey()) { ViewSearch(); } break; case GOTO_BLOCK_OPTION: if (GetBlockAddrType( &uiBlkAddress)) { BlkExp.uiType = 0xFF; BlkExp.uiLevel = 0xFF; BlkExp.uiNextAddr = 0xFFFFFFFF; BlkExp.uiPrevAddr = 0xFFFFFFFF; BlkExp.uiLfNum = 0; ViewBlocks( uiBlkAddress, uiBlkAddress, &BlkExp); } else { bRepaint = FALSE; } break; case EDIT_OPTION: case EDIT_RAW_OPTION: if (!ViewEdit( uiOption == EDIT_OPTION ? TRUE : FALSE)) { bRepaint = FALSE; } break; case HEX_OPTION: ViewDisable(); bViewHexFlag = !bViewHexFlag; break; default: bRepaint = FALSE; break; } } Exit: f_free( &pBlkHdr); ViewRestore( &SaveView); } libxflaim-5.1.969/util/flm_dlst.h0000644000175000017500000000656410511001742020202 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: FLAIM's dynamic, interactive list manager // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: flm_dlst.h 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #ifndef FLM_DLST_HPP #define FLM_DLST_HPP class F_DynaList; class F_DynamicList; #ifdef FLM_NLM #define DLIST_DUMPFILE_PATH (FLMBYTE*)"sys:/system/dlstdump.txt" #else #define DLIST_DUMPFILE_PATH (FLMBYTE*)"dlstdump.txt" #endif typedef RCODE (* F_DLIST_DISP_HOOK)( FTX_WINDOW * pWin, FLMBOOL bSelected, FLMUINT uiRow, FLMUINT uiKey, void * pvData, FLMUINT uiDataLen, F_DynamicList* pDynamicList); RCODE dlistDefaultDisplayHook( FTX_WINDOW * pWin, FLMBOOL bSelected, FLMUINT uiRow, FLMUINT uiKey, void * pvData, FLMUINT uiDataLen, F_DynamicList* pDynamicList); /* Types, enums, etc. */ typedef struct dlist_node { FLMUINT uiKey; dlist_node * pPrev; dlist_node * pNext; void * pvData; FLMUINT uiDataLen; F_DLIST_DISP_HOOK pDispHook; } DLIST_NODE; /* Class definitions */ #ifdef __cplusplus class F_DynamicList : public F_Object { private: /* Data */ DLIST_NODE * m_pFirst; DLIST_NODE * m_pLast; DLIST_NODE * m_pCur; FTX_WINDOW * m_pListWin; FLMUINT m_uiListRows; FLMUINT m_uiListCols; FLMUINT m_uiRow; FLMBOOL m_bChanged; FLMBOOL m_bShowHorizontalSelector; /* Methods */ DLIST_NODE * getNode( FLMUINT uiKey); void freeNode( DLIST_NODE * pNode); public: /* Methods */ F_DynamicList( void); ~F_DynamicList( void); RCODE setup( FTX_WINDOW * pInitializedWindow); void refresh( void); RCODE insert( FLMUINT uiKey, F_DLIST_DISP_HOOK pDisplayHook, void * pvData, FLMUINT uiDataLen); RCODE update( FLMUINT uiKey, F_DLIST_DISP_HOOK pDisplayHook, void * pvData, FLMUINT uiDataLen); RCODE remove( FLMUINT uiKey); FINLINE DLIST_NODE * getFirst( void) { return( m_pFirst); } FINLINE DLIST_NODE * getCurrent( void) { return( m_pCur); } FINLINE FTX_WINDOW * getListWin( void) { return( m_pListWin); } void defaultKeyAction( FLMUINT uiKey); void setShowHorizontalSelector( FLMBOOL bShow) { m_bShowHorizontalSelector = bShow; m_bChanged = TRUE; } FLMBOOL getShowHorizontalSelector() { return m_bShowHorizontalSelector;} RCODE dumpToFile(); /* Navigational Methods */ void cursorUp( void); void cursorDown( void); void pageUp( void); void pageDown( void); void home( void); void end( void); }; #endif // __cplusplus #endif // FLM_EDIT_HPP libxflaim-5.1.969/util/basictestsrv.cpp0000644000175000017500000005231310511001742021436 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: This is the main implementation of BasicTest component of our unit // test suite. It handles basic operations such as: creating a db, // deleting a db, backup and restore and add and remove nodes. // // Tabs: 3 // // Copyright (c) 2003-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: basictestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "flmunittest.h" #if defined( FLM_NLM) #define DB_NAME_STR "SYS:\\TST.DB" #define BACKUP_NAME_STR "SYS:\\TST.BAK" #define NEW_NAME_STR "SYS:\\NEW.DB" #else #define DB_NAME_STR "tst.db" #define BACKUP_NAME_STR "tst.bak" #define NEW_NAME_STR "new.db" #endif #define BACKUP_PASSWORD_STR "password" /**************************************************************************** Desc: ****************************************************************************/ class IFlmTestImpl : public TestBase { public: const char * getName( void); RCODE execute( void); }; /**************************************************************************** Desc: ****************************************************************************/ RCODE getTest( IFlmTest ** ppTest) { RCODE rc = NE_XFLM_OK; if( (*ppTest = f_new IFlmTestImpl) == NULL) { rc = NE_XFLM_MEM; goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ const char * IFlmTestImpl::getName() { return( "Basic Test"); } /**************************************************************************** Desc: ****************************************************************************/ RCODE IFlmTestImpl::execute() { RCODE rc = NE_XFLM_OK; char szMsgBuf[ 64]; FLMUINT uiTmp = 0; FLMUINT64 ui64Tmp; FLMUINT uiLoop; FLMUINT uiHighNode; FLMUINT uiDefNum; FLMUINT uiDefNum1; FLMUINT uiDefNum2; FLMUINT uiDefNum3; FLMUINT uiLargeTextSize; IF_Backup * pBackup = NULL; IF_DOMNode * pDoc = NULL; IF_DOMNode * pRootElement = NULL; IF_DOMNode * pUniqueElement = NULL; IF_DOMNode * pNonUniqueElement = NULL; IF_DOMNode * pElement = NULL; IF_DOMNode * pData = NULL; IF_DOMNode * pAttr = NULL; IF_DOMNode * pTmpNode = NULL; FLMBYTE * pucLargeBuf = NULL; FLMBOOL bStartedTrans = FALSE; FLMBOOL bDibCreated = FALSE; beginTest( "Character conversion test", "Test character conversions from UTF8, Unicode, and Native", "", ""); endTest("PASS"); beginTest( "dbCreate", "Create a FLAIM Database", "Get the DbSystemFactory object through COM and call dbCreate", "No Additional Details."); if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) { MAKE_FLM_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); goto Exit; } bDibCreated = TRUE; endTest("PASS"); beginTest( "createDocument", "Create a document and append 50000 child nodes", "createDocument; createElement; appendChild", "No Additional Details."); // Start an update transaction if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bStartedTrans = TRUE; // Create some schema definitions uiDefNum = 0; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "element1", XFLM_TEXT_TYPE, &uiDefNum))) { MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } // Create a document if( RC_BAD( rc = m_pDb->createDocument( XFLM_DATA_COLLECTION, &pDoc))) { MAKE_FLM_ERROR_STRING( "createDocument failed", m_szDetails, rc); goto Exit; } // Create an element if( RC_BAD( rc = pDoc->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_TAG, XFLM_FIRST_CHILD, &pRootElement))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } // Generate a large text value uiLargeTextSize = 1024; if( RC_BAD( rc = f_alloc( uiLargeTextSize, &pucLargeBuf))) { MAKE_FLM_ERROR_STRING( "f_alloc failed", m_szDetails, rc); goto Exit; } for( uiLoop = 0; uiLoop < uiLargeTextSize - 1; uiLoop++) { pucLargeBuf[ uiLoop] = (FLMBYTE)('A' + (uiLoop % 26)); } pucLargeBuf[ uiLargeTextSize - 1] = 0; for( uiLoop = 0; uiLoop < 5000; uiLoop++) { // Create an element if( RC_BAD( rc = pRootElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum, XFLM_LAST_CHILD, &pElement))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } // Create an attribute if( RC_BAD( rc = pElement->createAttribute( m_pDb, ATTR_NEXT_INDEX_NUM_TAG, &pAttr))) { MAKE_FLM_ERROR_STRING( "createAttribute failed", m_szDetails, rc); goto Exit; } // Set the attribute's value #ifdef FLM_UNIX if( RC_BAD( rc = pAttr->setUINT64( m_pDb, 0x8a306d2d713a8cfeULL))) #else if( RC_BAD( rc = pAttr->setUINT64( m_pDb, 0x8a306d2d713a8cfe))) #endif { MAKE_FLM_ERROR_STRING( "setUINT64 failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->getUINT64( m_pDb, &ui64Tmp))) { MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); goto Exit; } #ifdef FLM_UNIX if( ui64Tmp != 0x8a306d2d713a8cfeULL) #else if( ui64Tmp != 0x8a306d2d713a8cfe) #endif { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "getUINT returned invalid data", m_szDetails, rc); goto Exit; } // Create another attribute if( RC_BAD( rc = pElement->createAttribute( m_pDb, ATTR_COMPARE_RULES_TAG, &pAttr))) { MAKE_FLM_ERROR_STRING( "createAttribute failed", m_szDetails, rc); goto Exit; } // Set the attribute's value if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"1234567890", 0))) { MAKE_FLM_ERROR_STRING( "setNative failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiTmp))) { MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); goto Exit; } if( uiTmp != 1234567890) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "getUINT returned invalid data", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"987", 0, TRUE))) { MAKE_FLM_ERROR_STRING( "setNative failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiTmp))) { MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); goto Exit; } if( uiTmp != 987) { rc = NE_XFLM_FAILURE; MAKE_FLM_ERROR_STRING( "getUINT returned invalid data", m_szDetails, rc); goto Exit; } // Set a long value into the attribute if( RC_BAD( rc = pAttr->setUTF8( m_pDb, pucLargeBuf, 0, TRUE))) { MAKE_FLM_ERROR_STRING( "setNative failed", m_szDetails, rc); goto Exit; } f_memset( pucLargeBuf, 0, uiLargeTextSize); if( RC_BAD( rc = pAttr->getUTF8( m_pDb, pucLargeBuf, uiLargeTextSize, 0, uiLargeTextSize - 1))) { MAKE_FLM_ERROR_STRING( "getNative failed", m_szDetails, rc); goto Exit; } // Create a data node if( RC_BAD( rc = pElement->createNode( m_pDb, DATA_NODE, 0, XFLM_LAST_CHILD, &pData))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } if( (uiLoop % 1000) == 0) { if ( m_bDisplayVerbose) { if( RC_BAD( rc = pData->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } f_sprintf( szMsgBuf, "Node count = %I64u", ui64Tmp); display( szMsgBuf); } } } endTest("PASS"); // Read the nodes beginTest( "read nodes", "Read and verify the DOM nodes", "Self-Explanatory", "No Additional Details."); if( RC_BAD( rc = pData->getNodeId( m_pDb, &ui64Tmp))) { goto Exit; } uiHighNode = (FLMUINT)ui64Tmp; for( uiLoop = 1; uiLoop < uiHighNode; uiLoop++) { if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, uiLoop, &pTmpNode))) { MAKE_FLM_ERROR_STRING( "getNode failed", m_szDetails, rc); goto Exit; } if( (uiLoop % 1000) == 0) { if ( m_bDisplayVerbose) { f_sprintf( szMsgBuf, "Read = %I64u", (FLMUINT64)uiLoop); display( szMsgBuf); } } } endTest("PASS"); // Delete the document beginTest( "Document Delete", "delete the document we just created", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = pDoc->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); goto Exit; } // Commit the transaction bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } // Close the database m_pDb->Release(); m_pDb = NULL; endTest("PASS"); // Tests for unique child elements. beginTest( "Unique Child Elements", "Create/Read/Delete unique child elements", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } // Start an update transaction if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) { MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); goto Exit; } bStartedTrans = TRUE; // Create two element definitions, one that requires uniqueness, // another that does not. uiDefNum1 = 0; if( RC_BAD( rc = m_pDb->createUniqueElmDef( NULL, "u_element1", &uiDefNum1))) { MAKE_FLM_ERROR_STRING( "createUniqueElmDef failed", m_szDetails, rc); goto Exit; } uiDefNum2 = 0; if( RC_BAD( rc = m_pDb->createElementDef( NULL, "u_element2", XFLM_NODATA_TYPE, &uiDefNum2))) { MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } // Create 200 unique element definitions. uiDefNum3 = uiDefNum2 + 1; for (uiLoop = 0; uiLoop < 200; uiLoop++) { char szName [80]; FLMUINT uiNumToUse = uiDefNum3 + uiLoop; f_sprintf( szName, "c_element%u", (unsigned)uiLoop); if( RC_BAD( rc = m_pDb->createElementDef( NULL, szName, XFLM_NODATA_TYPE, &uiNumToUse))) { MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); goto Exit; } } // Create a root node, with two child nodes. if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, ELM_ELEMENT_TAG, &pRootElement))) { MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pRootElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum1, XFLM_FIRST_CHILD, &pUniqueElement, NULL))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pRootElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum2, XFLM_LAST_CHILD, &pNonUniqueElement, NULL))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } // Under each child node create 200 nodes, with a unique ID for (uiLoop = 0; uiLoop < 200; uiLoop++) { // Should not allow a data node. if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, DATA_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) { if (rc == NE_XFLM_BAD_DATA_TYPE || rc == NE_XFLM_DOM_INVALID_CHILD_TYPE) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } } else { // Should not have succeeded. endTest( "FAIL"); goto Finish_Test; } // 1st add should work, 2nd add under unique parent should fail if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) { if (rc == NE_XFLM_DOM_DUPLICATE_ELEMENT) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } } else { // Should not have succeeded. endTest("FAIL"); goto Finish_Test; } // Should be able to do it twice under non-unique parent if( RC_BAD( rc = pNonUniqueElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pNonUniqueElement->createNode( m_pDb, ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } // Should not allow a data node. if( RC_BAD( rc = pNonUniqueElement->createNode( m_pDb, DATA_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) { if( rc == NE_XFLM_BAD_DATA_TYPE || rc == NE_XFLM_DOM_INVALID_CHILD_TYPE) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); goto Exit; } } else { // Should not have succeeded. endTest("FAIL"); goto Finish_Test; } } // Go through the loop and get all 200, then delete, and get again for( uiLoop = 0; uiLoop < 200; uiLoop++) { // 1st get should work, then delete, 2nd get should fail if( RC_BAD( rc = pUniqueElement->getChildElement( m_pDb, uiDefNum3 + uiLoop, &pElement))) { MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = pElement->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pUniqueElement->getChildElement( m_pDb, uiDefNum3 + uiLoop, &pElement))) { if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) { rc = NE_XFLM_OK; } else { MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); goto Exit; } } else { // Should not have succeeded. endTest("FAIL"); goto Finish_Test; } // 1st get should work, then delete, 2nd should also work if( RC_BAD( rc = pNonUniqueElement->getChildElement( m_pDb, uiDefNum3 + uiLoop, &pElement))) { MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = pElement->deleteNode( m_pDb))) { MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pNonUniqueElement->getChildElement( m_pDb, uiDefNum3 + uiLoop, &pElement))) { MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); goto Exit; } } // Commit the transaction bStartedTrans = FALSE; if( RC_BAD( rc = m_pDb->transCommit())) { MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); goto Exit; } endTest("PASS"); Finish_Test: if( bStartedTrans) { m_pDb->transAbort(); bStartedTrans = FALSE; } // Close the database. m_pDb->Release(); m_pDb = NULL; // Re-open the database beginTest( "backup", "backup the database", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } // Backup the database if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, XFLM_READ_TRANS, 0, &pBackup))) { MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = pBackup->backup( BACKUP_NAME_STR, NULL, NULL, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); goto Exit; } pBackup->Release(); pBackup = NULL; endTest("PASS"); // Close the database again beginTest( "dbRemove", "remove the database", "Self-explanatory", "No Additional Details."); m_pDb->Release(); m_pDb = NULL; if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) { MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); goto Exit; } // Remove the database if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) { MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); goto Exit; } endTest("PASS"); // Restore the database beginTest( "dbRestore", "restore the database from the backup we just made", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, NULL, NULL, BACKUP_NAME_STR, NULL, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); goto Exit; } endTest("PASS"); beginTest( "backup (w/ password)", "backup the database using a password", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, FALSE, &m_pDb))) { MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } // Backup the database if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, XFLM_READ_TRANS, 0, &pBackup))) { MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); goto Exit; } rc = pBackup->backup( BACKUP_NAME_STR, BACKUP_PASSWORD_STR, NULL, NULL, NULL); #ifdef FLM_USE_NICI // The criteria for a successful test varies depending on whether we're // compiled with NICI or not... if( RC_BAD( rc)) #else if (rc != NE_XFLM_ENCRYPTION_UNAVAILABLE) #endif { MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); goto Exit; } pBackup->Release(); pBackup = NULL; m_pDb->Release(); m_pDb = NULL; endTest("PASS"); #ifdef FLM_USE_NICI // No point in trying to restore with a password when we // don't have NICI because there's no way a password // based backup could work... // Restore the database (using the password) beginTest( "dbRestore (w/ password)", "restore the database from the backup we just made", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) { MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); goto Exit; } // Remove the database if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) { MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, NULL, NULL, BACKUP_NAME_STR, BACKUP_PASSWORD_STR, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); goto Exit; } endTest("PASS"); // Wrap the database key back in the server key beginTest( "wrapKey", "wrap the database key in the NICI server key", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, &m_pDb))) { MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); goto Exit; } if (RC_BAD( rc = m_pDb->wrapKey())) { MAKE_FLM_ERROR_STRING( "wrapKey failed", m_szDetails, rc); goto Exit; } m_pDb->Release(); m_pDb = NULL; endTest("PASS"); #endif // FLM_USE_NICI // Rename the database beginTest( "dbRename", "rename the database", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbRename( DB_NAME_STR, NULL, NULL, NEW_NAME_STR, TRUE, NULL))) { MAKE_FLM_ERROR_STRING( "dbRename failed", m_szDetails, rc); goto Exit; } endTest("PASS"); // Copy the database beginTest( "dbCopy", "copy the database", "Self-explanatory", "No Additional Details."); if( RC_BAD( rc = m_pDbSystem->dbCopy( NEW_NAME_STR, NULL, NULL, DB_NAME_STR, NULL, NULL, NULL))) { MAKE_FLM_ERROR_STRING( "dbCopy failed", m_szDetails, rc); goto Exit; } // Remove both databases if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) { MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); goto Exit; } if( RC_BAD( rc = m_pDbSystem->dbRemove( NEW_NAME_STR, NULL, NULL, TRUE))) { MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); goto Exit; } endTest("PASS"); Exit: if( RC_BAD( rc)) { endTest("FAIL"); } if( bStartedTrans) { m_pDb->transAbort(); } if( pBackup) { pBackup->Release(); } if( pDoc) { pDoc->Release(); } if( pRootElement) { pRootElement->Release(); } if( pUniqueElement) { pUniqueElement->Release(); } if( pNonUniqueElement) { pNonUniqueElement->Release(); } if( pElement) { pElement->Release(); } if( pData) { pData->Release(); } if( pAttr) { pAttr->Release(); } if( pTmpNode) { pTmpNode->Release(); } if( pucLargeBuf) { f_free( &pucLargeBuf); } if( RC_BAD( rc)) { f_sprintf( szMsgBuf, "Error 0x%04X\n", (unsigned)rc); display( szMsgBuf); log( szMsgBuf); return( rc); } else { // Shut down the FLAIM database engine. This call must be made // even if the init call failed. No more FLAIM calls should be made // by the application. shutdownTestState( DB_NAME_STR, bDibCreated); return( NE_XFLM_OK); } } libxflaim-5.1.969/util/fshell.cpp0000644000175000017500000064516110511001742020210 0ustar ahodgkinsonahodgkinson//------------------------------------------------------------------------------ // Desc: Command-line environment for FLAIM utilities // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fshell.cpp 3133 2006-01-25 12:00:01 -0700 (Wed, 25 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "fshell.h" #include "domedit.h" // Imported global variables. extern FLMBOOL gv_bShutdown; // XML Import Error Handling #define flmErrorCodeEntry( c) { c, #c } #define MAX_IMPORT_ERROR_STRING 65 typedef struct { RCODE rc; const char * pszErrorStr; } XSHELL_ERROR_CODE_MAP; XSHELL_ERROR_CODE_MAP gv_XMLParseErrors[ XML_NUM_ERRORS - 1] = { flmErrorCodeEntry( XML_ERR_BAD_ELEMENT_NAME), flmErrorCodeEntry( XML_ERR_XMLNS_IN_ELEMENT_NAME), flmErrorCodeEntry( XML_ERR_ELEMENT_NAME_MISMATCH), flmErrorCodeEntry( XML_ERR_PREFIX_NOT_DEFINED), flmErrorCodeEntry( XML_ERR_EXPECTING_GT), flmErrorCodeEntry( XML_ERR_EXPECTING_ELEMENT_LT), flmErrorCodeEntry( XML_ERR_EXPECTING_EQ), flmErrorCodeEntry( XML_ERR_MULTIPLE_XMLNS_DECLS), flmErrorCodeEntry( XML_ERR_MULTIPLE_PREFIX_DECLS), flmErrorCodeEntry( XML_ERR_EXPECTING_QUEST_GT), flmErrorCodeEntry( XML_ERR_INVALID_XML_MARKUP), flmErrorCodeEntry( XML_ERR_MUST_HAVE_ONE_ATT_DEF), flmErrorCodeEntry( XML_ERR_EXPECTING_NDATA), flmErrorCodeEntry( XML_ERR_EXPECTING_SYSTEM_OR_PUBLIC), flmErrorCodeEntry( XML_ERR_EXPECTING_LPAREN), flmErrorCodeEntry( XML_ERR_EXPECTING_RPAREN_OR_PIPE), flmErrorCodeEntry( XML_ERR_EXPECTING_NAME), flmErrorCodeEntry( XML_ERR_INVALID_ATT_TYPE), flmErrorCodeEntry( XML_ERR_INVALID_DEFAULT_DECL), flmErrorCodeEntry( XML_ERR_EXPECTING_PCDATA), flmErrorCodeEntry( XML_ERR_EXPECTING_ASTERISK), flmErrorCodeEntry( XML_ERR_EMPTY_CONTENT_INVALID), flmErrorCodeEntry( XML_ERR_CANNOT_MIX_CHOICE_AND_SEQ), flmErrorCodeEntry( XML_ERR_XML_ILLEGAL_PI_NAME), flmErrorCodeEntry( XML_ERR_ILLEGAL_FIRST_NAME_CHAR), flmErrorCodeEntry( XML_ERR_ILLEGAL_COLON_IN_NAME), flmErrorCodeEntry( XML_ERR_EXPECTING_VERSION), flmErrorCodeEntry( XML_ERR_INVALID_VERSION_NUM), flmErrorCodeEntry( XML_ERR_UNSUPPORTED_ENCODING), flmErrorCodeEntry( XML_ERR_EXPECTING_YES_OR_NO), flmErrorCodeEntry( XML_ERR_EXPECTING_QUOTE_BEFORE_EOL), flmErrorCodeEntry( XML_ERR_EXPECTING_SEMI), flmErrorCodeEntry( XML_ERR_UNEXPECTED_EOL_IN_ENTITY), flmErrorCodeEntry( XML_ERR_INVALID_CHARACTER_NUMBER), flmErrorCodeEntry( XML_ERR_UNSUPPORTED_ENTITY), flmErrorCodeEntry( XML_ERR_EXPECTING_QUOTE), flmErrorCodeEntry( XML_ERR_INVALID_PUBLIC_ID_CHAR), flmErrorCodeEntry( XML_ERR_EXPECTING_WHITESPACE), flmErrorCodeEntry( XML_ERR_EXPECTING_HEX_DIGIT), flmErrorCodeEntry( XML_ERR_INVALID_BINARY_ATTR_VALUE), flmErrorCodeEntry( XML_ERR_CREATING_CDATA_NODE), flmErrorCodeEntry( XML_ERR_CREATING_COMMENT_NODE), flmErrorCodeEntry( XML_ERR_CREATING_PI_NODE), flmErrorCodeEntry( XML_ERR_CREATING_DATA_NODE), flmErrorCodeEntry( XML_ERR_CREATING_ROOT_ELEMENT), flmErrorCodeEntry( XML_ERR_CREATING_ELEMENT_NODE) }; #define FSMI_ENTRY_ELEMENT 2 #define FSMI_INTERNAL_ENTRY_FLAGS_ATTR 21 #define FSMI_ENTRY_MODIFY_TIME_ATTR 22 #define FSMI_ENTRY_FLAGS_ATTR 23 #define FSMI_PARTITION_ID_ATTR 24 #define FSMI_CLASS_ID_ATTR 25 #define FSMI_PARENT_ID_ATTR 26 #define FSMI_ALT_ID_ATTR 27 #define FSMI_SUBORDINATE_COUNT_ATTR 28 #define FSMI_RDN_ATTR 29 #define FSMI_FIRST_CHILD_ATTR 30 #define FSMI_LAST_CHILD_ATTR 31 #define FSMI_NEXT_SIBLING_ATTR 32 #define FSMI_PREV_SIBLING_ATTR 33 #define FSMI_ATTR_GVTS_ATTR 50 #define FSMI_ATTR_DTS_ATTR 51 #define FSMI_VALUE_FLAGS_ATTR 52 #define FSMI_VALUE_MTS_ATTR 53 #define FSMI_TTL_ATTR 54 #define FSMI_POLICY_DN_ATTR 55 typedef struct OVERHEAD_INFO { FLMUINT64 ui64DOMOverhead; FLMUINT64 ui64ValueBytes; } OVERHEAD_INFO; typedef struct ATTR_NODE_INFO { FLMUINT uiAttrNameId; char * pszAttrName; FLMUINT64 ui64NumValues; OVERHEAD_INFO GVTS; OVERHEAD_INFO DTS; OVERHEAD_INFO ValueFlags; OVERHEAD_INFO ValueMTS; OVERHEAD_INFO TTL; OVERHEAD_INFO PolicyDN; OVERHEAD_INFO OtherNodes; } ATTR_NODE_INFO; class Entry_Info { public: Entry_Info(); ~Entry_Info(); FINLINE void resetOverheadInfo( OVERHEAD_INFO * pOverhead) { pOverhead->ui64DOMOverhead = 0; pOverhead->ui64ValueBytes = 0; } FINLINE void initAttrInfo( FLMUINT uiAttrNameId, ATTR_NODE_INFO * pAttrInfo) { pAttrInfo->uiAttrNameId = uiAttrNameId; pAttrInfo->pszAttrName = NULL; pAttrInfo->ui64NumValues = 0; resetOverheadInfo( &pAttrInfo->GVTS); resetOverheadInfo( &pAttrInfo->DTS); resetOverheadInfo( &pAttrInfo->ValueFlags); resetOverheadInfo( &pAttrInfo->ValueMTS); resetOverheadInfo( &pAttrInfo->TTL); resetOverheadInfo( &pAttrInfo->PolicyDN); resetOverheadInfo( &pAttrInfo->OtherNodes); } void getOverheadInfo( OVERHEAD_INFO * pOverhead); RCODE getXMLAttrInfo( IF_Db * pDb, IF_DOMNode * pNode, OVERHEAD_INFO * pOverhead, FLMUINT uiAttrNameId); RCODE getAttrValueInfo( IF_Db * pDb, IF_DOMNode * pValueNode, ATTR_NODE_INFO * pAttrNodeInfo); RCODE processAttrValues( IF_Db * pDb, IF_DOMNode * pAttrNode, ATTR_NODE_INFO * pAttrNodeInfo); FLMBOOL findDirAttr( FLMUINT uiAttrNameId, FLMUINT * puiInsertPos); RCODE getDirAttrInfo( IF_Db * pDb, IF_DOMNode * pAttrNode); RCODE processEntryAttrs( IF_Db * pDb, IF_DOMNode * pEntryNode); RCODE addEntryInfo( IF_Db * pDb, IF_DOMNode * pEntryNode); FLMUINT64 m_ui64NumEntries; FLMUINT64 m_ui64TotalBytes; OVERHEAD_INFO m_EntryNode; OVERHEAD_INFO m_AttrNode; OVERHEAD_INFO m_InternalEntryFlags; OVERHEAD_INFO m_ModifyTime; OVERHEAD_INFO m_EntryFlags; OVERHEAD_INFO m_PartitionID; OVERHEAD_INFO m_ClassID; OVERHEAD_INFO m_ParentID; OVERHEAD_INFO m_AlternateID; OVERHEAD_INFO m_SubordinateCount; OVERHEAD_INFO m_RDN; OVERHEAD_INFO m_FirstChild; OVERHEAD_INFO m_LastChild; OVERHEAD_INFO m_NextSibling; OVERHEAD_INFO m_PrevSibling; ATTR_NODE_INFO * m_pAttrList; FLMUINT m_uiAttrListSize; FLMUINT m_uiNumAttrs; F_NodeInfo m_nodeInfo; F_Pool m_pool; }; // Local prototypes RCODE flmBackupProgFunc( FLMUINT uiStatusType, void * Parm1, void * Parm2, void * UserData); FLMINT fshellFileSystemTest( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell); FSTATIC void format64BitNum( FLMUINT64 ui64Num, char * pszBuf, FLMBOOL bOutputHex, FLMBOOL bAddCommas = FALSE); RCODE flmXmlImportStatus( eXMLStatus eStatusType, void * pvArg1, void * pvArg2, void * pvArg3, void * pvUserData); RCODE importXmlFiles( IF_Db * pDb, FLMUINT uiCollection, FlmShell * pShell, char * pszPath, XFLM_IMPORT_STATS * pImportStats); RCODE getErrFromFile( IF_PosIStream * pFileIStream, FLMUINT uiCurrLineFilePos, FLMUINT uiCurrLineBytes, XMLEncoding eXMLEncoding, char * pszErrorString); FSTATIC void domDisplayError( FTX_WINDOW * pWindow, const char * pszError, RCODE errRc); FSTATIC void domOutputValues( FTX_WINDOW * pWindow, FLMUINT * puiLineCount, IF_FileHdl * pFileHdl, const char * pszLabel, FLMUINT64 ui64Bytes, FLMUINT uiPercent, FLMUINT64 ui64Count); FSTATIC void domDisplayLine( FTX_WINDOW * pWindow, FLMUINT * puiLineCount, IF_FileHdl * pFileHdl, const char * pszLine, const char * pszWaitPrompt = NULL); FSTATIC void domDisplayValue( FTX_WINDOW * pWindow, FLMUINT * puiLineCount, IF_FileHdl * pFileHdl, const char * pszLabel, FLMUINT uiPercent, FLMUINT64 ui64Value); FSTATIC void domDisplayInfo( FTX_WINDOW * pWindow, FLMUINT * puiLineCount, IF_FileHdl * pFileHdl, const char * pszDomOverheadLabel, const char * pszValueBytesLabel, OVERHEAD_INFO * pInfo, FLMUINT64 ui64TotalBytes); const char * errorToString( XMLParseError errorType); /**************************************************************************** Desc: *****************************************************************************/ class F_LocalRestore : public F_FSRestore { public: ~F_LocalRestore() {} F_LocalRestore() { } FINLINE RCODE setup( char * pszDbPath, char * pszBackupSetPath, char * pszRflDir) { return( F_FSRestore::setup( pszDbPath, pszBackupSetPath, pszRflDir)); } FINLINE RCODE FLMAPI openRflFile( FLMUINT uiFileNum) { return( F_FSRestore::openRflFile( uiFileNum)); } FINLINE RCODE FLMAPI read( FLMUINT uiLength, void * pvBuffer, FLMUINT * puiBytesRead) { return( F_FSRestore::read( uiLength, pvBuffer, puiBytesRead)); } private: }; /**************************************************************************** Desc: *****************************************************************************/ class F_LocalRestoreStatus : public F_DefaultRestoreStatus { public: F_LocalRestoreStatus( FlmShell * pShell) { m_pShell = pShell; m_bFirstStatus = TRUE; m_uiTransCount = 0; m_uiAddCount = 0; m_uiDeleteCount = 0; m_uiModifyCount = 0; m_uiReserveCount = 0; m_uiIndexCount = 0; m_uiRflFileNum = 0; m_ui64RflBytesRead = 0; } RCODE FLMAPI reportProgress( eRestoreAction * peAction, FLMUINT64 ui64BytesToDo, FLMUINT64 ui64BytesDone); RCODE FLMAPI reportError( eRestoreAction * peAction, RCODE rcErr); RCODE FLMAPI reportBeginTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId); RCODE FLMAPI reportCommitTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId); RCODE FLMAPI reportAbortTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId); RCODE FLMAPI reportOpenRflFile( eRestoreAction * peAction, FLMUINT uiFileNum) { m_uiRflFileNum = uiFileNum; updateCountDisplay(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportRflRead( eRestoreAction * peAction, FLMUINT uiFileNum, FLMUINT uiBytesRead) { (void)uiFileNum; m_ui64RflBytesRead += uiBytesRead; *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportBlockChainFree( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT64, // ui64MaintDocNum, FLMUINT, // uiStartBlkAddr, FLMUINT, // uiEndBlkAddr, FLMUINT // uiCount ) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportEnableEncryption( eRestoreAction * peAction, FLMUINT64 ui64TransId); RCODE FLMAPI reportWrapKey( eRestoreAction * peAction, FLMUINT64 ui64TransId); RCODE FLMAPI reportRollOverDbKey( eRestoreAction * peAction, FLMUINT64) // ui64TransId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportDocumentDone( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64) // ui64NodeId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeCreate( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64RefNodeId, eDomNodeType, // eNodeType, FLMUINT, // uiNameId, eNodeInsertLoc) // eLocation) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeDelete( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64) // ui64NodeId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeChildrenDelete( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64NodeId, FLMUINT) // uiNameId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportInsertBefore( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64ParentId, FLMUINT64, // ui64NewChildId, FLMUINT64) // ui64RefChildId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeUpdate( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64) // ui64NodeId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeSetValue( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64) // ui64NodeId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeFlagsUpdate( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64NodeId, FLMUINT, // uiFlags, FLMBOOL) // bAdd) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeSetPrefixId( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64NodeId, FLMUINT) // uiPrefixId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportSetNextNodeId( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64) // ui64NextNodeId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeSetMetaValue( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64NodeId, FLMUINT64 // ui64MetaValue ) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportAttributeDelete( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64ElementId, FLMUINT) // uiAttrName) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportAttributeSetValue( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64ElementId, FLMUINT) // uiNameId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } RCODE FLMAPI reportNodeSetPrefixId( eRestoreAction * peAction, FLMUINT64, // ui64TransId, FLMUINT, // uiCollection, FLMUINT64, // ui64NodeId, FLMUINT, // uiAttrName, FLMUINT) // uiPrefixId) { *peAction = XFLM_RESTORE_ACTION_CONTINUE; return( NE_XFLM_OK); } private: RCODE report_preamble( FTX_WINDOW * pWin); RCODE report_postamble( FTX_WINDOW * pWin); void updateCountDisplay( void); FlmShell * m_pShell; FLMBOOL m_bFirstStatus; FLMUINT m_uiTransCount; FLMUINT m_uiAddCount; FLMUINT m_uiDeleteCount; FLMUINT m_uiModifyCount; FLMUINT m_uiReserveCount; FLMUINT m_uiIndexCount; FLMUINT m_uiRflFileNum; FLMUINT64 m_ui64RflBytesRead; }; /**************************************************************************** Desc: *****************************************************************************/ class F_LocalBackupClient : public F_DefaultBackupClient { public: F_LocalBackupClient( FlmShell * pShell, char * pszBackupPath) : F_DefaultBackupClient( pszBackupPath) { m_pShell = pShell; } private: FlmShell * m_pShell; }; /**************************************************************************** Desc: *****************************************************************************/ class F_LocalBackupStatus : public IF_BackupStatus { public: F_LocalBackupStatus( FlmShell * pShell) { m_pShell = pShell; } RCODE FLMAPI backupStatus( FLMUINT64 ui64BytesToDo, FLMUINT64 ui64BytesDone); FINLINE FLMINT FLMAPI getRefCount( void) { return( IF_BackupStatus::getRefCount()); } virtual FINLINE FLMINT FLMAPI AddRef( void) { return( IF_BackupStatus::AddRef()); } virtual FINLINE FLMINT FLMAPI Release( void) { return( IF_BackupStatus::Release()); } private: FlmShell * m_pShell; }; /**************************************************************************** Desc: *****************************************************************************/ FlmShell::FlmShell( void) : FlmThreadContext() { m_histPool.poolInit( 512); m_argPool.poolInit( 512); f_memset( m_DbList, 0, MAX_SHELL_OPEN_DB * sizeof( IF_Db *)); m_pTitleWin = NULL; m_iCurrArgC = 0; m_ppCurrArgV = NULL; m_iLastCmdExitCode = 0; m_bPagingEnabled = FALSE; m_pSharedContext = NULL; f_memset( m_ppCmdList, 0, sizeof( FlmCommand *) * MAX_REGISTERED_COMMANDS); f_memset( m_ppHistory, 0, sizeof( char *) * MAX_SHELL_HISTORY_ITEMS); } /**************************************************************************** Desc: *****************************************************************************/ FlmShell::~FlmShell( void) { FLMUINT uiLoop; m_histPool.poolFree(); m_argPool.poolFree(); // Free the command objects. for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) { if( m_ppCmdList[ uiLoop] != NULL) { m_ppCmdList[ uiLoop]->Release(); } } // Free the history items for( uiLoop = 0; uiLoop < MAX_SHELL_HISTORY_ITEMS; uiLoop++) { if( m_ppHistory[ uiLoop]) { f_free( &m_ppHistory[ uiLoop]); } } // Close all open databases for( uiLoop = 0; uiLoop < MAX_SHELL_OPEN_DB; uiLoop++) { if( m_DbList[ uiLoop]) { m_DbList[ uiLoop]->Release(); } } } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::setup( FlmSharedContext * pSharedContext) { FlmCommand * pCommand = NULL; RCODE rc = NE_XFLM_OK; flmAssert( pSharedContext != NULL); m_pSharedContext = pSharedContext; if( RC_BAD( rc = FlmThreadContext::setup( m_pSharedContext, "X-FLAIM Shell", NULL, NULL))) { goto Exit; } // Register dbopen command if( (pCommand = f_new FlmDbOpenCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register dbclose command if( (pCommand = f_new FlmDbCloseCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register dbcopy, dbrename, and dbremove command handler if( (pCommand = f_new FlmDbManageCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register trans command if( (pCommand = f_new FlmTransCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register backup command if( (pCommand = f_new FlmBackupCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register restore command if( (pCommand = f_new FlmRestoreCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register database config command if( (pCommand = f_new FlmDbConfigCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register database get config command if( (pCommand = f_new FlmDbGetConfigCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register sysinfo command if( (pCommand = f_new FlmSysInfoCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the hex conversion command if( (pCommand = f_new FlmHexConvertCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the base64 conversion command if( (pCommand = f_new FlmBase64ConvertCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the file delete command if( (pCommand = f_new FlmFileSysCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the import command if( (pCommand = f_new FlmImportCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the dom edit command if( (pCommand = f_new FlmDomEditCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the export command if( (pCommand = f_new FlmExportCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register the wrap key command if( (pCommand = f_new FlmWrapKeyCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register nodeinfo command if( (pCommand = f_new FlmNodeInfoCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; // Register btreeinfo command if( (pCommand = f_new FlmBTreeInfoCommand) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = registerCmd( pCommand))) { goto Exit; } pCommand = NULL; Exit: if( pCommand) { pCommand->Release(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::registerDatabase( IF_Db * pDb, FLMUINT * puiDbId) { FLMUINT uiLoop; RCODE rc = NE_XFLM_OK; *puiDbId = 0xFFFFFFFF; for( uiLoop = 0; uiLoop < MAX_SHELL_OPEN_DB; uiLoop++) { if( !m_DbList[ uiLoop]) { m_DbList[ uiLoop] = pDb; *puiDbId = uiLoop; break; } } if( *puiDbId == 0xFFFFFFFF) { rc = RC_SET( NE_XFLM_TOO_MANY_OPEN_DATABASES); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::getDatabase( FLMUINT uiDbId, IF_Db ** ppDb) { RCODE rc = NE_XFLM_OK; if( uiDbId >= MAX_SHELL_OPEN_DB) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } *ppDb = m_DbList[ uiDbId]; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::deregisterDatabase( FLMUINT uiDbId) { RCODE rc = NE_XFLM_OK; if( uiDbId >= MAX_SHELL_OPEN_DB) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } m_DbList[ uiDbId] = NULL; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::con_printf( const char * pszFormat, ...) { char szBuffer[ 512]; f_va_list args; if( m_pWindow) { f_va_start( args, pszFormat); f_vsprintf( szBuffer, pszFormat, &args); f_va_end( args); FTXWinPrintStr( m_pWindow, szBuffer); } return( NE_XFLM_OK); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::parseCmdLine( char * pszString) { FLMUINT uiArgCount = 0; FLMUINT uiCurrToken = 0; FLMUINT uiTokenLen; char * pszCurrToken; FLMBOOL bQuoted; FlmParse Parser; RCODE rc = NE_XFLM_OK; m_argPool.poolReset( NULL); m_iCurrArgC = 0; m_ppCurrArgV = NULL; m_pszOutputFile = NULL; Parser.setString( pszString); while( Parser.getNextToken()) { uiArgCount++; } if (RC_BAD( rc = m_argPool.poolCalloc( uiArgCount * sizeof( char *), (void **)&m_ppCurrArgV))) { goto Exit; } uiCurrToken = 0; Parser.setString( pszString); while( (pszCurrToken = Parser.getNextToken()) != NULL) { bQuoted = FALSE; if( *pszCurrToken == '\"') { // Skip the quote character pszCurrToken++; bQuoted = TRUE; } uiTokenLen = f_strlen( pszCurrToken); if (!bQuoted && uiTokenLen >= 2 && *pszCurrToken == '>' && !m_pszOutputFile) { if (RC_BAD( rc = m_argPool.poolCalloc( uiTokenLen, (void **)&m_pszOutputFile))) { goto Exit; } f_strcpy( m_pszOutputFile, pszCurrToken + 1); } else { if (RC_BAD( rc = m_argPool.poolCalloc( uiTokenLen + 1, (void **)&m_ppCurrArgV [uiCurrToken]))) { goto Exit; } f_strcpy( m_ppCurrArgV[ uiCurrToken], pszCurrToken); if( bQuoted) { // Strip off the trailing quote m_ppCurrArgV[ uiCurrToken][ uiTokenLen - 1] = '\0'; } uiCurrToken++; } } m_iCurrArgC = (FLMINT)uiCurrToken; Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::executeCmdLine( void) { RCODE rc = NE_XFLM_OK; FLMBOOL bValidCommand = FALSE; FLMUINT uiLoop; IF_BufferIStream * pBufIStream = NULL; if( !m_iCurrArgC) { goto Exit; } // Process internal commands if( f_stricmp( m_ppCurrArgV[ 0], "cls") == 0) { FTXWinClear( m_pWindow); bValidCommand = TRUE; } else if( f_stricmp( m_ppCurrArgV[ 0], "exit") == 0) { setShutdownFlag(); bValidCommand = TRUE; } else if( f_stricmp( m_ppCurrArgV[ 0], "echo") == 0) { FLMBOOL bNewline = FALSE; if( m_iCurrArgC > 1 && f_stricmp( m_ppCurrArgV[ 1], "-n") == 0) { bNewline = TRUE; uiLoop = 2; } else { uiLoop = 1; } for( ; uiLoop < (FLMUINT)m_iCurrArgC; uiLoop++) { con_printf( "%s", (char *)m_ppCurrArgV[ uiLoop]); } if( bNewline) { con_printf( "\n"); } bValidCommand = TRUE; } else if( f_stricmp( m_ppCurrArgV[ 0], "shell") == 0) { FlmShell * pTmpShell; if( (pTmpShell = f_new FlmShell) != NULL) { if( RC_BAD( pTmpShell->setup( m_pSharedContext))) { pTmpShell->Release(); } else { m_pSharedContext->spawn( pTmpShell); } } bValidCommand = TRUE; } else if( f_stricmp( m_ppCurrArgV[ 0], "qp") == 0) { F_XPath xpathObj; F_Query query; RCODE tmpRc; bValidCommand = TRUE; if( m_iCurrArgC != 3) { con_printf( "Invalid number of arguments.\n\n"); } else { FLMUINT uiDbId; F_Db * pDb; uiDbId = f_atol( m_ppCurrArgV[ 1]); if( RC_BAD( rc = getDatabase( uiDbId, (IF_Db **)&pDb))) { con_printf( "Invalid database ID.\n\n"); goto Exit; } if( !pBufIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufIStream))) { goto Exit; } } pBufIStream->openStream( m_ppCurrArgV[ 2], f_strlen( m_ppCurrArgV[ 2])); tmpRc = xpathObj.parseQuery( pDb, pBufIStream, &query); pBufIStream->closeStream(); con_printf( "Result: 0x%08X\n\n", (unsigned)tmpRc); } } else if( f_stricmp( m_ppCurrArgV[ 0], "meta") == 0) { FLMUINT uiMeta; FLMUINT uiAltMeta; bValidCommand = TRUE; if( m_iCurrArgC != 2) { con_printf( "Invalid number of arguments.\n\n"); } else { if( !pBufIStream) { if( RC_BAD( rc = FlmAllocBufferIStream( &pBufIStream))) { goto MetaExit; } } pBufIStream->openStream( m_ppCurrArgV[ 1], f_strlen( m_ppCurrArgV[ 1])); for( ;;) { RCODE tmpRc; if( RC_BAD( tmpRc = f_getNextMetaphone( pBufIStream, &uiMeta, &uiAltMeta))) { if( tmpRc != NE_XFLM_EOF_HIT) { con_printf( "Error: 0x%04X\n", tmpRc); } break; } con_printf( "Meta = 0x%04X, AltMeta = 0x%04X\n", uiMeta, uiAltMeta); } MetaExit: if( pBufIStream) { pBufIStream->closeStream(); } } } else if( f_stricmp( m_ppCurrArgV[ 0], "help") == 0 || f_stricmp( m_ppCurrArgV[ 0], "?") == 0 || f_stricmp( m_ppCurrArgV[ 0], "h") == 0) { if( m_iCurrArgC < 2) { con_printf( "Commands:\n"); displayCommand( "help, ?, h", "Show help"); displayCommand( "shell", "Start a new shell"); displayCommand( "echo", "Echo typed in command"); displayCommand( "cls", "Clear screen"); displayCommand( "exit", "Exit shell"); displayCommand( "echo", "Echo typed in command"); for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) { if( m_ppCmdList[ uiLoop] != NULL) { m_ppCmdList[ uiLoop]->displayHelp( this, NULL); } } } else { for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) { if( m_ppCmdList[ uiLoop] != NULL) { if (m_ppCmdList[ uiLoop]->canPerformCommand( (char *)m_ppCurrArgV [1])) { m_ppCmdList[ uiLoop]->displayHelp( this, (char *)m_ppCurrArgV [1]); break; } } } } bValidCommand = TRUE; } else { for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) { if( m_ppCmdList[ uiLoop] != NULL) { if( m_ppCmdList[ uiLoop]->canPerformCommand( (char *)m_ppCurrArgV[ 0])) { m_ppCmdList[ uiLoop]->execute( m_iCurrArgC, m_ppCurrArgV, this); bValidCommand = TRUE; break; } } } } if( !bValidCommand) { FTXWinPrintf( m_pWindow, "Unrecognized command: %s\n", m_ppCurrArgV[ 0]); rc = RC_SET( NE_XFLM_NOT_IMPLEMENTED); goto Exit; } Exit: if( pBufIStream) { pBufIStream->Release(); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::registerCmd( FlmCommand * pCmd) { RCODE rc = NE_XFLM_OK; FLMBOOL bRegistered = FALSE; FLMUINT uiLoop; for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) { if( m_ppCmdList[ uiLoop] == NULL) { m_ppCmdList[ uiLoop] = pCmd; bRegistered = TRUE; break; } } if( !bRegistered) { rc = RC_SET( NE_XFLM_FAILURE); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::addCmdHistory( char * pszCmd) { FLMUINT uiLoop; FLMUINT uiSlot; FLMUINT uiCmdLen; RCODE rc = NE_XFLM_OK; // If the command line is too long, don't store it in the // history buffer if( (uiCmdLen = f_strlen( pszCmd)) > MAX_CMD_LINE_LEN) { goto Exit; } // Look for a duplicate history item for( uiLoop = 0; uiLoop < MAX_SHELL_HISTORY_ITEMS; uiLoop++) { if( m_ppHistory[ uiLoop] && f_strcmp( pszCmd, m_ppHistory[ uiLoop]) == 0) { // Remove the command from the history list and compress // the history table f_free( &m_ppHistory[ uiLoop]); if( uiLoop < MAX_SHELL_HISTORY_ITEMS - 1) { f_memmove( &m_ppHistory[ uiLoop], &m_ppHistory[ uiLoop + 1], sizeof( char *) * (MAX_SHELL_HISTORY_ITEMS - uiLoop - 1)); m_ppHistory[ MAX_SHELL_HISTORY_ITEMS - 1] = NULL; break; } } } // Find an empty slot for the new history item for( uiSlot = MAX_SHELL_HISTORY_ITEMS; uiSlot > 0; uiSlot--) { if( m_ppHistory[ uiSlot - 1]) { break; } } if( uiSlot == MAX_SHELL_HISTORY_ITEMS) { f_free( &m_ppHistory[ 0]); f_memmove( &m_ppHistory[ 0], &m_ppHistory[ 1], sizeof( char *) * (MAX_SHELL_HISTORY_ITEMS - 1)); m_ppHistory[ MAX_SHELL_HISTORY_ITEMS - 1] = NULL; uiSlot = MAX_SHELL_HISTORY_ITEMS - 1; } if( RC_BAD( rc = f_alloc( uiCmdLen + 1, &m_ppHistory[ uiSlot]))) { goto Exit; } f_strcpy( m_ppHistory[ uiSlot], pszCmd); Exit: return( rc); } /**************************************************************************** Desc: *****************************************************************************/ RCODE FlmShell::execute( void) { char szBuffer[ MAX_CMD_LINE_LEN + 1]; char szThreadName[ MAX_THREAD_NAME_LEN + 1]; FLMUINT uiTermChar; FLMUINT uiRow; FLMUINT uiLastHistorySlot = MAX_SHELL_HISTORY_ITEMS; RCODE rc = NE_XFLM_OK; char szDir [F_PATH_MAX_SIZE]; DirectoryIterator directoryIterator; char * pszTabCompleteBegin = NULL; IF_PosIStream * pFileIStream = NULL; if( RC_BAD( rc = FTXScreenInit( "xshell main", &m_pScreen))) { goto Exit; } if( RC_BAD( rc = FTXScreenInitStandardWindows( m_pScreen, FLM_RED, FLM_WHITE, FLM_BLUE, FLM_WHITE, FALSE, FALSE, NULL, &m_pTitleWin, &m_pWindow))) { goto Exit; } FTXScreenDisplay( m_pScreen); FTXScreenSetShutdownFlag( m_pScreen, getShutdownFlagAddr()); szBuffer[ 0] = '\0'; for( ;;) { // Refresh the title bar getName( szThreadName); FTXWinSetCursorPos( m_pTitleWin, 0, 0); FTXWinPrintf( m_pTitleWin, "%5.5u: %s", (unsigned)getID(), szThreadName); FTXWinClearToEOL( m_pTitleWin); // Check for shutdown if( getShutdownFlag()) { break; } FTXWinGetCursorPos( m_pWindow, NULL, &uiRow); FTXWinSetCursorPos( m_pWindow, 0, uiRow); FTXWinClearToEOL( m_pWindow); if( RC_BAD( f_getcwd( szDir))) { szDir [0] = '\0'; } FTXWinPrintf( m_pWindow, "%s>", szDir); if( FTXLineEdit( m_pWindow, szBuffer, MAX_CMD_LINE_LEN, 255, 0, &uiTermChar)) { break; } if( uiTermChar == FKB_TAB) { char szBase[ 255]; char szWildcard[ 255]; szWildcard[0] = '\0'; pszTabCompleteBegin = positionToPath( szBuffer); if ( f_strchr( pszTabCompleteBegin, '\"')) { // remove quotes removeChars( pszTabCompleteBegin, '\"'); } // If we have not initialized our iterator to scan this directory // or if the command-line does not contain a path that we provided // we need to reinitialize the iterator. if( !directoryIterator.isInitialized() || !pszTabCompleteBegin || !directoryIterator.isInSet( pszTabCompleteBegin)) { extractBaseDirAndWildcard( pszTabCompleteBegin, szBase, szWildcard); directoryIterator.reset(); directoryIterator.setupForSearch( szDir, szBase, szWildcard); } if ( !directoryIterator.isEmpty()) { // Copy in the next entry along with its full path. directoryIterator.next( pszTabCompleteBegin, TRUE); } else { FTXBeep(); } // If the completed path contains spaces, quote it if ( f_strchr( pszTabCompleteBegin, ASCII_SPACE)) { f_memmove( pszTabCompleteBegin + 1, pszTabCompleteBegin, f_strlen( pszTabCompleteBegin) + 1); pszTabCompleteBegin[0] = '\"'; f_strcat( pszTabCompleteBegin, "\""); } continue; } directoryIterator.reset(); if( uiTermChar == FKB_UP) { for(; uiLastHistorySlot > 0; uiLastHistorySlot--) { if( m_ppHistory[ uiLastHistorySlot - 1]) { f_strcpy( szBuffer, m_ppHistory[ uiLastHistorySlot - 1]); uiLastHistorySlot--; break; } } continue; } if( uiTermChar == FKB_DOWN) { for(; uiLastHistorySlot < MAX_SHELL_HISTORY_ITEMS - 1; uiLastHistorySlot++) { if( m_ppHistory[ uiLastHistorySlot + 1]) { f_strcpy( szBuffer, m_ppHistory[ uiLastHistorySlot + 1]); uiLastHistorySlot++; break; } } continue; } if( uiTermChar == FKB_ESCAPE) { szBuffer[ 0] = '\0'; continue; } if( uiTermChar == FKB_F1) { FLMUINT uiLen; addCmdHistory( szBuffer); if( RC_BAD( rc = FlmOpenFileIStream( szBuffer, &pFileIStream))) { goto BatchError; } for( ;;) { FTXWinPrintf( m_pWindow, "\n"); uiLen = MAX_CMD_LINE_LEN; if( RC_BAD( rc = flmReadLine( pFileIStream, (FLMBYTE *)szBuffer, &uiLen))) { if( rc == NE_XFLM_EOF_HIT) { rc = NE_XFLM_OK; if( !uiLen) { break; } } goto BatchError; } parseCmdLine( szBuffer); szBuffer[ 0] = '\0'; if( RC_BAD( rc = executeCmdLine())) { goto BatchError; } } BatchError: if( RC_BAD( rc)) { FTXWinPrintf( m_pWindow, "Error: %e. Batch execution halted.\n", rc); rc = NE_XFLM_OK; } continue; } uiLastHistorySlot = MAX_SHELL_HISTORY_ITEMS; if( szBuffer [0]) { FTXWinPrintf( m_pWindow, "\n"); addCmdHistory( szBuffer); parseCmdLine( szBuffer); executeCmdLine(); szBuffer[0] = '\0'; continue; } FTXWinPrintf( m_pWindow, "\n"); } Exit: if( pFileIStream) { pFileIStream->Release(); } if( m_pWindow) { FTXWinFree( &m_pWindow); } if( m_pScreen) { FTXScreenFree( &m_pScreen); } return( rc); } /**************************************************************************** Desc: *****************************************************************************/ FlmParse::FlmParse( void) { m_szString [0] = 0; m_pszCurPos = &m_szString [0]; } /**************************************************************************** Desc: *****************************************************************************/ void FlmParse::setString( char * pszString) { if( pszString) { f_strcpy( m_szString, pszString); } else { m_szString [0]= 0; } m_pszCurPos = &m_szString [0]; } /**************************************************************************** Desc: *****************************************************************************/ char * FlmParse::getNextToken( void) { char * pszTokenPos = &m_szToken [0]; FLMBOOL bQuoted = FALSE; while( *m_pszCurPos && *m_pszCurPos == ' ') { m_pszCurPos++; } if( *m_pszCurPos == '$') { *pszTokenPos++ = *m_pszCurPos++; while( *m_pszCurPos) { if( (*m_pszCurPos >= 'A' && *m_pszCurPos <= 'Z') || (*m_pszCurPos >= 'a' && *m_pszCurPos <= 'z') || (*m_pszCurPos >= '0' && *m_pszCurPos <= '9') || (*m_pszCurPos == '_')) { *pszTokenPos++ = *m_pszCurPos++; } else { break; } } } else if( *m_pszCurPos == '=') { *pszTokenPos++ = *m_pszCurPos++; } else { while( *m_pszCurPos && (*m_pszCurPos != ' ' || bQuoted)) { if( *m_pszCurPos == '\"') { *pszTokenPos++ = *m_pszCurPos++; if( bQuoted) { break; } else { bQuoted = TRUE; } } else { *pszTokenPos++ = *m_pszCurPos++; } } } *pszTokenPos = '\0'; if( m_szToken [0] == 0) { return( NULL); } return( &m_szToken [0]); } /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmDbOpenCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { FLMINT iExitCode = 0; IF_Db * pDb = NULL; FLMUINT uiDbId; RCODE rc = NE_XFLM_OK; char * pszRflDir = NULL; char * pszPassword = NULL; char * pszAllowLimited; FLMBOOL bAllowLimited = FALSE; IF_DbSystem * pDbSystem = NULL; if( iArgC < 2) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if( iArgC >= 3) { pszRflDir = ppszArgV[ 2]; } if (iArgC >=4) { pszPassword = ppszArgV[ 3]; } if (iArgC >=5) { pszAllowLimited = ppszArgV[ 4]; if (f_strnicmp( pszAllowLimited, "TRUE", 4) == 0) { bAllowLimited = TRUE; } } if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem))) { goto Exit; } if( RC_BAD( rc = pDbSystem->dbOpen( ppszArgV[ 1], NULL, pszRflDir, pszPassword, bAllowLimited, &pDb))) { if( rc != NE_FLM_IO_PATH_NOT_FOUND) { goto Exit; } if( RC_BAD( rc = pDbSystem->dbCreate( ppszArgV[ 1], NULL, pszRflDir, NULL, NULL, NULL, &pDb))) { goto Exit; } } if( RC_BAD( rc = pShell->registerDatabase( pDb, &uiDbId))) { goto Exit; } pDb = NULL; pShell->con_printf( "Database #%u opened.\n", (unsigned)uiDbId); Exit: if( pDb) { pDb->Release(); } if( pDbSystem) { pDbSystem->Release(); } if( RC_BAD( rc)) { pShell->con_printf( "Error opening database: %e\n", rc); iExitCode = -1; } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmDbOpenCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbopen", "Open a database"); } else { pShell->con_printf("Usage:\n" " dbopen [ [ []]]\n"); pShell->con_printf(" : TRUE | FALSE \n"); } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmDbOpenCommand::canPerformCommand( char * pszCommand) { return( (f_stricmp( "dbopen", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmDbCloseCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { RCODE rc = NE_XFLM_OK; FLMINT iExitCode = 0; FLMUINT uiDbId; IF_Db * pDb = NULL; IF_DbSystem * pDbSystem = NULL; if( iArgC != 2) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem))) { goto Exit; } if( !f_stricmp( ppszArgV[ 1], "kill")) { pDbSystem->deactivateOpenDb( NULL, NULL); pShell->con_printf( "All handles killed, but not necessarily closed.\n"); } else if( !f_stricmp( ppszArgV[ 1], "all")) { for( uiDbId = 0; uiDbId < MAX_SHELL_OPEN_DB; uiDbId++) { if( RC_BAD( rc = pShell->getDatabase( uiDbId, &pDb))) { goto Exit; } if( pDb) { if( RC_BAD( rc = pShell->deregisterDatabase( uiDbId))) { goto Exit; } pDb->Release(); pShell->con_printf( "Database #%u closed.\n", (unsigned)uiDbId); } } pDbSystem->closeUnusedFiles( 0); } else { uiDbId = f_atol( ppszArgV[ 1]); if( RC_BAD( rc = pShell->getDatabase( uiDbId, &pDb))) { goto Exit; } if( pDb) { if( RC_BAD( rc = pShell->deregisterDatabase( uiDbId))) { goto Exit; } pDb->Release(); pShell->con_printf( "Database #%u closed.\n", (unsigned)uiDbId); } else { pShell->con_printf( "Database #%u already closed.\n", (unsigned)uiDbId); } } Exit: if( pDbSystem) { pDbSystem->Release(); } if( RC_BAD( rc)) { pShell->con_printf( "Error closing database: %e\n", rc); iExitCode = -1; } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmDbCloseCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbclose", "Close a database"); } else { pShell->con_printf("Usage:\n" " dbclose \n"); } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmDbCloseCommand::canPerformCommand( char * pszCommand) { return( (f_stricmp( "dbclose", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmTransCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { FLMINT iExitCode = 0; FLMUINT uiDbId; FLMUINT uiTimeout; eDbTransType eTransType; IF_Db * pDb; RCODE rc = NE_XFLM_OK; if( iArgC < 2) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } // Get the database ID and handle uiDbId = f_atol( ppszArgV[ 1]); if( RC_BAD( pShell->getDatabase( uiDbId, &pDb)) || !pDb) { pShell->con_printf( "Invalid database.\n"); iExitCode = -1; goto Exit; } eTransType = ((F_Db *)pDb)->getTransType(); if( f_stricmp( ppszArgV [0], "trbegin") == 0) { if( iArgC < 3) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if( eTransType != XFLM_NO_TRANS) { pShell->con_printf( "%s transaction is already active on database %u.\n", (char *)(eTransType == XFLM_READ_TRANS ? "A read" : "An update"), (unsigned)uiDbId); iExitCode = -1; goto Exit; } if( !f_stricmp( ppszArgV[ 2], "read")) { if( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) { goto Exit; } } else if( !f_stricmp( ppszArgV[ 2], "update")) { uiTimeout = 10; if( iArgC > 4) { uiTimeout = f_atol( ppszArgV[ 3]); } if( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, uiTimeout))) { goto Exit; } } else { pShell->con_printf( "Invalid parameter: %s\n", ppszArgV[ 3]); iExitCode = -1; goto Exit; } pShell->con_printf( "Transaction on %u started.\n", (unsigned)uiDbId); } else if( f_stricmp( ppszArgV[ 0], "trcommit") == 0) { if( eTransType == XFLM_NO_TRANS) { pShell->con_printf( "There is no active transaction on database %u.\n", (unsigned)uiDbId); iExitCode = -1; goto Exit; } if( RC_BAD( rc = pDb->transCommit())) { goto Exit; } pShell->con_printf( "Transaction committed on database %u.\n", (unsigned)uiDbId); } else if( f_stricmp( ppszArgV[ 0], "trabort") == 0) { if( eTransType == XFLM_NO_TRANS) { pShell->con_printf( "There is no active transaction on database %u.\n", (unsigned)uiDbId); iExitCode = -1; goto Exit; } if( RC_BAD( rc = pDb->transAbort())) { goto Exit; } pShell->con_printf( "Transaction aborted on database %u.\n", (unsigned)uiDbId); } else { // should never be able to get here! flmAssert( 0); iExitCode = -1; goto Exit; } Exit: if( RC_BAD( rc)) { pShell->con_printf( "\nError: %e\n", rc); if( !iExitCode) { iExitCode = rc; } } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmTransCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "trbegin", "Begin a transaction"); pShell->displayCommand( "trcommit", "Commit a transaction"); pShell->displayCommand( "trabort", "Abort a transaction"); } else { pShell->con_printf("Usage:\n"); if (f_stricmp( pszCommand, "trbegin") == 0) { pShell->con_printf( " trbegin db# [read | update ]\n"); } else { pShell->con_printf( " %s db#\n", pszCommand); } } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmTransCommand::canPerformCommand( char * pszCommand) { return( (f_stricmp( "trbegin", pszCommand) == 0 || f_stricmp( "trcommit", pszCommand) == 0 || f_stricmp( "trabort", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: Status class for reporting progress of database copy *****************************************************************************/ class FSHELL_CopyStatus : public IF_DbCopyStatus { public: FSHELL_CopyStatus( FlmShell * pShell) { m_pShell = pShell; m_pWin = m_pShell->getWindow(); } virtual ~FSHELL_CopyStatus() { } RCODE FLMAPI dbCopyStatus( FLMUINT64 ui64BytesToCopy, FLMUINT64 ui64BytesCopied, FLMBOOL bNewSrcFile, const char * pszSrcFileName, const char * pszDestFileName) { RCODE rc = NE_XFLM_OK; if (bNewSrcFile) { FTXWinPrintf( m_pWin, "\nCopying %s to %s ...\n", pszSrcFileName, pszDestFileName); } if( m_pShell->getShutdownFlag()) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } FTXWinPrintf( m_pWin, " %,I64u of %,I64u bytes copied\r", ui64BytesCopied, ui64BytesToCopy); f_yieldCPU(); if( RC_OK( FTXWinTestKB( m_pWin))) { FLMUINT uiChar; FTXWinInputChar( m_pWin, &uiChar); if (uiChar == FKB_ESC) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } Exit: return( rc); } private: FlmShell * m_pShell; FTX_WINDOW * m_pWin; }; /**************************************************************************** Desc: Status class for reporting progress of database rename *****************************************************************************/ class FSHELL_RenameStatus : public IF_DbRenameStatus { public: FSHELL_RenameStatus( FlmShell * pShell) { m_pShell = pShell; } virtual ~FSHELL_RenameStatus() { } FINLINE RCODE FLMAPI dbRenameStatus( const char * pszSrcFileName, const char * pszDstFileName) { m_pShell->con_printf( "Renaming %s to %s ...\n", pszSrcFileName, pszDstFileName); return( NE_XFLM_OK); } private: FlmShell * m_pShell; }; /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmDbManageCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { RCODE rc = NE_XFLM_OK; FLMINT iExitCode = 0; IF_DbSystem * pDbSystem = NULL; if( iArgC < 2) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem))) { goto Exit; } if (f_stricmp( ppszArgV [0], "dbremove") == 0) { if (RC_BAD( rc = pDbSystem->dbRemove( ppszArgV[ 1], NULL, NULL, TRUE))) { goto Exit; } } else { if( iArgC < 3) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if (f_stricmp( ppszArgV [0], "dbcopy") == 0) { FSHELL_CopyStatus copyStatus( pShell); if (RC_BAD( rc = pDbSystem->dbCopy( ppszArgV [1], NULL, NULL, ppszArgV [2], NULL, NULL, ©Status))) { goto Exit; } pShell->con_printf( "\n\n"); } else { FSHELL_RenameStatus renameStatus( pShell); if (RC_BAD( rc = pDbSystem->dbRename( ppszArgV [1], NULL, NULL, ppszArgV [2], TRUE, &renameStatus))) { goto Exit; } pShell->con_printf( "\n\n"); } } Exit: if( pDbSystem) { pDbSystem->Release(); } if( RC_BAD( rc)) { pShell->con_printf( "\nError: %e\n", rc); if( !iExitCode) { iExitCode = rc; } } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmDbManageCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbcopy", "Copy a database"); pShell->displayCommand( "dbrename", "Rename a database"); pShell->displayCommand( "dbremove", "Delete a database"); } else { pShell->con_printf("Usage:\n"); if (f_stricmp( pszCommand, "dbremove") == 0) { pShell->con_printf( " dbremove \n"); } else { pShell->con_printf( " %s \n", pszCommand); } } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmDbManageCommand::canPerformCommand( char * pszCommand ) { return( (f_stricmp( "dbcopy", pszCommand) == 0 || f_stricmp( "dbrename", pszCommand) == 0 || f_stricmp( "dbremove", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmBackupCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { FLMUINT uiDbId; FLMUINT uiIncSeqNum; IF_Db * pDb; IF_Backup * pBackup = NULL; IF_BackupClient * pBackupClient = NULL; IF_BackupStatus * pBackupStatus = NULL; FLMINT iExitCode = 0; eDbBackupType eBackupType = XFLM_FULL_BACKUP; RCODE rc = NE_XFLM_OK; FLMBOOL bUsePasswd = FALSE; if( iArgC < 3) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if (iArgC > 3) { bUsePasswd = TRUE; } if( iArgC > 4) { if( f_strnicmp( ppszArgV[ 3], "inc", 3) == 0) { eBackupType = XFLM_INCREMENTAL_BACKUP; } } // Get the database ID and handle uiDbId = f_atol( ppszArgV[ 1]); if( RC_BAD( pShell->getDatabase( uiDbId, &pDb)) || !pDb) { pShell->con_printf( "Invalid database.\n"); iExitCode = -1; goto Exit; } if( RC_BAD( rc = pDb->backupBegin( eBackupType, XFLM_READ_TRANS, 0, &pBackup))) { goto Exit; } if( (pBackupClient = f_new F_LocalBackupClient( pShell, ppszArgV[ 2])) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( (pBackupStatus = f_new F_LocalBackupStatus( pShell)) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pBackup->backup( ppszArgV[ 2], bUsePasswd?ppszArgV[3]:NULL, pBackupClient, pBackupStatus, &uiIncSeqNum))) { goto Exit; } if( RC_BAD( rc = pBackup->endBackup())) { goto Exit; } pShell->con_printf( "\nBackup complete.\n"); Exit: if( RC_BAD( rc)) { pShell->con_printf( "\nError: %e\n", rc); if( !iExitCode) { iExitCode = rc; } } if( pBackup) { pBackup->Release(); } if( pBackupClient) { pBackupClient->Release(); } if( pBackupStatus) { pBackupStatus->Release(); } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmBackupCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbbackup", "Backup a database"); } else { pShell->con_printf("Usage:\n"); pShell->con_printf( " %s [ [\"INC\"]]\n", pszCommand); } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmBackupCommand::canPerformCommand( char * pszCommand) { return( (f_stricmp( "dbbackup", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: *****************************************************************************/ RCODE F_LocalBackupStatus::backupStatus( FLMUINT64 ui64BytesToDo, FLMUINT64 ui64BytesDone) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); if( m_pShell->getShutdownFlag()) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } FTXWinPrintf( pWin, "%,I64u / %,I64u bytes backed up\r", ui64BytesDone, ui64BytesToDo); f_yieldCPU(); if( pWin && RC_OK( FTXWinTestKB( pWin))) { FLMUINT uiChar; FTXWinInputChar( pWin, &uiChar); if (uiChar == FKB_ESC) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } Exit: return( rc); } /************************************************************************ If we want status info for backups, we need to implement an IF_BackupClient..... RCODE flmBackupProgFunc( FLMUINT uiStatusType, void * Parm1, void * Parm2, void * UserData) { RCODE rc = NE_XFLM_OK; FLMUINT64 ui64BytesDone; FLMUINT64 ui64BytesToDo; FlmShell * pShell = (FlmShell *)UserData; FTX_WINDOW * pWin = pShell->getWindow(); F_UNREFERENCED_PARM( Parm2); F_UNREFERENCED_PARM( UserData); if( pShell->getShutdownFlag()) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } if( uiStatusType == FLM_DB_BACKUP_STATUS) { pDbBackupInfo = (DB_BACKUP_INFO *)Parm1; FTXWinPrintf( pWin, "%,I64u / %,I64u bytes backed up\r", ui64BytesDone, ui64BytesToDo); } f_yieldCPU(); if( pWin && FTXWinTestKB( pWin) == FTXRC_SUCCESS) { FLMUINT uiChar; FTXWinInputChar( pWin, &uiChar); if (uiChar == FKB_ESC) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } Exit: return( rc); } ***********************************************************************/ /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmRestoreCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { char * pszRflDir = NULL; FLMINT iExitCode = 0; F_LocalRestore * pRestore = NULL; F_LocalRestoreStatus restoreStatus( pShell); RCODE rc = NE_XFLM_OK; FLMBOOL bUsePasswd = FALSE; IF_DbSystem * pDbSystem = NULL; if( iArgC < 3) { pShell->con_printf( "Wrong number of parameters.\n"); iExitCode = -1; goto Exit; } if( iArgC > 3) { bUsePasswd = TRUE; } if( iArgC > 4) { pszRflDir = ppszArgV[ 4]; } if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem))) { goto Exit; } if( (pRestore = f_new F_LocalRestore) == NULL) { rc = RC_SET( NE_XFLM_MEM); goto Exit; } if( RC_BAD( rc = pRestore->setup( ppszArgV[ 1], ppszArgV[ 2], pszRflDir))) { goto Exit; } if( RC_BAD( rc = pDbSystem->dbRestore( ppszArgV[ 1], NULL, NULL, NULL, bUsePasswd?ppszArgV[3]:NULL, pRestore, &restoreStatus))) { goto Exit; } pShell->con_printf( "\nRestore complete.\n"); Exit: if( RC_BAD( rc)) { pShell->con_printf( "\nError: %e\n", rc); if( !iExitCode) { iExitCode = rc; } } if( pRestore) { pRestore->Release(); } if( pDbSystem) { pDbSystem->Release(); } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmRestoreCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbrestore", "Restore a database"); } else { pShell->con_printf("Usage:\n"); pShell->con_printf( " %s [ []]\n", pszCommand); } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmRestoreCommand::canPerformCommand( char * pszCommand) { return( (f_stricmp( "dbrestore", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: The report* functions are all status callbacks that allow XFlaim to pass some information back out *****************************************************************************/ void F_LocalRestoreStatus::updateCountDisplay( void) { FTX_WINDOW * pWin = m_pShell->getWindow(); if (pWin) { FTXWinSetCursorPos( pWin, 0, 2); FTXWinPrintf( pWin, "RFLFile#: %-10u TotalCnt: %-10u RflKBytes: %uK\n" "AddCnt: %-10u DelCnt: %-10u ModCnt: %u\n" "TrCnt: %-10u RsrvCnt: %-10u IxSetCnt: %u", m_uiRflFileNum, m_uiTransCount + m_uiAddCount + m_uiDeleteCount + m_uiModifyCount + m_uiReserveCount + m_uiIndexCount, (unsigned)(m_ui64RflBytesRead / 1024), m_uiAddCount, m_uiDeleteCount, m_uiModifyCount, m_uiTransCount, m_uiReserveCount, m_uiIndexCount); } } // Contains some common code that all of the report* functions call after // their primary processing... RCODE F_LocalRestoreStatus::report_preamble( FTX_WINDOW * pWin) { RCODE rc = NE_XFLM_OK; if( m_pShell->getShutdownFlag()) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } if( m_bFirstStatus) { FTXWinClear( pWin); m_bFirstStatus = FALSE; } Exit: return rc; } // Contains some common code that all of the report* functions call after // their primary processing... RCODE F_LocalRestoreStatus::report_postamble( FTX_WINDOW * pWin) { RCODE rc = NE_XFLM_OK; FTXWinSetCursorPos( pWin, 0, 5); f_yieldCPU(); if( pWin && RC_OK( FTXWinTestKB( pWin))) { FLMUINT uiChar; FTXWinInputChar( pWin, &uiChar); if (uiChar == FKB_ESC) { rc = RC_SET( NE_XFLM_USER_ABORT); goto Exit; } } Exit: return rc; } RCODE F_LocalRestoreStatus::reportProgress( eRestoreAction * peAction, FLMUINT64 ui64BytesToDo, FLMUINT64 ui64BytesDone) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 1); FTXWinPrintf( pWin, "%,I64u / %,I64u bytes restored", ui64BytesDone, ui64BytesToDo); FTXWinClearToEOL( pWin); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } RCODE F_LocalRestoreStatus::reportError( eRestoreAction * peAction, RCODE rcErr) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); FLMUINT uiChar; *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 6); FTXWinClearToEOL( pWin); FTXWinPrintf( pWin, "Error: 0x%04X. Retry (Y/N): ", (unsigned)rcErr); if( RC_BAD( FTXWinInputChar( pWin, &uiChar))) { uiChar = 0; goto Exit; } if( uiChar == 'Y' || uiChar == 'y') { *peAction = XFLM_RESTORE_ACTION_RETRY; } FTXWinClearToEOL( pWin); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } RCODE F_LocalRestoreStatus::reportBeginTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 5); FTXWinPrintf( pWin, "BEGIN_TRANS: ID = 0x%I64X", ui64TransId); FTXWinClearToEOL( pWin); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } RCODE F_LocalRestoreStatus::reportCommitTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 5); FTXWinPrintf( pWin, "COMMIT_TRANS: ID = 0x%I64X", ui64TransId); FTXWinClearToEOL( pWin); m_uiTransCount++; updateCountDisplay(); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } RCODE F_LocalRestoreStatus::reportAbortTrans( eRestoreAction * peAction, FLMUINT64 ui64TransId) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 5); FTXWinPrintf( pWin, "ABORT_TRANS: ID = 0x%I64X", ui64TransId); FTXWinClearToEOL( pWin); m_uiTransCount++; updateCountDisplay(); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } RCODE F_LocalRestoreStatus::reportEnableEncryption( eRestoreAction * peAction, FLMUINT64 ui64TransId) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 5); FTXWinPrintf( pWin, "ENABLE_ENCRYPTION: ID = 0x%I64X", ui64TransId); FTXWinClearToEOL( pWin); m_uiTransCount++; updateCountDisplay(); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } RCODE F_LocalRestoreStatus::reportWrapKey( eRestoreAction * peAction, FLMUINT64 ui64TransId) { RCODE rc = NE_XFLM_OK; FTX_WINDOW * pWin = m_pShell->getWindow(); *peAction = XFLM_RESTORE_ACTION_CONTINUE; if (RC_BAD(rc = report_preamble( pWin))) { goto Exit; } FTXWinSetCursorPos( pWin, 0, 5); FTXWinPrintf( pWin, "WRAP_KEY: ID = 0x%I64X", ui64TransId); FTXWinClearToEOL( pWin); m_uiTransCount++; updateCountDisplay(); if (RC_BAD(rc = report_postamble( pWin))) { goto Exit; } Exit: return rc; } /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmDbConfigCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { FLMUINT uiDbId; IF_Db * pDb; FLMINT iExitCode = 0; RCODE rc = NE_XFLM_OK; if( iArgC < 3) { pShell->con_printf( "Too few parameters.\n"); iExitCode = -1; goto Exit; } // Get the database ID and handle uiDbId = f_atol( ppszArgV[ 1]); if( RC_BAD( pShell->getDatabase( uiDbId, &pDb)) || !pDb) { pShell->con_printf( "Invalid database.\n"); iExitCode = -1; goto Exit; } if( f_stricmp( ppszArgV[ 2], "rflkeepfiles") == 0) { FLMBOOL bEnable; if( iArgC < 4) { pShell->con_printf( "Too few parameters.\n"); iExitCode = -1; goto Exit; } if (f_stricmp( ppszArgV[ 3], "on") == 0) { bEnable = TRUE; } else if (f_stricmp( ppszArgV[ 3], "off") == 0) { bEnable = FALSE; } else { pShell->con_printf( "Invalid value, must be 'on' or 'off'.\n"); iExitCode = -1; goto Exit; } if( RC_BAD( rc = ((F_Db *)pDb)->setRflKeepFilesFlag( bEnable))) { goto Exit; } } else if( f_stricmp( ppszArgV[ 2], "rfldir") == 0) { if( iArgC < 4) { pShell->con_printf( "Too few parameters.\n"); iExitCode = -1; goto Exit; } if( RC_BAD( rc = ((F_Db *)pDb)->setRflDir( ppszArgV[ 3]))) { goto Exit; } } else if( f_stricmp( ppszArgV[ 2], "rflfilelimits") == 0) { FLMUINT uiRflMinSize; FLMUINT uiRflMaxSize; if( iArgC < 5) { pShell->con_printf( "Too few parameters.\n"); iExitCode = -1; goto Exit; } uiRflMinSize = f_atol( ppszArgV[ 3]); uiRflMaxSize = f_atol( ppszArgV[ 4]); if( RC_BAD( rc = ((F_Db *)pDb)->setRflFileSizeLimits( uiRflMinSize, uiRflMaxSize))) { goto Exit; } } else if( f_stricmp( ppszArgV[ 2], "rflrolltonextfile") == 0) { if( RC_BAD( rc = ((F_Db *)pDb)->rflRollToNextFile())) { goto Exit; } } else { pShell->con_printf( "Invalid option.\n"); iExitCode = -1; goto Exit; } Exit: if( RC_BAD( rc)) { pShell->con_printf( "\nError: %e\n", rc); if( !iExitCode) { iExitCode = rc; } } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmDbConfigCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbconfig", "Configure a database"); } else { pShell->con_printf("Usage:\n"); pShell->con_printf( " %s rflkeepfiles \n", pszCommand); pShell->con_printf( " %s rfldir \n", pszCommand); pShell->con_printf( " %s rflfilelimits \n", pszCommand); pShell->con_printf( " %s rolltonextfile\n", pszCommand); } } /**************************************************************************** Desc: *****************************************************************************/ FLMBOOL FlmDbConfigCommand::canPerformCommand( char * pszCommand) { return( (f_stricmp( "dbconfig", pszCommand) == 0) ? TRUE : FALSE); } /**************************************************************************** Desc: *****************************************************************************/ FSTATIC void format64BitNum( FLMUINT64 ui64Num, char * pszBuf, FLMBOOL bOutputHex, FLMBOOL bAddCommas ) { char szTmpBuf [60]; FLMUINT uiDigit; FLMUINT uiChars = 0; FLMUINT uiCharsBetweenCommas; if (bOutputHex) { while (ui64Num) { uiDigit = (FLMUINT)(ui64Num & 0xF); szTmpBuf [uiChars++] = (char)(uiDigit + '0'); ui64Num >>= 4; } } else { uiCharsBetweenCommas = 0; while (ui64Num) { if (bAddCommas && uiCharsBetweenCommas == 3) { szTmpBuf [uiChars++] = ','; uiCharsBetweenCommas = 0; } uiDigit = (FLMUINT)(ui64Num % 10); szTmpBuf [uiChars++] = (char)(uiDigit + '0'); ui64Num /= 10; uiCharsBetweenCommas++; } } // Need to reverse the numbers going back out. while (uiChars) { uiChars--; *pszBuf++ = szTmpBuf [uiChars]; } *pszBuf = 0; } /**************************************************************************** Desc: *****************************************************************************/ FLMINT FlmDbGetConfigCommand::execute( FLMINT iArgC, char ** ppszArgV, FlmShell * pShell) { FLMUINT uiDbId; IF_Db * pIDb; F_Db * pDb; FLMINT iExitCode = 0; FLMUINT64 ui64Arg; FLMUINT uiArg; FLMUINT uiArg2; FLMBOOL bArg; char szTmpPath[ F_PATH_MAX_SIZE]; char ucBuf[ 256]; RCODE rc = NE_XFLM_OK; FLMBOOL bDoAll = FALSE; FLMBOOL bValidOption = FALSE; if( iArgC < 3) { pShell->con_printf( "Too few parameters.\n"); iExitCode = -1; goto Exit; } // Get the database ID and handle uiDbId = f_atol( ppszArgV[ 1]); if( RC_BAD( pShell->getDatabase( uiDbId, &pIDb)) || !pIDb) { pShell->con_printf( "Invalid database.\n"); iExitCode = -1; goto Exit; } pDb = (F_Db *)pIDb; if (f_stricmp( ppszArgV [2], "all") == 0) { bDoAll = TRUE; bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "rfldir") == 0) { pDb->getRflDir( szTmpPath); pShell->con_printf( "RFL directory = %s\n", szTmpPath); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "rflfilenum") == 0) { pDb->getRflFileNum( &uiArg); pShell->con_printf( "Current RFL file # = %u\n", (unsigned)uiArg); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "rflsizelimits") == 0) { pDb->getRflFileSizeLimits( &uiArg, &uiArg2); pShell->con_printf( "RFL file size limits = min:%u, max:%u\n", (unsigned)uiArg, (unsigned)uiArg2); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "diskusage") == 0) { FLMUINT64 ui64DbSize; FLMUINT64 ui64RollbackSize; FLMUINT64 ui64RflSize; char szBuf1 [40]; char szBuf2 [40]; char szBuf3 [40]; if( RC_BAD( rc = pDb->getDiskSpaceUsage( &ui64DbSize, &ui64RollbackSize, &ui64RflSize))) { goto Exit; } format64BitNum( ui64DbSize, szBuf1, FALSE); format64BitNum( ui64RollbackSize, szBuf2, FALSE); format64BitNum( ui64RflSize, szBuf3, FALSE); pShell->con_printf( "Sizes = db:%s, rollback:%s, rfl:%s", szBuf1, szBuf2, szBuf3); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "rflkeepfiles") == 0) { if( RC_BAD( rc = pDb->getRflKeepFlag( &bArg))) { goto Exit; } pShell->con_printf( "Keep RFL files = %s\n", bArg ? "Yes" : "No"); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "lastbackuptransid") == 0) { if( RC_BAD( rc = pDb->getLastBackupTransID( &ui64Arg))) { goto Exit; } //VISIT: Use formatter for 64 bit unsigned pShell->con_printf( "Last backup transaction ID = %u\n", (unsigned)ui64Arg); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "blockschangedsincebackup") == 0) { if( RC_BAD( rc = pDb->getBlocksChangedSinceBackup( &uiArg))) { goto Exit; } pShell->con_printf( "Blocks changed since last backup = %u\n", (unsigned)uiArg); bValidOption = TRUE; } if( bDoAll || f_stricmp( ppszArgV[ 2], "serialnumber") == 0) { pDb->getSerialNumber( ucBuf); pShell->con_printf( "Serial number = " "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", (unsigned)ucBuf[ 0], (unsigned)ucBuf[ 1], (unsigned)ucBuf[ 2], (unsigned)ucBuf[ 3], (unsigned)ucBuf[ 4], (unsigned)ucBuf[ 5], (unsigned)ucBuf[ 6], (unsigned)ucBuf[ 7], (unsigned)ucBuf[ 8], (unsigned)ucBuf[ 9], (unsigned)ucBuf[ 10], (unsigned)ucBuf[ 11], (unsigned)ucBuf[ 12], (unsigned)ucBuf[ 13], (unsigned)ucBuf[ 14], (unsigned)ucBuf[ 15]); bValidOption = TRUE; } if (!bValidOption) { pShell->con_printf( "Invalid option.\n"); iExitCode = -1; goto Exit; } Exit: if( RC_BAD( rc)) { pShell->con_printf( "\nError: %e\n", rc); if( !iExitCode) { iExitCode = rc; } } return( iExitCode); } /**************************************************************************** Desc: *****************************************************************************/ void FlmDbGetConfigCommand::displayHelp( FlmShell * pShell, char * pszCommand) { if (!pszCommand) { pShell->displayCommand( "dbgetconfig", "Display DB configuration"); } else { pShell->con_printf("Usage:\n"); pShell->con_printf( " %s